diff options
Diffstat (limited to 'sound')
226 files changed, 9249 insertions, 4527 deletions
diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c index 7487eb76e034..3edf736319fe 100644 --- a/sound/aoa/soundbus/core.c +++ b/sound/aoa/soundbus/core.c @@ -150,6 +150,8 @@ static int soundbus_device_resume(struct device * dev) #endif /* CONFIG_PM */ +/* soundbus_dev_attrs is declared in sysfs.c */ +ATTRIBUTE_GROUPS(soundbus_dev); static struct bus_type soundbus_bus_type = { .name = "aoa-soundbus", .probe = soundbus_probe, @@ -160,7 +162,7 @@ static struct bus_type soundbus_bus_type = { .suspend = soundbus_device_suspend, .resume = soundbus_device_resume, #endif - .dev_attrs = soundbus_dev_attrs, + .dev_groups = soundbus_dev_groups, }; int soundbus_add_one(struct soundbus_dev *dev) diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h index adecbf36f4f6..21e756cf2824 100644 --- a/sound/aoa/soundbus/soundbus.h +++ b/sound/aoa/soundbus/soundbus.h @@ -199,6 +199,6 @@ struct soundbus_driver { extern int soundbus_register_driver(struct soundbus_driver *drv); extern void soundbus_unregister_driver(struct soundbus_driver *drv); -extern struct device_attribute soundbus_dev_attrs[]; +extern struct attribute *soundbus_dev_attrs[]; #endif /* __SOUNDBUS_H */ diff --git a/sound/aoa/soundbus/sysfs.c b/sound/aoa/soundbus/sysfs.c index e0980b5c2cd8..5b2d51d99768 100644 --- a/sound/aoa/soundbus/sysfs.c +++ b/sound/aoa/soundbus/sysfs.c @@ -30,13 +30,16 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, return length; } +static DEVICE_ATTR_RO(modalias); soundbus_config_of_attr (name, "%s\n"); +static DEVICE_ATTR_RO(name); soundbus_config_of_attr (type, "%s\n"); +static DEVICE_ATTR_RO(type); -struct device_attribute soundbus_dev_attrs[] = { - __ATTR_RO(name), - __ATTR_RO(type), - __ATTR_RO(modalias), - __ATTR_NULL +struct attribute *soundbus_dev_attrs[] = { + &dev_attr_name.attr, + &dev_attr_type.attr, + &dev_attr_modalias.attr, + NULL, }; diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 313f22e9d929..6c96feeaf01e 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -6,6 +6,12 @@ config SND_PCM tristate select SND_TIMER +config SND_PCM_ELD + bool + +config SND_PCM_IEC958 + bool + config SND_DMAENGINE_PCM tristate @@ -176,9 +182,18 @@ config SND_SUPPORT_OLD_API Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3 or older). +config SND_PROC_FS + bool "Sound Proc FS Support" if EXPERT + depends on PROC_FS + default y + help + Say 'N' to disable Sound proc FS, which may reduce code size about + 9KB on x86_64 platform. + If unsure say Y. + config SND_VERBOSE_PROCFS bool "Verbose procfs contents" - depends on PROC_FS + depends on SND_PROC_FS default y help Say Y here to include code for verbose procfs contents (provides @@ -221,9 +236,6 @@ config SND_PCM_XRUN_DEBUG config SND_VMASTER bool -config SND_KCTL_JACK - bool - config SND_DMA_SGBUF def_bool y depends on X86 diff --git a/sound/core/Makefile b/sound/core/Makefile index 4daf2f58261c..3354f91e003a 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -3,16 +3,21 @@ # Copyright (c) 1999,2001 by Jaroslav Kysela <perex@perex.cz> # -snd-y := sound.o init.o memory.o info.o control.o misc.o device.o +snd-y := sound.o init.o memory.o control.o misc.o device.o +ifneq ($(CONFIG_SND_PROC_FS),) +snd-y += info.o +snd-$(CONFIG_SND_OSSEMUL) += info_oss.o +endif snd-$(CONFIG_ISA_DMA_API) += isadma.o -snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o +snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o snd-$(CONFIG_SND_VMASTER) += vmaster.o -snd-$(CONFIG_SND_KCTL_JACK) += ctljack.o -snd-$(CONFIG_SND_JACK) += jack.o +snd-$(CONFIG_SND_JACK) += ctljack.o jack.o snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o memalloc.o snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o +snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o +snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o # for trace-points CFLAGS_pcm_lib.o := -I$(src) diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c index e4b38fbe51da..84a3cd683068 100644 --- a/sound/core/ctljack.c +++ b/sound/core/ctljack.c @@ -31,19 +31,52 @@ static struct snd_kcontrol_new jack_detect_kctl = { .get = jack_detect_kctl_get, }; +static int get_available_index(struct snd_card *card, const char *name) +{ + struct snd_ctl_elem_id sid; + + memset(&sid, 0, sizeof(sid)); + + sid.index = 0; + sid.iface = SNDRV_CTL_ELEM_IFACE_CARD; + strlcpy(sid.name, name, sizeof(sid.name)); + + while (snd_ctl_find_id(card, &sid)) { + sid.index++; + /* reset numid; otherwise snd_ctl_find_id() hits this again */ + sid.numid = 0; + } + + return sid.index; +} + +static void jack_kctl_name_gen(char *name, const char *src_name, int size) +{ + size_t count = strlen(src_name); + bool need_cat = true; + + /* remove redundant " Jack" from src_name */ + if (count >= 5) + need_cat = strncmp(&src_name[count - 5], " Jack", 5) ? true : false; + + snprintf(name, size, need_cat ? "%s Jack" : "%s", src_name); + +} + struct snd_kcontrol * -snd_kctl_jack_new(const char *name, int idx, void *private_data) +snd_kctl_jack_new(const char *name, struct snd_card *card) { struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(&jack_detect_kctl, private_data); + + kctl = snd_ctl_new1(&jack_detect_kctl, NULL); if (!kctl) return NULL; - snprintf(kctl->id.name, sizeof(kctl->id.name), "%s Jack", name); - kctl->id.index = idx; + + jack_kctl_name_gen(kctl->id.name, name, sizeof(kctl->id.name)); + kctl->id.index = get_available_index(card, kctl->id.name); kctl->private_value = 0; return kctl; } -EXPORT_SYMBOL_GPL(snd_kctl_jack_new); void snd_kctl_jack_report(struct snd_card *card, struct snd_kcontrol *kctl, bool status) @@ -53,4 +86,3 @@ void snd_kctl_jack_report(struct snd_card *card, kctl->private_value = status; snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); } -EXPORT_SYMBOL_GPL(snd_kctl_jack_report); diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index 886be7da989d..f845ecf7e172 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -121,16 +121,9 @@ static struct snd_timer *mytimer; static int __init snd_hrtimer_init(void) { struct snd_timer *timer; - struct timespec tp; int err; - hrtimer_get_res(CLOCK_MONOTONIC, &tp); - if (tp.tv_sec > 0 || !tp.tv_nsec) { - pr_err("snd-hrtimer: Invalid resolution %u.%09u", - (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec); - return -EINVAL; - } - resolution = tp.tv_nsec; + resolution = hrtimer_resolution; /* Create a new timer and set up the fields */ err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER, diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 51692c8a39ea..36d2416f90d9 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -484,7 +484,7 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device) return 0; } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* * Info interface */ @@ -521,10 +521,10 @@ static void __exit snd_hwdep_proc_done(void) { snd_info_free_entry(snd_hwdep_proc_entry); } -#else /* !CONFIG_PROC_FS */ +#else /* !CONFIG_SND_PROC_FS */ #define snd_hwdep_proc_init() #define snd_hwdep_proc_done() -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ /* diff --git a/sound/core/info.c b/sound/core/info.c index 9f404e965ea2..895362a696c9 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -33,12 +33,6 @@ #include <linux/mutex.h> #include <stdarg.h> -/* - * - */ - -#ifdef CONFIG_PROC_FS - int snd_info_check_reserved_words(const char *str) { static char *reserved[] = @@ -78,81 +72,51 @@ struct snd_info_private_data { }; static int snd_info_version_init(void); -static int snd_info_version_done(void); static void snd_info_disconnect(struct snd_info_entry *entry); +/* -/* resize the proc r/w buffer */ -static int resize_info_buffer(struct snd_info_buffer *buffer, - unsigned int nsize) -{ - char *nbuf; + */ - nsize = PAGE_ALIGN(nsize); - nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL | __GFP_ZERO); - if (! nbuf) - return -ENOMEM; +static struct snd_info_entry *snd_proc_root; +struct snd_info_entry *snd_seq_root; +EXPORT_SYMBOL(snd_seq_root); - buffer->buffer = nbuf; - buffer->len = nsize; - return 0; -} +#ifdef CONFIG_SND_OSSEMUL +struct snd_info_entry *snd_oss_root; +#endif -/** - * snd_iprintf - printf on the procfs buffer - * @buffer: the procfs buffer - * @fmt: the printf format - * - * Outputs the string on the procfs buffer just like printf(). - * - * Return: The size of output string, or a negative error code. - */ -int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) +static int alloc_info_private(struct snd_info_entry *entry, + struct snd_info_private_data **ret) { - va_list args; - int len, res; - int err = 0; + struct snd_info_private_data *data; - might_sleep(); - if (buffer->stop || buffer->error) - return 0; - len = buffer->len - buffer->size; - va_start(args, fmt); - for (;;) { - va_list ap; - va_copy(ap, args); - res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap); - va_end(ap); - if (res < len) - break; - err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE); - if (err < 0) - break; - len = buffer->len - buffer->size; + if (!entry || !entry->p) + return -ENODEV; + if (!try_module_get(entry->module)) + return -EFAULT; + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + module_put(entry->module); + return -ENOMEM; } - va_end(args); - - if (err < 0) - return err; - buffer->curr += res; - buffer->size += res; - return res; + data->entry = entry; + *ret = data; + return 0; } -EXPORT_SYMBOL(snd_iprintf); +static bool valid_pos(loff_t pos, size_t count) +{ + if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) + return false; + if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) + return false; + return true; +} /* - + * file ops for binary proc files */ - -static struct proc_dir_entry *snd_proc_root; -struct snd_info_entry *snd_seq_root; -EXPORT_SYMBOL(snd_seq_root); - -#ifdef CONFIG_SND_OSSEMUL -struct snd_info_entry *snd_oss_root; -#endif - static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) { struct snd_info_private_data *data; @@ -162,17 +126,14 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) data = file->private_data; entry = data->entry; mutex_lock(&entry->access); - if (entry->content == SNDRV_INFO_CONTENT_DATA && - entry->c.ops->llseek) { + if (entry->c.ops->llseek) { offset = entry->c.ops->llseek(entry, data->file_private_data, file, offset, orig); goto out; } - if (entry->content == SNDRV_INFO_CONTENT_DATA) - size = entry->size; - else - size = 0; + + size = entry->size; switch (orig) { case SEEK_SET: break; @@ -201,45 +162,20 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, size_t count, loff_t * offset) { - struct snd_info_private_data *data; - struct snd_info_entry *entry; - struct snd_info_buffer *buf; - size_t size = 0; + struct snd_info_private_data *data = file->private_data; + struct snd_info_entry *entry = data->entry; + size_t size; loff_t pos; - data = file->private_data; - if (snd_BUG_ON(!data)) - return -ENXIO; pos = *offset; - if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) - return -EIO; - if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) + if (!valid_pos(pos, count)) return -EIO; - entry = data->entry; - switch (entry->content) { - case SNDRV_INFO_CONTENT_TEXT: - buf = data->rbuffer; - if (buf == NULL) - return -EIO; - if (pos >= buf->size) - return 0; - size = buf->size - pos; - size = min(count, size); - if (copy_to_user(buffer, buf->buffer + pos, size)) - return -EFAULT; - break; - case SNDRV_INFO_CONTENT_DATA: - if (pos >= entry->size) - return 0; - if (entry->c.ops->read) { - size = entry->size - pos; - size = min(count, size); - size = entry->c.ops->read(entry, - data->file_private_data, - file, buffer, size, pos); - } - break; - } + if (pos >= entry->size) + return 0; + size = entry->size - pos; + size = min(count, size); + size = entry->c.ops->read(entry, data->file_private_data, + file, buffer, size, pos); if ((ssize_t) size > 0) *offset = pos + size; return size; @@ -248,347 +184,319 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, size_t count, loff_t * offset) { - struct snd_info_private_data *data; - struct snd_info_entry *entry; - struct snd_info_buffer *buf; + struct snd_info_private_data *data = file->private_data; + struct snd_info_entry *entry = data->entry; ssize_t size = 0; loff_t pos; - data = file->private_data; - if (snd_BUG_ON(!data)) - return -ENXIO; - entry = data->entry; pos = *offset; - if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) - return -EIO; - if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) + if (!valid_pos(pos, count)) return -EIO; - switch (entry->content) { - case SNDRV_INFO_CONTENT_TEXT: - buf = data->wbuffer; - if (buf == NULL) - return -EIO; - mutex_lock(&entry->access); - if (pos + count >= buf->len) { - if (resize_info_buffer(buf, pos + count)) { - mutex_unlock(&entry->access); - return -ENOMEM; - } - } - if (copy_from_user(buf->buffer + pos, buffer, count)) { - mutex_unlock(&entry->access); - return -EFAULT; - } - buf->size = pos + count; - mutex_unlock(&entry->access); - size = count; - break; - case SNDRV_INFO_CONTENT_DATA: - if (entry->c.ops->write && count > 0) { - size_t maxsize = entry->size - pos; - count = min(count, maxsize); - size = entry->c.ops->write(entry, - data->file_private_data, - file, buffer, count, pos); - } - break; + if (count > 0) { + size_t maxsize = entry->size - pos; + count = min(count, maxsize); + size = entry->c.ops->write(entry, data->file_private_data, + file, buffer, count, pos); } - if ((ssize_t) size > 0) + if (size > 0) *offset = pos + size; return size; } -static int snd_info_entry_open(struct inode *inode, struct file *file) +static unsigned int snd_info_entry_poll(struct file *file, poll_table *wait) { + struct snd_info_private_data *data = file->private_data; + struct snd_info_entry *entry = data->entry; + unsigned int mask = 0; + + if (entry->c.ops->poll) + return entry->c.ops->poll(entry, + data->file_private_data, + file, wait); + if (entry->c.ops->read) + mask |= POLLIN | POLLRDNORM; + if (entry->c.ops->write) + mask |= POLLOUT | POLLWRNORM; + return mask; +} + +static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct snd_info_private_data *data = file->private_data; + struct snd_info_entry *entry = data->entry; + + if (!entry->c.ops->ioctl) + return -ENOTTY; + return entry->c.ops->ioctl(entry, data->file_private_data, + file, cmd, arg); +} + +static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode = file_inode(file); + struct snd_info_private_data *data; struct snd_info_entry *entry; + + data = file->private_data; + if (data == NULL) + return 0; + entry = data->entry; + if (!entry->c.ops->mmap) + return -ENXIO; + return entry->c.ops->mmap(entry, data->file_private_data, + inode, file, vma); +} + +static int snd_info_entry_open(struct inode *inode, struct file *file) +{ + struct snd_info_entry *entry = PDE_DATA(inode); struct snd_info_private_data *data; - struct snd_info_buffer *buffer; int mode, err; mutex_lock(&info_mutex); - entry = PDE_DATA(inode); - if (entry == NULL || ! entry->p) { - mutex_unlock(&info_mutex); - return -ENODEV; - } - if (!try_module_get(entry->module)) { - err = -EFAULT; - goto __error1; - } + err = alloc_info_private(entry, &data); + if (err < 0) + goto unlock; + mode = file->f_flags & O_ACCMODE; - if (mode == O_RDONLY || mode == O_RDWR) { - if ((entry->content == SNDRV_INFO_CONTENT_DATA && - entry->c.ops->read == NULL)) { - err = -ENODEV; - goto __error; - } + if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) || + ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) { + err = -ENODEV; + goto error; } - if (mode == O_WRONLY || mode == O_RDWR) { - if ((entry->content == SNDRV_INFO_CONTENT_DATA && - entry->c.ops->write == NULL)) { - err = -ENODEV; - goto __error; - } - } - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (data == NULL) { - err = -ENOMEM; - goto __error; - } - data->entry = entry; - switch (entry->content) { - case SNDRV_INFO_CONTENT_TEXT: - if (mode == O_RDONLY || mode == O_RDWR) { - buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); - if (buffer == NULL) - goto __nomem; - data->rbuffer = buffer; - buffer->len = PAGE_SIZE; - buffer->buffer = kzalloc(buffer->len, GFP_KERNEL); - if (buffer->buffer == NULL) - goto __nomem; - } - if (mode == O_WRONLY || mode == O_RDWR) { - buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); - if (buffer == NULL) - goto __nomem; - data->wbuffer = buffer; - buffer->len = PAGE_SIZE; - buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); - if (buffer->buffer == NULL) - goto __nomem; - } - break; - case SNDRV_INFO_CONTENT_DATA: /* data */ - if (entry->c.ops->open) { - if ((err = entry->c.ops->open(entry, mode, - &data->file_private_data)) < 0) { - kfree(data); - goto __error; - } - } - break; + + if (entry->c.ops->open) { + err = entry->c.ops->open(entry, mode, &data->file_private_data); + if (err < 0) + goto error; } + file->private_data = data; mutex_unlock(&info_mutex); - if (entry->content == SNDRV_INFO_CONTENT_TEXT && - (mode == O_RDONLY || mode == O_RDWR)) { - if (entry->c.text.read) { - mutex_lock(&entry->access); - entry->c.text.read(entry, data->rbuffer); - mutex_unlock(&entry->access); - } - } return 0; - __nomem: - if (data->rbuffer) { - kfree(data->rbuffer->buffer); - kfree(data->rbuffer); - } - if (data->wbuffer) { - kfree(data->wbuffer->buffer); - kfree(data->wbuffer); - } + error: kfree(data); - err = -ENOMEM; - __error: module_put(entry->module); - __error1: + unlock: mutex_unlock(&info_mutex); return err; } static int snd_info_entry_release(struct inode *inode, struct file *file) { - struct snd_info_entry *entry; - struct snd_info_private_data *data; - int mode; + struct snd_info_private_data *data = file->private_data; + struct snd_info_entry *entry = data->entry; - mode = file->f_flags & O_ACCMODE; - data = file->private_data; - entry = data->entry; - switch (entry->content) { - case SNDRV_INFO_CONTENT_TEXT: - if (data->rbuffer) { - kfree(data->rbuffer->buffer); - kfree(data->rbuffer); - } - if (data->wbuffer) { - if (entry->c.text.write) { - entry->c.text.write(entry, data->wbuffer); - if (data->wbuffer->error) { - if (entry->card) - dev_warn(entry->card->dev, "info: data write error to %s (%i)\n", - entry->name, - data->wbuffer->error); - else - pr_warn("ALSA: info: data write error to %s (%i)\n", - entry->name, - data->wbuffer->error); - } - } - kfree(data->wbuffer->buffer); - kfree(data->wbuffer); - } - break; - case SNDRV_INFO_CONTENT_DATA: - if (entry->c.ops->release) - entry->c.ops->release(entry, mode, - data->file_private_data); - break; - } + if (entry->c.ops->release) + entry->c.ops->release(entry, file->f_flags & O_ACCMODE, + data->file_private_data); module_put(entry->module); kfree(data); return 0; } -static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait) +static const struct file_operations snd_info_entry_operations = { - struct snd_info_private_data *data; - struct snd_info_entry *entry; - unsigned int mask; + .owner = THIS_MODULE, + .llseek = snd_info_entry_llseek, + .read = snd_info_entry_read, + .write = snd_info_entry_write, + .poll = snd_info_entry_poll, + .unlocked_ioctl = snd_info_entry_ioctl, + .mmap = snd_info_entry_mmap, + .open = snd_info_entry_open, + .release = snd_info_entry_release, +}; - data = file->private_data; - if (data == NULL) - return 0; - entry = data->entry; - mask = 0; - switch (entry->content) { - case SNDRV_INFO_CONTENT_DATA: - if (entry->c.ops->poll) - return entry->c.ops->poll(entry, - data->file_private_data, - file, wait); - if (entry->c.ops->read) - mask |= POLLIN | POLLRDNORM; - if (entry->c.ops->write) - mask |= POLLOUT | POLLWRNORM; - break; +/* + * file ops for text proc files + */ +static ssize_t snd_info_text_entry_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *offset) +{ + struct seq_file *m = file->private_data; + struct snd_info_private_data *data = m->private; + struct snd_info_entry *entry = data->entry; + struct snd_info_buffer *buf; + loff_t pos; + size_t next; + int err = 0; + + pos = *offset; + if (!valid_pos(pos, count)) + return -EIO; + next = pos + count; + mutex_lock(&entry->access); + buf = data->wbuffer; + if (!buf) { + data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto error; + } } - return mask; + if (next > buf->len) { + char *nbuf = krealloc(buf->buffer, PAGE_ALIGN(next), + GFP_KERNEL | __GFP_ZERO); + if (!nbuf) { + err = -ENOMEM; + goto error; + } + buf->buffer = nbuf; + buf->len = PAGE_ALIGN(next); + } + if (copy_from_user(buf->buffer + pos, buffer, count)) { + err = -EFAULT; + goto error; + } + buf->size = next; + error: + mutex_unlock(&entry->access); + if (err < 0) + return err; + *offset = next; + return count; } -static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static int snd_info_seq_show(struct seq_file *seq, void *p) { - struct snd_info_private_data *data; - struct snd_info_entry *entry; + struct snd_info_private_data *data = seq->private; + struct snd_info_entry *entry = data->entry; - data = file->private_data; - if (data == NULL) - return 0; - entry = data->entry; - switch (entry->content) { - case SNDRV_INFO_CONTENT_DATA: - if (entry->c.ops->ioctl) - return entry->c.ops->ioctl(entry, - data->file_private_data, - file, cmd, arg); - break; + if (entry->c.text.read) { + data->rbuffer->buffer = (char *)seq; /* XXX hack! */ + entry->c.text.read(entry, data->rbuffer); } - return -ENOTTY; + return 0; } -static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) +static int snd_info_text_entry_open(struct inode *inode, struct file *file) { - struct inode *inode = file_inode(file); + struct snd_info_entry *entry = PDE_DATA(inode); struct snd_info_private_data *data; - struct snd_info_entry *entry; + int err; - data = file->private_data; - if (data == NULL) - return 0; - entry = data->entry; - switch (entry->content) { - case SNDRV_INFO_CONTENT_DATA: - if (entry->c.ops->mmap) - return entry->c.ops->mmap(entry, - data->file_private_data, - inode, file, vma); - break; + mutex_lock(&info_mutex); + err = alloc_info_private(entry, &data); + if (err < 0) + goto unlock; + + data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL); + if (!data->rbuffer) { + err = -ENOMEM; + goto error; } - return -ENXIO; + if (entry->size) + err = single_open_size(file, snd_info_seq_show, data, + entry->size); + else + err = single_open(file, snd_info_seq_show, data); + if (err < 0) + goto error; + mutex_unlock(&info_mutex); + return 0; + + error: + kfree(data->rbuffer); + kfree(data); + module_put(entry->module); + unlock: + mutex_unlock(&info_mutex); + return err; } -static const struct file_operations snd_info_entry_operations = +static int snd_info_text_entry_release(struct inode *inode, struct file *file) +{ + struct seq_file *m = file->private_data; + struct snd_info_private_data *data = m->private; + struct snd_info_entry *entry = data->entry; + + if (data->wbuffer && entry->c.text.write) + entry->c.text.write(entry, data->wbuffer); + + single_release(inode, file); + kfree(data->rbuffer); + if (data->wbuffer) { + kfree(data->wbuffer->buffer); + kfree(data->wbuffer); + } + + module_put(entry->module); + kfree(data); + return 0; +} + +static const struct file_operations snd_info_text_entry_ops = { .owner = THIS_MODULE, - .llseek = snd_info_entry_llseek, - .read = snd_info_entry_read, - .write = snd_info_entry_write, - .poll = snd_info_entry_poll, - .unlocked_ioctl = snd_info_entry_ioctl, - .mmap = snd_info_entry_mmap, - .open = snd_info_entry_open, - .release = snd_info_entry_release, + .open = snd_info_text_entry_open, + .release = snd_info_text_entry_release, + .write = snd_info_text_entry_write, + .llseek = seq_lseek, + .read = seq_read, }; -int __init snd_info_init(void) +static struct snd_info_entry *create_subdir(struct module *mod, + const char *name) { - struct proc_dir_entry *p; + struct snd_info_entry *entry; - p = proc_mkdir("asound", NULL); - if (p == NULL) + entry = snd_info_create_module_entry(mod, name, NULL); + if (!entry) + return NULL; + entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + return NULL; + } + return entry; +} + +static struct snd_info_entry * +snd_info_create_entry(const char *name, struct snd_info_entry *parent); + +int __init snd_info_init(void) +{ + snd_proc_root = snd_info_create_entry("asound", NULL); + if (!snd_proc_root) return -ENOMEM; - snd_proc_root = p; + snd_proc_root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + snd_proc_root->p = proc_mkdir("asound", NULL); + if (!snd_proc_root->p) + goto error; #ifdef CONFIG_SND_OSSEMUL - { - struct snd_info_entry *entry; - if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL) - return -ENOMEM; - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - return -ENOMEM; - } - snd_oss_root = entry; - } + snd_oss_root = create_subdir(THIS_MODULE, "oss"); + if (!snd_oss_root) + goto error; #endif #if IS_ENABLED(CONFIG_SND_SEQUENCER) - { - struct snd_info_entry *entry; - if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL) - return -ENOMEM; - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - return -ENOMEM; - } - snd_seq_root = entry; - } + snd_seq_root = create_subdir(THIS_MODULE, "seq"); + if (!snd_seq_root) + goto error; #endif - snd_info_version_init(); - snd_minor_info_init(); - snd_minor_info_oss_init(); - snd_card_info_init(); + if (snd_info_version_init() < 0 || + snd_minor_info_init() < 0 || + snd_minor_info_oss_init() < 0 || + snd_card_info_init() < 0 || + snd_info_minor_register() < 0) + goto error; return 0; + + error: + snd_info_free_entry(snd_proc_root); + return -ENOMEM; } int __exit snd_info_done(void) { - snd_card_info_done(); - snd_minor_info_oss_done(); - snd_minor_info_done(); - snd_info_version_done(); - if (snd_proc_root) { -#if IS_ENABLED(CONFIG_SND_SEQUENCER) - snd_info_free_entry(snd_seq_root); -#endif -#ifdef CONFIG_SND_OSSEMUL - snd_info_free_entry(snd_oss_root); -#endif - proc_remove(snd_proc_root); - } + snd_info_free_entry(snd_proc_root); return 0; } /* - - */ - - -/* * create a card proc file * called from init.c */ @@ -601,33 +509,58 @@ int snd_info_card_create(struct snd_card *card) return -ENXIO; sprintf(str, "card%i", card->number); - if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL) - return -ENOMEM; - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); + entry = create_subdir(card->module, str); + if (!entry) return -ENOMEM; - } card->proc_root = entry; return 0; } +/* register all pending info entries */ +static int snd_info_register_recursive(struct snd_info_entry *entry) +{ + struct snd_info_entry *p; + int err; + + if (!entry->p) { + err = snd_info_register(entry); + if (err < 0) + return err; + } + + list_for_each_entry(p, &entry->children, list) { + err = snd_info_register_recursive(p); + if (err < 0) + return err; + } + + return 0; +} + /* * register the card proc file * called from init.c + * can be called multiple times for reinitialization */ int snd_info_card_register(struct snd_card *card) { struct proc_dir_entry *p; + int err; if (snd_BUG_ON(!card)) return -ENXIO; + err = snd_info_register_recursive(card->proc_root); + if (err < 0) + return err; + if (!strcmp(card->id, card->proc_root->name)) return 0; - p = proc_symlink(card->id, snd_proc_root, card->proc_root->name); - if (p == NULL) + if (card->proc_root_link) + return 0; + p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name); + if (!p) return -ENOMEM; card->proc_root_link = p; return 0; @@ -645,7 +578,7 @@ void snd_info_card_id_change(struct snd_card *card) } if (strcmp(card->id, card->proc_root->name)) card->proc_root_link = proc_symlink(card->id, - snd_proc_root, + snd_proc_root->p, card->proc_root->name); mutex_unlock(&info_mutex); } @@ -753,9 +686,10 @@ const char *snd_info_get_str(char *dest, const char *src, int len) EXPORT_SYMBOL(snd_info_get_str); -/** +/* * snd_info_create_entry - create an info entry * @name: the proc file name + * @parent: the parent directory * * Creates an info entry with the given file name and initializes as * the default state. @@ -765,7 +699,8 @@ EXPORT_SYMBOL(snd_info_get_str); * * Return: The pointer of the new instance, or %NULL on failure. */ -static struct snd_info_entry *snd_info_create_entry(const char *name) +static struct snd_info_entry * +snd_info_create_entry(const char *name, struct snd_info_entry *parent) { struct snd_info_entry *entry; entry = kzalloc(sizeof(*entry), GFP_KERNEL); @@ -781,6 +716,9 @@ static struct snd_info_entry *snd_info_create_entry(const char *name) mutex_init(&entry->access); INIT_LIST_HEAD(&entry->children); INIT_LIST_HEAD(&entry->list); + entry->parent = parent; + if (parent) + list_add_tail(&entry->list, &parent->children); return entry; } @@ -798,11 +736,9 @@ struct snd_info_entry *snd_info_create_module_entry(struct module * module, const char *name, struct snd_info_entry *parent) { - struct snd_info_entry *entry = snd_info_create_entry(name); - if (entry) { + struct snd_info_entry *entry = snd_info_create_entry(name, parent); + if (entry) entry->module = module; - entry->parent = parent; - } return entry; } @@ -822,11 +758,10 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry * parent) { - struct snd_info_entry *entry = snd_info_create_entry(name); + struct snd_info_entry *entry = snd_info_create_entry(name, parent); if (entry) { entry->module = card->module; entry->card = card; - entry->parent = parent; } return entry; } @@ -835,95 +770,39 @@ EXPORT_SYMBOL(snd_info_create_card_entry); static void snd_info_disconnect(struct snd_info_entry *entry) { - struct list_head *p, *n; - struct proc_dir_entry *root; - - list_for_each_safe(p, n, &entry->children) { - snd_info_disconnect(list_entry(p, struct snd_info_entry, list)); - } + struct snd_info_entry *p; - if (! entry->p) + if (!entry->p) return; - list_del_init(&entry->list); - root = entry->parent == NULL ? snd_proc_root : entry->parent->p; - snd_BUG_ON(!root); + list_for_each_entry(p, &entry->children, list) + snd_info_disconnect(p); proc_remove(entry->p); entry->p = NULL; } -static int snd_info_dev_free_entry(struct snd_device *device) -{ - struct snd_info_entry *entry = device->device_data; - snd_info_free_entry(entry); - return 0; -} - -static int snd_info_dev_register_entry(struct snd_device *device) -{ - struct snd_info_entry *entry = device->device_data; - return snd_info_register(entry); -} - -/** - * snd_card_proc_new - create an info entry for the given card - * @card: the card instance - * @name: the file name - * @entryp: the pointer to store the new info entry - * - * Creates a new info entry and assigns it to the given card. - * Unlike snd_info_create_card_entry(), this function registers the - * info entry as an ALSA device component, so that it can be - * unregistered/released without explicit call. - * Also, you don't have to register this entry via snd_info_register(), - * since this will be registered by snd_card_register() automatically. - * - * The parent is assumed as card->proc_root. - * - * For releasing this entry, use snd_device_free() instead of - * snd_info_free_entry(). - * - * Return: Zero if successful, or a negative error code on failure. - */ -int snd_card_proc_new(struct snd_card *card, const char *name, - struct snd_info_entry **entryp) -{ - static struct snd_device_ops ops = { - .dev_free = snd_info_dev_free_entry, - .dev_register = snd_info_dev_register_entry, - /* disconnect is done via snd_info_card_disconnect() */ - }; - struct snd_info_entry *entry; - int err; - - entry = snd_info_create_card_entry(card, name, card->proc_root); - if (! entry) - return -ENOMEM; - if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) { - snd_info_free_entry(entry); - return err; - } - if (entryp) - *entryp = entry; - return 0; -} - -EXPORT_SYMBOL(snd_card_proc_new); - /** * snd_info_free_entry - release the info entry * @entry: the info entry * - * Releases the info entry. Don't call this after registered. + * Releases the info entry. */ void snd_info_free_entry(struct snd_info_entry * entry) { - if (entry == NULL) + struct snd_info_entry *p, *n; + + if (!entry) return; if (entry->p) { mutex_lock(&info_mutex); snd_info_disconnect(entry); mutex_unlock(&info_mutex); } + + /* free all children at first */ + list_for_each_entry_safe(p, n, &entry->children, list) + snd_info_free_entry(p); + + list_del(&entry->list); kfree(entry->name); if (entry->private_free) entry->private_free(entry); @@ -946,7 +825,7 @@ int snd_info_register(struct snd_info_entry * entry) if (snd_BUG_ON(!entry)) return -ENXIO; - root = entry->parent == NULL ? snd_proc_root : entry->parent->p; + root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p; mutex_lock(&info_mutex); if (S_ISDIR(entry->mode)) { p = proc_mkdir_mode(entry->name, entry->mode, root); @@ -955,8 +834,13 @@ int snd_info_register(struct snd_info_entry * entry) return -ENOMEM; } } else { + const struct file_operations *ops; + if (entry->content == SNDRV_INFO_CONTENT_DATA) + ops = &snd_info_entry_operations; + else + ops = &snd_info_text_entry_ops; p = proc_create_data(entry->name, entry->mode, root, - &snd_info_entry_operations, entry); + ops, entry); if (!p) { mutex_unlock(&info_mutex); return -ENOMEM; @@ -964,8 +848,6 @@ int snd_info_register(struct snd_info_entry * entry) proc_set_size(p, entry->size); } entry->p = p; - if (entry->parent) - list_add_tail(&entry->list, &entry->parent->children); mutex_unlock(&info_mutex); return 0; } @@ -976,8 +858,6 @@ EXPORT_SYMBOL(snd_info_register); */ -static struct snd_info_entry *snd_info_version_entry; - static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { snd_iprintf(buffer, @@ -993,18 +873,5 @@ static int __init snd_info_version_init(void) if (entry == NULL) return -ENOMEM; entry->c.text.read = snd_info_version_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - return -ENOMEM; - } - snd_info_version_entry = entry; - return 0; + return snd_info_register(entry); /* freed in error path */ } - -static int __exit snd_info_version_done(void) -{ - snd_info_free_entry(snd_info_version_entry); - return 0; -} - -#endif /* CONFIG_PROC_FS */ diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index 83c29dbff9c0..1478c8dfd473 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -29,15 +29,12 @@ #include <linux/utsname.h> #include <linux/mutex.h> -#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) - /* * OSS compatible part */ static DEFINE_MUTEX(strings); static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT]; -static struct snd_info_entry *snd_sndstat_proc_entry; int snd_oss_info_register(int dev, int num, char *string) { @@ -112,27 +109,15 @@ static void snd_sndstat_proc_read(struct snd_info_entry *entry, snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS); } -int snd_info_minor_register(void) +int __init snd_info_minor_register(void) { struct snd_info_entry *entry; memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings)); - if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) { - entry->c.text.read = snd_sndstat_proc_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - snd_sndstat_proc_entry = entry; - return 0; + entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", + snd_oss_root); + if (!entry) + return -ENOMEM; + entry->c.text.read = snd_sndstat_proc_read; + return snd_info_register(entry); /* freed in error path */ } - -int snd_info_minor_unregister(void) -{ - snd_info_free_entry(snd_sndstat_proc_entry); - snd_sndstat_proc_entry = NULL; - return 0; -} - -#endif /* CONFIG_SND_OSSEMUL */ diff --git a/sound/core/init.c b/sound/core/init.c index 04734e047bfe..20f37fb3800e 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -100,35 +100,28 @@ int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag); EXPORT_SYMBOL(snd_mixer_oss_notify_callback); #endif -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS static void snd_card_id_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { snd_iprintf(buffer, "%s\n", entry->card->id); } -static inline int init_info_for_card(struct snd_card *card) +static int init_info_for_card(struct snd_card *card) { - int err; struct snd_info_entry *entry; - if ((err = snd_info_card_register(card)) < 0) { - dev_dbg(card->dev, "unable to create card info\n"); - return err; - } - if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { + entry = snd_info_create_card_entry(card, "id", card->proc_root); + if (!entry) { dev_dbg(card->dev, "unable to create card entry\n"); - return err; + return -ENOMEM; } entry->c.text.read = snd_card_id_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } card->proc_id = entry; - return 0; + + return snd_info_card_register(card); } -#else /* !CONFIG_PROC_FS */ +#else /* !CONFIG_SND_PROC_FS */ #define init_info_for_card(card) #endif @@ -756,7 +749,7 @@ int snd_card_register(struct snd_card *card) if (snd_cards[card->number]) { /* already registered */ mutex_unlock(&snd_card_mutex); - return 0; + return snd_info_card_register(card); /* register pending info */ } if (*card->id) { /* make a unique id name from the given string */ @@ -782,9 +775,7 @@ int snd_card_register(struct snd_card *card) EXPORT_SYMBOL(snd_card_register); -#ifdef CONFIG_PROC_FS -static struct snd_info_entry *snd_card_info_entry; - +#ifdef CONFIG_SND_PROC_FS static void snd_card_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -810,7 +801,6 @@ static void snd_card_info_read(struct snd_info_entry *entry, } #ifdef CONFIG_SND_OSSEMUL - void snd_card_info_read_oss(struct snd_info_buffer *buffer) { int idx, count; @@ -832,7 +822,6 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer) #endif #ifdef MODULE -static struct snd_info_entry *snd_card_module_info_entry; static void snd_card_module_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -857,36 +846,21 @@ int __init snd_card_info_init(void) if (! entry) return -ENOMEM; entry->c.text.read = snd_card_info_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - return -ENOMEM; - } - snd_card_info_entry = entry; + if (snd_info_register(entry) < 0) + return -ENOMEM; /* freed in error path */ #ifdef MODULE entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); - if (entry) { - entry->c.text.read = snd_card_module_info_read; - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); - else - snd_card_module_info_entry = entry; - } + if (!entry) + return -ENOMEM; + entry->c.text.read = snd_card_module_info_read; + if (snd_info_register(entry) < 0) + return -ENOMEM; /* freed in error path */ #endif return 0; } - -int __exit snd_card_info_done(void) -{ - snd_info_free_entry(snd_card_info_entry); -#ifdef MODULE - snd_info_free_entry(snd_card_module_info_entry); -#endif - return 0; -} - -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ /** * snd_component_add - add a component string diff --git a/sound/core/jack.c b/sound/core/jack.c index 8658578eb584..7237acbdcbbc 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -24,6 +24,13 @@ #include <linux/module.h> #include <sound/jack.h> #include <sound/core.h> +#include <sound/control.h> + +struct snd_jack_kctl { + struct snd_kcontrol *kctl; + struct list_head list; /* list of controls belong to the same jack */ + unsigned int mask_bits; /* only masked status bits are reported via kctl */ +}; static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { SW_HEADPHONE_INSERT, @@ -54,7 +61,13 @@ static int snd_jack_dev_disconnect(struct snd_device *device) static int snd_jack_dev_free(struct snd_device *device) { struct snd_jack *jack = device->device_data; + struct snd_card *card = device->card; + struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl; + list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) { + list_del_init(&jack_kctl->list); + snd_ctl_remove(card, jack_kctl->kctl); + } if (jack->private_free) jack->private_free(jack); @@ -74,6 +87,10 @@ static int snd_jack_dev_register(struct snd_device *device) snprintf(jack->name, sizeof(jack->name), "%s %s", card->shortname, jack->id); + + if (!jack->input_dev) + return 0; + jack->input_dev->name = jack->name; /* Default to the sound card device. */ @@ -100,6 +117,77 @@ static int snd_jack_dev_register(struct snd_device *device) return err; } +static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl) +{ + struct snd_jack_kctl *jack_kctl; + + jack_kctl = kctl->private_data; + if (jack_kctl) { + list_del(&jack_kctl->list); + kfree(jack_kctl); + } +} + +static void snd_jack_kctl_add(struct snd_jack *jack, struct snd_jack_kctl *jack_kctl) +{ + list_add_tail(&jack_kctl->list, &jack->kctl_list); +} + +static struct snd_jack_kctl * snd_jack_kctl_new(struct snd_card *card, const char *name, unsigned int mask) +{ + struct snd_kcontrol *kctl; + struct snd_jack_kctl *jack_kctl; + int err; + + kctl = snd_kctl_jack_new(name, card); + if (!kctl) + return NULL; + + err = snd_ctl_add(card, kctl); + if (err < 0) + return NULL; + + jack_kctl = kzalloc(sizeof(*jack_kctl), GFP_KERNEL); + + if (!jack_kctl) + goto error; + + jack_kctl->kctl = kctl; + jack_kctl->mask_bits = mask; + + kctl->private_data = jack_kctl; + kctl->private_free = snd_jack_kctl_private_free; + + return jack_kctl; +error: + snd_ctl_free_one(kctl); + return NULL; +} + +/** + * snd_jack_add_new_kctl - Create a new snd_jack_kctl and add it to jack + * @jack: the jack instance which the kctl will attaching to + * @name: the name for the snd_kcontrol object + * @mask: a bitmask of enum snd_jack_type values that can be detected + * by this snd_jack_kctl object. + * + * Creates a new snd_kcontrol object and adds it to the jack kctl_list. + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask) +{ + struct snd_jack_kctl *jack_kctl; + + jack_kctl = snd_jack_kctl_new(jack->card, name, mask); + if (!jack_kctl) + return -ENOMEM; + + snd_jack_kctl_add(jack, jack_kctl); + return 0; +} +EXPORT_SYMBOL(snd_jack_add_new_kctl); + /** * snd_jack_new - Create a new jack * @card: the card instance @@ -107,6 +195,8 @@ static int snd_jack_dev_register(struct snd_device *device) * @type: a bitmask of enum snd_jack_type values that can be detected by * this jack * @jjack: Used to provide the allocated jack object to the caller. + * @initial_kctl: if true, create a kcontrol and add it to the jack list. + * @phantom_jack: Don't create a input device for phantom jacks. * * Creates a new jack object. * @@ -114,9 +204,10 @@ static int snd_jack_dev_register(struct snd_device *device) * On success @jjack will be initialised. */ int snd_jack_new(struct snd_card *card, const char *id, int type, - struct snd_jack **jjack) + struct snd_jack **jjack, bool initial_kctl, bool phantom_jack) { struct snd_jack *jack; + struct snd_jack_kctl *jack_kctl = NULL; int err; int i; static struct snd_device_ops ops = { @@ -125,31 +216,47 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, .dev_disconnect = snd_jack_dev_disconnect, }; + if (initial_kctl) { + jack_kctl = snd_jack_kctl_new(card, id, type); + if (!jack_kctl) + return -ENOMEM; + } + jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL); if (jack == NULL) return -ENOMEM; jack->id = kstrdup(id, GFP_KERNEL); - jack->input_dev = input_allocate_device(); - if (jack->input_dev == NULL) { - err = -ENOMEM; - goto fail_input; - } + /* don't creat input device for phantom jack */ + if (!phantom_jack) { + jack->input_dev = input_allocate_device(); + if (jack->input_dev == NULL) { + err = -ENOMEM; + goto fail_input; + } - jack->input_dev->phys = "ALSA"; + jack->input_dev->phys = "ALSA"; - jack->type = type; + jack->type = type; - for (i = 0; i < SND_JACK_SWITCH_TYPES; i++) - if (type & (1 << i)) - input_set_capability(jack->input_dev, EV_SW, - jack_switch_types[i]); + for (i = 0; i < SND_JACK_SWITCH_TYPES; i++) + if (type & (1 << i)) + input_set_capability(jack->input_dev, EV_SW, + jack_switch_types[i]); + + } err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); if (err < 0) goto fail_input; + jack->card = card; + INIT_LIST_HEAD(&jack->kctl_list); + + if (initial_kctl) + snd_jack_kctl_add(jack, jack_kctl); + *jjack = jack; return 0; @@ -175,6 +282,8 @@ EXPORT_SYMBOL(snd_jack_new); void snd_jack_set_parent(struct snd_jack *jack, struct device *parent) { WARN_ON(jack->registered); + if (!jack->input_dev) + return; jack->input_dev->dev.parent = parent; } @@ -230,11 +339,19 @@ EXPORT_SYMBOL(snd_jack_set_key); */ void snd_jack_report(struct snd_jack *jack, int status) { + struct snd_jack_kctl *jack_kctl; int i; if (!jack) return; + list_for_each_entry(jack_kctl, &jack->kctl_list, list) + snd_kctl_jack_report(jack->card, jack_kctl->kctl, + status & jack_kctl->mask_bits); + + if (!jack->input_dev) + return; + for (i = 0; i < ARRAY_SIZE(jack->key); i++) { int testbit = SND_JACK_BTN_0 >> i; @@ -252,9 +369,6 @@ void snd_jack_report(struct snd_jack *jack, int status) } input_sync(jack->input_dev); + } EXPORT_SYMBOL(snd_jack_report); - -MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); -MODULE_DESCRIPTION("Jack detection support for ALSA"); -MODULE_LICENSE("GPL"); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 082509eb805d..f05cb6a8cbe0 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -124,7 +124,7 @@ static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) dmab->addr = 0; if (dev->of_node) - pool = of_get_named_gen_pool(dev->of_node, "iram", 0); + pool = of_gen_pool_get(dev->of_node, "iram", 0); if (!pool) return; diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 056f8e274851..a99f7200ff3f 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1111,7 +1111,7 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix return 0; } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* */ #define MIXER_VOL(name) [SOUND_MIXER_##name] = #name @@ -1255,10 +1255,10 @@ static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer) snd_info_free_entry(mixer->proc_entry); mixer->proc_entry = NULL; } -#else /* !CONFIG_PROC_FS */ +#else /* !CONFIG_SND_PROC_FS */ #define snd_mixer_oss_proc_init(mix) #define snd_mixer_oss_proc_done(mix) -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ static void snd_mixer_oss_build(struct snd_mixer_oss *mixer) { diff --git a/sound/core/pcm.c b/sound/core/pcm.c index b25bcf5b8644..02bd96954dc4 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1027,7 +1027,8 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) static ssize_t show_pcm_class(struct device *dev, struct device_attribute *attr, char *buf) { - struct snd_pcm *pcm; + struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); + struct snd_pcm *pcm = pstr->pcm; const char *str; static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { [SNDRV_PCM_CLASS_GENERIC] = "generic", @@ -1036,8 +1037,7 @@ static ssize_t show_pcm_class(struct device *dev, [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", }; - if (! (pcm = dev_get_drvdata(dev)) || - pcm->dev_class > SNDRV_PCM_CLASS_LAST) + if (pcm->dev_class > SNDRV_PCM_CLASS_LAST) str = "none"; else str = strs[pcm->dev_class]; @@ -1181,7 +1181,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) } EXPORT_SYMBOL(snd_pcm_notify); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* * Info interface */ @@ -1227,10 +1227,10 @@ static void snd_pcm_proc_done(void) snd_info_free_entry(snd_pcm_proc_entry); } -#else /* !CONFIG_PROC_FS */ +#else /* !CONFIG_SND_PROC_FS */ #define snd_pcm_proc_init() #define snd_pcm_proc_done() -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ /* diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c new file mode 100644 index 000000000000..e70379fb63d0 --- /dev/null +++ b/sound/core/pcm_drm_eld.c @@ -0,0 +1,99 @@ +/* + * PCM DRM helpers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/export.h> +#include <drm/drm_edid.h> +#include <sound/pcm.h> +#include <sound/pcm_drm_eld.h> + +static const unsigned int eld_rates[] = { + 32000, + 44100, + 48000, + 88200, + 96000, + 176400, + 192000, +}; + +static unsigned int sad_max_channels(const u8 *sad) +{ + return 1 + (sad[0] & 7); +} + +static int eld_limit_rates(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, rule->var); + struct snd_interval *c; + unsigned int rate_mask = 7, i; + const u8 *sad, *eld = rule->private; + + sad = drm_eld_sad(eld); + if (sad) { + c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + + for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) { + unsigned max_channels = sad_max_channels(sad); + + /* + * Exclude SADs which do not include the + * requested number of channels. + */ + if (c->min <= max_channels) + rate_mask |= sad[1]; + } + } + + return snd_interval_list(r, ARRAY_SIZE(eld_rates), eld_rates, + rate_mask); +} + +static int eld_limit_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, rule->var); + struct snd_interval *r; + struct snd_interval t = { .min = 1, .max = 2, .integer = 1, }; + unsigned int i; + const u8 *sad, *eld = rule->private; + + sad = drm_eld_sad(eld); + if (sad) { + unsigned int rate_mask = 0; + + /* Convert the rate interval to a mask */ + r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + for (i = 0; i < ARRAY_SIZE(eld_rates); i++) + if (r->min <= eld_rates[i] && r->max >= eld_rates[i]) + rate_mask |= BIT(i); + + for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) + if (rate_mask & sad[1]) + t.max = max(t.max, sad_max_channels(sad)); + } + + return snd_interval_refine(c, &t); +} + +int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld) +{ + int ret; + + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + eld_limit_rates, eld, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + eld_limit_channels, eld, + SNDRV_PCM_HW_PARAM_RATE, -1); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld); diff --git a/sound/core/pcm_iec958.c b/sound/core/pcm_iec958.c new file mode 100644 index 000000000000..36b2d7aca1bd --- /dev/null +++ b/sound/core/pcm_iec958.c @@ -0,0 +1,95 @@ +/* + * PCM DRM helpers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/export.h> +#include <linux/types.h> +#include <sound/asoundef.h> +#include <sound/pcm.h> +#include <sound/pcm_iec958.h> + +/** + * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status + * @runtime: pcm runtime structure with ->rate filled in + * @cs: channel status buffer, at least four bytes + * @len: length of channel status buffer + * + * Create the consumer format channel status data in @cs of maximum size + * @len corresponding to the parameters of the PCM runtime @runtime. + * + * Drivers may wish to tweak the contents of the buffer after creation. + * + * Returns: length of buffer, or negative error code if something failed. + */ +int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, + size_t len) +{ + unsigned int fs, ws; + + if (len < 4) + return -EINVAL; + + switch (runtime->rate) { + case 32000: + fs = IEC958_AES3_CON_FS_32000; + break; + case 44100: + fs = IEC958_AES3_CON_FS_44100; + break; + case 48000: + fs = IEC958_AES3_CON_FS_48000; + break; + case 88200: + fs = IEC958_AES3_CON_FS_88200; + break; + case 96000: + fs = IEC958_AES3_CON_FS_96000; + break; + case 176400: + fs = IEC958_AES3_CON_FS_176400; + break; + case 192000: + fs = IEC958_AES3_CON_FS_192000; + break; + default: + return -EINVAL; + } + + if (len > 4) { + switch (snd_pcm_format_width(runtime->format)) { + case 16: + ws = IEC958_AES4_CON_WORDLEN_20_16; + break; + case 18: + ws = IEC958_AES4_CON_WORDLEN_22_18; + break; + case 20: + ws = IEC958_AES4_CON_WORDLEN_20_16 | + IEC958_AES4_CON_MAX_WORDLEN_24; + break; + case 24: + ws = IEC958_AES4_CON_WORDLEN_24_20 | + IEC958_AES4_CON_MAX_WORDLEN_24; + break; + + default: + return -EINVAL; + } + } + + memset(cs, 0, len); + + cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE; + cs[1] = IEC958_AES1_CON_GENERAL; + cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC; + cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | fs; + + if (len > 4) + cs[4] = ws; + + return len; +} +EXPORT_SYMBOL(snd_pcm_create_iec958_consumer); diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index 941f64a853eb..b65fa5a1943b 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -6,7 +6,8 @@ snd-seq-device-objs := seq_device.o snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ seq_fifo.o seq_prioq.o seq_timer.o \ - seq_system.o seq_ports.o seq_info.o + seq_system.o seq_ports.o +snd-seq-$(CONFIG_SND_PROC_FS) += seq_info.o snd-seq-midi-objs := seq_midi.o snd-seq-midi-emul-objs := seq_midi_emul.o snd-seq-midi-event-objs := seq_midi_event.o diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 72873a46afeb..7354b8bed860 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -45,7 +45,7 @@ MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC); */ static int register_device(void); static void unregister_device(void); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS static int register_proc(void); static void unregister_proc(void); #else @@ -261,7 +261,7 @@ unregister_device(void) * /proc interface */ -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS static struct snd_info_entry *info_entry; @@ -303,4 +303,4 @@ unregister_proc(void) snd_info_free_entry(info_entry); info_entry = NULL; } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 2de3feff70d0..b1221b29728e 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -479,8 +479,7 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp) snd_seq_oss_timer_stop(dp->timer); } - -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* * misc. functions for proc interface */ @@ -531,4 +530,4 @@ snd_seq_oss_system_info_read(struct snd_info_buffer *buf) snd_seq_oss_readq_info_read(dp->readq, buf); } } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index 96e8395ae586..aaff9ee32695 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -665,7 +665,7 @@ snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* * proc interface */ @@ -705,4 +705,4 @@ snd_seq_oss_midi_info_read(struct snd_info_buffer *buf) snd_use_lock_free(&mdev->use_lock); } } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c index c080c73cea04..ccd893566f1d 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -222,7 +222,7 @@ snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *q, unsigned long curt, int } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* * proc interface */ @@ -233,4 +233,4 @@ snd_seq_oss_readq_info_read(struct seq_oss_readq *q, struct snd_info_buffer *buf (waitqueue_active(&q->midi_sleep) ? "sleeping":"running"), q->qlen, q->input_time); } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 48e4fe1b68ab..0f3b38184fe5 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -630,7 +630,7 @@ snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_in } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* * proc interface */ @@ -658,4 +658,4 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf) snd_use_lock_free(&rec->use_lock); } } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index edbdab85fc02..b64f20deba90 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -2447,7 +2447,7 @@ EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); /*---------------------------------------------------------------------------*/ -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* * /proc interface */ @@ -2549,7 +2549,7 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, snd_seq_client_unlock(client); } } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ /*---------------------------------------------------------------------------*/ diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index d99f99d61983..c4acf17e9f5e 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -72,7 +72,7 @@ static struct bus_type snd_seq_bus_type = { /* * proc interface -- just for compatibility */ -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS static struct snd_info_entry *info_entry; static int print_dev_info(struct device *dev, void *data) @@ -272,7 +272,7 @@ EXPORT_SYMBOL_GPL(snd_seq_driver_unregister); static int __init seq_dev_proc_init(void) { -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", snd_seq_root); if (info_entry == NULL) @@ -305,7 +305,7 @@ static void __exit alsa_seq_device_exit(void) #ifdef CONFIG_MODULES cancel_work_sync(&autoload_work); #endif -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS snd_info_free_entry(info_entry); #endif bus_unregister(&snd_seq_bus_type); diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c index acf7769419f0..97015447b9b3 100644 --- a/sound/core/seq/seq_info.c +++ b/sound/core/seq/seq_info.c @@ -27,7 +27,6 @@ #include "seq_clientmgr.h" #include "seq_timer.h" -#ifdef CONFIG_PROC_FS static struct snd_info_entry *queues_entry; static struct snd_info_entry *clients_entry; static struct snd_info_entry *timer_entry; @@ -51,6 +50,13 @@ create_info_entry(char *name, void (*read)(struct snd_info_entry *, return entry; } +static void free_info_entries(void) +{ + snd_info_free_entry(queues_entry); + snd_info_free_entry(clients_entry); + snd_info_free_entry(timer_entry); +} + /* create all our /proc entries */ int __init snd_seq_info_init(void) { @@ -59,14 +65,17 @@ int __init snd_seq_info_init(void) clients_entry = create_info_entry("clients", snd_seq_info_clients_read); timer_entry = create_info_entry("timer", snd_seq_info_timer_read); + if (!queues_entry || !clients_entry || !timer_entry) + goto error; return 0; + + error: + free_info_entries(); + return -ENOMEM; } int __exit snd_seq_info_done(void) { - snd_info_free_entry(queues_entry); - snd_info_free_entry(clients_entry); - snd_info_free_entry(timer_entry); + free_info_entries(); return 0; } -#endif diff --git a/sound/core/seq/seq_info.h b/sound/core/seq/seq_info.h index 4892a7f35c08..f8549f81a645 100644 --- a/sound/core/seq/seq_info.h +++ b/sound/core/seq/seq_info.h @@ -29,7 +29,7 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry, struct snd_info_buffe void snd_seq_info_queues_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS int snd_seq_info_init( void ); int snd_seq_info_done( void ); #else diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index a0cda38205b9..7dfd0f429410 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -753,7 +753,7 @@ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop) /*----------------------------------------------------------------*/ -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* exported to seq_info.c */ void snd_seq_info_queues_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) @@ -787,5 +787,5 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry, queuefree(q); } } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 186f1611103c..82b220c769c1 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -422,7 +422,7 @@ snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr) } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* exported to seq_info.c */ void snd_seq_info_timer_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) @@ -449,5 +449,5 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry, queuefree(q); } } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ diff --git a/sound/core/sound.c b/sound/core/sound.c index 5fc93d00572a..175f9e4e01c8 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -330,13 +330,10 @@ int snd_unregister_device(struct device *dev) } EXPORT_SYMBOL(snd_unregister_device); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* * INFO PART */ - -static struct snd_info_entry *snd_minor_info_entry; - static const char *snd_device_type_name(int type) { switch (type) { @@ -389,23 +386,12 @@ int __init snd_minor_info_init(void) struct snd_info_entry *entry; entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); - if (entry) { - entry->c.text.read = snd_minor_info_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - snd_minor_info_entry = entry; - return 0; -} - -int __exit snd_minor_info_done(void) -{ - snd_info_free_entry(snd_minor_info_entry); - return 0; + if (!entry) + return -ENOMEM; + entry->c.text.read = snd_minor_info_read; + return snd_info_register(entry); /* freed in error path */ } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ /* * INIT PART @@ -423,7 +409,6 @@ static int __init alsa_sound_init(void) unregister_chrdev(major, "alsa"); return -ENOMEM; } - snd_info_minor_register(); #ifndef MODULE pr_info("Advanced Linux Sound Architecture Driver Initialized.\n"); #endif @@ -432,7 +417,6 @@ static int __init alsa_sound_init(void) static void __exit alsa_sound_exit(void) { - snd_info_minor_unregister(); snd_info_done(); unregister_chrdev(major, "alsa"); } diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 573a65eb2b79..0ca9d72b2273 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -19,12 +19,6 @@ * */ -#ifdef CONFIG_SND_OSSEMUL - -#if !IS_ENABLED(CONFIG_SOUND) -#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel." -#endif - #include <linux/init.h> #include <linux/export.h> #include <linux/slab.h> @@ -213,10 +207,7 @@ EXPORT_SYMBOL(snd_unregister_oss_device); * INFO PART */ -#ifdef CONFIG_PROC_FS - -static struct snd_info_entry *snd_minor_info_oss_entry; - +#ifdef CONFIG_SND_PROC_FS static const char *snd_oss_device_type_name(int type) { switch (type) { @@ -263,22 +254,9 @@ int __init snd_minor_info_oss_init(void) struct snd_info_entry *entry; entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root); - if (entry) { - entry->c.text.read = snd_minor_info_oss_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - snd_minor_info_oss_entry = entry; - return 0; -} - -int __exit snd_minor_info_oss_done(void) -{ - snd_info_free_entry(snd_minor_info_oss_entry); - return 0; + if (!entry) + return -ENOMEM; + entry->c.text.read = snd_minor_info_oss_read; + return snd_info_register(entry); /* freed in error path */ } -#endif /* CONFIG_PROC_FS */ - -#endif /* CONFIG_SND_OSSEMUL */ +#endif /* CONFIG_SND_PROC_FS */ diff --git a/sound/core/timer.c b/sound/core/timer.c index a9a1a047c521..31f40f03e5b7 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1034,7 +1034,7 @@ static int snd_timer_register_system(void) return snd_timer_global_register(timer); } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* * Info interface */ @@ -1104,7 +1104,7 @@ static void __exit snd_timer_proc_done(void) { snd_info_free_entry(snd_timer_proc_entry); } -#else /* !CONFIG_PROC_FS */ +#else /* !CONFIG_SND_PROC_FS */ #define snd_timer_proc_init() #define snd_timer_proc_done() #endif diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 7f9126efc1e5..54f348a4fb78 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -1053,8 +1053,6 @@ static int loopback_mixer_new(struct loopback *loopback, int notify) return 0; } -#ifdef CONFIG_PROC_FS - static void print_dpcm_info(struct snd_info_buffer *buffer, struct loopback_pcm *dpcm, const char *id) @@ -1128,12 +1126,6 @@ static int loopback_proc_new(struct loopback *loopback, int cidx) return 0; } -#else /* !CONFIG_PROC_FS */ - -#define loopback_proc_new(loopback, cidx) do { } while (0) - -#endif - static int loopback_probe(struct platform_device *devptr) { struct snd_card *card; diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index d11baaf0f0b4..016e451ed506 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -156,13 +156,13 @@ static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime) return 0; } -struct dummy_model model_emu10k1 = { +static struct dummy_model model_emu10k1 = { .name = "emu10k1", .playback_constraints = emu10k1_playback_constraints, .buffer_bytes_max = 128 * 1024, }; -struct dummy_model model_rme9652 = { +static struct dummy_model model_rme9652 = { .name = "rme9652", .buffer_bytes_max = 26 * 64 * 1024, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -172,7 +172,7 @@ struct dummy_model model_rme9652 = { .periods_max = 2, }; -struct dummy_model model_ice1712 = { +static struct dummy_model model_ice1712 = { .name = "ice1712", .buffer_bytes_max = 256 * 1024, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -182,7 +182,7 @@ struct dummy_model model_ice1712 = { .periods_max = 1024, }; -struct dummy_model model_uda1341 = { +static struct dummy_model model_uda1341 = { .name = "uda1341", .buffer_bytes_max = 16380, .formats = SNDRV_PCM_FMTBIT_S16_LE, @@ -192,7 +192,7 @@ struct dummy_model model_uda1341 = { .periods_max = 255, }; -struct dummy_model model_ac97 = { +static struct dummy_model model_ac97 = { .name = "ac97", .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels_min = 2, @@ -202,7 +202,7 @@ struct dummy_model model_ac97 = { .rate_max = 48000, }; -struct dummy_model model_ca0106 = { +static struct dummy_model model_ca0106 = { .name = "ca0106", .formats = SNDRV_PCM_FMTBIT_S16_LE, .buffer_bytes_max = ((65536-64)*8), @@ -216,7 +216,7 @@ struct dummy_model model_ca0106 = { .rate_max = 192000, }; -struct dummy_model *dummy_models[] = { +static struct dummy_model *dummy_models[] = { &model_emu10k1, &model_rme9652, &model_ice1712, @@ -914,7 +914,7 @@ static int snd_card_dummy_new_mixer(struct snd_dummy *dummy) return 0; } -#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS) +#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_PROC_FS) /* * proc interface */ @@ -1042,7 +1042,7 @@ static void dummy_proc_init(struct snd_dummy *chip) } #else #define dummy_proc_init(x) -#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */ +#endif /* CONFIG_SND_DEBUG && CONFIG_SND_PROC_FS */ static int snd_dummy_probe(struct platform_device *devptr) { diff --git a/sound/drivers/opl4/Makefile b/sound/drivers/opl4/Makefile index b94009b0b19f..c8eaa433d71a 100644 --- a/sound/drivers/opl4/Makefile +++ b/sound/drivers/opl4/Makefile @@ -3,7 +3,8 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o +snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o +snd-opl4-lib-$(CONFIG_SND_PROC_FS) += opl4_proc.o snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c index 3b0ee42a5343..89c7aa04b3bc 100644 --- a/sound/drivers/opl4/opl4_lib.c +++ b/sound/drivers/opl4/opl4_lib.c @@ -176,9 +176,7 @@ static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device) static void snd_opl4_free(struct snd_opl4 *opl4) { -#ifdef CONFIG_PROC_FS snd_opl4_free_proc(opl4); -#endif release_and_free_resource(opl4->res_fm_port); release_and_free_resource(opl4->res_pcm_port); kfree(opl4); @@ -249,9 +247,7 @@ int snd_opl4_create(struct snd_card *card, snd_opl4_enable_opl4(opl4); snd_opl4_create_mixer(opl4); -#ifdef CONFIG_PROC_FS snd_opl4_create_proc(opl4); -#endif #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) opl4->seq_client = -1; diff --git a/sound/drivers/opl4/opl4_local.h b/sound/drivers/opl4/opl4_local.h index 470e5a758a02..9a41bdebce6b 100644 --- a/sound/drivers/opl4/opl4_local.h +++ b/sound/drivers/opl4/opl4_local.h @@ -178,7 +178,7 @@ struct snd_opl4 { spinlock_t reg_lock; struct snd_card *card; -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS struct snd_info_entry *proc_entry; int memory_access; #endif @@ -207,10 +207,13 @@ void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, i /* opl4_mixer.c */ int snd_opl4_create_mixer(struct snd_opl4 *opl4); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* opl4_proc.c */ int snd_opl4_create_proc(struct snd_opl4 *opl4); void snd_opl4_free_proc(struct snd_opl4 *opl4); +#else +static inline int snd_opl4_create_proc(struct snd_opl4 *opl4) { return 0; } +static inline void snd_opl4_free_proc(struct snd_opl4 *opl4) {} #endif /* opl4_seq.c */ diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c index 9b824bfc919d..cd2c07fa2ef4 100644 --- a/sound/drivers/opl4/opl4_proc.c +++ b/sound/drivers/opl4/opl4_proc.c @@ -22,8 +22,6 @@ #include <linux/export.h> #include <sound/info.h> -#ifdef CONFIG_PROC_FS - static int snd_opl4_mem_proc_open(struct snd_info_entry *entry, unsigned short mode, void **file_private_data) { @@ -129,5 +127,3 @@ void snd_opl4_free_proc(struct snd_opl4 *opl4) { snd_info_free_entry(opl4->proc_entry); } - -#endif /* CONFIG_PROC_FS */ diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c index d9647bd84d0f..27e25bb78c97 100644 --- a/sound/drivers/pcsp/pcsp.c +++ b/sound/drivers/pcsp/pcsp.c @@ -42,16 +42,13 @@ struct snd_pcsp pcsp_chip; static int snd_pcsp_create(struct snd_card *card) { static struct snd_device_ops ops = { }; - struct timespec tp; - int err; - int div, min_div, order; - - hrtimer_get_res(CLOCK_MONOTONIC, &tp); + unsigned int resolution = hrtimer_resolution; + int err, div, min_div, order; if (!nopcm) { - if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) { + if (resolution > PCSP_MAX_PERIOD_NS) { printk(KERN_ERR "PCSP: Timer resolution is not sufficient " - "(%linS)\n", tp.tv_nsec); + "(%unS)\n", resolution); printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI " "enabled.\n"); printk(KERN_ERR "PCSP: Turned into nopcm mode.\n"); @@ -59,13 +56,13 @@ static int snd_pcsp_create(struct snd_card *card) } } - if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS) + if (loops_per_jiffy >= PCSP_MIN_LPJ && resolution <= PCSP_MIN_PERIOD_NS) min_div = MIN_DIV; else min_div = MAX_DIV; #if PCSP_DEBUG - printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%li\n", - loops_per_jiffy, min_div, tp.tv_nsec); + printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%u\n", + loops_per_jiffy, min_div, resolution); #endif div = MAX_DIV / min_div; diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index ecec547782b2..8850b7de1d38 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -95,6 +95,7 @@ config SND_BEBOB * Tascam IF-FW/DM * Behringer XENIX UFX 1204/1604 * Behringer Digital Mixer X32 series (X-UF Card) + * Behringer FCA610/1616 * Apogee Rosetta 200/400 (X-FireWire card) * Apogee DA/AD/DD-16X (X-FireWire card) * Apogee Ensemble @@ -114,6 +115,7 @@ config SND_BEBOB * M-Audio FireWire410/AudioPhile/Solo * M-Audio Ozonic/NRV10/ProfireLightBridge * M-Audio FireWire 1814/ProjectMix IO + * Digidesign Mbox 2 Pro To compile this driver as a module, choose M here: the module will be called snd-bebob. diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index e061355f535f..7bb988fa6b6d 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -40,24 +40,28 @@ #define TAG_CIP 1 /* common isochronous packet header parameters */ -#define CIP_EOH (1u << 31) +#define CIP_EOH_SHIFT 31 +#define CIP_EOH (1u << CIP_EOH_SHIFT) #define CIP_EOH_MASK 0x80000000 -#define CIP_FMT_AM (0x10 << 24) +#define CIP_SID_SHIFT 24 +#define CIP_SID_MASK 0x3f000000 +#define CIP_DBS_MASK 0x00ff0000 +#define CIP_DBS_SHIFT 16 +#define CIP_DBC_MASK 0x000000ff +#define CIP_FMT_SHIFT 24 #define CIP_FMT_MASK 0x3f000000 +#define CIP_FDF_MASK 0x00ff0000 +#define CIP_FDF_SHIFT 16 #define CIP_SYT_MASK 0x0000ffff #define CIP_SYT_NO_INFO 0xffff -#define CIP_FDF_MASK 0x00ff0000 -#define CIP_FDF_SFC_SHIFT 16 /* * Audio and Music transfer protocol specific parameters * only "Clock-based rate control mode" is supported */ -#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SFC_SHIFT + 3)) +#define CIP_FMT_AM (0x10 << CIP_FMT_SHIFT) +#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SHIFT + 3)) #define AMDTP_FDF_NO_DATA 0xff -#define AMDTP_DBS_MASK 0x00ff0000 -#define AMDTP_DBS_SHIFT 16 -#define AMDTP_DBC_MASK 0x000000ff /* TODO: make these configurable */ #define INTERRUPT_INTERVAL 16 @@ -251,19 +255,24 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters); */ unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) { - return 8 + s->syt_interval * s->data_block_quadlets * 4; + unsigned int multiplier = 1; + + if (s->flags & CIP_JUMBO_PAYLOAD) + multiplier = 5; + + return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier; } EXPORT_SYMBOL(amdtp_stream_get_max_payload); -static void amdtp_write_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); -static void amdtp_write_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); -static void amdtp_read_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); +static void write_pcm_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void write_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void read_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); /** * amdtp_stream_set_pcm_format - set the PCM format @@ -286,16 +295,16 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s, /* fall through */ case SNDRV_PCM_FORMAT_S16: if (s->direction == AMDTP_OUT_STREAM) { - s->transfer_samples = amdtp_write_s16; + s->transfer_samples = write_pcm_s16; break; } WARN_ON(1); /* fall through */ case SNDRV_PCM_FORMAT_S32: if (s->direction == AMDTP_OUT_STREAM) - s->transfer_samples = amdtp_write_s32; + s->transfer_samples = write_pcm_s32; else - s->transfer_samples = amdtp_read_s32; + s->transfer_samples = read_pcm_s32; break; } } @@ -316,17 +325,25 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s) } EXPORT_SYMBOL(amdtp_stream_pcm_prepare); -static unsigned int calculate_data_blocks(struct amdtp_stream *s) +static unsigned int calculate_data_blocks(struct amdtp_stream *s, + unsigned int syt) { unsigned int phase, data_blocks; - if (s->flags & CIP_BLOCKING) - data_blocks = s->syt_interval; - else if (!cip_sfc_is_base_44100(s->sfc)) { - /* Sample_rate / 8000 is an integer, and precomputed. */ - data_blocks = s->data_block_state; + /* Blocking mode. */ + if (s->flags & CIP_BLOCKING) { + /* This module generate empty packet for 'no data'. */ + if (syt == CIP_SYT_NO_INFO) + data_blocks = 0; + else + data_blocks = s->syt_interval; + /* Non-blocking mode. */ } else { - phase = s->data_block_state; + if (!cip_sfc_is_base_44100(s->sfc)) { + /* Sample_rate / 8000 is an integer, and precomputed. */ + data_blocks = s->data_block_state; + } else { + phase = s->data_block_state; /* * This calculates the number of data blocks per packet so that @@ -336,16 +353,17 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s) * as possible in the sequence (to prevent underruns of the * device's buffer). */ - if (s->sfc == CIP_SFC_44100) - /* 6 6 5 6 5 6 5 ... */ - data_blocks = 5 + ((phase & 1) ^ - (phase == 0 || phase >= 40)); - else - /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ - data_blocks = 11 * (s->sfc >> 1) + (phase == 0); - if (++phase >= (80 >> (s->sfc >> 1))) - phase = 0; - s->data_block_state = phase; + if (s->sfc == CIP_SFC_44100) + /* 6 6 5 6 5 6 5 ... */ + data_blocks = 5 + ((phase & 1) ^ + (phase == 0 || phase >= 40)); + else + /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ + data_blocks = 11 * (s->sfc >> 1) + (phase == 0); + if (++phase >= (80 >> (s->sfc >> 1))) + phase = 0; + s->data_block_state = phase; + } } return data_blocks; @@ -394,9 +412,9 @@ static unsigned int calculate_syt(struct amdtp_stream *s, } } -static void amdtp_write_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) +static void write_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; unsigned int channels, remaining_frames, i, c; @@ -419,9 +437,9 @@ static void amdtp_write_s32(struct amdtp_stream *s, } } -static void amdtp_write_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) +static void write_pcm_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; unsigned int channels, remaining_frames, i, c; @@ -444,9 +462,9 @@ static void amdtp_write_s16(struct amdtp_stream *s, } } -static void amdtp_read_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) +static void read_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) { struct snd_pcm_runtime *runtime = pcm->runtime; unsigned int channels, remaining_frames, i, c; @@ -468,8 +486,8 @@ static void amdtp_read_s32(struct amdtp_stream *s, } } -static void amdtp_fill_pcm_silence(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) +static void write_pcm_silence(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) { unsigned int i, c; @@ -510,8 +528,8 @@ static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) s->midi_fifo_used[port] += amdtp_rate_table[s->sfc]; } -static void amdtp_fill_midi(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) +static void write_midi_messages(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) { unsigned int f, port; u8 *b; @@ -537,8 +555,8 @@ static void amdtp_fill_midi(struct amdtp_stream *s, } } -static void amdtp_pull_midi(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) +static void read_midi_messages(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) { unsigned int f, port; int len; @@ -633,57 +651,48 @@ static inline int queue_in_packet(struct amdtp_stream *s) amdtp_stream_get_max_payload(s), false); } -static void handle_out_packet(struct amdtp_stream *s, unsigned int syt) +static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, + unsigned int syt) { __be32 *buffer; - unsigned int data_blocks, payload_length; + unsigned int payload_length; struct snd_pcm_substream *pcm; - if (s->packet_index < 0) - return; - - /* this module generate empty packet for 'no data' */ - if (!(s->flags & CIP_BLOCKING) || (syt != CIP_SYT_NO_INFO)) - data_blocks = calculate_data_blocks(s); - else - data_blocks = 0; - buffer = s->buffer.packets[s->packet_index].buffer; buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | - (s->data_block_quadlets << AMDTP_DBS_SHIFT) | + (s->data_block_quadlets << CIP_DBS_SHIFT) | s->data_block_counter); buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | - (s->sfc << CIP_FDF_SFC_SHIFT) | syt); + (s->sfc << CIP_FDF_SHIFT) | syt); buffer += 2; pcm = ACCESS_ONCE(s->pcm); if (pcm) s->transfer_samples(s, pcm, buffer, data_blocks); else - amdtp_fill_pcm_silence(s, buffer, data_blocks); + write_pcm_silence(s, buffer, data_blocks); if (s->midi_ports) - amdtp_fill_midi(s, buffer, data_blocks); + write_midi_messages(s, buffer, data_blocks); s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; - if (queue_out_packet(s, payload_length, false) < 0) { - s->packet_index = -1; - amdtp_stream_pcm_abort(s); - return; - } + if (queue_out_packet(s, payload_length, false) < 0) + return -EIO; if (pcm) update_pcm_pointers(s, pcm, data_blocks); + + /* No need to return the number of handled data blocks. */ + return 0; } -static void handle_in_packet(struct amdtp_stream *s, - unsigned int payload_quadlets, - __be32 *buffer) +static int handle_in_packet(struct amdtp_stream *s, + unsigned int payload_quadlets, __be32 *buffer, + unsigned int *data_blocks) { u32 cip_header[2]; - unsigned int data_blocks, data_block_quadlets, data_block_counter, - dbc_interval; + unsigned int data_block_quadlets, data_block_counter, dbc_interval; struct snd_pcm_substream *pcm = NULL; bool lost; @@ -700,33 +709,34 @@ static void handle_in_packet(struct amdtp_stream *s, dev_info_ratelimited(&s->unit->device, "Invalid CIP header for AMDTP: %08X:%08X\n", cip_header[0], cip_header[1]); + *data_blocks = 0; goto end; } /* Calculate data blocks */ if (payload_quadlets < 3 || ((cip_header[1] & CIP_FDF_MASK) == - (AMDTP_FDF_NO_DATA << CIP_FDF_SFC_SHIFT))) { - data_blocks = 0; + (AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) { + *data_blocks = 0; } else { data_block_quadlets = - (cip_header[0] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT; + (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT; /* avoid division by zero */ if (data_block_quadlets == 0) { - dev_info_ratelimited(&s->unit->device, + dev_err(&s->unit->device, "Detect invalid value in dbs field: %08X\n", cip_header[0]); - goto err; + return -EPROTO; } if (s->flags & CIP_WRONG_DBS) data_block_quadlets = s->data_block_quadlets; - data_blocks = (payload_quadlets - 2) / data_block_quadlets; + *data_blocks = (payload_quadlets - 2) / data_block_quadlets; } /* Check data block counter continuity */ - data_block_counter = cip_header[0] & AMDTP_DBC_MASK; - if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && + data_block_counter = cip_header[0] & CIP_DBC_MASK; + if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && s->data_block_counter != UINT_MAX) data_block_counter = s->data_block_counter; @@ -736,49 +746,46 @@ static void handle_in_packet(struct amdtp_stream *s, } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { lost = data_block_counter != s->data_block_counter; } else { - if ((data_blocks > 0) && (s->tx_dbc_interval > 0)) + if ((*data_blocks > 0) && (s->tx_dbc_interval > 0)) dbc_interval = s->tx_dbc_interval; else - dbc_interval = data_blocks; + dbc_interval = *data_blocks; lost = data_block_counter != ((s->data_block_counter + dbc_interval) & 0xff); } if (lost) { - dev_info(&s->unit->device, - "Detect discontinuity of CIP: %02X %02X\n", - s->data_block_counter, data_block_counter); - goto err; + dev_err(&s->unit->device, + "Detect discontinuity of CIP: %02X %02X\n", + s->data_block_counter, data_block_counter); + return -EIO; } - if (data_blocks > 0) { + if (*data_blocks > 0) { buffer += 2; pcm = ACCESS_ONCE(s->pcm); if (pcm) - s->transfer_samples(s, pcm, buffer, data_blocks); + s->transfer_samples(s, pcm, buffer, *data_blocks); if (s->midi_ports) - amdtp_pull_midi(s, buffer, data_blocks); + read_midi_messages(s, buffer, *data_blocks); } if (s->flags & CIP_DBC_IS_END_EVENT) s->data_block_counter = data_block_counter; else s->data_block_counter = - (data_block_counter + data_blocks) & 0xff; + (data_block_counter + *data_blocks) & 0xff; end: if (queue_in_packet(s) < 0) - goto err; + return -EIO; if (pcm) - update_pcm_pointers(s, pcm, data_blocks); + update_pcm_pointers(s, pcm, *data_blocks); - return; -err: - s->packet_index = -1; - amdtp_stream_pcm_abort(s); + return 0; } static void out_stream_callback(struct fw_iso_context *context, u32 cycle, @@ -787,6 +794,10 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle, { struct amdtp_stream *s = private_data; unsigned int i, syt, packets = header_length / 4; + unsigned int data_blocks; + + if (s->packet_index < 0) + return; /* * Compute the cycle of the last queued packet. @@ -797,8 +808,15 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle, for (i = 0; i < packets; ++i) { syt = calculate_syt(s, ++cycle); - handle_out_packet(s, syt); + data_blocks = calculate_data_blocks(s, syt); + + if (handle_out_packet(s, data_blocks, syt) < 0) { + s->packet_index = -1; + amdtp_stream_pcm_abort(s); + return; + } } + fw_iso_context_queue_flush(s->context); } @@ -807,32 +825,55 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int p, syt, packets, payload_quadlets; + unsigned int p, syt, packets; + unsigned int payload_quadlets, max_payload_quadlets; + unsigned int data_blocks; __be32 *buffer, *headers = header; + if (s->packet_index < 0) + return; + /* The number of packets in buffer */ packets = header_length / IN_PACKET_HEADER_SIZE; + /* For buffer-over-run prevention. */ + max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4; + for (p = 0; p < packets; p++) { - if (s->packet_index < 0) + buffer = s->buffer.packets[s->packet_index].buffer; + + /* The number of quadlets in this packet */ + payload_quadlets = + (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + if (payload_quadlets > max_payload_quadlets) { + dev_err(&s->unit->device, + "Detect jumbo payload: %02x %02x\n", + payload_quadlets, max_payload_quadlets); + s->packet_index = -1; break; + } - buffer = s->buffer.packets[s->packet_index].buffer; + if (handle_in_packet(s, payload_quadlets, buffer, + &data_blocks) < 0) { + s->packet_index = -1; + break; + } /* Process sync slave stream */ if (s->sync_slave && s->sync_slave->callbacked) { syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; - handle_out_packet(s->sync_slave, syt); + if (handle_out_packet(s->sync_slave, + data_blocks, syt) < 0) { + s->packet_index = -1; + break; + } } - - /* The number of quadlets in this packet */ - payload_quadlets = - (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; - handle_in_packet(s, payload_quadlets, buffer); } /* Queueing error or detecting discontinuity */ if (s->packet_index < 0) { + amdtp_stream_pcm_abort(s); + /* Abort sync slave. */ if (s->sync_slave) { s->sync_slave->packet_index = -1; @@ -872,7 +913,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, if (s->direction == AMDTP_IN_STREAM) context->callback.sc = in_stream_callback; - else if ((s->flags & CIP_BLOCKING) && (s->flags & CIP_SYNC_TO_DEVICE)) + else if (s->flags & CIP_SYNC_TO_DEVICE) context->callback.sc = slave_stream_callback; else context->callback.sc = out_stream_callback; @@ -1013,8 +1054,10 @@ EXPORT_SYMBOL(amdtp_stream_pcm_pointer); */ void amdtp_stream_update(struct amdtp_stream *s) { + /* Precomputing. */ ACCESS_ONCE(s->source_node_id_field) = - (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24; + (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) & + CIP_SID_MASK; } EXPORT_SYMBOL(amdtp_stream_update); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 8a03a91e728b..26b909329e54 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -29,6 +29,9 @@ * packet is not continuous from an initial value. * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty * packet is wrong but the others are correct. + * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an + * packet is larger than IEC 61883-6 defines. Current implementation + * allows 5 times as large as IEC 61883-6 defines. */ enum cip_flags { CIP_NONBLOCKING = 0x00, @@ -40,6 +43,7 @@ enum cip_flags { CIP_SKIP_DBC_ZERO_CHECK = 0x20, CIP_SKIP_INIT_DBC_CHECK = 0x40, CIP_EMPTY_HAS_WRONG_DBC = 0x80, + CIP_JUMBO_PAYLOAD = 0x100, }; /** diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 611b7dae7ee5..27a04ac8ffee 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -33,6 +33,7 @@ static DEFINE_MUTEX(devices_mutex); static DECLARE_BITMAP(devices_used, SNDRV_CARDS); /* Offsets from information register. */ +#define INFO_OFFSET_BEBOB_VERSION 0x08 #define INFO_OFFSET_GUID 0x10 #define INFO_OFFSET_HW_MODEL_ID 0x18 #define INFO_OFFSET_HW_MODEL_REVISION 0x1c @@ -57,6 +58,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS); #define VEN_FOCUSRITE 0x0000130e #define VEN_MAUDIO1 0x00000d6c #define VEN_MAUDIO2 0x000007f5 +#define VEN_DIGIDESIGN 0x00a07e #define MODEL_FOCUSRITE_SAFFIRE_BOTH 0x00000000 #define MODEL_MAUDIO_AUDIOPHILE_BOTH 0x00010060 @@ -72,6 +74,7 @@ name_device(struct snd_bebob *bebob, unsigned int vendor_id) u32 hw_id; u32 data[2] = {0}; u32 revision; + u32 version; int err; /* get vendor name from root directory */ @@ -104,6 +107,12 @@ name_device(struct snd_bebob *bebob, unsigned int vendor_id) if (err < 0) goto end; + err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_BEBOB_VERSION, + &version); + if (err < 0) + goto end; + bebob->version = version; + strcpy(bebob->card->driver, "BeBoB"); strcpy(bebob->card->shortname, model); strcpy(bebob->card->mixername, model); @@ -364,6 +373,10 @@ static const struct ieee1394_device_id bebob_id_table[] = { SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal), /* Behringer, Digital Mixer X32 series (X-UF Card) */ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal), + /* Behringer, F-Control Audio 1616 */ + SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x001616, &spec_normal), + /* Behringer, F-Control Audio 610 */ + SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x000610, &spec_normal), /* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */ /* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */ SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal), @@ -433,11 +446,11 @@ static const struct ieee1394_device_id bebob_id_table[] = { /* M-Audio ProjectMix */ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_PROJECTMIX, &maudio_special_spec), + /* Digidesign Mbox 2 Pro */ + SND_BEBOB_DEV_ENTRY(VEN_DIGIDESIGN, 0x0000a9, &spec_normal), /* IDs are unknown but able to be supported */ /* Apogee, Mini-ME Firewire */ /* Apogee, Mini-DAC Firewire */ - /* Behringer, F-Control Audio 1616 */ - /* Behringer, F-Control Audio 610 */ /* Cakawalk, Sonar Power Studio 66 */ /* CME, UF400e */ /* ESI, Quotafire XL */ diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index dfbcd233178c..d23caca7f369 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -49,10 +49,15 @@ struct snd_bebob_stream_formation { extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES]; /* device specific operations */ -#define SND_BEBOB_CLOCK_INTERNAL "Internal" +enum snd_bebob_clock_type { + SND_BEBOB_CLOCK_TYPE_INTERNAL = 0, + SND_BEBOB_CLOCK_TYPE_EXTERNAL, + SND_BEBOB_CLOCK_TYPE_SYT, +}; struct snd_bebob_clock_spec { unsigned int num; const char *const *labels; + enum snd_bebob_clock_type *types; int (*get)(struct snd_bebob *bebob, unsigned int *id); }; struct snd_bebob_rate_spec { @@ -92,8 +97,7 @@ struct snd_bebob { struct amdtp_stream rx_stream; struct cmp_connection out_conn; struct cmp_connection in_conn; - atomic_t capture_substreams; - atomic_t playback_substreams; + atomic_t substreams_counter; struct snd_bebob_stream_formation tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES]; @@ -110,6 +114,9 @@ struct snd_bebob { /* for M-Audio special devices */ void *maudio_special_quirk; bool deferred_registration; + + /* For BeBoB version quirk. */ + unsigned int version; }; static inline int @@ -159,7 +166,8 @@ enum avc_bridgeco_plug_type { AVC_BRIDGECO_PLUG_TYPE_MIDI = 0x02, AVC_BRIDGECO_PLUG_TYPE_SYNC = 0x03, AVC_BRIDGECO_PLUG_TYPE_ANA = 0x04, - AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05 + AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05, + AVC_BRIDGECO_PLUG_TYPE_ADDITION = 0x06 }; static inline void avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES], @@ -205,8 +213,8 @@ int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit, /* for AMDTP streaming */ int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate); int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate); -int snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, - bool *internal); +int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob, + enum snd_bebob_clock_type *src); int snd_bebob_stream_discover(struct snd_bebob *bebob); int snd_bebob_stream_init_duplex(struct snd_bebob *bebob); int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate); diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c index fc67c1b7cb5b..a1a39494ea6c 100644 --- a/sound/firewire/bebob/bebob_focusrite.c +++ b/sound/firewire/bebob/bebob_focusrite.c @@ -103,11 +103,17 @@ saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value) &data, sizeof(__be32), 0); } -static const char *const saffirepro_10_clk_src_labels[] = { - SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock" +static enum snd_bebob_clock_type saffirepro_10_clk_src_types[] = { + SND_BEBOB_CLOCK_TYPE_INTERNAL, + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */ + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */ }; -static const char *const saffirepro_26_clk_src_labels[] = { - SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock" +static enum snd_bebob_clock_type saffirepro_26_clk_src_types[] = { + SND_BEBOB_CLOCK_TYPE_INTERNAL, + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */ + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT1 */ + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT2 */ + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */ }; /* Value maps between registers and labels for SaffirePro 10/26. */ static const signed char saffirepro_clk_maps[][SAFFIREPRO_CLOCK_SOURCE_COUNT] = { @@ -178,7 +184,7 @@ saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id) goto end; /* depending on hardware, use a different mapping */ - if (bebob->spec->clock->labels == saffirepro_10_clk_src_labels) + if (bebob->spec->clock->types == saffirepro_10_clk_src_types) map = saffirepro_clk_maps[0]; else map = saffirepro_clk_maps[1]; @@ -195,8 +201,9 @@ end: } struct snd_bebob_spec saffire_le_spec; -static const char *const saffire_both_clk_src_labels[] = { - SND_BEBOB_CLOCK_INTERNAL, "S/PDIF" +static enum snd_bebob_clock_type saffire_both_clk_src_types[] = { + SND_BEBOB_CLOCK_TYPE_INTERNAL, + SND_BEBOB_CLOCK_TYPE_EXTERNAL, }; static int saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id) @@ -259,8 +266,8 @@ static struct snd_bebob_rate_spec saffirepro_both_rate_spec = { }; /* Saffire Pro 26 I/O */ static struct snd_bebob_clock_spec saffirepro_26_clk_spec = { - .num = ARRAY_SIZE(saffirepro_26_clk_src_labels), - .labels = saffirepro_26_clk_src_labels, + .num = ARRAY_SIZE(saffirepro_26_clk_src_types), + .types = saffirepro_26_clk_src_types, .get = &saffirepro_both_clk_src_get, }; struct snd_bebob_spec saffirepro_26_spec = { @@ -270,8 +277,8 @@ struct snd_bebob_spec saffirepro_26_spec = { }; /* Saffire Pro 10 I/O */ static struct snd_bebob_clock_spec saffirepro_10_clk_spec = { - .num = ARRAY_SIZE(saffirepro_10_clk_src_labels), - .labels = saffirepro_10_clk_src_labels, + .num = ARRAY_SIZE(saffirepro_10_clk_src_types), + .types = saffirepro_10_clk_src_types, .get = &saffirepro_both_clk_src_get, }; struct snd_bebob_spec saffirepro_10_spec = { @@ -285,8 +292,8 @@ static struct snd_bebob_rate_spec saffire_both_rate_spec = { .set = &snd_bebob_stream_set_rate, }; static struct snd_bebob_clock_spec saffire_both_clk_spec = { - .num = ARRAY_SIZE(saffire_both_clk_src_labels), - .labels = saffire_both_clk_src_labels, + .num = ARRAY_SIZE(saffire_both_clk_src_types), + .types = saffire_both_clk_src_types, .get = &saffire_both_clk_src_get, }; /* Saffire LE */ diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c index 9ee25a63f684..057495d54ab0 100644 --- a/sound/firewire/bebob/bebob_maudio.c +++ b/sound/firewire/bebob/bebob_maudio.c @@ -340,9 +340,12 @@ end: } /* Clock source control for special firmware */ -static const char *const special_clk_labels[] = { - SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital", - "Word Clock", SND_BEBOB_CLOCK_INTERNAL}; +static enum snd_bebob_clock_type special_clk_types[] = { + SND_BEBOB_CLOCK_TYPE_INTERNAL, /* With digital mute */ + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* SPDIF/ADAT */ + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */ + SND_BEBOB_CLOCK_TYPE_INTERNAL, +}; static int special_clk_get(struct snd_bebob *bebob, unsigned int *id) { struct special_params *params = bebob->maudio_special_quirk; @@ -352,7 +355,13 @@ static int special_clk_get(struct snd_bebob *bebob, unsigned int *id) static int special_clk_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *einf) { - return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_labels), + static const char *const special_clk_labels[] = { + "Internal with Digital Mute", + "Digital", + "Word Clock", + "Internal" + }; + return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_types), special_clk_labels); } static int special_clk_ctl_get(struct snd_kcontrol *kctl, @@ -371,7 +380,7 @@ static int special_clk_ctl_put(struct snd_kcontrol *kctl, int err, id; id = uval->value.enumerated.item[0]; - if (id >= ARRAY_SIZE(special_clk_labels)) + if (id >= ARRAY_SIZE(special_clk_types)) return -EINVAL; mutex_lock(&bebob->mutex); @@ -708,8 +717,8 @@ static struct snd_bebob_rate_spec special_rate_spec = { .set = &special_set_rate, }; static struct snd_bebob_clock_spec special_clk_spec = { - .num = ARRAY_SIZE(special_clk_labels), - .labels = special_clk_labels, + .num = ARRAY_SIZE(special_clk_types), + .types = special_clk_types, .get = &special_clk_get, }; static struct snd_bebob_meter_spec special_meter_spec = { diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 63343d578df3..5681143925cd 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -17,7 +17,7 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) if (err < 0) goto end; - atomic_inc(&bebob->capture_substreams); + atomic_inc(&bebob->substreams_counter); err = snd_bebob_stream_start_duplex(bebob, 0); if (err < 0) snd_bebob_stream_lock_release(bebob); @@ -34,7 +34,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) if (err < 0) goto end; - atomic_inc(&bebob->playback_substreams); + atomic_inc(&bebob->substreams_counter); err = snd_bebob_stream_start_duplex(bebob, 0); if (err < 0) snd_bebob_stream_lock_release(bebob); @@ -46,7 +46,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; - atomic_dec(&bebob->capture_substreams); + atomic_dec(&bebob->substreams_counter); snd_bebob_stream_stop_duplex(bebob); snd_bebob_stream_lock_release(bebob); @@ -57,7 +57,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; - atomic_dec(&bebob->playback_substreams); + atomic_dec(&bebob->substreams_counter); snd_bebob_stream_stop_duplex(bebob); snd_bebob_stream_lock_release(bebob); diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 4a55561ed4ec..7a2c1f53bc44 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -157,7 +157,7 @@ pcm_open(struct snd_pcm_substream *substream) struct snd_bebob *bebob = substream->private_data; struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; - bool internal; + enum snd_bebob_clock_type src; int err; err = snd_bebob_stream_lock_try(bebob); @@ -168,7 +168,7 @@ pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - err = snd_bebob_stream_check_internal_clock(bebob, &internal); + err = snd_bebob_stream_get_clock_src(bebob, &src); if (err < 0) goto err_locked; @@ -176,7 +176,7 @@ pcm_open(struct snd_pcm_substream *substream) * When source of clock is internal or any PCM stream are running, * the available sampling rate is limited at current sampling rate. */ - if (!internal || + if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || amdtp_stream_pcm_running(&bebob->tx_stream) || amdtp_stream_pcm_running(&bebob->rx_stream)) { err = spec->get(bebob, &sampling_rate); @@ -213,7 +213,7 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) - atomic_inc(&bebob->capture_substreams); + atomic_inc(&bebob->substreams_counter); amdtp_stream_set_pcm_format(&bebob->tx_stream, params_format(hw_params)); return snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -226,7 +226,7 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) - atomic_inc(&bebob->playback_substreams); + atomic_inc(&bebob->substreams_counter); amdtp_stream_set_pcm_format(&bebob->rx_stream, params_format(hw_params)); return snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -239,7 +239,7 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream) struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - atomic_dec(&bebob->capture_substreams); + atomic_dec(&bebob->substreams_counter); snd_bebob_stream_stop_duplex(bebob); @@ -251,7 +251,7 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream) struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - atomic_dec(&bebob->playback_substreams); + atomic_dec(&bebob->substreams_counter); snd_bebob_stream_stop_duplex(bebob); diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c index 335da64506e0..301cc6a93945 100644 --- a/sound/firewire/bebob/bebob_proc.c +++ b/sound/firewire/bebob/bebob_proc.c @@ -132,25 +132,27 @@ static void proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { + static const char *const clk_labels[] = { + "Internal", + "External", + "SYT-Match", + }; struct snd_bebob *bebob = entry->private_data; struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; - unsigned int rate, id; - bool internal; + enum snd_bebob_clock_type src; + unsigned int rate; if (rate_spec->get(bebob, &rate) >= 0) snd_iprintf(buffer, "Sampling rate: %d\n", rate); - if (clk_spec) { - if (clk_spec->get(bebob, &id) >= 0) + if (snd_bebob_stream_get_clock_src(bebob, &src) >= 0) { + if (clk_spec) snd_iprintf(buffer, "Clock Source: %s\n", - clk_spec->labels[id]); - } else { - if (snd_bebob_stream_check_internal_clock(bebob, - &internal) >= 0) + clk_labels[src]); + else snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n", - (internal) ? "Internal" : "External", - bebob->sync_input_plug); + clk_labels[src], bebob->sync_input_plug); } } diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 98e4fc8121a1..5be5242e1ed8 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -8,7 +8,7 @@ #include "./bebob.h" -#define CALLBACK_TIMEOUT 1000 +#define CALLBACK_TIMEOUT 2000 #define FW_ISO_RESOURCE_DELAY 1000 /* @@ -116,16 +116,15 @@ end: return err; } -int -snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal) +int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob, + enum snd_bebob_clock_type *src) { struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7]; unsigned int id; + enum avc_bridgeco_plug_type type; int err = 0; - *internal = false; - /* 1.The device has its own operation to switch source of clock */ if (clk_spec) { err = clk_spec->get(bebob, &id); @@ -143,10 +142,7 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal) goto end; } - if (strncmp(clk_spec->labels[id], SND_BEBOB_CLOCK_INTERNAL, - strlen(SND_BEBOB_CLOCK_INTERNAL)) == 0) - *internal = true; - + *src = clk_spec->types[id]; goto end; } @@ -155,7 +151,7 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal) * to use internal clock always */ if (bebob->sync_input_plug < 0) { - *internal = true; + *src = SND_BEBOB_CLOCK_TYPE_INTERNAL; goto end; } @@ -178,18 +174,79 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal) * Here check the first field. This field is used for direction. */ if (input[0] == 0xff) { - *internal = true; + *src = SND_BEBOB_CLOCK_TYPE_INTERNAL; goto end; } - /* - * If source of clock is internal CSR, Music Sub Unit Sync Input is - * a destination of Music Sub Unit Sync Output. - */ - *internal = ((input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) && - (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT) && - (input[2] == 0x0c) && - (input[3] == 0x00)); + /* The source from any output plugs is for one purpose only. */ + if (input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) { + /* + * In BeBoB architecture, the source from music subunit may + * bypass from oPCR[0]. This means that this source gives + * synchronization to IEEE 1394 cycle start packet. + */ + if (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT && + input[2] == 0x0c) { + *src = SND_BEBOB_CLOCK_TYPE_INTERNAL; + goto end; + } + /* The source from any input units is for several purposes. */ + } else if (input[1] == AVC_BRIDGECO_PLUG_MODE_UNIT) { + if (input[2] == AVC_BRIDGECO_PLUG_UNIT_ISOC) { + if (input[3] == 0x00) { + /* + * This source comes from iPCR[0]. This means + * that presentation timestamp calculated by + * SYT series of the received packets. In + * short, this driver is the master of + * synchronization. + */ + *src = SND_BEBOB_CLOCK_TYPE_SYT; + goto end; + } else { + /* + * This source comes from iPCR[1-29]. This + * means that the synchronization stream is not + * the Audio/MIDI compound stream. + */ + *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL; + goto end; + } + } else if (input[2] == AVC_BRIDGECO_PLUG_UNIT_EXT) { + /* Check type of this plug. */ + avc_bridgeco_fill_unit_addr(addr, + AVC_BRIDGECO_PLUG_DIR_IN, + AVC_BRIDGECO_PLUG_UNIT_EXT, + input[3]); + err = avc_bridgeco_get_plug_type(bebob->unit, addr, + &type); + if (err < 0) + goto end; + + if (type == AVC_BRIDGECO_PLUG_TYPE_DIG) { + /* + * SPDIF/ADAT or sometimes (not always) word + * clock. + */ + *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL; + goto end; + } else if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) { + /* Often word clock. */ + *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL; + goto end; + } else if (type == AVC_BRIDGECO_PLUG_TYPE_ADDITION) { + /* + * Not standard. + * Mostly, additional internal clock. + */ + *src = SND_BEBOB_CLOCK_TYPE_INTERNAL; + goto end; + } + } + } + + /* Not supported. */ + err = -EIO; end: return err; } @@ -417,8 +474,24 @@ destroy_both_connections(struct snd_bebob *bebob) static int get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode) { - /* currently this module doesn't support SYT-Match mode */ - *sync_mode = CIP_SYNC_TO_DEVICE; + enum snd_bebob_clock_type src; + int err; + + err = snd_bebob_stream_get_clock_src(bebob, &src); + if (err < 0) + return err; + + switch (src) { + case SND_BEBOB_CLOCK_TYPE_INTERNAL: + case SND_BEBOB_CLOCK_TYPE_EXTERNAL: + *sync_mode = CIP_SYNC_TO_DEVICE; + break; + default: + case SND_BEBOB_CLOCK_TYPE_SYT: + *sync_mode = 0; + break; + } + return 0; } @@ -467,6 +540,17 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) /* See comments in next function */ init_completion(&bebob->bus_reset); bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK; + + /* + * BeBoB v3 transfers packets with these qurks: + * - In the beginning of streaming, the value of dbc is incremented + * even if no data blocks are transferred. + * - The value of dbc is reset suddenly. + */ + if (bebob->version > 2) + bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC | + CIP_SKIP_DBC_ZERO_CHECK; + /* * At high sampling rate, M-Audio special firmware transmits empty * packet with the value of dbc incremented by 8 but the others are @@ -490,7 +574,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) { struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; struct amdtp_stream *master, *slave; - atomic_t *slave_substreams; enum cip_flags sync_mode; unsigned int curr_rate; bool updated = false; @@ -515,8 +598,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) mutex_lock(&bebob->mutex); /* Need no substreams */ - if (atomic_read(&bebob->playback_substreams) == 0 && - atomic_read(&bebob->capture_substreams) == 0) + if (atomic_read(&bebob->substreams_counter) == 0) goto end; err = get_sync_mode(bebob, &sync_mode); @@ -525,11 +607,9 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) if (sync_mode == CIP_SYNC_TO_DEVICE) { master = &bebob->tx_stream; slave = &bebob->rx_stream; - slave_substreams = &bebob->playback_substreams; } else { master = &bebob->rx_stream; slave = &bebob->tx_stream; - slave_substreams = &bebob->capture_substreams; } /* @@ -630,7 +710,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) } /* start slave if needed */ - if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) { + if (!amdtp_stream_running(slave)) { err = start_stream(bebob, slave, rate); if (err < 0) { dev_err(&bebob->unit->device, @@ -656,31 +736,25 @@ end: void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) { struct amdtp_stream *master, *slave; - atomic_t *master_substreams, *slave_substreams; if (bebob->master == &bebob->rx_stream) { slave = &bebob->tx_stream; master = &bebob->rx_stream; - slave_substreams = &bebob->capture_substreams; - master_substreams = &bebob->playback_substreams; } else { slave = &bebob->rx_stream; master = &bebob->tx_stream; - slave_substreams = &bebob->playback_substreams; - master_substreams = &bebob->capture_substreams; } mutex_lock(&bebob->mutex); - if (atomic_read(slave_substreams) == 0) { + if (atomic_read(&bebob->substreams_counter) == 0) { + amdtp_stream_pcm_abort(master); + amdtp_stream_stop(master); + amdtp_stream_pcm_abort(slave); amdtp_stream_stop(slave); - if (atomic_read(master_substreams) == 0) { - amdtp_stream_pcm_abort(master); - amdtp_stream_stop(master); - break_both_connections(bebob); - } + break_both_connections(bebob); } mutex_unlock(&bebob->mutex); diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c index ad635004d699..9242e33d2cf1 100644 --- a/sound/firewire/bebob/bebob_terratec.c +++ b/sound/firewire/bebob/bebob_terratec.c @@ -8,8 +8,10 @@ #include "./bebob.h" -static const char *const phase88_rack_clk_src_labels[] = { - SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock" +static enum snd_bebob_clock_type phase88_rack_clk_src_types[] = { + SND_BEBOB_CLOCK_TYPE_INTERNAL, + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */ + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */ }; static int phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id) @@ -34,13 +36,23 @@ end: return err; } -static const char *const phase24_series_clk_src_labels[] = { - SND_BEBOB_CLOCK_INTERNAL, "Digital In" +static enum snd_bebob_clock_type phase24_series_clk_src_types[] = { + SND_BEBOB_CLOCK_TYPE_INTERNAL, + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */ }; static int phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id) { - return avc_audio_get_selector(bebob->unit, 0, 4, id); + int err; + + err = avc_audio_get_selector(bebob->unit, 0, 4, id); + if (err < 0) + return err; + + if (*id >= ARRAY_SIZE(phase24_series_clk_src_types)) + return -EIO; + + return 0; } static struct snd_bebob_rate_spec phase_series_rate_spec = { @@ -50,8 +62,8 @@ static struct snd_bebob_rate_spec phase_series_rate_spec = { /* PHASE 88 Rack FW */ static struct snd_bebob_clock_spec phase88_rack_clk = { - .num = ARRAY_SIZE(phase88_rack_clk_src_labels), - .labels = phase88_rack_clk_src_labels, + .num = ARRAY_SIZE(phase88_rack_clk_src_types), + .types = phase88_rack_clk_src_types, .get = &phase88_rack_clk_src_get, }; struct snd_bebob_spec phase88_rack_spec = { @@ -62,8 +74,8 @@ struct snd_bebob_spec phase88_rack_spec = { /* 'PHASE 24 FW' and 'PHASE X24 FW' */ static struct snd_bebob_clock_spec phase24_series_clk = { - .num = ARRAY_SIZE(phase24_series_clk_src_labels), - .labels = phase24_series_clk_src_labels, + .num = ARRAY_SIZE(phase24_series_clk_src_types), + .types = phase24_series_clk_src_types, .get = &phase24_series_clk_src_get, }; struct snd_bebob_spec phase24_series_spec = { diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c index ef1fe3823a9c..58101702410b 100644 --- a/sound/firewire/bebob/bebob_yamaha.c +++ b/sound/firewire/bebob/bebob_yamaha.c @@ -28,15 +28,27 @@ * reccomend users to close ffado-mixer at 192.0kHz if mixer is needless. */ -static const char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"}; +static enum snd_bebob_clock_type clk_src_types[] = { + SND_BEBOB_CLOCK_TYPE_INTERNAL, + SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */ +}; static int clk_src_get(struct snd_bebob *bebob, unsigned int *id) { - return avc_audio_get_selector(bebob->unit, 0, 4, id); + int err; + + err = avc_audio_get_selector(bebob->unit, 0, 4, id); + if (err < 0) + return err; + + if (*id >= ARRAY_SIZE(clk_src_types)) + return -EIO; + + return 0; } static struct snd_bebob_clock_spec clock_spec = { - .num = ARRAY_SIZE(clk_src_labels), - .labels = clk_src_labels, + .num = ARRAY_SIZE(clk_src_types), + .types = clk_src_types, .get = &clk_src_get, }; static struct snd_bebob_rate_spec rate_spec = { diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index e6757cd85724..873d40fc4509 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -232,9 +232,15 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, goto end; } - /* OXFW starts to transmit packets with non-zero dbc. */ + /* + * OXFW starts to transmit packets with non-zero dbc. + * OXFW postpone transferring packets till handling any asynchronous + * packets. As a result, next isochronous packet includes more data + * blocks than IEC 61883-6 defines. + */ if (stream == &oxfw->tx_stream) - oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK; + oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK | + CIP_JUMBO_PAYLOAD; end: return err; } diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index 001c6588a5ff..3129546398d0 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -1,3 +1,29 @@ config SND_HDA_CORE tristate select REGMAP + +config SND_HDA_DSP_LOADER + bool + +config SND_HDA_I915 + bool + default y + depends on DRM_I915 + depends on SND_HDA_CORE + +config SND_HDA_EXT_CORE + tristate + select SND_HDA_CORE + +config SND_HDA_PREALLOC_SIZE + int "Pre-allocated buffer size for HD-audio driver" + range 0 32768 + default 64 + help + Specifies the default pre-allocated buffer-size in kB for the + HD-audio driver. A larger buffer (e.g. 2048) is preferred + for systems using PulseAudio. The default 64 is chosen just + for compatibility reasons. + + Note that the pre-allocation size can be changed dynamically + via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 7a359f5b7e25..7e999c995cdc 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,7 +1,13 @@ snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ - hdac_regmap.o array.o + hdac_regmap.o hdac_controller.o hdac_stream.o array.o snd-hda-core-objs += trace.o CFLAGS_trace.o := -I$(src) +# for sync with i915 gfx driver +snd-hda-core-$(CONFIG_SND_HDA_I915) += hdac_i915.o + obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o + +#extended hda +obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/ diff --git a/sound/hda/ext/Makefile b/sound/hda/ext/Makefile new file mode 100644 index 000000000000..9b6f641c7777 --- /dev/null +++ b/sound/hda/ext/Makefile @@ -0,0 +1,3 @@ +snd-hda-ext-core-objs := hdac_ext_bus.o hdac_ext_controller.o hdac_ext_stream.o + +obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c new file mode 100644 index 000000000000..0aa5d9eb6c3f --- /dev/null +++ b/sound/hda/ext/hdac_ext_bus.c @@ -0,0 +1,174 @@ +/* + * hdac-ext-bus.c - HD-audio extended core bus functions. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Jeeja KP <jeeja.kp@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <sound/hdaudio_ext.h> + +MODULE_DESCRIPTION("HDA extended core"); +MODULE_LICENSE("GPL v2"); + +static void hdac_ext_writel(u32 value, u32 __iomem *addr) +{ + writel(value, addr); +} + +static u32 hdac_ext_readl(u32 __iomem *addr) +{ + return readl(addr); +} + +static void hdac_ext_writew(u16 value, u16 __iomem *addr) +{ + writew(value, addr); +} + +static u16 hdac_ext_readw(u16 __iomem *addr) +{ + return readw(addr); +} + +static void hdac_ext_writeb(u8 value, u8 __iomem *addr) +{ + writeb(value, addr); +} + +static u8 hdac_ext_readb(u8 __iomem *addr) +{ + return readb(addr); +} + +static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type, + size_t size, struct snd_dma_buffer *buf) +{ + return snd_dma_alloc_pages(type, bus->dev, size, buf); +} + +static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) +{ + snd_dma_free_pages(buf); +} + +static const struct hdac_io_ops hdac_ext_default_io = { + .reg_writel = hdac_ext_writel, + .reg_readl = hdac_ext_readl, + .reg_writew = hdac_ext_writew, + .reg_readw = hdac_ext_readw, + .reg_writeb = hdac_ext_writeb, + .reg_readb = hdac_ext_readb, + .dma_alloc_pages = hdac_ext_dma_alloc_pages, + .dma_free_pages = hdac_ext_dma_free_pages, +}; + +/** + * snd_hdac_ext_bus_init - initialize a HD-audio extended bus + * @ebus: the pointer to extended bus object + * @dev: device pointer + * @ops: bus verb operators + * @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use + * default ops + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev, + const struct hdac_bus_ops *ops, + const struct hdac_io_ops *io_ops) +{ + int ret; + static int idx; + + /* check if io ops are provided, if not load the defaults */ + if (io_ops == NULL) + io_ops = &hdac_ext_default_io; + + ret = snd_hdac_bus_init(&ebus->bus, dev, ops, io_ops); + if (ret < 0) + return ret; + + INIT_LIST_HEAD(&ebus->hlink_list); + ebus->idx = idx++; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); + +/** + * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus + * @ebus: the pointer to extended bus object + */ +void snd_hdac_ext_bus_exit(struct hdac_ext_bus *ebus) +{ + snd_hdac_bus_exit(&ebus->bus); + WARN_ON(!list_empty(&ebus->hlink_list)); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit); + +static void default_release(struct device *dev) +{ + snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev)); +} + +/** + * snd_hdac_ext_device_init - initialize the HDA extended codec base device + * @ebus: hdac extended bus to attach to + * @addr: codec address + * + * Returns zero for success or a negative error code. + */ +int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) +{ + struct hdac_device *hdev = NULL; + struct hdac_bus *bus = ebus_to_hbus(ebus); + char name[15]; + int ret; + + hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + + snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); + + ret = snd_hdac_device_init(hdev, bus, name, addr); + if (ret < 0) { + dev_err(bus->dev, "device init failed for hdac device\n"); + return ret; + } + hdev->type = HDA_DEV_ASOC; + hdev->dev.release = default_release; + + ret = snd_hdac_device_register(hdev); + if (ret) { + dev_err(bus->dev, "failed to register hdac device\n"); + snd_hdac_ext_bus_device_exit(hdev); + return ret; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init); + +/** + * snd_hdac_ext_bus_device_exit - clean up a HD-audio extended codec base device + * @hdev: hdac device to clean up + */ +void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev) +{ + snd_hdac_device_exit(hdev); + kfree(hdev); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c new file mode 100644 index 000000000000..b2da19b60f4e --- /dev/null +++ b/sound/hda/ext/hdac_ext_controller.c @@ -0,0 +1,288 @@ +/* + * hdac-ext-controller.c - HD-audio extended controller functions. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Jeeja KP <jeeja.kp@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/delay.h> +#include <linux/slab.h> +#include <sound/hda_register.h> +#include <sound/hdaudio_ext.h> + +/* + * maximum HDAC capablities we should parse to avoid endless looping: + * currently we have 4 extended caps, so this is future proof for now. + * extend when this limit is seen meeting in real HW + */ +#define HDAC_MAX_CAPS 10 + +/** + * snd_hdac_ext_bus_parse_capabilities - parse capablity structure + * @ebus: the pointer to extended bus object + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *ebus) +{ + unsigned int cur_cap; + unsigned int offset; + struct hdac_bus *bus = &ebus->bus; + unsigned int counter = 0; + + offset = snd_hdac_chip_readl(bus, LLCH); + + if (offset < 0) + return -EIO; + + /* Lets walk the linked capabilities list */ + do { + cur_cap = _snd_hdac_chip_read(l, bus, offset); + + if (cur_cap < 0) + return -EIO; + + dev_dbg(bus->dev, "Capability version: 0x%x\n", + ((cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF)); + + dev_dbg(bus->dev, "HDA capability ID: 0x%x\n", + (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF); + + switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) { + case AZX_ML_CAP_ID: + dev_dbg(bus->dev, "Found ML capability\n"); + ebus->mlcap = bus->remap_addr + offset; + break; + + case AZX_GTS_CAP_ID: + dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset); + ebus->gtscap = bus->remap_addr + offset; + break; + + case AZX_PP_CAP_ID: + /* PP capability found, the Audio DSP is present */ + dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset); + ebus->ppcap = bus->remap_addr + offset; + break; + + case AZX_SPB_CAP_ID: + /* SPIB capability found, handler function */ + dev_dbg(bus->dev, "Found SPB capability\n"); + ebus->spbcap = bus->remap_addr + offset; + break; + + default: + dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap); + break; + } + + counter++; + + if (counter > HDAC_MAX_CAPS) { + dev_err(bus->dev, "We exceeded HDAC Ext capablities!!!\n"); + break; + } + + /* read the offset of next capabiity */ + offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK; + + } while (offset); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_parse_capabilities); + +/* + * processing pipe helpers - these helpers are useful for dealing with HDA + * new capability of processing pipelines + */ + +/** + * snd_hdac_ext_bus_ppcap_enable - enable/disable processing pipe capability + * @ebus: HD-audio extended core bus + * @enable: flag to turn on/off the capability + */ +void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable) +{ + struct hdac_bus *bus = &ebus->bus; + + if (!ebus->ppcap) { + dev_err(bus->dev, "Address of PP capability is NULL"); + return; + } + + if (enable) + snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN); + else + snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable); + +/** + * snd_hdac_ext_bus_ppcap_int_enable - ppcap interrupt enable/disable + * @ebus: HD-audio extended core bus + * @enable: flag to enable/disable interrupt + */ +void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable) +{ + struct hdac_bus *bus = &ebus->bus; + + if (!ebus->ppcap) { + dev_err(bus->dev, "Address of PP capability is NULL\n"); + return; + } + + if (enable) + snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE); + else + snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable); + +/* + * Multilink helpers - these helpers are useful for dealing with HDA + * new multilink capability + */ + +/** + * snd_hdac_ext_bus_get_ml_capabilities - get multilink capability + * @ebus: HD-audio extended core bus + * + * This will parse all links and read the mlink capabilities and add them + * in hlink_list of extended hdac bus + * Note: this will be freed on bus exit by driver + */ +int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) +{ + int idx; + u32 link_count; + struct hdac_ext_link *hlink; + struct hdac_bus *bus = &ebus->bus; + + link_count = readl(ebus->mlcap + AZX_REG_ML_MLCD) + 1; + + dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count); + + for (idx = 0; idx < link_count; idx++) { + hlink = kzalloc(sizeof(*hlink), GFP_KERNEL); + if (!hlink) + return -ENOMEM; + hlink->index = idx; + hlink->bus = bus; + hlink->ml_addr = ebus->mlcap + AZX_ML_BASE + + (AZX_ML_INTERVAL * idx); + hlink->lcaps = snd_hdac_chip_readl(bus, ML_LCAP); + hlink->lsdiid = snd_hdac_chip_readw(bus, ML_LSDIID); + + list_add_tail(&hlink->list, &ebus->hlink_list); + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities); + +/** + * snd_hdac_link_free_all- free hdac extended link objects + * + * @ebus: HD-audio ext core bus + */ + +void snd_hdac_link_free_all(struct hdac_ext_bus *ebus) +{ + struct hdac_ext_link *l; + + while (!list_empty(&ebus->hlink_list)) { + l = list_first_entry(&ebus->hlink_list, struct hdac_ext_link, list); + list_del(&l->list); + kfree(l); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_link_free_all); + +/** + * snd_hdac_ext_bus_get_link_index - get link based on codec name + * @ebus: HD-audio extended core bus + * @codec_name: codec name + */ +struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *ebus, + const char *codec_name) +{ + int i; + struct hdac_ext_link *hlink = NULL; + int bus_idx, addr; + + if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2) + return NULL; + if (ebus->idx != bus_idx) + return NULL; + + list_for_each_entry(hlink, &ebus->hlink_list, list) { + for (i = 0; i < HDA_MAX_CODECS; i++) { + if (hlink->lsdiid & (0x1 << addr)) + return hlink; + } + } + + return NULL; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_link); + +static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable) +{ + int timeout; + u32 val; + int mask = (1 << AZX_MLCTL_CPA); + + udelay(3); + timeout = 50; + + do { + val = snd_hdac_chip_readl(link->bus, ML_LCTL); + if (enable) { + if (((val & mask) >> AZX_MLCTL_CPA)) + return 0; + } else { + if (!((val & mask) >> AZX_MLCTL_CPA)) + return 0; + } + udelay(3); + } while (--timeout); + + return -EIO; +} + +/** + * snd_hdac_ext_bus_link_power_up -power up hda link + * @link: HD-audio extended link + */ +int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link) +{ + snd_hdac_chip_updatel(link->bus, ML_LCTL, 0, AZX_MLCTL_SPA); + + return check_hdac_link_power_active(link, true); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up); + +/** + * snd_hdac_ext_bus_link_power_down -power down hda link + * @link: HD-audio extended link + */ +int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *link) +{ + snd_hdac_chip_updatel(link->bus, ML_LCTL, AZX_MLCTL_SPA, 0); + + return check_hdac_link_power_active(link, false); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c new file mode 100644 index 000000000000..f8ffbdbb450d --- /dev/null +++ b/sound/hda/ext/hdac_ext_stream.c @@ -0,0 +1,452 @@ +/* + * hdac-ext-stream.c - HD-audio extended stream operations. + * + * Copyright (C) 2015 Intel Corp + * Author: Jeeja KP <jeeja.kp@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/delay.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/hda_register.h> +#include <sound/hdaudio_ext.h> + +/** + * snd_hdac_ext_stream_init - initialize each stream (aka device) + * @ebus: HD-audio ext core bus + * @stream: HD-audio ext core stream object to initialize + * @idx: stream index number + * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) + * @tag: the tag id to assign + * + * initialize the stream, if ppcap is enabled then init those and then + * invoke hdac stream initialization routine + */ +void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus, + struct hdac_ext_stream *stream, + int idx, int direction, int tag) +{ + struct hdac_bus *bus = &ebus->bus; + + if (ebus->ppcap) { + stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE + + AZX_PPHC_INTERVAL * idx; + + stream->pplc_addr = ebus->ppcap + AZX_PPLC_BASE + + AZX_PPLC_MULTI * ebus->num_streams + + AZX_PPLC_INTERVAL * idx; + } + + stream->decoupled = false; + snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); + +/** + * snd_hdac_ext_stream_init_all - create and initialize the stream objects + * for an extended hda bus + * @ebus: HD-audio ext core bus + * @start_idx: start index for streams + * @num_stream: number of streams to initialize + * @dir: direction of streams + */ +int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx, + int num_stream, int dir) +{ + int stream_tag = 0; + int i, tag, idx = start_idx; + + for (i = 0; i < num_stream; i++) { + struct hdac_ext_stream *stream = + kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + tag = ++stream_tag; + snd_hdac_ext_stream_init(ebus, stream, idx, dir, tag); + idx++; + } + + return 0; + +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); + +/** + * snd_hdac_stream_free_all - free hdac extended stream objects + * + * @ebus: HD-audio ext core bus + */ +void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus) +{ + struct hdac_stream *s; + struct hdac_ext_stream *stream; + struct hdac_bus *bus = ebus_to_hbus(ebus); + + while (!list_empty(&bus->stream_list)) { + s = list_first_entry(&bus->stream_list, struct hdac_stream, list); + stream = stream_to_hdac_ext_stream(s); + list_del(&s->list); + kfree(stream); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all); + +/** + * snd_hdac_ext_stream_decouple - decouple the hdac stream + * @ebus: HD-audio ext core bus + * @stream: HD-audio ext core stream object to initialize + * @decouple: flag to decouple + */ +void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus, + struct hdac_ext_stream *stream, bool decouple) +{ + struct hdac_stream *hstream = &stream->hstream; + struct hdac_bus *bus = &ebus->bus; + + spin_lock_irq(&bus->reg_lock); + if (decouple) + snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, + AZX_PPCTL_PROCEN(hstream->index)); + else + snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, + AZX_PPCTL_PROCEN(hstream->index), 0); + stream->decoupled = decouple; + spin_unlock_irq(&bus->reg_lock); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); + +/** + * snd_hdac_ext_linkstream_start - start a stream + * @stream: HD-audio ext core stream to start + */ +void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream) +{ + snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_RUN); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start); + +/** + * snd_hdac_ext_link_stream_clear - stop a stream DMA + * @stream: HD-audio ext core stream to stop + */ +void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream) +{ + snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear); + +/** + * snd_hdac_ext_link_stream_reset - reset a stream + * @stream: HD-audio ext core stream to reset + */ +void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream) +{ + unsigned char val; + int timeout; + + snd_hdac_ext_link_stream_clear(stream); + + snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_STRST); + udelay(3); + timeout = 50; + do { + val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & + AZX_PPLCCTL_STRST; + if (val) + break; + udelay(3); + } while (--timeout); + val &= ~AZX_PPLCCTL_STRST; + writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); + udelay(3); + + timeout = 50; + /* waiting for hardware to report that the stream is out of reset */ + do { + val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; + if (!val) + break; + udelay(3); + } while (--timeout); + +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset); + +/** + * snd_hdac_ext_link_stream_setup - set up the SD for streaming + * @stream: HD-audio ext core stream to set up + * @fmt: stream format + */ +int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt) +{ + struct hdac_stream *hstream = &stream->hstream; + unsigned int val; + + /* make sure the run bit is zero for SD */ + snd_hdac_ext_link_stream_clear(stream); + /* program the stream_tag */ + val = readl(stream->pplc_addr + AZX_REG_PPLCCTL); + val = (val & ~AZX_PPLCCTL_STRM_MASK) | + (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT); + writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); + + /* program the stream format */ + writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup); + +/** + * snd_hdac_ext_link_set_stream_id - maps stream id to link output + * @link: HD-audio ext link to set up + * @stream: stream id + */ +void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, + int stream) +{ + snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id); + +/** + * snd_hdac_ext_link_clear_stream_id - maps stream id to link output + * @link: HD-audio ext link to set up + * @stream: stream id + */ +void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, + int stream) +{ + snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, 0, (1 << stream)); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id); + +static struct hdac_ext_stream * +hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus, + struct snd_pcm_substream *substream) +{ + struct hdac_ext_stream *res = NULL; + struct hdac_stream *stream = NULL; + struct hdac_bus *hbus = &ebus->bus; + + if (!ebus->ppcap) { + dev_err(hbus->dev, "stream type not supported\n"); + return NULL; + } + + list_for_each_entry(stream, &hbus->stream_list, list) { + struct hdac_ext_stream *hstream = container_of(stream, + struct hdac_ext_stream, + hstream); + if (stream->direction != substream->stream) + continue; + + /* check if decoupled stream and not in use is available */ + if (hstream->decoupled && !hstream->link_locked) { + res = hstream; + break; + } + + if (!hstream->link_locked) { + snd_hdac_ext_stream_decouple(ebus, hstream, true); + res = hstream; + break; + } + } + if (res) { + spin_lock_irq(&hbus->reg_lock); + res->link_locked = 1; + res->link_substream = substream; + spin_unlock_irq(&hbus->reg_lock); + } + return res; +} + +static struct hdac_ext_stream * +hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, + struct snd_pcm_substream *substream) +{ + struct hdac_ext_stream *res = NULL; + struct hdac_stream *stream = NULL; + struct hdac_bus *hbus = &ebus->bus; + int key; + + if (!ebus->ppcap) { + dev_err(hbus->dev, "stream type not supported\n"); + return NULL; + } + + /* make a non-zero unique key for the substream */ + key = (substream->pcm->device << 16) | (substream->number << 2) | + (substream->stream + 1); + + list_for_each_entry(stream, &hbus->stream_list, list) { + struct hdac_ext_stream *hstream = container_of(stream, + struct hdac_ext_stream, + hstream); + if (stream->direction != substream->stream) + continue; + + if (stream->opened) { + if (!hstream->decoupled) + snd_hdac_ext_stream_decouple(ebus, hstream, true); + res = hstream; + break; + } + } + if (res) { + spin_lock_irq(&hbus->reg_lock); + res->hstream.opened = 1; + res->hstream.running = 0; + res->hstream.assigned_key = key; + res->hstream.substream = substream; + spin_unlock_irq(&hbus->reg_lock); + } + + return res; +} + +/** + * snd_hdac_ext_stream_assign - assign a stream for the PCM + * @ebus: HD-audio ext core bus + * @substream: PCM substream to assign + * @type: type of stream (coupled, host or link stream) + * + * This assigns the stream based on the type (coupled/host/link), for the + * given PCM substream, assigns it and returns the stream object + * + * coupled: Looks for an unused stream + * host: Looks for an unused decoupled host stream + * link: Looks for an unused decoupled link stream + * + * If no stream is free, returns NULL. The function tries to keep using + * the same stream object when it's used beforehand. when a stream is + * decoupled, it becomes a host stream and link stream. + */ +struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_ext_bus *ebus, + struct snd_pcm_substream *substream, + int type) +{ + struct hdac_ext_stream *hstream = NULL; + struct hdac_stream *stream = NULL; + struct hdac_bus *hbus = &ebus->bus; + + switch (type) { + case HDAC_EXT_STREAM_TYPE_COUPLED: + stream = snd_hdac_stream_assign(hbus, substream); + if (stream) + hstream = container_of(stream, + struct hdac_ext_stream, hstream); + return hstream; + + case HDAC_EXT_STREAM_TYPE_HOST: + return hdac_ext_host_stream_assign(ebus, substream); + + case HDAC_EXT_STREAM_TYPE_LINK: + return hdac_ext_link_stream_assign(ebus, substream); + + default: + return NULL; + } +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); + +/** + * snd_hdac_ext_stream_release - release the assigned stream + * @stream: HD-audio ext core stream to release + * @type: type of stream (coupled, host or link stream) + * + * Release the stream that has been assigned by snd_hdac_ext_stream_assign(). + */ +void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) +{ + struct hdac_bus *bus = stream->hstream.bus; + struct hdac_ext_bus *ebus = hbus_to_ebus(bus); + + switch (type) { + case HDAC_EXT_STREAM_TYPE_COUPLED: + snd_hdac_stream_release(&stream->hstream); + break; + + case HDAC_EXT_STREAM_TYPE_HOST: + if (stream->decoupled) { + snd_hdac_ext_stream_decouple(ebus, stream, false); + snd_hdac_stream_release(&stream->hstream); + } + break; + + case HDAC_EXT_STREAM_TYPE_LINK: + if (stream->decoupled) + snd_hdac_ext_stream_decouple(ebus, stream, false); + spin_lock_irq(&bus->reg_lock); + stream->link_locked = 0; + stream->link_substream = NULL; + spin_unlock_irq(&bus->reg_lock); + break; + + default: + dev_dbg(bus->dev, "Invalid type %d\n", type); + } + +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release); + +/** + * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream + * @ebus: HD-audio ext core bus + * @enable: flag to enable/disable SPIB + * @index: stream index for which SPIB need to be enabled + */ +void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus, + bool enable, int index) +{ + u32 mask = 0; + u32 register_mask = 0; + struct hdac_bus *bus = &ebus->bus; + + if (!ebus->spbcap) { + dev_err(bus->dev, "Address of SPB capability is NULL"); + return; + } + + mask |= (1 << index); + + register_mask = snd_hdac_chip_readl(bus, SPB_SPBFCCTL); + + mask |= register_mask; + + if (enable) + snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask); + else + snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); + +/** + * snd_hdac_ext_stop_streams - stop all stream if running + * @ebus: HD-audio ext core bus + */ +void snd_hdac_ext_stop_streams(struct hdac_ext_bus *ebus) +{ + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_stream *stream; + + if (bus->chip_init) { + list_for_each_entry(stream, &bus->stream_list, list) + snd_hdac_stream_stop(stream); + snd_hdac_bus_stop_chip(bus); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c index 519914a12e8a..89c2711baaaf 100644 --- a/sound/hda/hda_bus_type.c +++ b/sound/hda/hda_bus_type.c @@ -10,6 +10,40 @@ MODULE_DESCRIPTION("HD-audio bus"); MODULE_LICENSE("GPL"); +/** + * hdac_get_device_id - gets the hdac device id entry + * @hdev: HD-audio core device + * @drv: HD-audio codec driver + * + * Compares the hdac device vendor_id and revision_id to the hdac_device + * driver id_table and returns the matching device id entry. + */ +const struct hda_device_id * +hdac_get_device_id(struct hdac_device *hdev, struct hdac_driver *drv) +{ + if (drv->id_table) { + const struct hda_device_id *id = drv->id_table; + + while (id->vendor_id) { + if (hdev->vendor_id == id->vendor_id && + (!id->rev_id || id->rev_id == hdev->revision_id)) + return id; + id++; + } + } + + return NULL; +} +EXPORT_SYMBOL_GPL(hdac_get_device_id); + +static int hdac_codec_match(struct hdac_device *dev, struct hdac_driver *drv) +{ + if (hdac_get_device_id(dev, drv)) + return 1; + else + return 0; +} + static int hda_bus_match(struct device *dev, struct device_driver *drv) { struct hdac_device *hdev = dev_to_hdac_dev(dev); @@ -17,8 +51,15 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv) if (hdev->type != hdrv->type) return 0; + + /* + * if driver provided a match function use that otherwise we will + * use hdac_codec_match function + */ if (hdrv->match) return hdrv->match(hdev, hdrv); + else + return hdac_codec_match(hdev, hdrv); return 1; } diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 8e262da74f6a..27c447e4fe5c 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -11,21 +11,36 @@ static void process_unsol_events(struct work_struct *work); +static const struct hdac_bus_ops default_ops = { + .command = snd_hdac_bus_send_cmd, + .get_response = snd_hdac_bus_get_response, +}; + /** * snd_hdac_bus_init - initialize a HD-audio bas bus * @bus: the pointer to bus object + * @ops: bus verb operators + * @io_ops: lowlevel I/O operators * * Returns 0 if successful, or a negative error code. */ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, - const struct hdac_bus_ops *ops) + const struct hdac_bus_ops *ops, + const struct hdac_io_ops *io_ops) { memset(bus, 0, sizeof(*bus)); bus->dev = dev; - bus->ops = ops; + if (ops) + bus->ops = ops; + else + bus->ops = &default_ops; + bus->io_ops = io_ops; + INIT_LIST_HEAD(&bus->stream_list); INIT_LIST_HEAD(&bus->codec_list); INIT_WORK(&bus->unsol_work, process_unsol_events); + spin_lock_init(&bus->reg_lock); mutex_init(&bus->cmd_mutex); + bus->irq = -1; return 0; } EXPORT_SYMBOL_GPL(snd_hdac_bus_init); @@ -36,6 +51,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_init); */ void snd_hdac_bus_exit(struct hdac_bus *bus) { + WARN_ON(!list_empty(&bus->stream_list)); WARN_ON(!list_empty(&bus->codec_list)); cancel_work_sync(&bus->unsol_work); } diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c new file mode 100644 index 000000000000..b5a17cb510a0 --- /dev/null +++ b/sound/hda/hdac_controller.c @@ -0,0 +1,507 @@ +/* + * HD-audio controller helpers + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <sound/core.h> +#include <sound/hdaudio.h> +#include <sound/hda_register.h> + +/* clear CORB read pointer properly */ +static void azx_clear_corbrp(struct hdac_bus *bus) +{ + int timeout; + + for (timeout = 1000; timeout > 0; timeout--) { + if (snd_hdac_chip_readw(bus, CORBRP) & AZX_CORBRP_RST) + break; + udelay(1); + } + if (timeout <= 0) + dev_err(bus->dev, "CORB reset timeout#1, CORBRP = %d\n", + snd_hdac_chip_readw(bus, CORBRP)); + + snd_hdac_chip_writew(bus, CORBRP, 0); + for (timeout = 1000; timeout > 0; timeout--) { + if (snd_hdac_chip_readw(bus, CORBRP) == 0) + break; + udelay(1); + } + if (timeout <= 0) + dev_err(bus->dev, "CORB reset timeout#2, CORBRP = %d\n", + snd_hdac_chip_readw(bus, CORBRP)); +} + +/** + * snd_hdac_bus_init_cmd_io - set up CORB/RIRB buffers + * @bus: HD-audio core bus + */ +void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) +{ + spin_lock_irq(&bus->reg_lock); + /* CORB set up */ + bus->corb.addr = bus->rb.addr; + bus->corb.buf = (__le32 *)bus->rb.area; + snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr); + snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr)); + + /* set the corb size to 256 entries (ULI requires explicitly) */ + snd_hdac_chip_writeb(bus, CORBSIZE, 0x02); + /* set the corb write pointer to 0 */ + snd_hdac_chip_writew(bus, CORBWP, 0); + + /* reset the corb hw read pointer */ + snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST); + if (!bus->corbrp_self_clear) + azx_clear_corbrp(bus); + + /* enable corb dma */ + snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN); + + /* RIRB set up */ + bus->rirb.addr = bus->rb.addr + 2048; + bus->rirb.buf = (__le32 *)(bus->rb.area + 2048); + bus->rirb.wp = bus->rirb.rp = 0; + memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds)); + snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr); + snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr)); + + /* set the rirb size to 256 entries (ULI requires explicitly) */ + snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02); + /* reset the rirb hw write pointer */ + snd_hdac_chip_writew(bus, RIRBWP, AZX_RIRBWP_RST); + /* set N=1, get RIRB response interrupt for new entry */ + snd_hdac_chip_writew(bus, RINTCNT, 1); + /* enable rirb dma and response irq */ + snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); + spin_unlock_irq(&bus->reg_lock); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io); + +/** + * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers + * @bus: HD-audio core bus + */ +void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus) +{ + spin_lock_irq(&bus->reg_lock); + /* disable ringbuffer DMAs */ + snd_hdac_chip_writeb(bus, RIRBCTL, 0); + snd_hdac_chip_writeb(bus, CORBCTL, 0); + /* disable unsolicited responses */ + snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0); + spin_unlock_irq(&bus->reg_lock); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_cmd_io); + +static unsigned int azx_command_addr(u32 cmd) +{ + unsigned int addr = cmd >> 28; + + if (snd_BUG_ON(addr >= HDA_MAX_CODECS)) + addr = 0; + return addr; +} + +/** + * snd_hdac_bus_send_cmd - send a command verb via CORB + * @bus: HD-audio core bus + * @val: encoded verb value to send + * + * Returns zero for success or a negative error code. + */ +int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val) +{ + unsigned int addr = azx_command_addr(val); + unsigned int wp, rp; + + spin_lock_irq(&bus->reg_lock); + + bus->last_cmd[azx_command_addr(val)] = val; + + /* add command to corb */ + wp = snd_hdac_chip_readw(bus, CORBWP); + if (wp == 0xffff) { + /* something wrong, controller likely turned to D3 */ + spin_unlock_irq(&bus->reg_lock); + return -EIO; + } + wp++; + wp %= AZX_MAX_CORB_ENTRIES; + + rp = snd_hdac_chip_readw(bus, CORBRP); + if (wp == rp) { + /* oops, it's full */ + spin_unlock_irq(&bus->reg_lock); + return -EAGAIN; + } + + bus->rirb.cmds[addr]++; + bus->corb.buf[wp] = cpu_to_le32(val); + snd_hdac_chip_writew(bus, CORBWP, wp); + + spin_unlock_irq(&bus->reg_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd); + +#define AZX_RIRB_EX_UNSOL_EV (1<<4) + +/** + * snd_hdac_bus_update_rirb - retrieve RIRB entries + * @bus: HD-audio core bus + * + * Usually called from interrupt handler. + */ +void snd_hdac_bus_update_rirb(struct hdac_bus *bus) +{ + unsigned int rp, wp; + unsigned int addr; + u32 res, res_ex; + + wp = snd_hdac_chip_readw(bus, RIRBWP); + if (wp == 0xffff) { + /* something wrong, controller likely turned to D3 */ + return; + } + + if (wp == bus->rirb.wp) + return; + bus->rirb.wp = wp; + + while (bus->rirb.rp != wp) { + bus->rirb.rp++; + bus->rirb.rp %= AZX_MAX_RIRB_ENTRIES; + + rp = bus->rirb.rp << 1; /* an RIRB entry is 8-bytes */ + res_ex = le32_to_cpu(bus->rirb.buf[rp + 1]); + res = le32_to_cpu(bus->rirb.buf[rp]); + addr = res_ex & 0xf; + if (addr >= HDA_MAX_CODECS) { + dev_err(bus->dev, + "spurious response %#x:%#x, rp = %d, wp = %d", + res, res_ex, bus->rirb.rp, wp); + snd_BUG(); + } else if (res_ex & AZX_RIRB_EX_UNSOL_EV) + snd_hdac_bus_queue_event(bus, res, res_ex); + else if (bus->rirb.cmds[addr]) { + bus->rirb.res[addr] = res; + bus->rirb.cmds[addr]--; + } else { + dev_err_ratelimited(bus->dev, + "spurious response %#x:%#x, last cmd=%#08x\n", + res, res_ex, bus->last_cmd[addr]); + } + } +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb); + +/** + * snd_hdac_bus_get_response - receive a response via RIRB + * @bus: HD-audio core bus + * @addr: codec address + * @res: pointer to store the value, NULL when not needed + * + * Returns zero if a value is read, or a negative error code. + */ +int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, + unsigned int *res) +{ + unsigned long timeout; + unsigned long loopcounter; + + timeout = jiffies + msecs_to_jiffies(1000); + + for (loopcounter = 0;; loopcounter++) { + spin_lock_irq(&bus->reg_lock); + if (!bus->rirb.cmds[addr]) { + if (res) + *res = bus->rirb.res[addr]; /* the last value */ + spin_unlock_irq(&bus->reg_lock); + return 0; + } + spin_unlock_irq(&bus->reg_lock); + if (time_after(jiffies, timeout)) + break; + if (loopcounter > 3000) + msleep(2); /* temporary workaround */ + else { + udelay(10); + cond_resched(); + } + } + + return -EIO; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response); + +/* + * Lowlevel interface + */ + +/** + * snd_hdac_bus_enter_link_reset - enter link reset + * @bus: HD-audio core bus + * + * Enter to the link reset state. + */ +void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus) +{ + unsigned long timeout; + + /* reset controller */ + snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, 0); + + timeout = jiffies + msecs_to_jiffies(100); + while ((snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET) && + time_before(jiffies, timeout)) + usleep_range(500, 1000); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_enter_link_reset); + +/** + * snd_hdac_bus_exit_link_reset - exit link reset + * @bus: HD-audio core bus + * + * Exit from the link reset state. + */ +void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus) +{ + unsigned long timeout; + + snd_hdac_chip_updateb(bus, GCTL, 0, AZX_GCTL_RESET); + + timeout = jiffies + msecs_to_jiffies(100); + while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout)) + usleep_range(500, 1000); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset); + +/* reset codec link */ +static int azx_reset(struct hdac_bus *bus, bool full_reset) +{ + if (!full_reset) + goto skip_reset; + + /* clear STATESTS */ + snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK); + + /* reset controller */ + snd_hdac_bus_enter_link_reset(bus); + + /* delay for >= 100us for codec PLL to settle per spec + * Rev 0.9 section 5.5.1 + */ + usleep_range(500, 1000); + + /* Bring controller out of reset */ + snd_hdac_bus_exit_link_reset(bus); + + /* Brent Chartrand said to wait >= 540us for codecs to initialize */ + usleep_range(1000, 1200); + + skip_reset: + /* check to see if controller is ready */ + if (!snd_hdac_chip_readb(bus, GCTL)) { + dev_dbg(bus->dev, "azx_reset: controller not ready!\n"); + return -EBUSY; + } + + /* Accept unsolicited responses */ + snd_hdac_chip_updatel(bus, GCTL, 0, AZX_GCTL_UNSOL); + + /* detect codecs */ + if (!bus->codec_mask) { + bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); + dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); + } + + return 0; +} + +/* enable interrupts */ +static void azx_int_enable(struct hdac_bus *bus) +{ + /* enable controller CIE and GIE */ + snd_hdac_chip_updatel(bus, INTCTL, 0, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN); +} + +/* disable interrupts */ +static void azx_int_disable(struct hdac_bus *bus) +{ + struct hdac_stream *azx_dev; + + /* disable interrupts in stream descriptor */ + list_for_each_entry(azx_dev, &bus->stream_list, list) + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0); + + /* disable SIE for all streams */ + snd_hdac_chip_writeb(bus, INTCTL, 0); + + /* disable controller CIE and GIE */ + snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN, 0); +} + +/* clear interrupts */ +static void azx_int_clear(struct hdac_bus *bus) +{ + struct hdac_stream *azx_dev; + + /* clear stream status */ + list_for_each_entry(azx_dev, &bus->stream_list, list) + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); + + /* clear STATESTS */ + snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK); + + /* clear rirb status */ + snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); + + /* clear int status */ + snd_hdac_chip_writel(bus, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM); +} + +/** + * snd_hdac_bus_init_chip - reset and start the controller registers + * @bus: HD-audio core bus + * @full_reset: Do full reset + */ +bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) +{ + if (bus->chip_init) + return false; + + /* reset controller */ + azx_reset(bus, full_reset); + + /* initialize interrupts */ + azx_int_clear(bus); + azx_int_enable(bus); + + /* initialize the codec command I/O */ + snd_hdac_bus_init_cmd_io(bus); + + /* program the position buffer */ + if (bus->use_posbuf && bus->posbuf.addr) { + snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr); + snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr)); + } + + bus->chip_init = true; + return true; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip); + +/** + * snd_hdac_bus_stop_chip - disable the whole IRQ and I/Os + * @bus: HD-audio core bus + */ +void snd_hdac_bus_stop_chip(struct hdac_bus *bus) +{ + if (!bus->chip_init) + return; + + /* disable interrupts */ + azx_int_disable(bus); + azx_int_clear(bus); + + /* disable CORB/RIRB */ + snd_hdac_bus_stop_cmd_io(bus); + + /* disable position buffer */ + if (bus->posbuf.addr) { + snd_hdac_chip_writel(bus, DPLBASE, 0); + snd_hdac_chip_writel(bus, DPUBASE, 0); + } + + bus->chip_init = false; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip); + +/** + * snd_hdac_bus_handle_stream_irq - interrupt handler for streams + * @bus: HD-audio core bus + * @status: INTSTS register value + * @ask: callback to be called for woken streams + */ +void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, + void (*ack)(struct hdac_bus *, + struct hdac_stream *)) +{ + struct hdac_stream *azx_dev; + u8 sd_status; + + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (status & azx_dev->sd_int_sta_mask) { + sd_status = snd_hdac_stream_readb(azx_dev, SD_STS); + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); + if (!azx_dev->substream || !azx_dev->running || + !(sd_status & SD_INT_COMPLETE)) + continue; + if (ack) + ack(bus, azx_dev); + } + } +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq); + +/** + * snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers + * @bus: HD-audio core bus + * + * Call this after assigning the all streams. + * Returns zero for success, or a negative error code. + */ +int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus) +{ + struct hdac_stream *s; + int num_streams = 0; + int err; + + list_for_each_entry(s, &bus->stream_list, list) { + /* allocate memory for the BDL for each stream */ + err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, + BDL_SIZE, &s->bdl); + num_streams++; + if (err < 0) + return -ENOMEM; + } + + if (WARN_ON(!num_streams)) + return -EINVAL; + /* allocate memory for the position buffer */ + err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, + num_streams * 8, &bus->posbuf); + if (err < 0) + return -ENOMEM; + list_for_each_entry(s, &bus->stream_list, list) + s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8); + + /* single page (at least 4096 bytes) must suffice for both ringbuffes */ + return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, + PAGE_SIZE, &bus->rb); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages); + +/** + * snd_hdac_bus_free_stream_pages - release BDL and other buffers + * @bus: HD-audio core bus + */ +void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus) +{ + struct hdac_stream *s; + + list_for_each_entry(s, &bus->stream_list, list) { + if (s->bdl.area) + bus->io_ops->dma_free_pages(bus, &s->bdl); + } + + if (bus->rb.area) + bus->io_ops->dma_free_pages(bus, &bus->rb); + if (bus->posbuf.area) + bus->io_ops->dma_free_pages(bus, &bus->posbuf); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index f75bf5622687..cdee7103f649 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -10,6 +10,7 @@ #include <linux/pm_runtime.h> #include <sound/hdaudio.h> #include <sound/hda_regmap.h> +#include <sound/pcm.h> #include "local.h" static void setup_fg_nodes(struct hdac_device *codec); @@ -551,6 +552,21 @@ void snd_hdac_power_down_pm(struct hdac_device *codec) EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); #endif +/* + * Enable/disable the link power for a codec. + */ +int snd_hdac_link_power(struct hdac_device *codec, bool enable) +{ + if (!codec->link_power_control) + return 0; + + if (codec->bus->ops->link_power) + return codec->bus->ops->link_power(codec->bus, enable); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_hdac_link_power); + /* codec vendor labels */ struct hda_vendor_id { unsigned int id; @@ -597,3 +613,302 @@ static int get_codec_vendor_name(struct hdac_device *codec) codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id); return codec->vendor_name ? 0 : -ENOMEM; } + +/* + * stream formats + */ +struct hda_rate_tbl { + unsigned int hz; + unsigned int alsa_bits; + unsigned int hda_fmt; +}; + +/* rate = base * mult / div */ +#define HDA_RATE(base, mult, div) \ + (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ + (((div) - 1) << AC_FMT_DIV_SHIFT)) + +static struct hda_rate_tbl rate_bits[] = { + /* rate in Hz, ALSA rate bitmask, HDA format value */ + + /* autodetected value used in snd_hda_query_supported_pcm */ + { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) }, + { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, + { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, + { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, + { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, + { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, + { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, + { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, + { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, + { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, + { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, +#define AC_PAR_PCM_RATE_BITS 11 + /* up to bits 10, 384kHZ isn't supported properly */ + + /* not autodetected value */ + { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, + + { 0 } /* terminator */ +}; + +/** + * snd_hdac_calc_stream_format - calculate the format bitset + * @rate: the sample rate + * @channels: the number of channels + * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) + * @maxbps: the max. bps + * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) + * + * Calculate the format bitset from the given rate, channels and th PCM format. + * + * Return zero if invalid. + */ +unsigned int snd_hdac_calc_stream_format(unsigned int rate, + unsigned int channels, + unsigned int format, + unsigned int maxbps, + unsigned short spdif_ctls) +{ + int i; + unsigned int val = 0; + + for (i = 0; rate_bits[i].hz; i++) + if (rate_bits[i].hz == rate) { + val = rate_bits[i].hda_fmt; + break; + } + if (!rate_bits[i].hz) + return 0; + + if (channels == 0 || channels > 8) + return 0; + val |= channels - 1; + + switch (snd_pcm_format_width(format)) { + case 8: + val |= AC_FMT_BITS_8; + break; + case 16: + val |= AC_FMT_BITS_16; + break; + case 20: + case 24: + case 32: + if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) + val |= AC_FMT_BITS_32; + else if (maxbps >= 24) + val |= AC_FMT_BITS_24; + else + val |= AC_FMT_BITS_20; + break; + default: + return 0; + } + + if (spdif_ctls & AC_DIG1_NONAUDIO) + val |= AC_FMT_TYPE_NON_PCM; + + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format); + +static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int val = 0; + + if (nid != codec->afg && + (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) + val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM); + if (!val || val == -1) + val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM); + if (!val || val == -1) + return 0; + return val; +} + +static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM); + + if (!streams || streams == -1) + streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM); + if (!streams || streams == -1) + return 0; + return streams; +} + +/** + * snd_hdac_query_supported_pcm - query the supported PCM rates and formats + * @codec: the codec object + * @nid: NID to query + * @ratesp: the pointer to store the detected rate bitflags + * @formatsp: the pointer to store the detected formats + * @bpsp: the pointer to store the detected format widths + * + * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp + * or @bsps argument is ignored. + * + * Returns 0 if successful, otherwise a negative error code. + */ +int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, + u32 *ratesp, u64 *formatsp, unsigned int *bpsp) +{ + unsigned int i, val, wcaps; + + wcaps = get_wcaps(codec, nid); + val = query_pcm_param(codec, nid); + + if (ratesp) { + u32 rates = 0; + for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) { + if (val & (1 << i)) + rates |= rate_bits[i].alsa_bits; + } + if (rates == 0) { + dev_err(&codec->dev, + "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n", + nid, val, + (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); + return -EIO; + } + *ratesp = rates; + } + + if (formatsp || bpsp) { + u64 formats = 0; + unsigned int streams, bps; + + streams = query_stream_param(codec, nid); + if (!streams) + return -EIO; + + bps = 0; + if (streams & AC_SUPFMT_PCM) { + if (val & AC_SUPPCM_BITS_8) { + formats |= SNDRV_PCM_FMTBIT_U8; + bps = 8; + } + if (val & AC_SUPPCM_BITS_16) { + formats |= SNDRV_PCM_FMTBIT_S16_LE; + bps = 16; + } + if (wcaps & AC_WCAP_DIGITAL) { + if (val & AC_SUPPCM_BITS_32) + formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24)) + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (val & AC_SUPPCM_BITS_24) + bps = 24; + else if (val & AC_SUPPCM_BITS_20) + bps = 20; + } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24| + AC_SUPPCM_BITS_32)) { + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (val & AC_SUPPCM_BITS_32) + bps = 32; + else if (val & AC_SUPPCM_BITS_24) + bps = 24; + else if (val & AC_SUPPCM_BITS_20) + bps = 20; + } + } +#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ + if (streams & AC_SUPFMT_FLOAT32) { + formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; + if (!bps) + bps = 32; + } +#endif + if (streams == AC_SUPFMT_AC3) { + /* should be exclusive */ + /* temporary hack: we have still no proper support + * for the direct AC3 stream... + */ + formats |= SNDRV_PCM_FMTBIT_U8; + bps = 8; + } + if (formats == 0) { + dev_err(&codec->dev, + "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n", + nid, val, + (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, + streams); + return -EIO; + } + if (formatsp) + *formatsp = formats; + if (bpsp) + *bpsp = bps; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm); + +/** + * snd_hdac_is_supported_format - Check the validity of the format + * @codec: the codec object + * @nid: NID to check + * @format: the HD-audio format value to check + * + * Check whether the given node supports the format value. + * + * Returns true if supported, false if not. + */ +bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, + unsigned int format) +{ + int i; + unsigned int val = 0, rate, stream; + + val = query_pcm_param(codec, nid); + if (!val) + return false; + + rate = format & 0xff00; + for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) + if (rate_bits[i].hda_fmt == rate) { + if (val & (1 << i)) + break; + return false; + } + if (i >= AC_PAR_PCM_RATE_BITS) + return false; + + stream = query_stream_param(codec, nid); + if (!stream) + return false; + + if (stream & AC_SUPFMT_PCM) { + switch (format & 0xf0) { + case 0x00: + if (!(val & AC_SUPPCM_BITS_8)) + return false; + break; + case 0x10: + if (!(val & AC_SUPPCM_BITS_16)) + return false; + break; + case 0x20: + if (!(val & AC_SUPPCM_BITS_20)) + return false; + break; + case 0x30: + if (!(val & AC_SUPPCM_BITS_24)) + return false; + break; + case 0x40: + if (!(val & AC_SUPPCM_BITS_32)) + return false; + break; + default: + return false; + } + } else { + /* FIXME: check for float32 and AC3? */ + } + + return true; +} +EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format); diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c new file mode 100644 index 000000000000..442500e06b7c --- /dev/null +++ b/sound/hda/hdac_i915.c @@ -0,0 +1,196 @@ +/* + * hdac_i915.c - routines for sync between HD-A core and i915 display driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/component.h> +#include <drm/i915_component.h> +#include <sound/core.h> +#include <sound/hdaudio.h> +#include <sound/hda_i915.h> + +static struct i915_audio_component *hdac_acomp; + +int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable) +{ + struct i915_audio_component *acomp = bus->audio_component; + + if (!acomp || !acomp->ops) + return -ENODEV; + + if (!acomp->ops->codec_wake_override) { + dev_warn(bus->dev, + "Invalid codec wake callback\n"); + return 0; + } + + dev_dbg(bus->dev, "%s codec wakeup\n", + enable ? "enable" : "disable"); + + acomp->ops->codec_wake_override(acomp->dev, enable); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup); + +int snd_hdac_display_power(struct hdac_bus *bus, bool enable) +{ + struct i915_audio_component *acomp = bus->audio_component; + + if (!acomp || !acomp->ops) + return -ENODEV; + + dev_dbg(bus->dev, "display power %s\n", + enable ? "enable" : "disable"); + + if (enable) { + if (!bus->i915_power_refcount++) + acomp->ops->get_power(acomp->dev); + } else { + WARN_ON(!bus->i915_power_refcount); + if (!--bus->i915_power_refcount) + acomp->ops->put_power(acomp->dev); + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_display_power); + +int snd_hdac_get_display_clk(struct hdac_bus *bus) +{ + struct i915_audio_component *acomp = bus->audio_component; + + if (!acomp || !acomp->ops) + return -ENODEV; + + return acomp->ops->get_cdclk_freq(acomp->dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk); + +static int hdac_component_master_bind(struct device *dev) +{ + struct i915_audio_component *acomp = hdac_acomp; + int ret; + + ret = component_bind_all(dev, acomp); + if (ret < 0) + return ret; + + if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power && + acomp->ops->put_power && acomp->ops->get_cdclk_freq))) { + ret = -EINVAL; + goto out_unbind; + } + + /* + * Atm, we don't support dynamic unbinding initiated by the child + * component, so pin its containing module until we unbind. + */ + if (!try_module_get(acomp->ops->owner)) { + ret = -ENODEV; + goto out_unbind; + } + + return 0; + +out_unbind: + component_unbind_all(dev, acomp); + + return ret; +} + +static void hdac_component_master_unbind(struct device *dev) +{ + struct i915_audio_component *acomp = hdac_acomp; + + module_put(acomp->ops->owner); + component_unbind_all(dev, acomp); + WARN_ON(acomp->ops || acomp->dev); +} + +static const struct component_master_ops hdac_component_master_ops = { + .bind = hdac_component_master_bind, + .unbind = hdac_component_master_unbind, +}; + +static int hdac_component_master_match(struct device *dev, void *data) +{ + /* i915 is the only supported component */ + return !strcmp(dev->driver->name, "i915"); +} + +int snd_hdac_i915_init(struct hdac_bus *bus) +{ + struct component_match *match = NULL; + struct device *dev = bus->dev; + struct i915_audio_component *acomp; + int ret; + + acomp = kzalloc(sizeof(*acomp), GFP_KERNEL); + if (!acomp) + return -ENOMEM; + bus->audio_component = acomp; + hdac_acomp = acomp; + + component_match_add(dev, &match, hdac_component_master_match, bus); + ret = component_master_add_with_match(dev, &hdac_component_master_ops, + match); + if (ret < 0) + goto out_err; + + /* + * Atm, we don't support deferring the component binding, so make sure + * i915 is loaded and that the binding successfully completes. + */ + request_module("i915"); + + if (!acomp->ops) { + ret = -ENODEV; + goto out_master_del; + } + dev_dbg(dev, "bound to i915 component master\n"); + + return 0; +out_master_del: + component_master_del(dev, &hdac_component_master_ops); +out_err: + kfree(acomp); + bus->audio_component = NULL; + dev_err(dev, "failed to add i915 component master (%d)\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_hdac_i915_init); + +int snd_hdac_i915_exit(struct hdac_bus *bus) +{ + struct device *dev = bus->dev; + struct i915_audio_component *acomp = bus->audio_component; + + if (!acomp) + return 0; + + WARN_ON(bus->i915_power_refcount); + if (bus->i915_power_refcount > 0 && acomp->ops) + acomp->ops->put_power(acomp->dev); + + component_master_del(dev, &hdac_component_master_ops); + + kfree(acomp); + bus->audio_component = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_i915_exit); diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index 7371e0c3926f..1eabcdf69457 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -246,6 +246,9 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) return hda_reg_read_stereo_amp(codec, reg, val); if (verb == AC_VERB_GET_PROC_COEF) return hda_reg_read_coef(codec, reg, val); + if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE) + reg &= ~AC_AMP_FAKE_MUTE; + err = snd_hdac_exec_verb(codec, reg, 0, val); if (err < 0) return err; @@ -265,6 +268,9 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) unsigned int verb; int i, bytes, err; + if (codec->caps_overwriting) + return 0; + reg &= ~0x00080000U; /* drop GET bit */ reg |= (codec->addr << 28); verb = get_verb(reg); @@ -280,6 +286,8 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) switch (verb & 0xf00) { case AC_VERB_SET_AMP_GAIN_MUTE: + if ((reg & AC_AMP_FAKE_MUTE) && (val & AC_AMP_MUTE)) + val = 0; verb = AC_VERB_SET_AMP_GAIN_MUTE; if (reg & AC_AMP_GET_LEFT) verb |= AC_AMP_SET_LEFT >> 8; diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c new file mode 100644 index 000000000000..4c15d0accc9e --- /dev/null +++ b/sound/hda/hdac_stream.c @@ -0,0 +1,697 @@ +/* + * HD-audio stream operations + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/clocksource.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/hdaudio.h> +#include <sound/hda_register.h> +#include "trace.h" + +/** + * snd_hdac_stream_init - initialize each stream (aka device) + * @bus: HD-audio core bus + * @azx_dev: HD-audio core stream object to initialize + * @idx: stream index number + * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) + * @tag: the tag id to assign + * + * Assign the starting bdl address to each stream (device) and initialize. + */ +void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, + int idx, int direction, int tag) +{ + azx_dev->bus = bus; + /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ + azx_dev->sd_addr = bus->remap_addr + (0x20 * idx + 0x80); + /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ + azx_dev->sd_int_sta_mask = 1 << idx; + azx_dev->index = idx; + azx_dev->direction = direction; + azx_dev->stream_tag = tag; + snd_hdac_dsp_lock_init(azx_dev); + list_add_tail(&azx_dev->list, &bus->stream_list); +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_init); + +/** + * snd_hdac_stream_start - start a stream + * @azx_dev: HD-audio core stream to start + * @fresh_start: false = wallclock timestamp relative to period wallclock + * + * Start a stream, set start_wallclk and set the running flag. + */ +void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) +{ + struct hdac_bus *bus = azx_dev->bus; + + trace_snd_hdac_stream_start(bus, azx_dev); + + azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK); + if (!fresh_start) + azx_dev->start_wallclk -= azx_dev->period_wallclk; + + /* enable SIE */ + snd_hdac_chip_updatel(bus, INTCTL, 0, 1 << azx_dev->index); + /* set DMA start and interrupt mask */ + snd_hdac_stream_updateb(azx_dev, SD_CTL, + 0, SD_CTL_DMA_START | SD_INT_MASK); + azx_dev->running = true; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_start); + +/** + * snd_hdac_stream_clear - stop a stream DMA + * @azx_dev: HD-audio core stream to stop + */ +void snd_hdac_stream_clear(struct hdac_stream *azx_dev) +{ + snd_hdac_stream_updateb(azx_dev, SD_CTL, + SD_CTL_DMA_START | SD_INT_MASK, 0); + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ + azx_dev->running = false; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_clear); + +/** + * snd_hdac_stream_stop - stop a stream + * @azx_dev: HD-audio core stream to stop + * + * Stop a stream DMA and disable stream interrupt + */ +void snd_hdac_stream_stop(struct hdac_stream *azx_dev) +{ + trace_snd_hdac_stream_stop(azx_dev->bus, azx_dev); + + snd_hdac_stream_clear(azx_dev); + /* disable SIE */ + snd_hdac_chip_updatel(azx_dev->bus, INTCTL, 1 << azx_dev->index, 0); +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_stop); + +/** + * snd_hdac_stream_reset - reset a stream + * @azx_dev: HD-audio core stream to reset + */ +void snd_hdac_stream_reset(struct hdac_stream *azx_dev) +{ + unsigned char val; + int timeout; + + snd_hdac_stream_clear(azx_dev); + + snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET); + udelay(3); + timeout = 300; + do { + val = snd_hdac_stream_readb(azx_dev, SD_CTL) & + SD_CTL_STREAM_RESET; + if (val) + break; + } while (--timeout); + val &= ~SD_CTL_STREAM_RESET; + snd_hdac_stream_writeb(azx_dev, SD_CTL, val); + udelay(3); + + timeout = 300; + /* waiting for hardware to report that the stream is out of reset */ + do { + val = snd_hdac_stream_readb(azx_dev, SD_CTL) & + SD_CTL_STREAM_RESET; + if (!val) + break; + } while (--timeout); + + /* reset first position - may not be synced with hw at this time */ + if (azx_dev->posbuf) + *azx_dev->posbuf = 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_reset); + +/** + * snd_hdac_stream_setup - set up the SD for streaming + * @azx_dev: HD-audio core stream to set up + */ +int snd_hdac_stream_setup(struct hdac_stream *azx_dev) +{ + struct hdac_bus *bus = azx_dev->bus; + struct snd_pcm_runtime *runtime; + unsigned int val; + + if (azx_dev->substream) + runtime = azx_dev->substream->runtime; + else + runtime = NULL; + /* make sure the run bit is zero for SD */ + snd_hdac_stream_clear(azx_dev); + /* program the stream_tag */ + val = snd_hdac_stream_readl(azx_dev, SD_CTL); + val = (val & ~SD_CTL_STREAM_TAG_MASK) | + (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT); + if (!bus->snoop) + val |= SD_CTL_TRAFFIC_PRIO; + snd_hdac_stream_writel(azx_dev, SD_CTL, val); + + /* program the length of samples in cyclic buffer */ + snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize); + + /* program the stream format */ + /* this value needs to be the same as the one programmed */ + snd_hdac_stream_writew(azx_dev, SD_FORMAT, azx_dev->format_val); + + /* program the stream LVI (last valid index) of the BDL */ + snd_hdac_stream_writew(azx_dev, SD_LVI, azx_dev->frags - 1); + + /* program the BDL address */ + /* lower BDL address */ + snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); + /* upper BDL address */ + snd_hdac_stream_writel(azx_dev, SD_BDLPU, + upper_32_bits(azx_dev->bdl.addr)); + + /* enable the position buffer */ + if (bus->use_posbuf && bus->posbuf.addr) { + if (!(snd_hdac_chip_readl(bus, DPLBASE) & AZX_DPLBASE_ENABLE)) + snd_hdac_chip_writel(bus, DPLBASE, + (u32)bus->posbuf.addr | AZX_DPLBASE_ENABLE); + } + + /* set the interrupt enable bits in the descriptor control register */ + snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK); + + if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK) + azx_dev->fifo_size = + snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1; + else + azx_dev->fifo_size = 0; + + /* when LPIB delay correction gives a small negative value, + * we ignore it; currently set the threshold statically to + * 64 frames + */ + if (runtime && runtime->period_size > 64) + azx_dev->delay_negative_threshold = + -frames_to_bytes(runtime, 64); + else + azx_dev->delay_negative_threshold = 0; + + /* wallclk has 24Mhz clock source */ + if (runtime) + azx_dev->period_wallclk = (((runtime->period_size * 24000) / + runtime->rate) * 1000); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_setup); + +/** + * snd_hdac_stream_cleanup - cleanup a stream + * @azx_dev: HD-audio core stream to clean up + */ +void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev) +{ + snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); + snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); + snd_hdac_stream_writel(azx_dev, SD_CTL, 0); + azx_dev->bufsize = 0; + azx_dev->period_bytes = 0; + azx_dev->format_val = 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_cleanup); + +/** + * snd_hdac_stream_assign - assign a stream for the PCM + * @bus: HD-audio core bus + * @substream: PCM substream to assign + * + * Look for an unused stream for the given PCM substream, assign it + * and return the stream object. If no stream is free, returns NULL. + * The function tries to keep using the same stream object when it's used + * beforehand. Also, when bus->reverse_assign flag is set, the last free + * or matching entry is returned. This is needed for some strange codecs. + */ +struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *azx_dev; + struct hdac_stream *res = NULL; + + /* make a non-zero unique key for the substream */ + int key = (substream->pcm->device << 16) | (substream->number << 2) | + (substream->stream + 1); + + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (azx_dev->direction != substream->stream) + continue; + if (azx_dev->opened) + continue; + if (azx_dev->assigned_key == key) { + res = azx_dev; + break; + } + if (!res || bus->reverse_assign) + res = azx_dev; + } + if (res) { + spin_lock_irq(&bus->reg_lock); + res->opened = 1; + res->running = 0; + res->assigned_key = key; + res->substream = substream; + spin_unlock_irq(&bus->reg_lock); + } + return res; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_assign); + +/** + * snd_hdac_stream_release - release the assigned stream + * @azx_dev: HD-audio core stream to release + * + * Release the stream that has been assigned by snd_hdac_stream_assign(). + */ +void snd_hdac_stream_release(struct hdac_stream *azx_dev) +{ + struct hdac_bus *bus = azx_dev->bus; + + spin_lock_irq(&bus->reg_lock); + azx_dev->opened = 0; + azx_dev->running = 0; + azx_dev->substream = NULL; + spin_unlock_irq(&bus->reg_lock); +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_release); + +/* + * set up a BDL entry + */ +static int setup_bdle(struct hdac_bus *bus, + struct snd_dma_buffer *dmab, + struct hdac_stream *azx_dev, __le32 **bdlp, + int ofs, int size, int with_ioc) +{ + __le32 *bdl = *bdlp; + + while (size > 0) { + dma_addr_t addr; + int chunk; + + if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES) + return -EINVAL; + + addr = snd_sgbuf_get_addr(dmab, ofs); + /* program the address field of the BDL entry */ + bdl[0] = cpu_to_le32((u32)addr); + bdl[1] = cpu_to_le32(upper_32_bits(addr)); + /* program the size field of the BDL entry */ + chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size); + /* one BDLE cannot cross 4K boundary on CTHDA chips */ + if (bus->align_bdle_4k) { + u32 remain = 0x1000 - (ofs & 0xfff); + + if (chunk > remain) + chunk = remain; + } + bdl[2] = cpu_to_le32(chunk); + /* program the IOC to enable interrupt + * only when the whole fragment is processed + */ + size -= chunk; + bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01); + bdl += 4; + azx_dev->frags++; + ofs += chunk; + } + *bdlp = bdl; + return ofs; +} + +/** + * snd_hdac_stream_setup_periods - set up BDL entries + * @azx_dev: HD-audio core stream to set up + * + * Set up the buffer descriptor table of the given stream based on the + * period and buffer sizes of the assigned PCM substream. + */ +int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) +{ + struct hdac_bus *bus = azx_dev->bus; + struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + __le32 *bdl; + int i, ofs, periods, period_bytes; + int pos_adj, pos_align; + + /* reset BDL address */ + snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); + snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); + + period_bytes = azx_dev->period_bytes; + periods = azx_dev->bufsize / period_bytes; + + /* program the initial BDL entries */ + bdl = (__le32 *)azx_dev->bdl.area; + ofs = 0; + azx_dev->frags = 0; + + pos_adj = bus->bdl_pos_adj; + if (!azx_dev->no_period_wakeup && pos_adj > 0) { + pos_align = pos_adj; + pos_adj = (pos_adj * runtime->rate + 47999) / 48000; + if (!pos_adj) + pos_adj = pos_align; + else + pos_adj = ((pos_adj + pos_align - 1) / pos_align) * + pos_align; + pos_adj = frames_to_bytes(runtime, pos_adj); + if (pos_adj >= period_bytes) { + dev_warn(bus->dev, "Too big adjustment %d\n", + pos_adj); + pos_adj = 0; + } else { + ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), + azx_dev, + &bdl, ofs, pos_adj, true); + if (ofs < 0) + goto error; + } + } else + pos_adj = 0; + + for (i = 0; i < periods; i++) { + if (i == periods - 1 && pos_adj) + ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), + azx_dev, &bdl, ofs, + period_bytes - pos_adj, 0); + else + ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), + azx_dev, &bdl, ofs, + period_bytes, + !azx_dev->no_period_wakeup); + if (ofs < 0) + goto error; + } + return 0; + + error: + dev_err(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n", + azx_dev->bufsize, period_bytes); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods); + +/* snd_hdac_stream_set_params - set stream parameters + * @azx_dev: HD-audio core stream for which parameters are to be set + * @format_val: format value parameter + * + * Setup the HD-audio core stream parameters from substream of the stream + * and passed format value + */ +int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, + unsigned int format_val) +{ + + unsigned int bufsize, period_bytes; + struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_runtime *runtime; + int err; + + if (!substream) + return -EINVAL; + runtime = substream->runtime; + bufsize = snd_pcm_lib_buffer_bytes(substream); + period_bytes = snd_pcm_lib_period_bytes(substream); + + if (bufsize != azx_dev->bufsize || + period_bytes != azx_dev->period_bytes || + format_val != azx_dev->format_val || + runtime->no_period_wakeup != azx_dev->no_period_wakeup) { + azx_dev->bufsize = bufsize; + azx_dev->period_bytes = period_bytes; + azx_dev->format_val = format_val; + azx_dev->no_period_wakeup = runtime->no_period_wakeup; + err = snd_hdac_stream_setup_periods(azx_dev); + if (err < 0) + return err; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_set_params); + +static cycle_t azx_cc_read(const struct cyclecounter *cc) +{ + struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc); + + return snd_hdac_chip_readl(azx_dev->bus, WALLCLK); +} + +static void azx_timecounter_init(struct hdac_stream *azx_dev, + bool force, cycle_t last) +{ + struct timecounter *tc = &azx_dev->tc; + struct cyclecounter *cc = &azx_dev->cc; + u64 nsec; + + cc->read = azx_cc_read; + cc->mask = CLOCKSOURCE_MASK(32); + + /* + * Converting from 24 MHz to ns means applying a 125/3 factor. + * To avoid any saturation issues in intermediate operations, + * the 125 factor is applied first. The division is applied + * last after reading the timecounter value. + * Applying the 1/3 factor as part of the multiplication + * requires at least 20 bits for a decent precision, however + * overflows occur after about 4 hours or less, not a option. + */ + + cc->mult = 125; /* saturation after 195 years */ + cc->shift = 0; + + nsec = 0; /* audio time is elapsed time since trigger */ + timecounter_init(tc, cc, nsec); + if (force) { + /* + * force timecounter to use predefined value, + * used for synchronized starts + */ + tc->cycle_last = last; + } +} + +/** + * snd_hdac_stream_timecounter_init - initialize time counter + * @azx_dev: HD-audio core stream (master stream) + * @streams: bit flags of streams to set up + * + * Initializes the time counter of streams marked by the bit flags (each + * bit corresponds to the stream index). + * The trigger timestamp of PCM substream assigned to the given stream is + * updated accordingly, too. + */ +void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, + unsigned int streams) +{ + struct hdac_bus *bus = azx_dev->bus; + struct snd_pcm_runtime *runtime = azx_dev->substream->runtime; + struct hdac_stream *s; + bool inited = false; + cycle_t cycle_last = 0; + int i = 0; + + list_for_each_entry(s, &bus->stream_list, list) { + if (streams & (1 << i)) { + azx_timecounter_init(s, inited, cycle_last); + if (!inited) { + inited = true; + cycle_last = s->tc.cycle_last; + } + } + i++; + } + + snd_pcm_gettime(runtime, &runtime->trigger_tstamp); + runtime->trigger_tstamp_latched = true; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_timecounter_init); + +/** + * snd_hdac_stream_sync_trigger - turn on/off stream sync register + * @azx_dev: HD-audio core stream (master stream) + * @streams: bit flags of streams to sync + */ +void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set, + unsigned int streams, unsigned int reg) +{ + struct hdac_bus *bus = azx_dev->bus; + unsigned int val; + + if (!reg) + reg = AZX_REG_SSYNC; + val = _snd_hdac_chip_read(l, bus, reg); + if (set) + val |= streams; + else + val &= ~streams; + _snd_hdac_chip_write(l, bus, reg, val); +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_sync_trigger); + +/** + * snd_hdac_stream_sync - sync with start/strop trigger operation + * @azx_dev: HD-audio core stream (master stream) + * @start: true = start, false = stop + * @streams: bit flags of streams to sync + * + * For @start = true, wait until all FIFOs get ready. + * For @start = false, wait until all RUN bits are cleared. + */ +void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, + unsigned int streams) +{ + struct hdac_bus *bus = azx_dev->bus; + int i, nwait, timeout; + struct hdac_stream *s; + + for (timeout = 5000; timeout; timeout--) { + nwait = 0; + i = 0; + list_for_each_entry(s, &bus->stream_list, list) { + if (streams & (1 << i)) { + if (start) { + /* check FIFO gets ready */ + if (!(snd_hdac_stream_readb(s, SD_STS) & + SD_STS_FIFO_READY)) + nwait++; + } else { + /* check RUN bit is cleared */ + if (snd_hdac_stream_readb(s, SD_CTL) & + SD_CTL_DMA_START) + nwait++; + } + } + i++; + } + if (!nwait) + break; + cpu_relax(); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_sync); + +#ifdef CONFIG_SND_HDA_DSP_LOADER +/** + * snd_hdac_dsp_prepare - prepare for DSP loading + * @azx_dev: HD-audio core stream used for DSP loading + * @format: HD-audio stream format + * @byte_size: data chunk byte size + * @bufp: allocated buffer + * + * Allocate the buffer for the given size and set up the given stream for + * DSP loading. Returns the stream tag (>= 0), or a negative error code. + */ +int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, + unsigned int byte_size, struct snd_dma_buffer *bufp) +{ + struct hdac_bus *bus = azx_dev->bus; + u32 *bdl; + int err; + + snd_hdac_dsp_lock(azx_dev); + spin_lock_irq(&bus->reg_lock); + if (azx_dev->running || azx_dev->locked) { + spin_unlock_irq(&bus->reg_lock); + err = -EBUSY; + goto unlock; + } + azx_dev->locked = true; + spin_unlock_irq(&bus->reg_lock); + + err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV_SG, + byte_size, bufp); + if (err < 0) + goto err_alloc; + + azx_dev->substream = NULL; + azx_dev->bufsize = byte_size; + azx_dev->period_bytes = byte_size; + azx_dev->format_val = format; + + snd_hdac_stream_reset(azx_dev); + + /* reset BDL address */ + snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); + snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); + + azx_dev->frags = 0; + bdl = (u32 *)azx_dev->bdl.area; + err = setup_bdle(bus, bufp, azx_dev, &bdl, 0, byte_size, 0); + if (err < 0) + goto error; + + snd_hdac_stream_setup(azx_dev); + snd_hdac_dsp_unlock(azx_dev); + return azx_dev->stream_tag; + + error: + bus->io_ops->dma_free_pages(bus, bufp); + err_alloc: + spin_lock_irq(&bus->reg_lock); + azx_dev->locked = false; + spin_unlock_irq(&bus->reg_lock); + unlock: + snd_hdac_dsp_unlock(azx_dev); + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare); + +/** + * snd_hdac_dsp_trigger - start / stop DSP loading + * @azx_dev: HD-audio core stream used for DSP loading + * @start: trigger start or stop + */ +void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start) +{ + if (start) + snd_hdac_stream_start(azx_dev, true); + else + snd_hdac_stream_stop(azx_dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_dsp_trigger); + +/** + * snd_hdac_dsp_cleanup - clean up the stream from DSP loading to normal + * @azx_dev: HD-audio core stream used for DSP loading + * @dmab: buffer used by DSP loading + */ +void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev, + struct snd_dma_buffer *dmab) +{ + struct hdac_bus *bus = azx_dev->bus; + + if (!dmab->area || !azx_dev->locked) + return; + + snd_hdac_dsp_lock(azx_dev); + /* reset BDL address */ + snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); + snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); + snd_hdac_stream_writel(azx_dev, SD_CTL, 0); + azx_dev->bufsize = 0; + azx_dev->period_bytes = 0; + azx_dev->format_val = 0; + + bus->io_ops->dma_free_pages(bus, dmab); + dmab->area = NULL; + + spin_lock_irq(&bus->reg_lock); + azx_dev->locked = false; + spin_unlock_irq(&bus->reg_lock); + snd_hdac_dsp_unlock(azx_dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_dsp_cleanup); +#endif /* CONFIG_SND_HDA_DSP_LOADER */ diff --git a/sound/hda/trace.h b/sound/hda/trace.h index 33a7eb5573d4..e27e2c0b7b17 100644 --- a/sound/hda/trace.h +++ b/sound/hda/trace.h @@ -50,6 +50,33 @@ TRACE_EVENT(hda_unsol_event, ), TP_printk("%s", __get_str(msg)) ); + +DECLARE_EVENT_CLASS(hdac_stream, + TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev), + + TP_ARGS(bus, azx_dev), + + TP_STRUCT__entry( + __field(unsigned char, stream_tag) + ), + + TP_fast_assign( + __entry->stream_tag = (azx_dev)->stream_tag; + ), + + TP_printk("stream_tag: %d", __entry->stream_tag) +); + +DEFINE_EVENT(hdac_stream, snd_hdac_stream_start, + TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev), + TP_ARGS(bus, azx_dev) +); + +DEFINE_EVENT(hdac_stream, snd_hdac_stream_stop, + TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev), + TP_ARGS(bus, azx_dev) +); + #endif /* __HDAC_TRACE_H */ /* This part must be outside protection */ diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index c65731088aa2..bf377dc192aa 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -859,7 +859,6 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs) return 0; } -#ifdef CONFIG_PROC_FS static void proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -884,9 +883,6 @@ static int proc_init(struct snd_akm4xxx *ak) snd_info_set_text_ops(entry, ak, proc_regs_read); return 0; } -#else /* !CONFIG_PROC_FS */ -static int proc_init(struct snd_akm4xxx *ak) { return 0; } -#endif int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak) { diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c index 0dd43414016e..3b5d9a7a63eb 100644 --- a/sound/isa/gus/gus_mixer.c +++ b/sound/isa/gus/gus_mixer.c @@ -109,7 +109,7 @@ static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem unsigned long flags; int addr = kcontrol->private_value & 0xff; int change; - unsigned char val1, val2, oval1, oval2, tmp; + unsigned char val1, val2, oval1, oval2; val1 = ucontrol->value.integer.value[0] & 127; val2 = ucontrol->value.integer.value[1] & 127; @@ -120,11 +120,8 @@ static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem gus->gf1.ics_regs[addr][0] = val1; gus->gf1.ics_regs[addr][1] = val2; if (gus->ics_flag && gus->ics_flipped && - (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) { - tmp = val1; - val1 = val2; - val2 = tmp; - } + (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) + swap(val1, val2); addr <<= 3; outb(addr | 0, GUSP(gus, MIXCNTRLPORT)); outb(1, GUSP(gus, MIXDATAPORT)); diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig index d2f615ab177a..2153d31fb663 100644 --- a/sound/mips/Kconfig +++ b/sound/mips/Kconfig @@ -12,12 +12,14 @@ if SND_MIPS config SND_SGI_O2 tristate "SGI O2 Audio" depends on SGI_IP32 + select SND_PCM help Sound support for the SGI O2 Workstation. config SND_SGI_HAL2 tristate "SGI HAL2 Audio" depends on SGI_HAS_HAL2 + select SND_PCM help Sound support for the SGI Indy and Indigo2 Workstation. diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c index ec1ee07df59d..10c8de1f8d29 100644 --- a/sound/oss/ad1848.c +++ b/sound/oss/ad1848.c @@ -2860,6 +2860,7 @@ static struct { {NULL} }; +#ifdef MODULE static struct isapnp_device_id id_table[] = { { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, @@ -2877,6 +2878,7 @@ static struct isapnp_device_id id_table[] = { }; MODULE_DEVICE_TABLE(isapnp, id_table); +#endif static struct pnp_dev *activate_dev(char *devname, char *resname, struct pnp_dev *dev) { diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c index a8ceef8d1a8d..a8bb4a06ba6f 100644 --- a/sound/oss/msnd_pinnacle.c +++ b/sound/oss/msnd_pinnacle.c @@ -1288,8 +1288,7 @@ static int __init calibrate_adc(WORD srate) & ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags); if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 && chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) { - __set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 3); + schedule_timeout_interruptible(HZ / 3); return 0; } printk(KERN_WARNING LOGNAME ": ADC calibration failed\n"); diff --git a/sound/oss/sb_audio.c b/sound/oss/sb_audio.c index 048439a16000..dc91072f4d82 100644 --- a/sound/oss/sb_audio.c +++ b/sound/oss/sb_audio.c @@ -102,12 +102,8 @@ void sb_audio_close(int dev) if(devc->duplex && !devc->fullduplex && (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE)) - { - struct dma_buffparms *dmap_temp; - dmap_temp = audio_devs[dev]->dmap_out; - audio_devs[dev]->dmap_out = audio_devs[dev]->dmap_in; - audio_devs[dev]->dmap_in = dmap_temp; - } + swap(audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_in); + audio_devs[dev]->dmap_out->dma = devc->dma8; audio_devs[dev]->dmap_in->dma = ( devc->duplex ) ? devc->dma16 : devc->dma8; diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile index 41fa322f0971..526175333710 100644 --- a/sound/pci/ac97/Makefile +++ b/sound/pci/ac97/Makefile @@ -4,7 +4,7 @@ # snd-ac97-codec-y := ac97_codec.o ac97_pcm.o -snd-ac97-codec-$(CONFIG_PROC_FS) += ac97_proc.o +snd-ac97-codec-$(CONFIG_SND_PROC_FS) += ac97_proc.o # Toplevel Module Dependency obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o diff --git a/sound/pci/ac97/ac97_local.h b/sound/pci/ac97/ac97_local.h index c276a5e3f7ac..941a5062cc20 100644 --- a/sound/pci/ac97/ac97_local.h +++ b/sound/pci/ac97/ac97_local.h @@ -28,7 +28,7 @@ int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value); /* ac97_proc.c */ -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS void snd_ac97_bus_proc_init(struct snd_ac97_bus * ac97); void snd_ac97_bus_proc_done(struct snd_ac97_bus * ac97); void snd_ac97_proc_init(struct snd_ac97 * ac97); diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 66ddd981d1d5..1fc6d8bc09e5 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -898,8 +898,8 @@ snd_ad1889_create(struct snd_card *card, return err; /* check PCI availability (32bit DMA) */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) { dev_err(card->dev, "error setting 32-bit DMA mask.\n"); pci_disable_device(pci); return -ENXIO; diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c index 3bf0dc53360a..2fb1fbba3e5e 100644 --- a/sound/pci/ak4531_codec.c +++ b/sound/pci/ak4531_codec.c @@ -35,11 +35,7 @@ MODULE_DESCRIPTION("Universal routines for AK4531 codec"); MODULE_LICENSE("GPL"); */ -#ifdef CONFIG_PROC_FS static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531); -#else -#define snd_ak4531_proc_init(card,ak) -#endif /* * @@ -466,7 +462,6 @@ void snd_ak4531_resume(struct snd_ak4531 *ak4531) } #endif -#ifdef CONFIG_PROC_FS /* * /proc interface */ @@ -491,4 +486,3 @@ snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531) if (! snd_card_proc_new(card, "ak4531", &entry)) snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read); } -#endif diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index c8d499575c01..36470af7eda7 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -2105,8 +2105,8 @@ static int snd_ali_create(struct snd_card *card, if (err < 0) return err; /* check, if we can restrict PCI DMA transfers to 31 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(31)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(31)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(31)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(31)) < 0) { dev_err(card->dev, "architecture does not support 31bit PCI busmaster DMA\n"); pci_disable_device(pci); diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 57e034f208dc..add3176398d3 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -658,8 +658,8 @@ static int snd_als300_create(struct snd_card *card, if ((err = pci_enable_device(pci)) < 0) return err; - if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) { dev_err(card->dev, "error setting 28bit DMA mask\n"); pci_disable_device(pci); return -ENXIO; diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index a3dea464134d..ff39a0c7277b 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -871,8 +871,8 @@ static int snd_card_als4000_probe(struct pci_dev *pci, return err; } /* check, if we can restrict PCI DMA transfers to 24 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) { dev_err(&pci->dev, "architecture does not support 24bit PCI busmaster DMA\n"); pci_disable_device(pci); return -ENXIO; diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 6610bd096fc9..d17937b92331 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -32,6 +32,7 @@ #include <linux/pci.h> #include <linux/stringify.h> #include <linux/module.h> +#include <linux/vmalloc.h> #ifdef MODULE_FIRMWARE MODULE_FIRMWARE("asihpi/dsp5000.bin"); diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 42a20c806b39..1028fc8bdff5 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1530,7 +1530,6 @@ static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); #endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PROC_FS /* * proc interface for register dump */ @@ -1552,9 +1551,6 @@ static void snd_atiixp_proc_init(struct atiixp *chip) if (! snd_card_proc_new(chip->card, "atiixp", &entry)) snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read); } -#else /* !CONFIG_PROC_FS */ -#define snd_atiixp_proc_init(chip) -#endif /* diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 0a38e08164ab..27ed678a46df 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1156,7 +1156,6 @@ static SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume); #define SND_ATIIXP_PM_OPS NULL #endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PROC_FS /* * proc interface for register dump */ @@ -1178,9 +1177,6 @@ static void snd_atiixp_proc_init(struct atiixp_modem *chip) if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry)) snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read); } -#else -#define snd_atiixp_proc_init(chip) -#endif /* diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index 996369134ea8..32092184bbf2 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -150,8 +150,8 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) // check PCI availability (DMA). if ((err = pci_enable_device(pci)) < 0) return err; - if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) { dev_err(card->dev, "error to set DMA mask\n"); pci_disable_device(pci); return -ENXIO; diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index 8d2fee7b33bd..167714303070 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -258,8 +258,8 @@ static int snd_aw2_create(struct snd_card *card, pci_set_master(pci); /* check PCI availability (32bit DMA) */ - if ((pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) || - (pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0)) { + if ((dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) || + (dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0)) { dev_err(card->dev, "Impossible to set 32bit mask DMA\n"); pci_disable_device(pci); return -ENXIO; diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 33b2a0af1b59..07a4acc99541 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -2420,8 +2420,8 @@ snd_azf3328_create(struct snd_card *card, chip->irq = -1; /* check if we can restrict PCI DMA transfers to 24 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) { dev_err(card->dev, "architecture does not support 24bit PCI busmaster DMA\n" ); diff --git a/sound/pci/ca0106/Makefile b/sound/pci/ca0106/Makefile index dcbae7b31546..c1455fc5588c 100644 --- a/sound/pci/ca0106/Makefile +++ b/sound/pci/ca0106/Makefile @@ -1,3 +1,4 @@ -snd-ca0106-objs := ca0106_main.o ca0106_proc.o ca0106_mixer.o ca_midi.o +snd-ca0106-objs := ca0106_main.o ca0106_mixer.o ca_midi.o +snd-ca0106-$(CONFIG_SND_PROC_FS) += ca0106_proc.o obj-$(CONFIG_SND_CA0106) += snd-ca0106.o diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index dd75b7536fa2..d3cd95633ee2 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1676,8 +1676,8 @@ static int snd_ca0106_create(int dev, struct snd_card *card, err = pci_enable_device(pci); if (err < 0) return err; - if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) { dev_err(card->dev, "error to set 32bit mask DMA\n"); pci_disable_device(pci); return -ENXIO; @@ -1885,7 +1885,7 @@ static int snd_ca0106_probe(struct pci_dev *pci, goto error; dev_dbg(card->dev, " done.\n"); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS snd_ca0106_proc_init(chip); #endif diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index 2c5c28adbefd..9b2b8b38122f 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -75,8 +75,6 @@ #include "ca0106.h" -#ifdef CONFIG_PROC_FS - struct snd_ca0106_category_str { int val; const char *name; @@ -453,5 +451,3 @@ int snd_ca0106_proc_init(struct snd_ca0106 *emu) snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2); return 0; } - -#endif /* CONFIG_PROC_FS */ diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 6cf464d9043d..24cdcba06d27 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2772,7 +2772,6 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) * proc interface */ -#ifdef CONFIG_PROC_FS static void snd_cmipci_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -2798,10 +2797,6 @@ static void snd_cmipci_proc_init(struct cmipci *cm) if (! snd_card_proc_new(cm->card, "cmipci", &entry)) snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read); } -#else /* !CONFIG_PROC_FS */ -static inline void snd_cmipci_proc_init(struct cmipci *cm) {} -#endif - static const struct pci_device_id snd_cmipci_ids[] = { {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A), 0}, diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 8d74004b1ed2..2a9f4a345171 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2816,7 +2816,7 @@ int snd_cs46xx_gameport(struct snd_cs46xx *chip) { return -ENOSYS; } static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip) { } #endif /* CONFIG_GAMEPORT */ -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS /* * proc interface */ @@ -2865,7 +2865,7 @@ static int snd_cs46xx_proc_done(struct snd_cs46xx *chip) #endif return 0; } -#else /* !CONFIG_PROC_FS */ +#else /* !CONFIG_SND_PROC_FS */ #define snd_cs46xx_proc_init(card, chip) #define snd_cs46xx_proc_done(chip) #endif diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h index 86f14620f817..bdf4114167ea 100644 --- a/sound/pci/cs46xx/cs46xx_lib.h +++ b/sound/pci/cs46xx/cs46xx_lib.h @@ -95,7 +95,7 @@ int cs46xx_dsp_resume(struct snd_cs46xx * chip); #endif struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, int symbol_type); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip); int cs46xx_dsp_proc_done (struct snd_cs46xx *chip); #else @@ -118,7 +118,7 @@ int cs46xx_dsp_disable_adc_capture (struct snd_cs46xx *chip); int cs46xx_poke_via_dsp (struct snd_cs46xx *chip, u32 address, u32 data); struct dsp_scb_descriptor * cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb); void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb); diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index 5c99efb004c0..d2951ed4bf71 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -476,7 +476,7 @@ cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, int symb } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS static struct dsp_symbol_entry * cs46xx_dsp_lookup_symbol_addr (struct snd_cs46xx * chip, u32 address, int symbol_type) { @@ -929,7 +929,7 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip) return 0; } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ static void _dsp_create_task_tree (struct snd_cs46xx *chip, u32 * task_data, u32 dest, int size) diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index 2c90c0bded69..7488e1b7a770 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -67,7 +67,7 @@ static void remove_symbol (struct snd_cs46xx * chip, struct dsp_symbol_entry * s } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -228,7 +228,7 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb) { if (scb->proc_info) { @@ -285,7 +285,7 @@ out: scb->proc_info = entry; } } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ static struct dsp_scb_descriptor * _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest, diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 963b912550d4..de409cda50aa 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -289,8 +289,8 @@ static int snd_cs5535audio_create(struct snd_card *card, if ((err = pci_enable_device(pci)) < 0) return err; - if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) { dev_warn(card->dev, "unable to get 32bit dma\n"); err = -ENXIO; goto pcifail; diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 1cac55fd1139..9667cbfb0ca2 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -1910,8 +1910,8 @@ static int hw_card_start(struct hw *hw) return err; /* Set DMA transfer mask */ - if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 || - pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) { + if (dma_set_mask(&pci->dev, CT_XFI_DMA_MASK) < 0 || + dma_set_coherent_mask(&pci->dev, CT_XFI_DMA_MASK) < 0) { dev_err(hw->card->dev, "architecture does not support PCI busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK); diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index 955ad871e9a8..9dc2950e1ab7 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -2035,8 +2035,8 @@ static int hw_card_start(struct hw *hw) return err; /* Set DMA transfer mask */ - if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 || - pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) { + if (dma_set_mask(&pci->dev, CT_XFI_DMA_MASK) < 0 || + dma_set_coherent_mask(&pci->dev, CT_XFI_DMA_MASK) < 0) { dev_err(hw->card->dev, "architecture does not support PCI busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK); diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile index fc5591e7777e..29b44ca27010 100644 --- a/sound/pci/emu10k1/Makefile +++ b/sound/pci/emu10k1/Makefile @@ -5,7 +5,8 @@ snd-emu10k1-objs := emu10k1.o emu10k1_main.o \ irq.o memory.o voice.o emumpu401.o emupcm.o io.o \ - emuproc.o emumixer.o emufx.o timer.o p16v.o + emumixer.o emufx.o timer.o p16v.o +snd-emu10k1-$(CONFIG_SND_PROC_FS) += emuproc.o snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o snd-emu10k1x-objs := emu10k1x.o diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index a4548147c621..28e2f8b42f5e 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1911,8 +1911,8 @@ int snd_emu10k1_create(struct snd_card *card, emu->address_mode = is_audigy ? 0 : 1; /* set the DMA transfer mask */ emu->dma_mask = emu->address_mode ? EMU10K1_DMA_MASK : AUDIGY_DMA_MASK; - if (pci_set_dma_mask(pci, emu->dma_mask) < 0 || - pci_set_consistent_dma_mask(pci, emu->dma_mask) < 0) { + if (dma_set_mask(&pci->dev, emu->dma_mask) < 0 || + dma_set_coherent_mask(&pci->dev, emu->dma_mask) < 0) { dev_err(card->dev, "architecture does not support PCI busmaster DMA with mask 0x%lx\n", emu->dma_mask); @@ -2063,7 +2063,7 @@ int snd_emu10k1_create(struct snd_card *card, if (err < 0) goto error; -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS snd_emu10k1_proc_init(emu); #endif diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 53745f4c2bf5..cf05229b569b 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -34,7 +34,6 @@ #include <sound/emu10k1.h> #include "p16v.h" -#ifdef CONFIG_PROC_FS static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu, struct snd_info_buffer *buffer, char *title, @@ -656,4 +655,3 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu) } return 0; } -#endif /* CONFIG_PROC_FS */ diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index e1858d9d23d8..8963d7688fb0 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1580,8 +1580,8 @@ static int snd_es1938_create(struct snd_card *card, if ((err = pci_enable_device(pci)) < 0) return err; /* check, if we can restrict PCI DMA transfers to 24 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) { dev_err(card->dev, "architecture does not support 24bit PCI busmaster DMA\n"); pci_disable_device(pci); diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 059f3846d7b8..e0d9363dc7fd 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2689,8 +2689,8 @@ static int snd_es1968_create(struct snd_card *card, if ((err = pci_enable_device(pci)) < 0) return err; /* check, if we can restrict PCI DMA transfers to 28 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) { dev_err(card->dev, "architecture does not support 28bit PCI busmaster DMA\n"); pci_disable_device(pci); diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index a5ed1c181784..e94cfd5c69f7 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -4,7 +4,7 @@ config SND_HDA tristate select SND_PCM select SND_VMASTER - select SND_KCTL_JACK + select SND_JACK if INPUT=y || INPUT=SND select SND_HDA_CORE config SND_HDA_INTEL @@ -38,22 +38,6 @@ config SND_HDA_TEGRA if SND_HDA -config SND_HDA_DSP_LOADER - bool - -config SND_HDA_PREALLOC_SIZE - int "Pre-allocated buffer size for HD-audio driver" - range 0 32768 - default 64 - help - Specifies the default pre-allocated buffer-size in kB for the - HD-audio driver. A larger buffer (e.g. 2048) is preferred - for systems using PulseAudio. The default 64 is chosen just - for compatibility reasons. - - Note that the pre-allocation size can be changed dynamically - via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. - config SND_HDA_HWDEP bool "Build hwdep interface for HD-audio driver" select SND_HWDEP @@ -87,14 +71,6 @@ config SND_HDA_INPUT_BEEP_MODE Set 1 to always enable the digital beep interface for HD-audio by default. -config SND_HDA_INPUT_JACK - bool "Support jack plugging notification via input layer" - depends on INPUT=y || INPUT=SND - select SND_JACK - help - Say Y here to enable the jack plugging notification via - input layer. - config SND_HDA_PATCH_LOADER bool "Support initialization patch loading for HD-audio" select FW_LOADER @@ -156,11 +132,6 @@ config SND_HDA_CODEC_HDMI comment "Set to Y if you want auto-loading the codec driver" depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m -config SND_HDA_I915 - bool - default y - depends on DRM_I915 - config SND_HDA_CODEC_CIRRUS tristate "Build Cirrus Logic codec support" select SND_HDA_GENERIC diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index af78fb33a4fd..6d83c6e0396a 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,16 +1,16 @@ snd-hda-intel-objs := hda_intel.o -snd-hda-controller-objs := hda_controller.o snd-hda-tegra-objs := hda_tegra.o -# for haswell power well -snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o -snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o +snd-hda-codec-y += hda_controller.o +snd-hda-codec-$(CONFIG_SND_PROC_FS) += hda_proc.o + snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o # for trace-points CFLAGS_hda_controller.o := -I$(src) +CFLAGS_hda_intel.o := -I$(src) snd-hda-codec-generic-objs := hda_generic.o snd-hda-codec-realtek-objs := patch_realtek.o @@ -27,7 +27,6 @@ snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o # common driver obj-$(CONFIG_SND_HDA) := snd-hda-codec.o -obj-$(CONFIG_SND_HDA) += snd-hda-controller.o # codec drivers obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 3364dc0fdeab..c397e7da0eac 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -1,7 +1,7 @@ /* * Digital Beep Input Interface for HD-audio codec * - * Author: Matthew Ranostay <mranostay@embeddedalley.com> + * Author: Matt Ranostay <mranostay@gmail.com> * Copyright (c) 2008 Embedded Alley Solutions Inc * * This driver is free software; you can redistribute it and/or modify diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index 46524ff7e79e..1052ad380e97 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -1,7 +1,7 @@ /* * Digital Beep Input Interface for HD-audio codec * - * Author: Matthew Ranostay <mranostay@embeddedalley.com> + * Author: Matt Ranostay <mranostay@gmail.com> * Copyright (c) 2008 Embedded Alley Solutions Inc * * This driver is free software; you can redistribute it and/or modify diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 00aa31c5f08e..d5ac25cc7fee 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -27,15 +27,7 @@ static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv) u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id; for (preset = driver->preset; preset->id; preset++) { - u32 mask = preset->mask; - - if (preset->afg && preset->afg != codec->core.afg) - continue; - if (preset->mfg && preset->mfg != codec->core.mfg) - continue; - if (!mask) - mask = ~0; - if (preset->id == (id & mask) && + if (preset->id == id && (!preset->rev || preset->rev == codec->core.revision_id)) { codec->preset = preset; return 1; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b49feff0a319..5de3c5d8c2c0 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -146,11 +146,11 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, bus->no_response_fallback = 0; mutex_unlock(&bus->core.cmd_mutex); snd_hda_power_down_pm(codec); - if (!codec_in_pm(codec) && res && err < 0 && bus->rirb_error) { + if (!codec_in_pm(codec) && res && err == -EAGAIN) { if (bus->response_reset) { codec_dbg(codec, "resetting BUS due to fatal communication error\n"); - bus->ops.bus_reset(bus); + snd_hda_bus_reset(bus); } goto again; } @@ -436,8 +436,8 @@ static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid) get_wcaps_type(wcaps) != AC_WID_PIN) return 0; - parm = snd_hda_param_read(codec, nid, AC_PAR_DEVLIST_LEN); - if (parm == -1 && codec->bus->rirb_error) + parm = snd_hdac_read_parm_uncached(&codec->core, nid, AC_PAR_DEVLIST_LEN); + if (parm == -1) parm = 0; return parm & AC_DEV_LIST_LEN_MASK; } @@ -467,10 +467,9 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, devices = 0; while (devices < dev_len) { - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DEVICE_LIST, devices); - if (parm == -1 && codec->bus->rirb_error) - break; + if (snd_hdac_read(&codec->core, nid, + AC_VERB_GET_DEVICE_LIST, devices, &parm)) + break; /* error */ for (i = 0; i < 8; i++) { dev_list[devices] = (u8)parm; @@ -484,96 +483,6 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, } /* - * destructor - */ -static void snd_hda_bus_free(struct hda_bus *bus) -{ - if (!bus) - return; - if (bus->ops.private_free) - bus->ops.private_free(bus); - snd_hdac_bus_exit(&bus->core); - kfree(bus); -} - -static int snd_hda_bus_dev_free(struct snd_device *device) -{ - snd_hda_bus_free(device->device_data); - return 0; -} - -static int snd_hda_bus_dev_disconnect(struct snd_device *device) -{ - struct hda_bus *bus = device->device_data; - bus->shutdown = 1; - return 0; -} - -/* hdac_bus_ops translations */ -static int _hda_bus_command(struct hdac_bus *_bus, unsigned int cmd) -{ - struct hda_bus *bus = container_of(_bus, struct hda_bus, core); - return bus->ops.command(bus, cmd); -} - -static int _hda_bus_get_response(struct hdac_bus *_bus, unsigned int addr, - unsigned int *res) -{ - struct hda_bus *bus = container_of(_bus, struct hda_bus, core); - *res = bus->ops.get_response(bus, addr); - return bus->rirb_error ? -EIO : 0; -} - -static const struct hdac_bus_ops bus_ops = { - .command = _hda_bus_command, - .get_response = _hda_bus_get_response, -}; - -/** - * snd_hda_bus_new - create a HDA bus - * @card: the card entry - * @busp: the pointer to store the created bus instance - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_bus_new(struct snd_card *card, - struct hda_bus **busp) -{ - struct hda_bus *bus; - int err; - static struct snd_device_ops dev_ops = { - .dev_disconnect = snd_hda_bus_dev_disconnect, - .dev_free = snd_hda_bus_dev_free, - }; - - if (busp) - *busp = NULL; - - bus = kzalloc(sizeof(*bus), GFP_KERNEL); - if (!bus) - return -ENOMEM; - - err = snd_hdac_bus_init(&bus->core, card->dev, &bus_ops); - if (err < 0) { - kfree(bus); - return err; - } - - bus->card = card; - mutex_init(&bus->prepare_mutex); - - err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); - if (err < 0) { - snd_hda_bus_free(bus); - return err; - } - if (busp) - *busp = bus; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_bus_new); - -/* * read widget caps for each widget and store in cache */ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) @@ -950,6 +859,7 @@ void snd_hda_codec_register(struct hda_codec *codec) return; if (device_is_registered(hda_codec_dev(codec))) { snd_hda_register_beep_device(codec); + snd_hdac_link_power(&codec->core, true); pm_runtime_enable(hda_codec_dev(codec)); /* it was powered up in snd_hda_codec_new(), now all done */ snd_hda_power_down(codec); @@ -977,6 +887,7 @@ static int snd_hda_codec_dev_free(struct snd_device *device) codec->in_freeing = 1; snd_hdac_device_unregister(&codec->core); + snd_hdac_link_power(&codec->core, false); put_device(hda_codec_dev(codec)); return 0; } @@ -1376,6 +1287,31 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); /** + * snd_hda_codec_amp_update - update the AMP mono value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel to update (0 or 1) + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP values for the given channel, direction and index. + */ +int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, + int ch, int dir, int idx, int mask, int val) +{ + unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); + + /* enable fake mute if no h/w mute but min=mute */ + if ((query_amp_caps(codec, nid, dir) & + (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE) + cmd |= AC_AMP_FAKE_MUTE; + return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val); +} +EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update); + +/** * snd_hda_codec_amp_stereo - update the AMP stereo values * @codec: HD-audio codec * @nid: NID to read the AMP value @@ -3198,6 +3134,7 @@ static int hda_codec_runtime_suspend(struct device *dev) if (codec_has_clkstop(codec) && codec_has_epss(codec) && (state & AC_PWRST_CLK_STOP_OK)) snd_hdac_codec_link_down(&codec->core); + snd_hdac_link_power(&codec->core, false); return 0; } @@ -3205,6 +3142,7 @@ static int hda_codec_runtime_resume(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); + snd_hdac_link_power(&codec->core, true); snd_hdac_codec_link_up(&codec->core); hda_call_codec_resume(codec); pm_runtime_mark_last_busy(dev); @@ -3288,311 +3226,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) } /* - * stream formats - */ -struct hda_rate_tbl { - unsigned int hz; - unsigned int alsa_bits; - unsigned int hda_fmt; -}; - -/* rate = base * mult / div */ -#define HDA_RATE(base, mult, div) \ - (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ - (((div) - 1) << AC_FMT_DIV_SHIFT)) - -static struct hda_rate_tbl rate_bits[] = { - /* rate in Hz, ALSA rate bitmask, HDA format value */ - - /* autodetected value used in snd_hda_query_supported_pcm */ - { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) }, - { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, - { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, - { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, - { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, - { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, - { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, - { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, - { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, - { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, - { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, -#define AC_PAR_PCM_RATE_BITS 11 - /* up to bits 10, 384kHZ isn't supported properly */ - - /* not autodetected value */ - { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, - - { 0 } /* terminator */ -}; - -/** - * snd_hda_calc_stream_format - calculate format bitset - * @codec: HD-audio codec - * @rate: the sample rate - * @channels: the number of channels - * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) - * @maxbps: the max. bps - * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) - * - * Calculate the format bitset from the given rate, channels and th PCM format. - * - * Return zero if invalid. - */ -unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, - unsigned int rate, - unsigned int channels, - unsigned int format, - unsigned int maxbps, - unsigned short spdif_ctls) -{ - int i; - unsigned int val = 0; - - for (i = 0; rate_bits[i].hz; i++) - if (rate_bits[i].hz == rate) { - val = rate_bits[i].hda_fmt; - break; - } - if (!rate_bits[i].hz) { - codec_dbg(codec, "invalid rate %d\n", rate); - return 0; - } - - if (channels == 0 || channels > 8) { - codec_dbg(codec, "invalid channels %d\n", channels); - return 0; - } - val |= channels - 1; - - switch (snd_pcm_format_width(format)) { - case 8: - val |= AC_FMT_BITS_8; - break; - case 16: - val |= AC_FMT_BITS_16; - break; - case 20: - case 24: - case 32: - if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) - val |= AC_FMT_BITS_32; - else if (maxbps >= 24) - val |= AC_FMT_BITS_24; - else - val |= AC_FMT_BITS_20; - break; - default: - codec_dbg(codec, "invalid format width %d\n", - snd_pcm_format_width(format)); - return 0; - } - - if (spdif_ctls & AC_DIG1_NONAUDIO) - val |= AC_FMT_TYPE_NON_PCM; - - return val; -} -EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format); - -static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int val = 0; - if (nid != codec->core.afg && - (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) - val = snd_hda_param_read(codec, nid, AC_PAR_PCM); - if (!val || val == -1) - val = snd_hda_param_read(codec, codec->core.afg, AC_PAR_PCM); - if (!val || val == -1) - return 0; - return val; -} - -static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); - if (!streams || streams == -1) - streams = snd_hda_param_read(codec, codec->core.afg, AC_PAR_STREAM); - if (!streams || streams == -1) - return 0; - return streams; -} - -/** - * snd_hda_query_supported_pcm - query the supported PCM rates and formats - * @codec: the HDA codec - * @nid: NID to query - * @ratesp: the pointer to store the detected rate bitflags - * @formatsp: the pointer to store the detected formats - * @bpsp: the pointer to store the detected format widths - * - * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp - * or @bsps argument is ignored. - * - * Returns 0 if successful, otherwise a negative error code. - */ -int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, - u32 *ratesp, u64 *formatsp, unsigned int *bpsp) -{ - unsigned int i, val, wcaps; - - wcaps = get_wcaps(codec, nid); - val = query_pcm_param(codec, nid); - - if (ratesp) { - u32 rates = 0; - for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) { - if (val & (1 << i)) - rates |= rate_bits[i].alsa_bits; - } - if (rates == 0) { - codec_err(codec, - "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n", - nid, val, - (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); - return -EIO; - } - *ratesp = rates; - } - - if (formatsp || bpsp) { - u64 formats = 0; - unsigned int streams, bps; - - streams = query_stream_param(codec, nid); - if (!streams) - return -EIO; - - bps = 0; - if (streams & AC_SUPFMT_PCM) { - if (val & AC_SUPPCM_BITS_8) { - formats |= SNDRV_PCM_FMTBIT_U8; - bps = 8; - } - if (val & AC_SUPPCM_BITS_16) { - formats |= SNDRV_PCM_FMTBIT_S16_LE; - bps = 16; - } - if (wcaps & AC_WCAP_DIGITAL) { - if (val & AC_SUPPCM_BITS_32) - formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; - if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24)) - formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (val & AC_SUPPCM_BITS_24) - bps = 24; - else if (val & AC_SUPPCM_BITS_20) - bps = 20; - } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24| - AC_SUPPCM_BITS_32)) { - formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (val & AC_SUPPCM_BITS_32) - bps = 32; - else if (val & AC_SUPPCM_BITS_24) - bps = 24; - else if (val & AC_SUPPCM_BITS_20) - bps = 20; - } - } -#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ - if (streams & AC_SUPFMT_FLOAT32) { - formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; - if (!bps) - bps = 32; - } -#endif - if (streams == AC_SUPFMT_AC3) { - /* should be exclusive */ - /* temporary hack: we have still no proper support - * for the direct AC3 stream... - */ - formats |= SNDRV_PCM_FMTBIT_U8; - bps = 8; - } - if (formats == 0) { - codec_err(codec, - "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n", - nid, val, - (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, - streams); - return -EIO; - } - if (formatsp) - *formatsp = formats; - if (bpsp) - *bpsp = bps; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_query_supported_pcm); - -/** - * snd_hda_is_supported_format - Check the validity of the format - * @codec: HD-audio codec - * @nid: NID to check - * @format: the HD-audio format value to check - * - * Check whether the given node supports the format value. - * - * Returns 1 if supported, 0 if not. - */ -int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, - unsigned int format) -{ - int i; - unsigned int val = 0, rate, stream; - - val = query_pcm_param(codec, nid); - if (!val) - return 0; - - rate = format & 0xff00; - for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) - if (rate_bits[i].hda_fmt == rate) { - if (val & (1 << i)) - break; - return 0; - } - if (i >= AC_PAR_PCM_RATE_BITS) - return 0; - - stream = query_stream_param(codec, nid); - if (!stream) - return 0; - - if (stream & AC_SUPFMT_PCM) { - switch (format & 0xf0) { - case 0x00: - if (!(val & AC_SUPPCM_BITS_8)) - return 0; - break; - case 0x10: - if (!(val & AC_SUPPCM_BITS_16)) - return 0; - break; - case 0x20: - if (!(val & AC_SUPPCM_BITS_20)) - return 0; - break; - case 0x30: - if (!(val & AC_SUPPCM_BITS_24)) - return 0; - break; - case 0x40: - if (!(val & AC_SUPPCM_BITS_32)) - return 0; - break; - default: - return 0; - } - } else { - /* FIXME: check for float32 and AC3? */ - } - - return 1; -} -EXPORT_SYMBOL_GPL(snd_hda_is_supported_format); - -/* * PCM stuff */ static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo, @@ -3804,9 +3437,6 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) struct hda_pcm *cpcm; int dev, err; - if (snd_BUG_ON(!bus->ops.attach_pcm)) - return -EINVAL; - err = snd_hda_codec_parse_pcms(codec); if (err < 0) { snd_hda_codec_reset(codec); @@ -3824,7 +3454,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) if (dev < 0) continue; /* no fatal error */ cpcm->device = dev; - err = bus->ops.attach_pcm(bus, codec, cpcm); + err = snd_hda_attach_pcm_stream(bus, codec, cpcm); if (err < 0) { codec_err(codec, "cannot attach PCM stream %d for codec #%d\n", @@ -3891,6 +3521,9 @@ static void codec_set_power_save(struct hda_codec *codec, int delay) { struct device *dev = hda_codec_dev(codec); + if (delay == 0 && codec->auto_runtime_pm) + delay = 3000; + if (delay > 0) { pm_runtime_set_autosuspend_delay(dev, delay); pm_runtime_use_autosuspend(dev); @@ -4494,10 +4127,10 @@ int snd_hda_add_imux_item(struct hda_codec *codec, EXPORT_SYMBOL_GPL(snd_hda_add_imux_item); /** - * snd_hda_bus_reset - Reset the bus + * snd_hda_bus_reset_codecs - Reset the bus * @bus: HD-audio bus */ -void snd_hda_bus_reset(struct hda_bus *bus) +void snd_hda_bus_reset_codecs(struct hda_bus *bus) { struct hda_codec *codec; @@ -4512,7 +4145,6 @@ void snd_hda_bus_reset(struct hda_bus *bus) #endif } } -EXPORT_SYMBOL_GPL(snd_hda_bus_reset); /** * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 9075ac28dc4b..12837abbbbe5 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -40,32 +40,6 @@ struct hda_codec; struct hda_pcm; struct hda_pcm_stream; -/* bus operators */ -struct hda_bus_ops { - /* send a single command */ - int (*command)(struct hda_bus *bus, unsigned int cmd); - /* get a response from the last command */ - unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr); - /* free the private data */ - void (*private_free)(struct hda_bus *); - /* attach a PCM stream */ - int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec, - struct hda_pcm *pcm); - /* reset bus for retry verb */ - void (*bus_reset)(struct hda_bus *bus); -#ifdef CONFIG_SND_HDA_DSP_LOADER - /* prepare DSP transfer */ - int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format, - unsigned int byte_size, - struct snd_dma_buffer *bufp); - /* start/stop DSP transfer */ - void (*load_dsp_trigger)(struct hda_bus *bus, bool start); - /* clean up DSP transfer */ - void (*load_dsp_cleanup)(struct hda_bus *bus, - struct snd_dma_buffer *dmab); -#endif -}; - /* * codec bus * @@ -77,10 +51,8 @@ struct hda_bus { struct snd_card *card; - void *private_data; struct pci_dev *pci; const char *modelname; - struct hda_bus_ops ops; struct mutex prepare_mutex; @@ -92,7 +64,6 @@ struct hda_bus { unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ /* status for codec/controller */ unsigned int shutdown :1; /* being unloaded */ - unsigned int rirb_error:1; /* error in codec communication */ unsigned int response_reset:1; /* controller was reset */ unsigned int in_reset:1; /* during reset operation */ unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ @@ -100,6 +71,9 @@ struct hda_bus { int primary_dig_out_type; /* primary digital out PCM type */ }; +/* from hdac_bus to hda_bus */ +#define to_hda_bus(bus) container_of(bus, struct hda_bus, core) + /* * codec preset * @@ -108,11 +82,7 @@ struct hda_bus { */ struct hda_codec_preset { unsigned int id; - unsigned int mask; - unsigned int subs; - unsigned int subs_mask; unsigned int rev; - hda_nid_t afg, mfg; const char *name; int (*patch)(struct hda_codec *codec); }; @@ -281,6 +251,7 @@ struct hda_codec { unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */ unsigned int dump_coef:1; /* dump processing coefs in codec proc file */ unsigned int power_save_node:1; /* advanced PM for each widget */ + unsigned int auto_runtime_pm:1; /* enable automatic codec runtime pm */ #ifdef CONFIG_PM unsigned long power_on_acct; unsigned long power_off_acct; @@ -300,10 +271,8 @@ struct hda_codec { unsigned long jackpoll_interval; /* In jiffies. Zero means no poll, rely on unsol events */ struct delayed_work jackpoll_work; -#ifdef CONFIG_SND_HDA_INPUT_JACK /* jack detection */ struct snd_array jacks; -#endif int depop_delay; /* depop delay in ms, -1 for default delay time */ @@ -328,7 +297,10 @@ struct hda_codec { /* * constructors */ -int snd_hda_bus_new(struct snd_card *card, struct hda_bus **busp); +int snd_hda_bus_new(struct snd_card *card, + const struct hdac_bus_ops *ops, + const struct hdac_io_ops *io_ops, + struct hda_bus **busp); int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp); int snd_hda_codec_configure(struct hda_codec *codec); @@ -367,8 +339,6 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive); int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, u8 *dev_list, int max_devices); -int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, - u32 *ratesp, u64 *formatsp, unsigned int *bpsp); struct hda_verb { hda_nid_t nid; @@ -460,17 +430,17 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, int do_now); #define snd_hda_codec_cleanup_stream(codec, nid) \ __snd_hda_codec_cleanup_stream(codec, nid, 0) -unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, - unsigned int rate, - unsigned int channels, - unsigned int format, - unsigned int maxbps, - unsigned short spdif_ctls); -int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, - unsigned int format); + +#define snd_hda_query_supported_pcm(codec, nid, ratesp, fmtsp, bpsp) \ + snd_hdac_query_supported_pcm(&(codec)->core, nid, ratesp, fmtsp, bpsp) +#define snd_hda_is_supported_format(codec, nid, fmt) \ + snd_hdac_is_supported_format(&(codec)->core, nid, fmt) extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[]; +int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec, + struct hda_pcm *cpcm); + /* * Misc */ @@ -481,6 +451,7 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, int snd_hda_lock_devices(struct hda_bus *bus); void snd_hda_unlock_devices(struct hda_bus *bus); void snd_hda_bus_reset(struct hda_bus *bus); +void snd_hda_bus_reset_codecs(struct hda_bus *bus); /* * power management @@ -526,24 +497,12 @@ int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf); #endif #ifdef CONFIG_SND_HDA_DSP_LOADER -static inline int -snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format, - unsigned int size, - struct snd_dma_buffer *bufp) -{ - return codec->bus->ops.load_dsp_prepare(codec->bus, format, size, bufp); -} -static inline void -snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) -{ - return codec->bus->ops.load_dsp_trigger(codec->bus, start); -} -static inline void -snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec, - struct snd_dma_buffer *dmab) -{ - return codec->bus->ops.load_dsp_cleanup(codec->bus, dmab); -} +int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format, + unsigned int size, + struct snd_dma_buffer *bufp); +void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start); +void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec, + struct snd_dma_buffer *dmab); #else static inline int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format, diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 26ce990592a0..944455997fdc 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -32,229 +32,29 @@ #include "hda_controller.h" #define CREATE_TRACE_POINTS -#include "hda_intel_trace.h" +#include "hda_controller_trace.h" /* DSP lock helpers */ -#ifdef CONFIG_SND_HDA_DSP_LOADER -#define dsp_lock_init(dev) mutex_init(&(dev)->dsp_mutex) -#define dsp_lock(dev) mutex_lock(&(dev)->dsp_mutex) -#define dsp_unlock(dev) mutex_unlock(&(dev)->dsp_mutex) -#define dsp_is_locked(dev) ((dev)->locked) -#else -#define dsp_lock_init(dev) do {} while (0) -#define dsp_lock(dev) do {} while (0) -#define dsp_unlock(dev) do {} while (0) -#define dsp_is_locked(dev) 0 -#endif - -/* - * AZX stream operations. - */ - -/* start a stream */ -static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev) -{ - /* - * Before stream start, initialize parameter - */ - azx_dev->insufficient = 1; - - /* enable SIE */ - azx_writel(chip, INTCTL, - azx_readl(chip, INTCTL) | (1 << azx_dev->index)); - /* set DMA start and interrupt mask */ - azx_sd_writeb(chip, azx_dev, SD_CTL, - azx_sd_readb(chip, azx_dev, SD_CTL) | - SD_CTL_DMA_START | SD_INT_MASK); -} - -/* stop DMA */ -static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev) -{ - azx_sd_writeb(chip, azx_dev, SD_CTL, - azx_sd_readb(chip, azx_dev, SD_CTL) & - ~(SD_CTL_DMA_START | SD_INT_MASK)); - azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ -} - -/* stop a stream */ -void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev) -{ - azx_stream_clear(chip, azx_dev); - /* disable SIE */ - azx_writel(chip, INTCTL, - azx_readl(chip, INTCTL) & ~(1 << azx_dev->index)); -} -EXPORT_SYMBOL_GPL(azx_stream_stop); - -/* reset stream */ -static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev) -{ - unsigned char val; - int timeout; - - azx_stream_clear(chip, azx_dev); - - azx_sd_writeb(chip, azx_dev, SD_CTL, - azx_sd_readb(chip, azx_dev, SD_CTL) | - SD_CTL_STREAM_RESET); - udelay(3); - timeout = 300; - while (!((val = azx_sd_readb(chip, azx_dev, SD_CTL)) & - SD_CTL_STREAM_RESET) && --timeout) - ; - val &= ~SD_CTL_STREAM_RESET; - azx_sd_writeb(chip, azx_dev, SD_CTL, val); - udelay(3); - - timeout = 300; - /* waiting for hardware to report that the stream is out of reset */ - while (((val = azx_sd_readb(chip, azx_dev, SD_CTL)) & - SD_CTL_STREAM_RESET) && --timeout) - ; - - /* reset first position - may not be synced with hw at this time */ - *azx_dev->posbuf = 0; -} - -/* - * set up the SD for streaming - */ -static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) -{ - unsigned int val; - /* make sure the run bit is zero for SD */ - azx_stream_clear(chip, azx_dev); - /* program the stream_tag */ - val = azx_sd_readl(chip, azx_dev, SD_CTL); - val = (val & ~SD_CTL_STREAM_TAG_MASK) | - (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT); - if (!azx_snoop(chip)) - val |= SD_CTL_TRAFFIC_PRIO; - azx_sd_writel(chip, azx_dev, SD_CTL, val); - - /* program the length of samples in cyclic buffer */ - azx_sd_writel(chip, azx_dev, SD_CBL, azx_dev->bufsize); - - /* program the stream format */ - /* this value needs to be the same as the one programmed */ - azx_sd_writew(chip, azx_dev, SD_FORMAT, azx_dev->format_val); - - /* program the stream LVI (last valid index) of the BDL */ - azx_sd_writew(chip, azx_dev, SD_LVI, azx_dev->frags - 1); - - /* program the BDL address */ - /* lower BDL address */ - azx_sd_writel(chip, azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); - /* upper BDL address */ - azx_sd_writel(chip, azx_dev, SD_BDLPU, - upper_32_bits(azx_dev->bdl.addr)); - - /* enable the position buffer */ - if (chip->get_position[0] != azx_get_pos_lpib || - chip->get_position[1] != azx_get_pos_lpib) { - if (!(azx_readl(chip, DPLBASE) & AZX_DPLBASE_ENABLE)) - azx_writel(chip, DPLBASE, - (u32)chip->posbuf.addr | AZX_DPLBASE_ENABLE); - } - - /* set the interrupt enable bits in the descriptor control register */ - azx_sd_writel(chip, azx_dev, SD_CTL, - azx_sd_readl(chip, azx_dev, SD_CTL) | SD_INT_MASK); - - return 0; -} +#define dsp_lock(dev) snd_hdac_dsp_lock(azx_stream(dev)) +#define dsp_unlock(dev) snd_hdac_dsp_unlock(azx_stream(dev)) +#define dsp_is_locked(dev) snd_hdac_stream_is_locked(azx_stream(dev)) /* assign a stream for the PCM */ static inline struct azx_dev * azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream) { - int dev, i, nums; - struct azx_dev *res = NULL; - /* make a non-zero unique key for the substream */ - int key = (substream->pcm->device << 16) | (substream->number << 2) | - (substream->stream + 1); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dev = chip->playback_index_offset; - nums = chip->playback_streams; - } else { - dev = chip->capture_index_offset; - nums = chip->capture_streams; - } - for (i = 0; i < nums; i++, dev++) { - struct azx_dev *azx_dev = &chip->azx_dev[dev]; - dsp_lock(azx_dev); - if (!azx_dev->opened && !dsp_is_locked(azx_dev)) { - if (azx_dev->assigned_key == key) { - azx_dev->opened = 1; - azx_dev->assigned_key = key; - dsp_unlock(azx_dev); - return azx_dev; - } - if (!res || - (chip->driver_caps & AZX_DCAPS_REVERSE_ASSIGN)) - res = azx_dev; - } - dsp_unlock(azx_dev); - } - if (res) { - dsp_lock(res); - res->opened = 1; - res->assigned_key = key; - dsp_unlock(res); - } - return res; + struct hdac_stream *s; + + s = snd_hdac_stream_assign(azx_bus(chip), substream); + if (!s) + return NULL; + return stream_to_azx_dev(s); } /* release the assigned stream */ static inline void azx_release_device(struct azx_dev *azx_dev) { - azx_dev->opened = 0; -} - -static cycle_t azx_cc_read(const struct cyclecounter *cc) -{ - struct azx_dev *azx_dev = container_of(cc, struct azx_dev, azx_cc); - struct snd_pcm_substream *substream = azx_dev->substream; - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - - return azx_readl(chip, WALLCLK); -} - -static void azx_timecounter_init(struct snd_pcm_substream *substream, - bool force, cycle_t last) -{ - struct azx_dev *azx_dev = get_azx_dev(substream); - struct timecounter *tc = &azx_dev->azx_tc; - struct cyclecounter *cc = &azx_dev->azx_cc; - u64 nsec; - - cc->read = azx_cc_read; - cc->mask = CLOCKSOURCE_MASK(32); - - /* - * Converting from 24 MHz to ns means applying a 125/3 factor. - * To avoid any saturation issues in intermediate operations, - * the 125 factor is applied first. The division is applied - * last after reading the timecounter value. - * Applying the 1/3 factor as part of the multiplication - * requires at least 20 bits for a decent precision, however - * overflows occur after about 4 hours or less, not a option. - */ - - cc->mult = 125; /* saturation after 195 years */ - cc->shift = 0; - - nsec = 0; /* audio time is elapsed time since trigger */ - timecounter_init(tc, cc, nsec); - if (force) - /* - * force timecounter to use predefined value, - * used for synchronized starts - */ - tc->cycle_last = last; + snd_hdac_stream_release(azx_stream(azx_dev)); } static inline struct hda_pcm_stream * @@ -285,119 +85,6 @@ static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream, } /* - * set up a BDL entry - */ -static int setup_bdle(struct azx *chip, - struct snd_dma_buffer *dmab, - struct azx_dev *azx_dev, u32 **bdlp, - int ofs, int size, int with_ioc) -{ - u32 *bdl = *bdlp; - - while (size > 0) { - dma_addr_t addr; - int chunk; - - if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES) - return -EINVAL; - - addr = snd_sgbuf_get_addr(dmab, ofs); - /* program the address field of the BDL entry */ - bdl[0] = cpu_to_le32((u32)addr); - bdl[1] = cpu_to_le32(upper_32_bits(addr)); - /* program the size field of the BDL entry */ - chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size); - /* one BDLE cannot cross 4K boundary on CTHDA chips */ - if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) { - u32 remain = 0x1000 - (ofs & 0xfff); - if (chunk > remain) - chunk = remain; - } - bdl[2] = cpu_to_le32(chunk); - /* program the IOC to enable interrupt - * only when the whole fragment is processed - */ - size -= chunk; - bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01); - bdl += 4; - azx_dev->frags++; - ofs += chunk; - } - *bdlp = bdl; - return ofs; -} - -/* - * set up BDL entries - */ -static int azx_setup_periods(struct azx *chip, - struct snd_pcm_substream *substream, - struct azx_dev *azx_dev) -{ - u32 *bdl; - int i, ofs, periods, period_bytes; - int pos_adj = 0; - - /* reset BDL address */ - azx_sd_writel(chip, azx_dev, SD_BDLPL, 0); - azx_sd_writel(chip, azx_dev, SD_BDLPU, 0); - - period_bytes = azx_dev->period_bytes; - periods = azx_dev->bufsize / period_bytes; - - /* program the initial BDL entries */ - bdl = (u32 *)azx_dev->bdl.area; - ofs = 0; - azx_dev->frags = 0; - - if (chip->bdl_pos_adj) - pos_adj = chip->bdl_pos_adj[chip->dev_index]; - if (!azx_dev->no_period_wakeup && pos_adj > 0) { - struct snd_pcm_runtime *runtime = substream->runtime; - int pos_align = pos_adj; - pos_adj = (pos_adj * runtime->rate + 47999) / 48000; - if (!pos_adj) - pos_adj = pos_align; - else - pos_adj = ((pos_adj + pos_align - 1) / pos_align) * - pos_align; - pos_adj = frames_to_bytes(runtime, pos_adj); - if (pos_adj >= period_bytes) { - dev_warn(chip->card->dev,"Too big adjustment %d\n", - pos_adj); - pos_adj = 0; - } else { - ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream), - azx_dev, - &bdl, ofs, pos_adj, true); - if (ofs < 0) - goto error; - } - } else - pos_adj = 0; - - for (i = 0; i < periods; i++) { - if (i == periods - 1 && pos_adj) - ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes - pos_adj, 0); - else - ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes, - !azx_dev->no_period_wakeup); - if (ofs < 0) - goto error; - } - return 0; - - error: - dev_err(chip->card->dev, "Too many BDL entries: buffer=%d, period=%d\n", - azx_dev->bufsize, period_bytes); - return -EINVAL; -} - -/* * PCM ops */ @@ -407,13 +94,9 @@ static int azx_pcm_close(struct snd_pcm_substream *substream) struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); - unsigned long flags; + trace_azx_pcm_close(chip, azx_dev); mutex_lock(&chip->open_mutex); - spin_lock_irqsave(&chip->reg_lock, flags); - azx_dev->substream = NULL; - azx_dev->running = 0; - spin_unlock_irqrestore(&chip->reg_lock, flags); azx_release_device(azx_dev); if (hinfo->ops.close) hinfo->ops.close(hinfo, apcm->codec, substream); @@ -428,18 +111,23 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; + struct azx_dev *azx_dev = get_azx_dev(substream); int ret; - dsp_lock(get_azx_dev(substream)); - if (dsp_is_locked(get_azx_dev(substream))) { + trace_azx_pcm_hw_params(chip, azx_dev); + dsp_lock(azx_dev); + if (dsp_is_locked(azx_dev)) { ret = -EBUSY; goto unlock; } + azx_dev->core.bufsize = 0; + azx_dev->core.period_bytes = 0; + azx_dev->core.format_val = 0; ret = chip->ops->substream_alloc_pages(chip, substream, params_buffer_bytes(hw_params)); unlock: - dsp_unlock(get_azx_dev(substream)); + dsp_unlock(azx_dev); return ret; } @@ -453,19 +141,13 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream) /* reset BDL address */ dsp_lock(azx_dev); - if (!dsp_is_locked(azx_dev)) { - azx_sd_writel(chip, azx_dev, SD_BDLPL, 0); - azx_sd_writel(chip, azx_dev, SD_BDLPU, 0); - azx_sd_writel(chip, azx_dev, SD_CTL, 0); - azx_dev->bufsize = 0; - azx_dev->period_bytes = 0; - azx_dev->format_val = 0; - } + if (!dsp_is_locked(azx_dev)) + snd_hdac_stream_cleanup(azx_stream(azx_dev)); snd_hda_codec_cleanup(apcm->codec, hinfo, substream); err = chip->ops->substream_free_pages(chip, substream); - azx_dev->prepared = 0; + azx_stream(azx_dev)->prepared = 0; dsp_unlock(azx_dev); return err; } @@ -477,21 +159,21 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) struct azx_dev *azx_dev = get_azx_dev(substream); struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int bufsize, period_bytes, format_val, stream_tag; + unsigned int format_val, stream_tag; int err; struct hda_spdif_out *spdif = snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid); unsigned short ctls = spdif ? spdif->ctls : 0; + trace_azx_pcm_prepare(chip, azx_dev); dsp_lock(azx_dev); if (dsp_is_locked(azx_dev)) { err = -EBUSY; goto unlock; } - azx_stream_reset(chip, azx_dev); - format_val = snd_hda_calc_stream_format(apcm->codec, - runtime->rate, + snd_hdac_stream_reset(azx_stream(azx_dev)); + format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, hinfo->maxbps, @@ -504,55 +186,23 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) goto unlock; } - bufsize = snd_pcm_lib_buffer_bytes(substream); - period_bytes = snd_pcm_lib_period_bytes(substream); - - dev_dbg(chip->card->dev, "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n", - bufsize, format_val); - - if (bufsize != azx_dev->bufsize || - period_bytes != azx_dev->period_bytes || - format_val != azx_dev->format_val || - runtime->no_period_wakeup != azx_dev->no_period_wakeup) { - azx_dev->bufsize = bufsize; - azx_dev->period_bytes = period_bytes; - azx_dev->format_val = format_val; - azx_dev->no_period_wakeup = runtime->no_period_wakeup; - err = azx_setup_periods(chip, substream, azx_dev); - if (err < 0) - goto unlock; - } + err = snd_hdac_stream_set_params(azx_stream(azx_dev), format_val); + if (err < 0) + goto unlock; - /* when LPIB delay correction gives a small negative value, - * we ignore it; currently set the threshold statically to - * 64 frames - */ - if (runtime->period_size > 64) - azx_dev->delay_negative_threshold = -frames_to_bytes(runtime, 64); - else - azx_dev->delay_negative_threshold = 0; - - /* wallclk has 24Mhz clock source */ - azx_dev->period_wallclk = (((runtime->period_size * 24000) / - runtime->rate) * 1000); - azx_setup_controller(chip, azx_dev); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - azx_dev->fifo_size = - azx_sd_readw(chip, azx_dev, SD_FIFOSIZE) + 1; - else - azx_dev->fifo_size = 0; + snd_hdac_stream_setup(azx_stream(azx_dev)); - stream_tag = azx_dev->stream_tag; + stream_tag = azx_dev->core.stream_tag; /* CA-IBG chips need the playback stream starting from 1 */ if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) && stream_tag > chip->capture_streams) stream_tag -= chip->capture_streams; err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag, - azx_dev->format_val, substream); + azx_dev->core.format_val, substream); unlock: if (!err) - azx_dev->prepared = 1; + azx_stream(azx_dev)->prepared = 1; dsp_unlock(azx_dev); return err; } @@ -561,28 +211,36 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; + struct hdac_bus *bus = azx_bus(chip); struct azx_dev *azx_dev; struct snd_pcm_substream *s; - int rstart = 0, start, nsync = 0, sbits = 0; - int nwait, timeout; + struct hdac_stream *hstr; + bool start; + int sbits = 0; + int sync_reg; azx_dev = get_azx_dev(substream); trace_azx_pcm_trigger(chip, azx_dev, cmd); - if (dsp_is_locked(azx_dev) || !azx_dev->prepared) + hstr = azx_stream(azx_dev); + if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) + sync_reg = AZX_REG_OLD_SSYNC; + else + sync_reg = AZX_REG_SSYNC; + + if (dsp_is_locked(azx_dev) || !hstr->prepared) return -EPIPE; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - rstart = 1; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: - start = 1; + start = true; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - start = 0; + start = false; break; default: return -EINVAL; @@ -592,115 +250,55 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (s->pcm->card != substream->pcm->card) continue; azx_dev = get_azx_dev(s); - sbits |= 1 << azx_dev->index; - nsync++; + sbits |= 1 << azx_dev->core.index; snd_pcm_trigger_done(s, substream); } - spin_lock(&chip->reg_lock); + spin_lock(&bus->reg_lock); /* first, set SYNC bits of corresponding streams */ - if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) - azx_writel(chip, OLD_SSYNC, - azx_readl(chip, OLD_SSYNC) | sbits); - else - azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits); + snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg); snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; azx_dev = get_azx_dev(s); if (start) { - azx_dev->start_wallclk = azx_readl(chip, WALLCLK); - if (!rstart) - azx_dev->start_wallclk -= - azx_dev->period_wallclk; - azx_stream_start(chip, azx_dev); + azx_dev->insufficient = 1; + snd_hdac_stream_start(azx_stream(azx_dev), true); } else { - azx_stream_stop(chip, azx_dev); - } - azx_dev->running = start; - } - spin_unlock(&chip->reg_lock); - if (start) { - /* wait until all FIFOs get ready */ - for (timeout = 5000; timeout; timeout--) { - nwait = 0; - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_dev = get_azx_dev(s); - if (!(azx_sd_readb(chip, azx_dev, SD_STS) & - SD_STS_FIFO_READY)) - nwait++; - } - if (!nwait) - break; - cpu_relax(); - } - } else { - /* wait until all RUN bits are cleared */ - for (timeout = 5000; timeout; timeout--) { - nwait = 0; - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_dev = get_azx_dev(s); - if (azx_sd_readb(chip, azx_dev, SD_CTL) & - SD_CTL_DMA_START) - nwait++; - } - if (!nwait) - break; - cpu_relax(); + snd_hdac_stream_stop(azx_stream(azx_dev)); } } - spin_lock(&chip->reg_lock); + spin_unlock(&bus->reg_lock); + + snd_hdac_stream_sync(hstr, start, sbits); + + spin_lock(&bus->reg_lock); /* reset SYNC bits */ - if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) - azx_writel(chip, OLD_SSYNC, - azx_readl(chip, OLD_SSYNC) & ~sbits); - else - azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits); - if (start) { - azx_timecounter_init(substream, 0, 0); - snd_pcm_gettime(substream->runtime, &substream->runtime->trigger_tstamp); - substream->runtime->trigger_tstamp_latched = true; - - if (nsync > 1) { - cycle_t cycle_last; - - /* same start cycle for master and group */ - azx_dev = get_azx_dev(substream); - cycle_last = azx_dev->azx_tc.cycle_last; - - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_timecounter_init(s, 1, cycle_last); - } - } - } - spin_unlock(&chip->reg_lock); + snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg); + if (start) + snd_hdac_stream_timecounter_init(hstr, sbits); + spin_unlock(&bus->reg_lock); return 0; } unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev) { - return azx_sd_readl(chip, azx_dev, SD_LPIB); + return snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev)); } EXPORT_SYMBOL_GPL(azx_get_pos_lpib); unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev) { - return le32_to_cpu(*azx_dev->posbuf); + return snd_hdac_stream_get_pos_posbuf(azx_stream(azx_dev)); } EXPORT_SYMBOL_GPL(azx_get_pos_posbuf); unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev) { - struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_substream *substream = azx_dev->core.substream; unsigned int pos; int stream = substream->stream; int delay = 0; @@ -710,7 +308,7 @@ unsigned int azx_get_position(struct azx *chip, else /* use the position buffer as default */ pos = azx_get_pos_posbuf(chip, azx_dev); - if (pos >= azx_dev->bufsize) + if (pos >= azx_dev->core.bufsize) pos = 0; if (substream->runtime) { @@ -752,7 +350,7 @@ static int azx_get_time_info(struct snd_pcm_substream *substream, snd_pcm_gettime(substream->runtime, system_ts); - nsec = timecounter_read(&azx_dev->azx_tc); + nsec = timecounter_read(&azx_dev->core.tc); nsec = div_u64(nsec, 3); /* can be optimized */ if (audio_tstamp_config->report_delay) nsec = azx_adjust_codec_delay(substream, nsec); @@ -802,17 +400,18 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) struct azx *chip = apcm->chip; struct azx_dev *azx_dev; struct snd_pcm_runtime *runtime = substream->runtime; - unsigned long flags; int err; int buff_step; snd_hda_codec_pcm_get(apcm->info); mutex_lock(&chip->open_mutex); azx_dev = azx_assign_device(chip, substream); + trace_azx_pcm_open(chip, azx_dev); if (azx_dev == NULL) { err = -EBUSY; goto unlock; } + runtime->private_data = azx_dev; runtime->hw = azx_pcm_hw; runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; @@ -874,12 +473,6 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME; } - spin_lock_irqsave(&chip->reg_lock, flags); - azx_dev->substream = substream; - azx_dev->running = 0; - spin_unlock_irqrestore(&chip->reg_lock, flags); - - runtime->private_data = azx_dev; snd_pcm_set_sync(substream); mutex_unlock(&chip->open_mutex); return 0; @@ -928,10 +521,11 @@ static void azx_pcm_free(struct snd_pcm *pcm) #define MAX_PREALLOC_SIZE (32 * 1024 * 1024) -static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, - struct hda_pcm *cpcm) +int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec, + struct hda_pcm *cpcm) { - struct azx *chip = bus->private_data; + struct hdac_bus *bus = &_bus->core; + struct azx *chip = bus_to_azx(bus); struct snd_pcm *pcm; struct azx_pcm *apcm; int pcm_dev = cpcm->device; @@ -979,89 +573,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, return 0; } -/* - * CORB / RIRB interface - */ -static int azx_alloc_cmd_io(struct azx *chip) -{ - /* single page (at least 4096 bytes) must suffice for both ringbuffes */ - return chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, - PAGE_SIZE, &chip->rb); -} - -static void azx_init_cmd_io(struct azx *chip) -{ - int timeout; - - spin_lock_irq(&chip->reg_lock); - /* CORB set up */ - chip->corb.addr = chip->rb.addr; - chip->corb.buf = (u32 *)chip->rb.area; - azx_writel(chip, CORBLBASE, (u32)chip->corb.addr); - azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr)); - - /* set the corb size to 256 entries (ULI requires explicitly) */ - azx_writeb(chip, CORBSIZE, 0x02); - /* set the corb write pointer to 0 */ - azx_writew(chip, CORBWP, 0); - - /* reset the corb hw read pointer */ - azx_writew(chip, CORBRP, AZX_CORBRP_RST); - if (!(chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)) { - for (timeout = 1000; timeout > 0; timeout--) { - if ((azx_readw(chip, CORBRP) & AZX_CORBRP_RST) == AZX_CORBRP_RST) - break; - udelay(1); - } - if (timeout <= 0) - dev_err(chip->card->dev, "CORB reset timeout#1, CORBRP = %d\n", - azx_readw(chip, CORBRP)); - - azx_writew(chip, CORBRP, 0); - for (timeout = 1000; timeout > 0; timeout--) { - if (azx_readw(chip, CORBRP) == 0) - break; - udelay(1); - } - if (timeout <= 0) - dev_err(chip->card->dev, "CORB reset timeout#2, CORBRP = %d\n", - azx_readw(chip, CORBRP)); - } - - /* enable corb dma */ - azx_writeb(chip, CORBCTL, AZX_CORBCTL_RUN); - - /* RIRB set up */ - chip->rirb.addr = chip->rb.addr + 2048; - chip->rirb.buf = (u32 *)(chip->rb.area + 2048); - chip->rirb.wp = chip->rirb.rp = 0; - memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds)); - azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr); - azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr)); - - /* set the rirb size to 256 entries (ULI requires explicitly) */ - azx_writeb(chip, RIRBSIZE, 0x02); - /* reset the rirb hw write pointer */ - azx_writew(chip, RIRBWP, AZX_RIRBWP_RST); - /* set N=1, get RIRB response interrupt for new entry */ - if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) - azx_writew(chip, RINTCNT, 0xc0); - else - azx_writew(chip, RINTCNT, 1); - /* enable rirb dma and response irq */ - azx_writeb(chip, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); - spin_unlock_irq(&chip->reg_lock); -} - -static void azx_free_cmd_io(struct azx *chip) -{ - spin_lock_irq(&chip->reg_lock); - /* disable ringbuffer DMAs */ - azx_writeb(chip, RIRBCTL, 0); - azx_writeb(chip, CORBCTL, 0); - spin_unlock_irq(&chip->reg_lock); -} - static unsigned int azx_command_addr(u32 cmd) { unsigned int addr = cmd >> 28; @@ -1074,92 +585,12 @@ static unsigned int azx_command_addr(u32 cmd) return addr; } -/* send a command */ -static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) -{ - struct azx *chip = bus->private_data; - unsigned int addr = azx_command_addr(val); - unsigned int wp, rp; - - spin_lock_irq(&chip->reg_lock); - - /* add command to corb */ - wp = azx_readw(chip, CORBWP); - if (wp == 0xffff) { - /* something wrong, controller likely turned to D3 */ - spin_unlock_irq(&chip->reg_lock); - return -EIO; - } - wp++; - wp %= AZX_MAX_CORB_ENTRIES; - - rp = azx_readw(chip, CORBRP); - if (wp == rp) { - /* oops, it's full */ - spin_unlock_irq(&chip->reg_lock); - return -EAGAIN; - } - - chip->rirb.cmds[addr]++; - chip->corb.buf[wp] = cpu_to_le32(val); - azx_writew(chip, CORBWP, wp); - - spin_unlock_irq(&chip->reg_lock); - - return 0; -} - -#define AZX_RIRB_EX_UNSOL_EV (1<<4) - -/* retrieve RIRB entry - called from interrupt handler */ -static void azx_update_rirb(struct azx *chip) -{ - unsigned int rp, wp; - unsigned int addr; - u32 res, res_ex; - - wp = azx_readw(chip, RIRBWP); - if (wp == 0xffff) { - /* something wrong, controller likely turned to D3 */ - return; - } - - if (wp == chip->rirb.wp) - return; - chip->rirb.wp = wp; - - while (chip->rirb.rp != wp) { - chip->rirb.rp++; - chip->rirb.rp %= AZX_MAX_RIRB_ENTRIES; - - rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */ - res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]); - res = le32_to_cpu(chip->rirb.buf[rp]); - addr = res_ex & 0xf; - if ((addr >= AZX_MAX_CODECS) || !(chip->codec_mask & (1 << addr))) { - dev_err(chip->card->dev, "spurious response %#x:%#x, rp = %d, wp = %d", - res, res_ex, - chip->rirb.rp, wp); - snd_BUG(); - } else if (res_ex & AZX_RIRB_EX_UNSOL_EV) - snd_hda_queue_unsol_event(chip->bus, res, res_ex); - else if (chip->rirb.cmds[addr]) { - chip->rirb.res[addr] = res; - smp_wmb(); - chip->rirb.cmds[addr]--; - } else if (printk_ratelimit()) { - dev_err(chip->card->dev, "spurious response %#x:%#x, last cmd=%#08x\n", - res, res_ex, - chip->last_cmd[addr]); - } - } -} - /* receive a response */ -static unsigned int azx_rirb_get_response(struct hda_bus *bus, - unsigned int addr) +static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr, + unsigned int *res) { - struct azx *chip = bus->private_data; + struct azx *chip = bus_to_azx(bus); + struct hda_bus *hbus = &chip->bus; unsigned long timeout; unsigned long loopcounter; int do_poll = 0; @@ -1168,22 +599,21 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, timeout = jiffies + msecs_to_jiffies(1000); for (loopcounter = 0;; loopcounter++) { - if (chip->polling_mode || do_poll) { - spin_lock_irq(&chip->reg_lock); - azx_update_rirb(chip); - spin_unlock_irq(&chip->reg_lock); - } - if (!chip->rirb.cmds[addr]) { - smp_rmb(); - bus->rirb_error = 0; - + spin_lock_irq(&bus->reg_lock); + if (chip->polling_mode || do_poll) + snd_hdac_bus_update_rirb(bus); + if (!bus->rirb.cmds[addr]) { if (!do_poll) chip->poll_count = 0; - return chip->rirb.res[addr]; /* the last value */ + if (res) + *res = bus->rirb.res[addr]; /* the last value */ + spin_unlock_irq(&bus->reg_lock); + return 0; } + spin_unlock_irq(&bus->reg_lock); if (time_after(jiffies, timeout)) break; - if (bus->needs_damn_long_delay || loopcounter > 3000) + if (hbus->needs_damn_long_delay || loopcounter > 3000) msleep(2); /* temporary workaround */ else { udelay(10); @@ -1191,13 +621,13 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, } } - if (bus->no_response_fallback) - return -1; + if (hbus->no_response_fallback) + return -EIO; if (!chip->polling_mode && chip->poll_count < 2) { dev_dbg(chip->card->dev, "azx_get_response timeout, polling the codec once: last cmd=0x%08x\n", - chip->last_cmd[addr]); + bus->last_cmd[addr]); do_poll = 1; chip->poll_count++; goto again; @@ -1207,7 +637,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, if (!chip->polling_mode) { dev_warn(chip->card->dev, "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n", - chip->last_cmd[addr]); + bus->last_cmd[addr]); chip->polling_mode = 1; goto again; } @@ -1215,12 +645,10 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, if (chip->msi) { dev_warn(chip->card->dev, "No response from codec, disabling MSI: last cmd=0x%08x\n", - chip->last_cmd[addr]); - if (chip->ops->disable_msi_reset_irq(chip) && - chip->ops->disable_msi_reset_irq(chip) < 0) { - bus->rirb_error = 1; - return -1; - } + bus->last_cmd[addr]); + if (chip->ops->disable_msi_reset_irq && + chip->ops->disable_msi_reset_irq(chip) < 0) + return -EIO; goto again; } @@ -1229,28 +657,24 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, * phase, this is likely an access to a non-existing codec * slot. Better to return an error and reset the system. */ - return -1; + return -EIO; } /* a fatal communication error; need either to reset or to fallback * to the single_cmd mode */ - bus->rirb_error = 1; - if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) { - bus->response_reset = 1; - return -1; /* give a chance to retry */ + if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) { + hbus->response_reset = 1; + return -EAGAIN; /* give a chance to retry */ } dev_err(chip->card->dev, "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n", - chip->last_cmd[addr]); + bus->last_cmd[addr]); chip->single_cmd = 1; - bus->response_reset = 0; - /* release CORB/RIRB */ - azx_free_cmd_io(chip); - /* disable unsolicited responses */ - azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_UNSOL); - return -1; + hbus->response_reset = 0; + snd_hdac_bus_stop_cmd_io(bus); + return -EIO; } /* @@ -1272,7 +696,7 @@ static int azx_single_wait_for_response(struct azx *chip, unsigned int addr) /* check IRV busy bit */ if (azx_readw(chip, IRS) & AZX_IRS_VALID) { /* reuse rirb.res as the response return value */ - chip->rirb.res[addr] = azx_readl(chip, IR); + azx_bus(chip)->rirb.res[addr] = azx_readl(chip, IR); return 0; } udelay(1); @@ -1280,18 +704,18 @@ static int azx_single_wait_for_response(struct azx *chip, unsigned int addr) if (printk_ratelimit()) dev_dbg(chip->card->dev, "get_response timeout: IRS=0x%x\n", azx_readw(chip, IRS)); - chip->rirb.res[addr] = -1; + azx_bus(chip)->rirb.res[addr] = -1; return -EIO; } /* send a command */ -static int azx_single_send_cmd(struct hda_bus *bus, u32 val) +static int azx_single_send_cmd(struct hdac_bus *bus, u32 val) { - struct azx *chip = bus->private_data; + struct azx *chip = bus_to_azx(bus); unsigned int addr = azx_command_addr(val); int timeout = 50; - bus->rirb_error = 0; + bus->last_cmd[azx_command_addr(val)] = val; while (timeout--) { /* check ICB busy bit */ if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) { @@ -1313,11 +737,12 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) } /* receive a response */ -static unsigned int azx_single_get_response(struct hda_bus *bus, - unsigned int addr) +static int azx_single_get_response(struct hdac_bus *bus, unsigned int addr, + unsigned int *res) { - struct azx *chip = bus->private_data; - return chip->rirb.res[addr]; + if (res) + *res = bus->rirb.res[addr]; + return 0; } /* @@ -1328,32 +753,48 @@ static unsigned int azx_single_get_response(struct hda_bus *bus, */ /* send a command */ -static int azx_send_cmd(struct hda_bus *bus, unsigned int val) +static int azx_send_cmd(struct hdac_bus *bus, unsigned int val) { - struct azx *chip = bus->private_data; + struct azx *chip = bus_to_azx(bus); if (chip->disabled) return 0; - chip->last_cmd[azx_command_addr(val)] = val; if (chip->single_cmd) return azx_single_send_cmd(bus, val); else - return azx_corb_send_cmd(bus, val); + return snd_hdac_bus_send_cmd(bus, val); } /* get a response */ -static unsigned int azx_get_response(struct hda_bus *bus, - unsigned int addr) +static int azx_get_response(struct hdac_bus *bus, unsigned int addr, + unsigned int *res) { - struct azx *chip = bus->private_data; + struct azx *chip = bus_to_azx(bus); + if (chip->disabled) return 0; if (chip->single_cmd) - return azx_single_get_response(bus, addr); + return azx_single_get_response(bus, addr, res); + else + return azx_rirb_get_response(bus, addr, res); +} + +static int azx_link_power(struct hdac_bus *bus, bool enable) +{ + struct azx *chip = bus_to_azx(bus); + + if (chip->ops->link_power) + return chip->ops->link_power(chip, enable); else - return azx_rirb_get_response(bus, addr); + return -EINVAL; } +static const struct hdac_bus_ops bus_core_ops = { + .command = azx_send_cmd, + .get_response = azx_get_response, + .link_power = azx_link_power, +}; + #ifdef CONFIG_SND_HDA_DSP_LOADER /* * DSP loading code (e.g. for CA0132) @@ -1363,339 +804,132 @@ static unsigned int azx_get_response(struct hda_bus *bus, static struct azx_dev * azx_get_dsp_loader_dev(struct azx *chip) { - return &chip->azx_dev[chip->playback_index_offset]; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + + list_for_each_entry(s, &bus->stream_list, list) + if (s->index == chip->playback_index_offset) + return stream_to_azx_dev(s); + + return NULL; } -static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, - unsigned int byte_size, - struct snd_dma_buffer *bufp) +int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format, + unsigned int byte_size, + struct snd_dma_buffer *bufp) { - u32 *bdl; - struct azx *chip = bus->private_data; + struct hdac_bus *bus = &codec->bus->core; + struct azx *chip = bus_to_azx(bus); struct azx_dev *azx_dev; + struct hdac_stream *hstr; + bool saved = false; int err; azx_dev = azx_get_dsp_loader_dev(chip); - - dsp_lock(azx_dev); - spin_lock_irq(&chip->reg_lock); - if (azx_dev->running || azx_dev->locked) { - spin_unlock_irq(&chip->reg_lock); - err = -EBUSY; - goto unlock; + hstr = azx_stream(azx_dev); + spin_lock_irq(&bus->reg_lock); + if (hstr->opened) { + chip->saved_azx_dev = *azx_dev; + saved = true; } - azx_dev->prepared = 0; - chip->saved_azx_dev = *azx_dev; - azx_dev->locked = 1; - spin_unlock_irq(&chip->reg_lock); - - err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV_SG, - byte_size, bufp); - if (err < 0) - goto err_alloc; + spin_unlock_irq(&bus->reg_lock); - azx_dev->bufsize = byte_size; - azx_dev->period_bytes = byte_size; - azx_dev->format_val = format; - - azx_stream_reset(chip, azx_dev); - - /* reset BDL address */ - azx_sd_writel(chip, azx_dev, SD_BDLPL, 0); - azx_sd_writel(chip, azx_dev, SD_BDLPU, 0); - - azx_dev->frags = 0; - bdl = (u32 *)azx_dev->bdl.area; - err = setup_bdle(chip, bufp, azx_dev, &bdl, 0, byte_size, 0); - if (err < 0) - goto error; - - azx_setup_controller(chip, azx_dev); - dsp_unlock(azx_dev); - return azx_dev->stream_tag; + err = snd_hdac_dsp_prepare(hstr, format, byte_size, bufp); + if (err < 0) { + spin_lock_irq(&bus->reg_lock); + if (saved) + *azx_dev = chip->saved_azx_dev; + spin_unlock_irq(&bus->reg_lock); + return err; + } - error: - chip->ops->dma_free_pages(chip, bufp); - err_alloc: - spin_lock_irq(&chip->reg_lock); - if (azx_dev->opened) - *azx_dev = chip->saved_azx_dev; - azx_dev->locked = 0; - spin_unlock_irq(&chip->reg_lock); - unlock: - dsp_unlock(azx_dev); + hstr->prepared = 0; return err; } +EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_prepare); -static void azx_load_dsp_trigger(struct hda_bus *bus, bool start) +void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) { - struct azx *chip = bus->private_data; + struct hdac_bus *bus = &codec->bus->core; + struct azx *chip = bus_to_azx(bus); struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip); - if (start) - azx_stream_start(chip, azx_dev); - else - azx_stream_stop(chip, azx_dev); - azx_dev->running = start; + snd_hdac_dsp_trigger(azx_stream(azx_dev), start); } +EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_trigger); -static void azx_load_dsp_cleanup(struct hda_bus *bus, - struct snd_dma_buffer *dmab) +void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec, + struct snd_dma_buffer *dmab) { - struct azx *chip = bus->private_data; + struct hdac_bus *bus = &codec->bus->core; + struct azx *chip = bus_to_azx(bus); struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip); + struct hdac_stream *hstr = azx_stream(azx_dev); - if (!dmab->area || !azx_dev->locked) + if (!dmab->area || !hstr->locked) return; - dsp_lock(azx_dev); - /* reset BDL address */ - azx_sd_writel(chip, azx_dev, SD_BDLPL, 0); - azx_sd_writel(chip, azx_dev, SD_BDLPU, 0); - azx_sd_writel(chip, azx_dev, SD_CTL, 0); - azx_dev->bufsize = 0; - azx_dev->period_bytes = 0; - azx_dev->format_val = 0; - - chip->ops->dma_free_pages(chip, dmab); - dmab->area = NULL; - - spin_lock_irq(&chip->reg_lock); - if (azx_dev->opened) + snd_hdac_dsp_cleanup(hstr, dmab); + spin_lock_irq(&bus->reg_lock); + if (hstr->opened) *azx_dev = chip->saved_azx_dev; - azx_dev->locked = 0; - spin_unlock_irq(&chip->reg_lock); - dsp_unlock(azx_dev); + hstr->locked = false; + spin_unlock_irq(&bus->reg_lock); } +EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_cleanup); #endif /* CONFIG_SND_HDA_DSP_LOADER */ -int azx_alloc_stream_pages(struct azx *chip) -{ - int i, err; - - for (i = 0; i < chip->num_streams; i++) { - dsp_lock_init(&chip->azx_dev[i]); - /* allocate memory for the BDL for each stream */ - err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, - BDL_SIZE, - &chip->azx_dev[i].bdl); - if (err < 0) - return -ENOMEM; - } - /* allocate memory for the position buffer */ - err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, - chip->num_streams * 8, &chip->posbuf); - if (err < 0) - return -ENOMEM; - - /* allocate CORB/RIRB */ - err = azx_alloc_cmd_io(chip); - if (err < 0) - return err; - return 0; -} -EXPORT_SYMBOL_GPL(azx_alloc_stream_pages); - -void azx_free_stream_pages(struct azx *chip) -{ - int i; - if (chip->azx_dev) { - for (i = 0; i < chip->num_streams; i++) - if (chip->azx_dev[i].bdl.area) - chip->ops->dma_free_pages( - chip, &chip->azx_dev[i].bdl); - } - if (chip->rb.area) - chip->ops->dma_free_pages(chip, &chip->rb); - if (chip->posbuf.area) - chip->ops->dma_free_pages(chip, &chip->posbuf); -} -EXPORT_SYMBOL_GPL(azx_free_stream_pages); - /* - * Lowlevel interface + * reset and start the controller registers */ - -/* enter link reset */ -void azx_enter_link_reset(struct azx *chip) -{ - unsigned long timeout; - - /* reset controller */ - azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_RESET); - - timeout = jiffies + msecs_to_jiffies(100); - while ((azx_readb(chip, GCTL) & AZX_GCTL_RESET) && - time_before(jiffies, timeout)) - usleep_range(500, 1000); -} -EXPORT_SYMBOL_GPL(azx_enter_link_reset); - -/* exit link reset */ -static void azx_exit_link_reset(struct azx *chip) -{ - unsigned long timeout; - - azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | AZX_GCTL_RESET); - - timeout = jiffies + msecs_to_jiffies(100); - while (!azx_readb(chip, GCTL) && - time_before(jiffies, timeout)) - usleep_range(500, 1000); -} - -/* reset codec link */ -static int azx_reset(struct azx *chip, bool full_reset) -{ - if (!full_reset) - goto __skip; - - /* clear STATESTS */ - azx_writew(chip, STATESTS, STATESTS_INT_MASK); - - /* reset controller */ - azx_enter_link_reset(chip); - - /* delay for >= 100us for codec PLL to settle per spec - * Rev 0.9 section 5.5.1 - */ - usleep_range(500, 1000); - - /* Bring controller out of reset */ - azx_exit_link_reset(chip); - - /* Brent Chartrand said to wait >= 540us for codecs to initialize */ - usleep_range(1000, 1200); - - __skip: - /* check to see if controller is ready */ - if (!azx_readb(chip, GCTL)) { - dev_dbg(chip->card->dev, "azx_reset: controller not ready!\n"); - return -EBUSY; - } - - /* Accept unsolicited responses */ - if (!chip->single_cmd) - azx_writel(chip, GCTL, azx_readl(chip, GCTL) | - AZX_GCTL_UNSOL); - - /* detect codecs */ - if (!chip->codec_mask) { - chip->codec_mask = azx_readw(chip, STATESTS); - dev_dbg(chip->card->dev, "codec_mask = 0x%x\n", - chip->codec_mask); - } - - return 0; -} - -/* enable interrupts */ -static void azx_int_enable(struct azx *chip) -{ - /* enable controller CIE and GIE */ - azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) | - AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN); -} - -/* disable interrupts */ -static void azx_int_disable(struct azx *chip) -{ - int i; - - /* disable interrupts in stream descriptor */ - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; - azx_sd_writeb(chip, azx_dev, SD_CTL, - azx_sd_readb(chip, azx_dev, SD_CTL) & - ~SD_INT_MASK); - } - - /* disable SIE for all streams */ - azx_writeb(chip, INTCTL, 0); - - /* disable controller CIE and GIE */ - azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) & - ~(AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN)); -} - -/* clear interrupts */ -static void azx_int_clear(struct azx *chip) +void azx_init_chip(struct azx *chip, bool full_reset) { - int i; - - /* clear stream status */ - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; - azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK); + if (snd_hdac_bus_init_chip(azx_bus(chip), full_reset)) { + /* correct RINTCNT for CXT */ + if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) + azx_writew(chip, RINTCNT, 0xc0); } - - /* clear STATESTS */ - azx_writew(chip, STATESTS, STATESTS_INT_MASK); - - /* clear rirb status */ - azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); - - /* clear int status */ - azx_writel(chip, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM); } +EXPORT_SYMBOL_GPL(azx_init_chip); -/* - * reset and start the controller registers - */ -void azx_init_chip(struct azx *chip, bool full_reset) +void azx_stop_all_streams(struct azx *chip) { - if (chip->initialized) - return; - - /* reset controller */ - azx_reset(chip, full_reset); - - /* initialize interrupts */ - azx_int_clear(chip); - azx_int_enable(chip); - - /* initialize the codec command I/O */ - if (!chip->single_cmd) - azx_init_cmd_io(chip); - - /* program the position buffer */ - azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr); - azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr)); + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; - chip->initialized = 1; + list_for_each_entry(s, &bus->stream_list, list) + snd_hdac_stream_stop(s); } -EXPORT_SYMBOL_GPL(azx_init_chip); +EXPORT_SYMBOL_GPL(azx_stop_all_streams); void azx_stop_chip(struct azx *chip) { - if (!chip->initialized) - return; - - /* disable interrupts */ - azx_int_disable(chip); - azx_int_clear(chip); - - /* disable CORB/RIRB */ - azx_free_cmd_io(chip); - - /* disable position buffer */ - azx_writel(chip, DPLBASE, 0); - azx_writel(chip, DPUBASE, 0); - - chip->initialized = 0; + snd_hdac_bus_stop_chip(azx_bus(chip)); } EXPORT_SYMBOL_GPL(azx_stop_chip); /* * interrupt handler */ +static void stream_update(struct hdac_bus *bus, struct hdac_stream *s) +{ + struct azx *chip = bus_to_azx(bus); + struct azx_dev *azx_dev = stream_to_azx_dev(s); + + /* check whether this IRQ is really acceptable */ + if (!chip->ops->position_check || + chip->ops->position_check(chip, azx_dev)) { + spin_unlock(&bus->reg_lock); + snd_pcm_period_elapsed(azx_stream(azx_dev)->substream); + spin_lock(&bus->reg_lock); + } +} + irqreturn_t azx_interrupt(int irq, void *dev_id) { struct azx *chip = dev_id; - struct azx_dev *azx_dev; + struct hdac_bus *bus = azx_bus(chip); u32 status; - u8 sd_status; - int i; #ifdef CONFIG_PM if (azx_has_pm_runtime(chip)) @@ -1703,36 +937,20 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) return IRQ_NONE; #endif - spin_lock(&chip->reg_lock); + spin_lock(&bus->reg_lock); if (chip->disabled) { - spin_unlock(&chip->reg_lock); + spin_unlock(&bus->reg_lock); return IRQ_NONE; } status = azx_readl(chip, INTSTS); if (status == 0 || status == 0xffffffff) { - spin_unlock(&chip->reg_lock); + spin_unlock(&bus->reg_lock); return IRQ_NONE; } - for (i = 0; i < chip->num_streams; i++) { - azx_dev = &chip->azx_dev[i]; - if (status & azx_dev->sd_int_sta_mask) { - sd_status = azx_sd_readb(chip, azx_dev, SD_STS); - azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK); - if (!azx_dev->substream || !azx_dev->running || - !(sd_status & SD_INT_COMPLETE)) - continue; - /* check whether this IRQ is really acceptable */ - if (!chip->ops->position_check || - chip->ops->position_check(chip, azx_dev)) { - spin_unlock(&chip->reg_lock); - snd_pcm_period_elapsed(azx_dev->substream); - spin_lock(&chip->reg_lock); - } - } - } + snd_hdac_bus_handle_stream_irq(bus, status, stream_update); /* clear rirb int */ status = azx_readb(chip, RIRBSTS); @@ -1740,12 +958,12 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) if (status & RIRB_INT_RESPONSE) { if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY) udelay(80); - azx_update_rirb(chip); + snd_hdac_bus_update_rirb(bus); } azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); } - spin_unlock(&chip->reg_lock); + spin_unlock(&bus->reg_lock); return IRQ_HANDLED; } @@ -1762,29 +980,31 @@ static int probe_codec(struct azx *chip, int addr) { unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; - unsigned int res; + struct hdac_bus *bus = azx_bus(chip); + int err; + unsigned int res = -1; - mutex_lock(&chip->bus->core.cmd_mutex); + mutex_lock(&bus->cmd_mutex); chip->probing = 1; - azx_send_cmd(chip->bus, cmd); - res = azx_get_response(chip->bus, addr); + azx_send_cmd(bus, cmd); + err = azx_get_response(bus, addr, &res); chip->probing = 0; - mutex_unlock(&chip->bus->core.cmd_mutex); - if (res == -1) + mutex_unlock(&bus->cmd_mutex); + if (err < 0 || res == -1) return -EIO; dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr); return 0; } -static void azx_bus_reset(struct hda_bus *bus) +void snd_hda_bus_reset(struct hda_bus *bus) { - struct azx *chip = bus->private_data; + struct azx *chip = bus_to_azx(&bus->core); bus->in_reset = 1; azx_stop_chip(chip); azx_init_chip(chip, true); - if (chip->initialized) - snd_hda_bus_reset(chip->bus); + if (bus->core.chip_init) + snd_hda_bus_reset_codecs(bus); bus->in_reset = 0; } @@ -1809,33 +1029,30 @@ static int get_jackpoll_interval(struct azx *chip) return j; } -static struct hda_bus_ops bus_ops = { - .command = azx_send_cmd, - .get_response = azx_get_response, - .attach_pcm = azx_attach_pcm_stream, - .bus_reset = azx_bus_reset, -#ifdef CONFIG_SND_HDA_DSP_LOADER - .load_dsp_prepare = azx_load_dsp_prepare, - .load_dsp_trigger = azx_load_dsp_trigger, - .load_dsp_cleanup = azx_load_dsp_cleanup, -#endif -}; - /* HD-audio bus initialization */ -int azx_bus_create(struct azx *chip, const char *model) +int azx_bus_init(struct azx *chip, const char *model, + const struct hdac_io_ops *io_ops) { - struct hda_bus *bus; + struct hda_bus *bus = &chip->bus; int err; - err = snd_hda_bus_new(chip->card, &bus); + err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops, + io_ops); if (err < 0) return err; - chip->bus = bus; - bus->private_data = chip; + bus->card = chip->card; + mutex_init(&bus->prepare_mutex); bus->pci = chip->pci; bus->modelname = model; - bus->ops = bus_ops; + bus->core.snoop = azx_snoop(chip); + if (chip->get_position[0] != azx_get_pos_lpib || + chip->get_position[1] != azx_get_pos_lpib) + bus->core.use_posbuf = true; + if (chip->bdl_pos_adj) + bus->core.bdl_pos_adj = chip->bdl_pos_adj[chip->dev_index]; + if (chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR) + bus->core.corbrp_self_clear = true; if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) { dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n"); @@ -1854,12 +1071,12 @@ int azx_bus_create(struct azx *chip, const char *model) return 0; } -EXPORT_SYMBOL_GPL(azx_bus_create); +EXPORT_SYMBOL_GPL(azx_bus_init); /* Probe codecs */ int azx_probe_codecs(struct azx *chip, unsigned int max_slots) { - struct hda_bus *bus = chip->bus; + struct hdac_bus *bus = azx_bus(chip); int c, codecs, err; codecs = 0; @@ -1868,14 +1085,14 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots) /* First try to probe all given codec slots */ for (c = 0; c < max_slots; c++) { - if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { + if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) { if (probe_codec(chip, c) < 0) { /* Some BIOSen give you wrong codec addresses * that don't exist */ dev_warn(chip->card->dev, "Codec #%d probe error; disabling it...\n", c); - chip->codec_mask &= ~(1 << c); + bus->codec_mask &= ~(1 << c); /* More badly, accessing to a non-existing * codec often screws up the controller chip, * and disturbs the further communications. @@ -1891,9 +1108,9 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots) /* Then create codec instances */ for (c = 0; c < max_slots; c++) { - if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { + if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) { struct hda_codec *codec; - err = snd_hda_codec_new(bus, bus->card, c, &codec); + err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec); if (err < 0) continue; codec->jackpoll_interval = get_jackpoll_interval(chip); @@ -1913,40 +1130,39 @@ EXPORT_SYMBOL_GPL(azx_probe_codecs); int azx_codec_configure(struct azx *chip) { struct hda_codec *codec; - list_for_each_codec(codec, chip->bus) { + list_for_each_codec(codec, &chip->bus) { snd_hda_codec_configure(codec); } return 0; } EXPORT_SYMBOL_GPL(azx_codec_configure); - -static bool is_input_stream(struct azx *chip, unsigned char index) +static int stream_direction(struct azx *chip, unsigned char index) { - return (index >= chip->capture_index_offset && - index < chip->capture_index_offset + chip->capture_streams); + if (index >= chip->capture_index_offset && + index < chip->capture_index_offset + chip->capture_streams) + return SNDRV_PCM_STREAM_CAPTURE; + return SNDRV_PCM_STREAM_PLAYBACK; } /* initialize SD streams */ -int azx_init_stream(struct azx *chip) +int azx_init_streams(struct azx *chip) { int i; - int in_stream_tag = 0; - int out_stream_tag = 0; + int stream_tags[2] = { 0, 0 }; /* initialize each stream (aka device) * assign the starting bdl address to each stream (device) * and initialize */ for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; - azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8); - /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ - azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80); - /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ - azx_dev->sd_int_sta_mask = 1 << i; - azx_dev->index = i; + struct azx_dev *azx_dev = kzalloc(sizeof(*azx_dev), GFP_KERNEL); + int dir, tag; + + if (!azx_dev) + return -ENOMEM; + dir = stream_direction(chip, i); /* stream tag must be unique throughout * the stream direction group, * valid values 1...15 @@ -1954,17 +1170,26 @@ int azx_init_stream(struct azx *chip) * AZX_DCAPS_SEPARATE_STREAM_TAG is used */ if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) - azx_dev->stream_tag = - is_input_stream(chip, i) ? - ++in_stream_tag : - ++out_stream_tag; + tag = ++stream_tags[dir]; else - azx_dev->stream_tag = i + 1; + tag = i + 1; + snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev), + i, dir, tag); } return 0; } -EXPORT_SYMBOL_GPL(azx_init_stream); +EXPORT_SYMBOL_GPL(azx_init_streams); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Common HDA driver functions"); +void azx_free_streams(struct azx *chip) +{ + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + + while (!list_empty(&bus->stream_list)) { + s = list_first_entry(&bus->stream_list, struct hdac_stream, list); + list_del(&s->list); + kfree(stream_to_azx_dev(s)); + } +} +EXPORT_SYMBOL_GPL(azx_free_streams); diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index 0efdb094d21c..314105cd5061 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -21,135 +21,10 @@ #include <sound/pcm.h> #include <sound/initval.h> #include "hda_codec.h" +#include <sound/hda_register.h> -/* - * registers - */ -#define AZX_REG_GCAP 0x00 -#define AZX_GCAP_64OK (1 << 0) /* 64bit address support */ -#define AZX_GCAP_NSDO (3 << 1) /* # of serial data out signals */ -#define AZX_GCAP_BSS (31 << 3) /* # of bidirectional streams */ -#define AZX_GCAP_ISS (15 << 8) /* # of input streams */ -#define AZX_GCAP_OSS (15 << 12) /* # of output streams */ -#define AZX_REG_VMIN 0x02 -#define AZX_REG_VMAJ 0x03 -#define AZX_REG_OUTPAY 0x04 -#define AZX_REG_INPAY 0x06 -#define AZX_REG_GCTL 0x08 -#define AZX_GCTL_RESET (1 << 0) /* controller reset */ -#define AZX_GCTL_FCNTRL (1 << 1) /* flush control */ -#define AZX_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ -#define AZX_REG_WAKEEN 0x0c -#define AZX_REG_STATESTS 0x0e -#define AZX_REG_GSTS 0x10 -#define AZX_GSTS_FSTS (1 << 1) /* flush status */ -#define AZX_REG_INTCTL 0x20 -#define AZX_REG_INTSTS 0x24 -#define AZX_REG_WALLCLK 0x30 /* 24Mhz source */ -#define AZX_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */ -#define AZX_REG_SSYNC 0x38 -#define AZX_REG_CORBLBASE 0x40 -#define AZX_REG_CORBUBASE 0x44 -#define AZX_REG_CORBWP 0x48 -#define AZX_REG_CORBRP 0x4a -#define AZX_CORBRP_RST (1 << 15) /* read pointer reset */ -#define AZX_REG_CORBCTL 0x4c -#define AZX_CORBCTL_RUN (1 << 1) /* enable DMA */ -#define AZX_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */ -#define AZX_REG_CORBSTS 0x4d -#define AZX_CORBSTS_CMEI (1 << 0) /* memory error indication */ -#define AZX_REG_CORBSIZE 0x4e - -#define AZX_REG_RIRBLBASE 0x50 -#define AZX_REG_RIRBUBASE 0x54 -#define AZX_REG_RIRBWP 0x58 -#define AZX_RIRBWP_RST (1 << 15) /* write pointer reset */ -#define AZX_REG_RINTCNT 0x5a -#define AZX_REG_RIRBCTL 0x5c -#define AZX_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */ -#define AZX_RBCTL_DMA_EN (1 << 1) /* enable DMA */ -#define AZX_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */ -#define AZX_REG_RIRBSTS 0x5d -#define AZX_RBSTS_IRQ (1 << 0) /* response irq */ -#define AZX_RBSTS_OVERRUN (1 << 2) /* overrun irq */ -#define AZX_REG_RIRBSIZE 0x5e - -#define AZX_REG_IC 0x60 -#define AZX_REG_IR 0x64 -#define AZX_REG_IRS 0x68 -#define AZX_IRS_VALID (1<<1) -#define AZX_IRS_BUSY (1<<0) - -#define AZX_REG_DPLBASE 0x70 -#define AZX_REG_DPUBASE 0x74 -#define AZX_DPLBASE_ENABLE 0x1 /* Enable position buffer */ - -/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ -enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; - -/* stream register offsets from stream base */ -#define AZX_REG_SD_CTL 0x00 -#define AZX_REG_SD_STS 0x03 -#define AZX_REG_SD_LPIB 0x04 -#define AZX_REG_SD_CBL 0x08 -#define AZX_REG_SD_LVI 0x0c -#define AZX_REG_SD_FIFOW 0x0e -#define AZX_REG_SD_FIFOSIZE 0x10 -#define AZX_REG_SD_FORMAT 0x12 -#define AZX_REG_SD_BDLPL 0x18 -#define AZX_REG_SD_BDLPU 0x1c - -/* PCI space */ -#define AZX_PCIREG_TCSEL 0x44 - -/* - * other constants - */ - -/* max number of fragments - we may use more if allocating more pages for BDL */ -#define BDL_SIZE 4096 -#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) -#define AZX_MAX_FRAG 32 -/* max buffer size - no h/w limit, you can increase as you like */ -#define AZX_MAX_BUF_SIZE (1024*1024*1024) - -/* RIRB int mask: overrun[2], response[0] */ -#define RIRB_INT_RESPONSE 0x01 -#define RIRB_INT_OVERRUN 0x04 -#define RIRB_INT_MASK 0x05 - -/* STATESTS int mask: S3,SD2,SD1,SD0 */ -#define AZX_MAX_CODECS 8 +#define AZX_MAX_CODECS HDA_MAX_CODECS #define AZX_DEFAULT_CODECS 4 -#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1) - -/* SD_CTL bits */ -#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ -#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ -#define SD_CTL_STRIPE (3 << 16) /* stripe control */ -#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ -#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ -#define SD_CTL_STREAM_TAG_MASK (0xf << 20) -#define SD_CTL_STREAM_TAG_SHIFT 20 - -/* SD_CTL and SD_STS */ -#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ -#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ -#define SD_INT_COMPLETE 0x04 /* completion interrupt */ -#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\ - SD_INT_COMPLETE) - -/* SD_STS */ -#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ - -/* INTCTL and INTSTS */ -#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */ -#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ -#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ - -/* below are so far hardcoded - should read registers in future */ -#define AZX_MAX_CORB_ENTRIES 256 -#define AZX_MAX_RIRB_ENTRIES 256 /* driver quirks (capabilities) */ /* bits 0-7 are used for indicating driver type */ @@ -183,40 +58,10 @@ enum { AZX_SNOOP_TYPE_NVIDIA, }; -/* HD Audio class code */ -#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 - struct azx_dev { - struct snd_dma_buffer bdl; /* BDL buffer */ - u32 *posbuf; /* position buffer pointer */ - - unsigned int bufsize; /* size of the play buffer in bytes */ - unsigned int period_bytes; /* size of the period in bytes */ - unsigned int frags; /* number for period in the play buffer */ - unsigned int fifo_size; /* FIFO size */ - unsigned long start_wallclk; /* start + minimum wallclk */ - unsigned long period_wallclk; /* wallclk for period */ - - void __iomem *sd_addr; /* stream descriptor pointer */ - - u32 sd_int_sta_mask; /* stream int status mask */ - - /* pcm support */ - struct snd_pcm_substream *substream; /* assigned substream, - * set in PCM open - */ - unsigned int format_val; /* format value to be set in the - * controller and the codec - */ - unsigned char stream_tag; /* assigned stream */ - unsigned char index; /* stream index */ - int assigned_key; /* last device# key assigned to */ - - unsigned int opened:1; - unsigned int running:1; + struct hdac_stream core; + unsigned int irq_pending:1; - unsigned int prepared:1; - unsigned int locked:1; /* * For VIA: * A flag to ensure DMA position is 0 @@ -224,50 +69,17 @@ struct azx_dev { */ unsigned int insufficient:1; unsigned int wc_marked:1; - unsigned int no_period_wakeup:1; - - struct timecounter azx_tc; - struct cyclecounter azx_cc; - - int delay_negative_threshold; - -#ifdef CONFIG_SND_HDA_DSP_LOADER - /* Allows dsp load to have sole access to the playback stream. */ - struct mutex dsp_mutex; -#endif }; -/* CORB/RIRB */ -struct azx_rb { - u32 *buf; /* CORB/RIRB buffer - * Each CORB entry is 4byte, RIRB is 8byte - */ - dma_addr_t addr; /* physical address of CORB/RIRB buffer */ - /* for RIRB */ - unsigned short rp, wp; /* read/write pointers */ - int cmds[AZX_MAX_CODECS]; /* number of pending requests */ - u32 res[AZX_MAX_CODECS]; /* last read value */ -}; +#define azx_stream(dev) (&(dev)->core) +#define stream_to_azx_dev(s) container_of(s, struct azx_dev, core) struct azx; /* Functions to read/write to hda registers. */ struct hda_controller_ops { - /* Register Access */ - void (*reg_writel)(u32 value, u32 __iomem *addr); - u32 (*reg_readl)(u32 __iomem *addr); - void (*reg_writew)(u16 value, u16 __iomem *addr); - u16 (*reg_readw)(u16 __iomem *addr); - void (*reg_writeb)(u8 value, u8 __iomem *addr); - u8 (*reg_readb)(u8 __iomem *addr); /* Disable msi if supported, PCI only */ int (*disable_msi_reset_irq)(struct azx *); - /* Allocation ops */ - int (*dma_alloc_pages)(struct azx *chip, - int type, - size_t size, - struct snd_dma_buffer *buf); - void (*dma_free_pages)(struct azx *chip, struct snd_dma_buffer *buf); int (*substream_alloc_pages)(struct azx *chip, struct snd_pcm_substream *substream, size_t size); @@ -277,6 +89,8 @@ struct hda_controller_ops { struct vm_area_struct *area); /* Check if current position is acceptable */ int (*position_check)(struct azx *chip, struct azx_dev *azx_dev); + /* enable/disable the link power */ + int (*link_power)(struct azx *chip, bool enable); }; struct azx_pcm { @@ -291,6 +105,8 @@ typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *); typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos); struct azx { + struct hda_bus bus; + struct snd_card *card; struct pci_dev *pci; int dev_index; @@ -312,35 +128,16 @@ struct azx { azx_get_pos_callback_t get_position[2]; azx_get_delay_callback_t get_delay[2]; - /* pci resources */ - unsigned long addr; - void __iomem *remap_addr; - int irq; - /* locks */ - spinlock_t reg_lock; struct mutex open_mutex; /* Prevents concurrent open/close operations */ - /* streams (x num_streams) */ - struct azx_dev *azx_dev; - /* PCM */ struct list_head pcm_list; /* azx_pcm list */ /* HD codec */ - unsigned short codec_mask; int codec_probe_mask; /* copied from probe_mask option */ - struct hda_bus *bus; unsigned int beep_mode; - /* CORB/RIRB */ - struct azx_rb corb; - struct azx_rb rirb; - - /* CORB/RIRB and position buffers */ - struct snd_dma_buffer rb; - struct snd_dma_buffer posbuf; - #ifdef CONFIG_SND_HDA_PATCH_LOADER const struct firmware *fw; #endif @@ -349,7 +146,6 @@ struct azx { const int *bdl_pos_adj; int poll_count; unsigned int running:1; - unsigned int initialized:1; unsigned int single_cmd:1; unsigned int polling_mode:1; unsigned int msi:1; @@ -359,14 +155,14 @@ struct azx { unsigned int region_requested:1; unsigned int disabled:1; /* disabled by VGA-switcher */ - /* for debugging */ - unsigned int last_cmd[AZX_MAX_CODECS]; - #ifdef CONFIG_SND_HDA_DSP_LOADER struct azx_dev saved_azx_dev; #endif }; +#define azx_bus(chip) (&(chip)->bus.core) +#define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core) + #ifdef CONFIG_X86 #define azx_snoop(chip) ((chip)->snoop) #else @@ -378,30 +174,17 @@ struct azx { */ #define azx_writel(chip, reg, value) \ - ((chip)->ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_writel(azx_bus(chip), reg, value) #define azx_readl(chip, reg) \ - ((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_readl(azx_bus(chip), reg) #define azx_writew(chip, reg, value) \ - ((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_writew(azx_bus(chip), reg, value) #define azx_readw(chip, reg) \ - ((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_readw(azx_bus(chip), reg) #define azx_writeb(chip, reg, value) \ - ((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_writeb(azx_bus(chip), reg, value) #define azx_readb(chip, reg) \ - ((chip)->ops->reg_readb((chip)->remap_addr + AZX_REG_##reg)) - -#define azx_sd_writel(chip, dev, reg, value) \ - ((chip)->ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg)) -#define azx_sd_readl(chip, dev, reg) \ - ((chip)->ops->reg_readl((dev)->sd_addr + AZX_REG_##reg)) -#define azx_sd_writew(chip, dev, reg, value) \ - ((chip)->ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg)) -#define azx_sd_readw(chip, dev, reg) \ - ((chip)->ops->reg_readw((dev)->sd_addr + AZX_REG_##reg)) -#define azx_sd_writeb(chip, dev, reg, value) \ - ((chip)->ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg)) -#define azx_sd_readb(chip, dev, reg) \ - ((chip)->ops->reg_readb((dev)->sd_addr + AZX_REG_##reg)) + snd_hdac_chip_readb(azx_bus(chip), reg) #define azx_has_pm_runtime(chip) \ ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME) @@ -416,22 +199,27 @@ unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev); unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev); /* Stream control. */ -void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev); +void azx_stop_all_streams(struct azx *chip); /* Allocation functions. */ -int azx_alloc_stream_pages(struct azx *chip); -void azx_free_stream_pages(struct azx *chip); +#define azx_alloc_stream_pages(chip) \ + snd_hdac_bus_alloc_stream_pages(azx_bus(chip)) +#define azx_free_stream_pages(chip) \ + snd_hdac_bus_free_stream_pages(azx_bus(chip)) /* Low level azx interface */ void azx_init_chip(struct azx *chip, bool full_reset); void azx_stop_chip(struct azx *chip); -void azx_enter_link_reset(struct azx *chip); +#define azx_enter_link_reset(chip) \ + snd_hdac_bus_enter_link_reset(azx_bus(chip)) irqreturn_t azx_interrupt(int irq, void *dev_id); /* Codec interface */ -int azx_bus_create(struct azx *chip, const char *model); +int azx_bus_init(struct azx *chip, const char *model, + const struct hdac_io_ops *io_ops); int azx_probe_codecs(struct azx *chip, unsigned int max_slots); int azx_codec_configure(struct azx *chip); -int azx_init_stream(struct azx *chip); +int azx_init_streams(struct azx *chip); +void azx_free_streams(struct azx *chip); #endif /* __SOUND_HDA_CONTROLLER_H */ diff --git a/sound/pci/hda/hda_controller_trace.h b/sound/pci/hda/hda_controller_trace.h new file mode 100644 index 000000000000..3e18d99bfb70 --- /dev/null +++ b/sound/pci/hda/hda_controller_trace.h @@ -0,0 +1,98 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM hda_controller +#define TRACE_INCLUDE_FILE hda_controller_trace + +#if !defined(_TRACE_HDA_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HDA_CONTROLLER_H + +#include <linux/tracepoint.h> + +struct azx; +struct azx_dev; + +TRACE_EVENT(azx_pcm_trigger, + + TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd), + + TP_ARGS(chip, dev, cmd), + + TP_STRUCT__entry( + __field( int, card ) + __field( int, idx ) + __field( int, cmd ) + ), + + TP_fast_assign( + __entry->card = (chip)->card->number; + __entry->idx = (dev)->core.index; + __entry->cmd = cmd; + ), + + TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd) +); + +TRACE_EVENT(azx_get_position, + + TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay), + + TP_ARGS(chip, dev, pos, delay), + + TP_STRUCT__entry( + __field( int, card ) + __field( int, idx ) + __field( unsigned int, pos ) + __field( unsigned int, delay ) + ), + + TP_fast_assign( + __entry->card = (chip)->card->number; + __entry->idx = (dev)->core.index; + __entry->pos = pos; + __entry->delay = delay; + ), + + TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay) +); + +DECLARE_EVENT_CLASS(azx_pcm, + TP_PROTO(struct azx *chip, struct azx_dev *azx_dev), + + TP_ARGS(chip, azx_dev), + + TP_STRUCT__entry( + __field( unsigned char, stream_tag ) + ), + + TP_fast_assign( + __entry->stream_tag = (azx_dev)->core.stream_tag; + ), + + TP_printk("stream_tag: %d", __entry->stream_tag) +); + +DEFINE_EVENT(azx_pcm, azx_pcm_open, + TP_PROTO(struct azx *chip, struct azx_dev *azx_dev), + TP_ARGS(chip, azx_dev) +); + +DEFINE_EVENT(azx_pcm, azx_pcm_close, + TP_PROTO(struct azx *chip, struct azx_dev *azx_dev), + TP_ARGS(chip, azx_dev) +); + +DEFINE_EVENT(azx_pcm, azx_pcm_hw_params, + TP_PROTO(struct azx *chip, struct azx_dev *azx_dev), + TP_ARGS(chip, azx_dev) +); + +DEFINE_EVENT(azx_pcm, azx_pcm_prepare, + TP_PROTO(struct azx *chip, struct azx_dev *azx_dev), + TP_ARGS(chip, azx_dev) +); + +#endif /* _TRACE_HDA_CONTROLLER_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#include <trace/define_trace.h> diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 0e6d7534f491..c746cd9a4450 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -448,7 +448,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) hdmi_show_short_audio_desc(codec, e->sad + i); } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS static void hdmi_print_sad_info(int i, struct cea_sad *a, struct snd_info_buffer *buffer) @@ -586,7 +586,7 @@ void snd_hdmi_write_eld_info(struct hdmi_eld *eld, } } } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_SND_PROC_FS */ /* update PCM info based on ELD */ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index ac0db1679f09..b077bb644434 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -5175,7 +5175,7 @@ static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, int err = 0; mutex_lock(&spec->pcm_mutex); - if (!spec->indep_hp_enabled) + if (spec->indep_hp && !spec->indep_hp_enabled) err = -EBUSY; else spec->active_streams |= 1 << STREAM_INDEP_HP; diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c deleted file mode 100644 index 3052a2b095f7..000000000000 --- a/sound/pci/hda/hda_i915.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * hda_i915.c - routines for Haswell HDA controller power well support - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/component.h> -#include <drm/i915_component.h> -#include <sound/core.h> -#include "hda_controller.h" -#include "hda_intel.h" - -/* Intel HSW/BDW display HDA controller Extended Mode registers. - * EM4 (M value) and EM5 (N Value) are used to convert CDClk (Core Display - * Clock) to 24MHz BCLK: BCLK = CDCLK * M / N - * The values will be lost when the display power well is disabled. - */ -#define AZX_REG_EM4 0x100c -#define AZX_REG_EM5 0x1010 - -int hda_display_power(struct hda_intel *hda, bool enable) -{ - struct i915_audio_component *acomp = &hda->audio_component; - - if (!acomp->ops) - return -ENODEV; - - dev_dbg(&hda->chip.pci->dev, "display power %s\n", - enable ? "enable" : "disable"); - if (enable) - acomp->ops->get_power(acomp->dev); - else - acomp->ops->put_power(acomp->dev); - - return 0; -} - -void haswell_set_bclk(struct hda_intel *hda) -{ - int cdclk_freq; - unsigned int bclk_m, bclk_n; - struct i915_audio_component *acomp = &hda->audio_component; - struct pci_dev *pci = hda->chip.pci; - - /* Only Haswell/Broadwell need set BCLK */ - if (pci->device != 0x0a0c && pci->device != 0x0c0c - && pci->device != 0x0d0c && pci->device != 0x160c) - return; - - if (!acomp->ops) - return; - - cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev); - switch (cdclk_freq) { - case 337500: - bclk_m = 16; - bclk_n = 225; - break; - - case 450000: - default: /* default CDCLK 450MHz */ - bclk_m = 4; - bclk_n = 75; - break; - - case 540000: - bclk_m = 4; - bclk_n = 90; - break; - - case 675000: - bclk_m = 8; - bclk_n = 225; - break; - } - - azx_writew(&hda->chip, EM4, bclk_m); - azx_writew(&hda->chip, EM5, bclk_n); -} - -static int hda_component_master_bind(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip = card->private_data; - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - struct i915_audio_component *acomp = &hda->audio_component; - int ret; - - ret = component_bind_all(dev, acomp); - if (ret < 0) - return ret; - - if (WARN_ON(!(acomp->dev && acomp->ops && acomp->ops->get_power && - acomp->ops->put_power && acomp->ops->get_cdclk_freq))) { - ret = -EINVAL; - goto out_unbind; - } - - /* - * Atm, we don't support dynamic unbinding initiated by the child - * component, so pin its containing module until we unbind. - */ - if (!try_module_get(acomp->ops->owner)) { - ret = -ENODEV; - goto out_unbind; - } - - return 0; - -out_unbind: - component_unbind_all(dev, acomp); - - return ret; -} - -static void hda_component_master_unbind(struct device *dev) -{ - struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip = card->private_data; - struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - struct i915_audio_component *acomp = &hda->audio_component; - - module_put(acomp->ops->owner); - component_unbind_all(dev, acomp); - WARN_ON(acomp->ops || acomp->dev); -} - -static const struct component_master_ops hda_component_master_ops = { - .bind = hda_component_master_bind, - .unbind = hda_component_master_unbind, -}; - -static int hda_component_master_match(struct device *dev, void *data) -{ - /* i915 is the only supported component */ - return !strcmp(dev->driver->name, "i915"); -} - -int hda_i915_init(struct hda_intel *hda) -{ - struct component_match *match = NULL; - struct device *dev = &hda->chip.pci->dev; - struct i915_audio_component *acomp = &hda->audio_component; - int ret; - - component_match_add(dev, &match, hda_component_master_match, hda); - ret = component_master_add_with_match(dev, &hda_component_master_ops, - match); - if (ret < 0) - goto out_err; - - /* - * Atm, we don't support deferring the component binding, so make sure - * i915 is loaded and that the binding successfully completes. - */ - request_module("i915"); - - if (!acomp->ops) { - ret = -ENODEV; - goto out_master_del; - } - - dev_dbg(dev, "bound to i915 component master\n"); - - return 0; -out_master_del: - component_master_del(dev, &hda_component_master_ops); -out_err: - dev_err(dev, "failed to add i915 component master (%d)\n", ret); - - return ret; -} - -int hda_i915_exit(struct hda_intel *hda) -{ - struct device *dev = &hda->chip.pci->dev; - - component_master_del(dev, &hda_component_master_ops); - - return 0; -} diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index fea198c58196..745535d1840a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -57,6 +57,8 @@ #endif #include <sound/core.h> #include <sound/initval.h> +#include <sound/hdaudio.h> +#include <sound/hda_i915.h> #include <linux/vgaarb.h> #include <linux/vga_switcheroo.h> #include <linux/firmware.h> @@ -64,6 +66,9 @@ #include "hda_controller.h" #include "hda_intel.h" +#define CREATE_TRACE_POINTS +#include "hda_intel_trace.h" + /* position fix mode */ enum { POS_FIX_AUTO, @@ -166,7 +171,7 @@ MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " #ifdef CONFIG_PM static int param_set_xint(const char *val, const struct kernel_param *kp); -static struct kernel_param_ops param_ops_xint = { +static const struct kernel_param_ops param_ops_xint = { .set = param_set_xint, .get = param_get_int, }; @@ -340,6 +345,11 @@ enum { #define use_vga_switcheroo(chip) 0 #endif +#define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \ + ((pci)->device == 0x0c0c) || \ + ((pci)->device == 0x0d0c) || \ + ((pci)->device == 0x160c)) + static char *driver_short_names[] = { [AZX_DRIVER_ICH] = "HDA Intel", [AZX_DRIVER_PCH] = "HDA Intel PCH", @@ -491,11 +501,22 @@ static void azx_init_pci(struct azx *chip) } } +static void hda_intel_init_chip(struct azx *chip, bool full_reset) +{ + struct hdac_bus *bus = azx_bus(chip); + + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + snd_hdac_set_codec_wakeup(bus, true); + azx_init_chip(chip, full_reset); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + snd_hdac_set_codec_wakeup(bus, false); +} + /* calculate runtime delay from LPIB */ static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev, unsigned int pos) { - struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_substream *substream = azx_dev->core.substream; int stream = substream->stream; unsigned int lpib_pos = azx_get_pos_lpib(chip, azx_dev); int delay; @@ -505,16 +526,16 @@ static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev, else delay = lpib_pos - pos; if (delay < 0) { - if (delay >= azx_dev->delay_negative_threshold) + if (delay >= azx_dev->core.delay_negative_threshold) delay = 0; else - delay += azx_dev->bufsize; + delay += azx_dev->core.bufsize; } - if (delay >= azx_dev->period_bytes) { + if (delay >= azx_dev->core.period_bytes) { dev_info(chip->card->dev, "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", - delay, azx_dev->period_bytes); + delay, azx_dev->core.period_bytes); delay = 0; chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; chip->get_delay[stream] = NULL; @@ -543,6 +564,14 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) return 0; } +/* Enable/disable i915 display power for the link */ +static int azx_intel_link_power(struct azx *chip, bool enable) +{ + struct hdac_bus *bus = azx_bus(chip); + + return snd_hdac_display_power(bus, enable); +} + /* * Check whether the current DMA position is acceptable for updating * periods. Returns non-zero if it's OK. @@ -554,13 +583,13 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) */ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) { - struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_substream *substream = azx_dev->core.substream; int stream = substream->stream; u32 wallclk; unsigned int pos; - wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk; - if (wallclk < (azx_dev->period_wallclk * 2) / 3) + wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk; + if (wallclk < (azx_dev->core.period_wallclk * 2) / 3) return -1; /* bogus (too early) interrupt */ if (chip->get_position[stream]) @@ -571,6 +600,9 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) dev_info(chip->card->dev, "Invalid position buffer, using LPIB read method instead.\n"); chip->get_position[stream] = azx_get_pos_lpib; + if (chip->get_position[0] == azx_get_pos_lpib && + chip->get_position[1] == azx_get_pos_lpib) + azx_bus(chip)->use_posbuf = false; pos = azx_get_pos_lpib(chip, azx_dev); chip->get_delay[stream] = NULL; } else { @@ -580,17 +612,17 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) } } - if (pos >= azx_dev->bufsize) + if (pos >= azx_dev->core.bufsize) pos = 0; - if (WARN_ONCE(!azx_dev->period_bytes, + if (WARN_ONCE(!azx_dev->core.period_bytes, "hda-intel: zero azx_dev->period_bytes")) return -1; /* this shouldn't happen! */ - if (wallclk < (azx_dev->period_wallclk * 5) / 4 && - pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) + if (wallclk < (azx_dev->core.period_wallclk * 5) / 4 && + pos % azx_dev->core.period_bytes > azx_dev->core.period_bytes / 2) /* NG - it's below the first next period boundary */ return chip->bdl_pos_adj[chip->dev_index] ? 0 : -1; - azx_dev->start_wallclk += wallclk; + azx_dev->core.start_wallclk += wallclk; return 1; /* OK, it's fine */ } @@ -601,7 +633,9 @@ static void azx_irq_pending_work(struct work_struct *work) { struct hda_intel *hda = container_of(work, struct hda_intel, irq_pending_work); struct azx *chip = &hda->chip; - int i, pending, ok; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + int pending, ok; if (!hda->irq_pending_warned) { dev_info(chip->card->dev, @@ -612,25 +646,25 @@ static void azx_irq_pending_work(struct work_struct *work) for (;;) { pending = 0; - spin_lock_irq(&chip->reg_lock); - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; + spin_lock_irq(&bus->reg_lock); + list_for_each_entry(s, &bus->stream_list, list) { + struct azx_dev *azx_dev = stream_to_azx_dev(s); if (!azx_dev->irq_pending || - !azx_dev->substream || - !azx_dev->running) + !s->substream || + !s->running) continue; ok = azx_position_ok(chip, azx_dev); if (ok > 0) { azx_dev->irq_pending = 0; - spin_unlock(&chip->reg_lock); - snd_pcm_period_elapsed(azx_dev->substream); - spin_lock(&chip->reg_lock); + spin_unlock(&bus->reg_lock); + snd_pcm_period_elapsed(s->substream); + spin_lock(&bus->reg_lock); } else if (ok < 0) { pending = 0; /* too early */ } else pending++; } - spin_unlock_irq(&chip->reg_lock); + spin_unlock_irq(&bus->reg_lock); if (!pending) return; msleep(1); @@ -640,16 +674,21 @@ static void azx_irq_pending_work(struct work_struct *work) /* clear irq_pending flags and assure no on-going workq */ static void azx_clear_irq_pending(struct azx *chip) { - int i; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; - spin_lock_irq(&chip->reg_lock); - for (i = 0; i < chip->num_streams; i++) - chip->azx_dev[i].irq_pending = 0; - spin_unlock_irq(&chip->reg_lock); + spin_lock_irq(&bus->reg_lock); + list_for_each_entry(s, &bus->stream_list, list) { + struct azx_dev *azx_dev = stream_to_azx_dev(s); + azx_dev->irq_pending = 0; + } + spin_unlock_irq(&bus->reg_lock); } static int azx_acquire_irq(struct azx *chip, int do_disconnect) { + struct hdac_bus *bus = azx_bus(chip); + if (request_irq(chip->pci->irq, azx_interrupt, chip->msi ? 0 : IRQF_SHARED, KBUILD_MODNAME, chip)) { @@ -660,7 +699,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) snd_card_disconnect(chip->card); return -1; } - chip->irq = chip->pci->irq; + bus->irq = chip->pci->irq; pci_intx(chip->pci, !chip->msi); return 0; } @@ -673,8 +712,8 @@ static unsigned int azx_via_get_position(struct azx *chip, unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos; unsigned int fifo_size; - link_pos = azx_sd_readl(chip, azx_dev, SD_LPIB); - if (azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + link_pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev)); + if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* Playback, no problem using link position */ return link_pos; } @@ -683,13 +722,14 @@ static unsigned int azx_via_get_position(struct azx *chip, /* For new chipset, * use mod to get the DMA position just like old chipset */ - mod_dma_pos = le32_to_cpu(*azx_dev->posbuf); - mod_dma_pos %= azx_dev->period_bytes; + mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf); + mod_dma_pos %= azx_dev->core.period_bytes; /* azx_dev->fifo_size can't get FIFO size of in stream. * Get from base address + offset. */ - fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET); + fifo_size = readw(azx_bus(chip)->remap_addr + + VIA_IN_STREAM0_FIFO_SIZE_OFFSET); if (azx_dev->insufficient) { /* Link position never gather than FIFO size */ @@ -700,20 +740,20 @@ static unsigned int azx_via_get_position(struct azx *chip, } if (link_pos <= fifo_size) - mini_pos = azx_dev->bufsize + link_pos - fifo_size; + mini_pos = azx_dev->core.bufsize + link_pos - fifo_size; else mini_pos = link_pos - fifo_size; /* Find nearest previous boudary */ - mod_mini_pos = mini_pos % azx_dev->period_bytes; - mod_link_pos = link_pos % azx_dev->period_bytes; + mod_mini_pos = mini_pos % azx_dev->core.period_bytes; + mod_link_pos = link_pos % azx_dev->core.period_bytes; if (mod_link_pos >= fifo_size) bound_pos = link_pos - mod_link_pos; else if (mod_dma_pos >= mod_mini_pos) bound_pos = mini_pos - mod_mini_pos; else { - bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes; - if (bound_pos >= azx_dev->bufsize) + bound_pos = mini_pos - mod_mini_pos + azx_dev->core.period_bytes; + if (bound_pos >= azx_dev->core.bufsize) bound_pos = 0; } @@ -755,9 +795,9 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) mutex_lock(&card_list_lock); list_for_each_entry(hda, &card_list, list) { chip = &hda->chip; - if (!chip->bus || chip->disabled) + if (!hda->probe_continued || chip->disabled) continue; - snd_hda_set_power_save(chip->bus, power_save * 1000); + snd_hda_set_power_save(&chip->bus, power_save * 1000); } mutex_unlock(&card_list_lock); return 0; @@ -767,6 +807,50 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) #define azx_del_card_list(chip) /* NOP */ #endif /* CONFIG_PM */ +/* Intel HSW/BDW display HDA controller is in GPU. Both its power and link BCLK + * depends on GPU. Two Extended Mode registers EM4 (M value) and EM5 (N Value) + * are used to convert CDClk (Core Display Clock) to 24MHz BCLK: + * BCLK = CDCLK * M / N + * The values will be lost when the display power well is disabled and need to + * be restored to avoid abnormal playback speed. + */ +static void haswell_set_bclk(struct hda_intel *hda) +{ + struct azx *chip = &hda->chip; + int cdclk_freq; + unsigned int bclk_m, bclk_n; + + if (!hda->need_i915_power) + return; + + cdclk_freq = snd_hdac_get_display_clk(azx_bus(chip)); + switch (cdclk_freq) { + case 337500: + bclk_m = 16; + bclk_n = 225; + break; + + case 450000: + default: /* default CDCLK 450MHz */ + bclk_m = 4; + bclk_n = 75; + break; + + case 540000: + bclk_m = 4; + bclk_n = 90; + break; + + case 675000: + bclk_m = 8; + bclk_n = 225; + break; + } + + azx_writew(chip, HSW_EM4, bclk_m); + azx_writew(chip, HSW_EM5, bclk_n); +} + #if defined(CONFIG_PM_SLEEP) || defined(SUPPORT_VGA_SWITCHEROO) /* * power management @@ -776,6 +860,7 @@ static int azx_suspend(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; struct hda_intel *hda; + struct hdac_bus *bus; if (!card) return 0; @@ -785,19 +870,23 @@ static int azx_suspend(struct device *dev) if (chip->disabled || hda->init_failed) return 0; + bus = azx_bus(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); azx_clear_irq_pending(chip); azx_stop_chip(chip); azx_enter_link_reset(chip); - if (chip->irq >= 0) { - free_irq(chip->irq, chip); - chip->irq = -1; + if (bus->irq >= 0) { + free_irq(bus->irq, chip); + bus->irq = -1; } if (chip->msi) pci_disable_msi(chip->pci); - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - hda_display_power(hda, false); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL + && hda->need_i915_power) + snd_hdac_display_power(bus, false); + + trace_azx_suspend(chip); return 0; } @@ -816,8 +905,9 @@ static int azx_resume(struct device *dev) if (chip->disabled || hda->init_failed) return 0; - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - hda_display_power(hda, true); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL + && hda->need_i915_power) { + snd_hdac_display_power(azx_bus(chip), true); haswell_set_bclk(hda); } if (chip->msi) @@ -827,9 +917,11 @@ static int azx_resume(struct device *dev) return -EIO; azx_init_pci(chip); - azx_init_chip(chip, true); + hda_intel_init_chip(chip, true); snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + trace_azx_resume(chip); return 0; } #endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */ @@ -859,9 +951,11 @@ static int azx_runtime_suspend(struct device *dev) azx_stop_chip(chip); azx_enter_link_reset(chip); azx_clear_irq_pending(chip); - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) - hda_display_power(hda, false); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL + && hda->need_i915_power) + snd_hdac_display_power(azx_bus(chip), false); + trace_azx_runtime_suspend(chip); return 0; } @@ -870,7 +964,7 @@ static int azx_runtime_resume(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; struct hda_intel *hda; - struct hda_bus *bus; + struct hdac_bus *bus; struct hda_codec *codec; int status; @@ -885,20 +979,24 @@ static int azx_runtime_resume(struct device *dev) if (!azx_has_pm_runtime(chip)) return 0; - if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - hda_display_power(hda, true); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL + && hda->need_i915_power) { + bus = azx_bus(chip); + snd_hdac_display_power(bus, true); haswell_set_bclk(hda); + /* toggle codec wakeup bit for STATESTS read */ + snd_hdac_set_codec_wakeup(bus, true); + snd_hdac_set_codec_wakeup(bus, false); } /* Read STATESTS before controller reset */ status = azx_readw(chip, STATESTS); azx_init_pci(chip); - azx_init_chip(chip, true); + hda_intel_init_chip(chip, true); - bus = chip->bus; - if (status && bus) { - list_for_each_codec(codec, bus) + if (status) { + list_for_each_codec(codec, &chip->bus) if (status & (1 << codec->addr)) schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval); @@ -908,6 +1006,7 @@ static int azx_runtime_resume(struct device *dev) azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK); + trace_azx_runtime_resume(chip); return 0; } @@ -926,7 +1025,7 @@ static int azx_runtime_idle(struct device *dev) return 0; if (!power_save_controller || !azx_has_pm_runtime(chip) || - chip->bus->core.codec_powered) + azx_bus(chip)->codec_powered) return -EBUSY; return 0; @@ -964,7 +1063,7 @@ static void azx_vs_set_state(struct pci_dev *pci, if (chip->disabled == disabled) return; - if (!chip->bus) { + if (!hda->probe_continued) { chip->disabled = disabled; if (!disabled) { dev_info(chip->card->dev, @@ -985,11 +1084,11 @@ static void azx_vs_set_state(struct pci_dev *pci, * put ourselves there */ pci->current_state = PCI_D3cold; chip->disabled = true; - if (snd_hda_lock_devices(chip->bus)) + if (snd_hda_lock_devices(&chip->bus)) dev_warn(chip->card->dev, "Cannot lock devices!\n"); } else { - snd_hda_unlock_devices(chip->bus); + snd_hda_unlock_devices(&chip->bus); pm_runtime_get_noresume(card->dev); chip->disabled = false; azx_resume(card->dev); @@ -1006,11 +1105,11 @@ static bool azx_vs_can_switch(struct pci_dev *pci) wait_for_completion(&hda->probe_wait); if (hda->init_failed) return false; - if (chip->disabled || !chip->bus) + if (chip->disabled || !hda->probe_continued) return true; - if (snd_hda_lock_devices(chip->bus)) + if (snd_hda_lock_devices(&chip->bus)) return false; - snd_hda_unlock_devices(chip->bus); + snd_hda_unlock_devices(&chip->bus); return true; } @@ -1043,7 +1142,7 @@ static int register_vga_switcheroo(struct azx *chip) */ err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, VGA_SWITCHEROO_DIS, - chip->bus != NULL); + hda->probe_continued); if (err < 0) return err; hda->vga_switcheroo_registered = 1; @@ -1066,7 +1165,7 @@ static int azx_free(struct azx *chip) { struct pci_dev *pci = chip->pci; struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - int i; + struct hdac_bus *bus = azx_bus(chip); if (azx_has_pm_runtime(chip) && chip->running) pm_runtime_get_noresume(&pci->dev); @@ -1077,42 +1176,54 @@ static int azx_free(struct azx *chip) complete_all(&hda->probe_wait); if (use_vga_switcheroo(hda)) { - if (chip->disabled && chip->bus) - snd_hda_unlock_devices(chip->bus); + if (chip->disabled && hda->probe_continued) + snd_hda_unlock_devices(&chip->bus); if (hda->vga_switcheroo_registered) vga_switcheroo_unregister_client(chip->pci); } - if (chip->initialized) { + if (bus->chip_init) { azx_clear_irq_pending(chip); - for (i = 0; i < chip->num_streams; i++) - azx_stream_stop(chip, &chip->azx_dev[i]); + azx_stop_all_streams(chip); azx_stop_chip(chip); } - if (chip->irq >= 0) - free_irq(chip->irq, (void*)chip); + if (bus->irq >= 0) + free_irq(bus->irq, (void*)chip); if (chip->msi) pci_disable_msi(chip->pci); - iounmap(chip->remap_addr); + iounmap(bus->remap_addr); azx_free_stream_pages(chip); + azx_free_streams(chip); + snd_hdac_bus_exit(bus); + if (chip->region_requested) pci_release_regions(chip->pci); + pci_disable_device(chip->pci); - kfree(chip->azx_dev); #ifdef CONFIG_SND_HDA_PATCH_LOADER release_firmware(chip->fw); #endif + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - hda_display_power(hda, false); - hda_i915_exit(hda); + if (hda->need_i915_power) + snd_hdac_display_power(bus, false); + snd_hdac_i915_exit(bus); } kfree(hda); return 0; } +static int azx_dev_disconnect(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + chip->bus.shutdown = 1; + return 0; +} + static int azx_dev_free(struct snd_device *device) { return azx_free(device->device_data); @@ -1279,9 +1390,9 @@ static void check_probe_mask(struct azx *chip, int dev) /* check forced option */ if (chip->codec_probe_mask != -1 && (chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) { - chip->codec_mask = chip->codec_probe_mask & 0xff; + azx_bus(chip)->codec_mask = chip->codec_probe_mask & 0xff; dev_info(chip->card->dev, "codec_mask forced to 0x%x\n", - chip->codec_mask); + (int)azx_bus(chip)->codec_mask); } } @@ -1368,12 +1479,15 @@ static void azx_probe_work(struct work_struct *work) /* * constructor */ +static const struct hdac_io_ops pci_hda_io_ops; +static const struct hda_controller_ops pci_hda_ops; + static int azx_create(struct snd_card *card, struct pci_dev *pci, int dev, unsigned int driver_caps, - const struct hda_controller_ops *hda_ops, struct azx **rchip) { static struct snd_device_ops ops = { + .dev_disconnect = azx_dev_disconnect, .dev_free = azx_dev_free, }; struct hda_intel *hda; @@ -1393,12 +1507,10 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, } chip = &hda->chip; - spin_lock_init(&chip->reg_lock); mutex_init(&chip->open_mutex); chip->card = card; chip->pci = pci; - chip->ops = hda_ops; - chip->irq = -1; + chip->ops = &pci_hda_ops; chip->driver_caps = driver_caps; chip->driver_type = driver_caps & 0xff; check_msi(chip); @@ -1430,6 +1542,13 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, } chip->bdl_pos_adj = bdl_pos_adj; + err = azx_bus_init(chip, model[dev], &pci_hda_io_ops); + if (err < 0) { + kfree(hda); + pci_disable_device(pci); + return err; + } + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); if (err < 0) { dev_err(card->dev, "Error creating device [card]!\n"); @@ -1450,6 +1569,7 @@ static int azx_first_init(struct azx *chip) int dev = chip->dev_index; struct pci_dev *pci = chip->pci; struct snd_card *card = chip->card; + struct hdac_bus *bus = azx_bus(chip); int err; unsigned short gcap; unsigned int dma_bits = 64; @@ -1469,9 +1589,9 @@ static int azx_first_init(struct azx *chip) return err; chip->region_requested = 1; - chip->addr = pci_resource_start(pci, 0); - chip->remap_addr = pci_ioremap_bar(pci, 0); - if (chip->remap_addr == NULL) { + bus->addr = pci_resource_start(pci, 0); + bus->remap_addr = pci_ioremap_bar(pci, 0); + if (bus->remap_addr == NULL) { dev_err(card->dev, "ioremap error\n"); return -ENXIO; } @@ -1489,7 +1609,7 @@ static int azx_first_init(struct azx *chip) return -EBUSY; pci_set_master(pci); - synchronize_irq(chip->irq); + synchronize_irq(bus->irq); gcap = azx_readw(chip, GCAP); dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); @@ -1531,11 +1651,11 @@ static int azx_first_init(struct azx *chip) /* allow 64bit DMA address if supported by H/W */ if (!(gcap & AZX_GCAP_64OK)) dma_bits = 32; - if (!pci_set_dma_mask(pci, DMA_BIT_MASK(dma_bits))) { - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(dma_bits)); + if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) { + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits)); } else { - pci_set_dma_mask(pci, DMA_BIT_MASK(32)); - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)); + dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32)); } /* read number of streams from GCAP register instead of using @@ -1566,17 +1686,15 @@ static int azx_first_init(struct azx *chip) chip->capture_index_offset = 0; chip->playback_index_offset = chip->capture_streams; chip->num_streams = chip->playback_streams + chip->capture_streams; - chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), - GFP_KERNEL); - if (!chip->azx_dev) - return -ENOMEM; - err = azx_alloc_stream_pages(chip); + /* initialize streams */ + err = azx_init_streams(chip); if (err < 0) return err; - /* initialize streams */ - azx_init_stream(chip); + err = azx_alloc_stream_pages(chip); + if (err < 0) + return err; /* initialize chip */ azx_init_pci(chip); @@ -1588,10 +1706,10 @@ static int azx_first_init(struct azx *chip) haswell_set_bclk(hda); } - azx_init_chip(chip, (probe_only[dev] & 2) == 0); + hda_intel_init_chip(chip, (probe_only[dev] & 2) == 0); /* codec detection */ - if (!chip->codec_mask) { + if (!azx_bus(chip)->codec_mask) { dev_err(card->dev, "no codecs found!\n"); return -ENODEV; } @@ -1601,7 +1719,7 @@ static int azx_first_init(struct azx *chip) sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", - card->shortname, chip->addr, chip->irq); + card->shortname, bus->addr, bus->irq); return 0; } @@ -1670,10 +1788,11 @@ static u8 pci_azx_readb(u8 __iomem *addr) static int disable_msi_reset_irq(struct azx *chip) { + struct hdac_bus *bus = azx_bus(chip); int err; - free_irq(chip->irq, chip); - chip->irq = -1; + free_irq(bus->irq, chip); + bus->irq = -1; pci_disable_msi(chip->pci); chip->msi = 0; err = azx_acquire_irq(chip, 1); @@ -1684,15 +1803,16 @@ static int disable_msi_reset_irq(struct azx *chip) } /* DMA page allocation helpers. */ -static int dma_alloc_pages(struct azx *chip, +static int dma_alloc_pages(struct hdac_bus *bus, int type, size_t size, struct snd_dma_buffer *buf) { + struct azx *chip = bus_to_azx(bus); int err; err = snd_dma_alloc_pages(type, - chip->card->dev, + bus->dev, size, buf); if (err < 0) return err; @@ -1700,8 +1820,10 @@ static int dma_alloc_pages(struct azx *chip, return 0; } -static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf) +static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) { + struct azx *chip = bus_to_azx(bus); + mark_pages_wc(chip, buf, false); snd_dma_free_pages(buf); } @@ -1714,9 +1836,6 @@ static int substream_alloc_pages(struct azx *chip, int ret; mark_runtime_wc(chip, azx_dev, substream, false); - azx_dev->bufsize = 0; - azx_dev->period_bytes = 0; - azx_dev->format_val = 0; ret = snd_pcm_lib_malloc_pages(substream, size); if (ret < 0) return ret; @@ -1743,20 +1862,24 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream, #endif } -static const struct hda_controller_ops pci_hda_ops = { +static const struct hdac_io_ops pci_hda_io_ops = { .reg_writel = pci_azx_writel, .reg_readl = pci_azx_readl, .reg_writew = pci_azx_writew, .reg_readw = pci_azx_readw, .reg_writeb = pci_azx_writeb, .reg_readb = pci_azx_readb, - .disable_msi_reset_irq = disable_msi_reset_irq, .dma_alloc_pages = dma_alloc_pages, .dma_free_pages = dma_free_pages, +}; + +static const struct hda_controller_ops pci_hda_ops = { + .disable_msi_reset_irq = disable_msi_reset_irq, .substream_alloc_pages = substream_alloc_pages, .substream_free_pages = substream_free_pages, .pcm_mmap_prepare = pcm_mmap_prepare, .position_check = azx_position_check, + .link_power = azx_intel_link_power, }; static int azx_probe(struct pci_dev *pci, @@ -1783,8 +1906,7 @@ static int azx_probe(struct pci_dev *pci, return err; } - err = azx_create(card, pci, dev, pci_id->driver_data, - &pci_hda_ops, &chip); + err = azx_create(card, pci, dev, pci_id->driver_data, &chip); if (err < 0) goto out_free; card->private_data = chip; @@ -1846,25 +1968,45 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = { static int azx_probe_continue(struct azx *chip) { struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct hdac_bus *bus = azx_bus(chip); struct pci_dev *pci = chip->pci; int dev = chip->dev_index; int err; - /* Request power well for Haswell HDA controller and codec */ + hda->probe_continued = 1; + + /* Request display power well for the HDA controller or codec. For + * Haswell/Broadwell, both the display HDA controller and codec need + * this power. For other platforms, like Baytrail/Braswell, only the + * display codec needs the power and it can be released after probe. + */ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { -#ifdef CONFIG_SND_HDA_I915 - err = hda_i915_init(hda); - if (err < 0) - goto out_free; - err = hda_display_power(hda, true); + /* HSW/BDW controllers need this power */ + if (CONTROLLER_IN_GPU(pci)) + hda->need_i915_power = 1; + + err = snd_hdac_i915_init(bus); + if (err < 0) { + /* if the controller is bound only with HDMI/DP + * (for HSW and BDW), we need to abort the probe; + * for other chips, still continue probing as other + * codecs can be on the same link. + */ + if (CONTROLLER_IN_GPU(pci)) + goto out_free; + else + goto skip_i915; + } + + err = snd_hdac_display_power(bus, true); if (err < 0) { dev_err(chip->card->dev, "Cannot turn on display power on i915\n"); - goto out_free; + goto i915_power_fail; } -#endif } + skip_i915: err = azx_first_init(chip); if (err < 0) goto out_free; @@ -1874,17 +2016,13 @@ static int azx_probe_continue(struct azx *chip) #endif /* create codec instances */ - err = azx_bus_create(chip, model[dev]); - if (err < 0) - goto out_free; - err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); if (err < 0) goto out_free; #ifdef CONFIG_SND_HDA_PATCH_LOADER if (chip->fw) { - err = snd_hda_load_patch(chip->bus, chip->fw->size, + err = snd_hda_load_patch(&chip->bus, chip->fw->size, chip->fw->data); if (err < 0) goto out_free; @@ -1906,11 +2044,16 @@ static int azx_probe_continue(struct azx *chip) chip->running = 1; azx_add_card_list(chip); - snd_hda_set_power_save(chip->bus, power_save * 1000); + snd_hda_set_power_save(&chip->bus, power_save * 1000); if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo) pm_runtime_put_noidle(&pci->dev); out_free: + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL + && !hda->need_i915_power) + snd_hdac_display_power(bus, false); + +i915_power_fail: if (err < 0) hda->init_failed = 1; complete_all(&hda->probe_wait); @@ -2037,6 +2180,8 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x1022, 0x780d), .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB }, /* ATI HDMI */ + { PCI_DEVICE(0x1002, 0x1308), + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, { PCI_DEVICE(0x1002, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, { PCI_DEVICE(0x1002, 0x7919), @@ -2045,6 +2190,8 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, { PCI_DEVICE(0x1002, 0x970f), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, + { PCI_DEVICE(0x1002, 0x9840), + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, { PCI_DEVICE(0x1002, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, { PCI_DEVICE(0x1002, 0xaa08), diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h index d5231f7216a7..354f0bbed833 100644 --- a/sound/pci/hda/hda_intel.h +++ b/sound/pci/hda/hda_intel.h @@ -16,7 +16,6 @@ #ifndef __SOUND_HDA_INTEL_H #define __SOUND_HDA_INTEL_H -#include <drm/i915_component.h> #include "hda_controller.h" struct hda_intel { @@ -34,6 +33,7 @@ struct hda_intel { /* extra flags */ unsigned int irq_pending_warned:1; + unsigned int probe_continued:1; /* VGA-switcheroo setup */ unsigned int use_vga_switcheroo:1; @@ -43,29 +43,7 @@ struct hda_intel { /* secondary power domain for hdmi audio under vga device */ struct dev_pm_domain hdmi_pm_domain; - /* i915 component interface */ - struct i915_audio_component audio_component; + bool need_i915_power:1; /* the hda controller needs i915 power */ }; -#ifdef CONFIG_SND_HDA_I915 -int hda_display_power(struct hda_intel *hda, bool enable); -void haswell_set_bclk(struct hda_intel *hda); -int hda_i915_init(struct hda_intel *hda); -int hda_i915_exit(struct hda_intel *hda); -#else -static inline int hda_display_power(struct hda_intel *hda, bool enable) -{ - return 0; -} -static inline void haswell_set_bclk(struct hda_intel *hda) { return; } -static inline int hda_i915_init(struct hda_intel *hda) -{ - return -ENODEV; -} -static inline int hda_i915_exit(struct hda_intel *hda) -{ - return 0; -} -#endif - #endif diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/pci/hda/hda_intel_trace.h index 7b5e4c2cf9d5..0922d8b1b17d 100644 --- a/sound/pci/hda/hda_intel_trace.h +++ b/sound/pci/hda/hda_intel_trace.h @@ -7,52 +7,43 @@ #include <linux/tracepoint.h> -struct azx; -struct azx_dev; +DECLARE_EVENT_CLASS(hda_pm, + TP_PROTO(struct azx *chip), -TRACE_EVENT(azx_pcm_trigger, - - TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd), - - TP_ARGS(chip, dev, cmd), + TP_ARGS(chip), TP_STRUCT__entry( - __field( int, card ) - __field( int, idx ) - __field( int, cmd ) + __field(int, dev_index) ), TP_fast_assign( - __entry->card = (chip)->card->number; - __entry->idx = (dev)->index; - __entry->cmd = cmd; + __entry->dev_index = (chip)->dev_index; ), - TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd) + TP_printk("card index: %d", __entry->dev_index) ); -TRACE_EVENT(azx_get_position, - - TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay), - - TP_ARGS(chip, dev, pos, delay), +DEFINE_EVENT(hda_pm, azx_suspend, + TP_PROTO(struct azx *chip), + TP_ARGS(chip) +); - TP_STRUCT__entry( - __field( int, card ) - __field( int, idx ) - __field( unsigned int, pos ) - __field( unsigned int, delay ) - ), +DEFINE_EVENT(hda_pm, azx_resume, + TP_PROTO(struct azx *chip), + TP_ARGS(chip) +); - TP_fast_assign( - __entry->card = (chip)->card->number; - __entry->idx = (dev)->index; - __entry->pos = pos; - __entry->delay = delay; - ), +#ifdef CONFIG_PM +DEFINE_EVENT(hda_pm, azx_runtime_suspend, + TP_PROTO(struct azx *chip), + TP_ARGS(chip) +); - TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay) +DEFINE_EVENT(hda_pm, azx_runtime_resume, + TP_PROTO(struct azx *chip), + TP_ARGS(chip) ); +#endif #endif /* _TRACE_HDA_INTEL_H */ diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index d7cfe7b8c32b..366efbf87d41 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -132,11 +132,11 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec) for (i = 0; i < codec->jacktbl.used; i++, jack++) { struct hda_jack_callback *cb, *next; -#ifdef CONFIG_SND_HDA_INPUT_JACK + /* free jack instances manually when clearing/reconfiguring */ if (!codec->bus->shutdown && jack->jack) snd_device_free(codec->card, jack->jack); -#endif + for (cb = jack->callback; cb; cb = next) { next = cb->next; kfree(cb); @@ -337,20 +337,15 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) jack = codec->jacktbl.list; for (i = 0; i < codec->jacktbl.used; i++, jack++) if (jack->nid) { - if (!jack->kctl || jack->block_report) + if (!jack->jack || jack->block_report) continue; state = get_jack_plug_state(jack->pin_sense); - snd_kctl_jack_report(codec->card, jack->kctl, state); -#ifdef CONFIG_SND_HDA_INPUT_JACK - if (jack->jack) - snd_jack_report(jack->jack, - state ? jack->type : 0); -#endif + snd_jack_report(jack->jack, + state ? jack->type : 0); } } EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync); -#ifdef CONFIG_SND_HDA_INPUT_JACK /* guess the jack type from the pin-config */ static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid) { @@ -377,54 +372,42 @@ static void hda_free_jack_priv(struct snd_jack *jack) jacks->nid = 0; jacks->jack = NULL; } -#endif /** * snd_hda_jack_add_kctl - Add a kctl for the given pin * @codec: the HDA codec * @nid: pin NID to assign * @name: string name for the jack - * @idx: index number for the jack * @phantom_jack: flag to deal as a phantom jack * * This assigns a jack-detection kctl to the given pin. The kcontrol * will have the given name and index. */ static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, - const char *name, int idx, bool phantom_jack) + const char *name, bool phantom_jack) { struct hda_jack_tbl *jack; - struct snd_kcontrol *kctl; - int err, state; + int err, state, type; jack = snd_hda_jack_tbl_new(codec, nid); if (!jack) return 0; - if (jack->kctl) + if (jack->jack) return 0; /* already created */ - kctl = snd_kctl_jack_new(name, idx, codec); - if (!kctl) - return -ENOMEM; - err = snd_hda_ctl_add(codec, nid, kctl); + + type = get_input_jack_type(codec, nid); + err = snd_jack_new(codec->card, name, type, + &jack->jack, true, phantom_jack); if (err < 0) return err; - jack->kctl = kctl; - jack->phantom_jack = !!phantom_jack; + jack->phantom_jack = !!phantom_jack; + jack->type = type; + jack->jack->private_data = jack; + jack->jack->private_free = hda_free_jack_priv; state = snd_hda_jack_detect(codec, nid); - snd_kctl_jack_report(codec->card, kctl, state); -#ifdef CONFIG_SND_HDA_INPUT_JACK - if (!phantom_jack) { - jack->type = get_input_jack_type(codec, nid); - err = snd_jack_new(codec->card, name, jack->type, - &jack->jack); - if (err < 0) - return err; - jack->jack->private_data = jack; - jack->jack->private_free = hda_free_jack_priv; - snd_jack_report(jack->jack, state ? jack->type : 0); - } -#endif + snd_jack_report(jack->jack, state ? jack->type : 0); + return 0; } @@ -433,44 +416,23 @@ static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, * @codec: the HDA codec * @nid: pin NID * @name: the name string for the jack ctl - * @idx: the ctl index for the jack ctl * * This is a simple helper calling __snd_hda_jack_add_kctl(). */ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, - const char *name, int idx) + const char *name) { - return __snd_hda_jack_add_kctl(codec, nid, name, idx, false); + return __snd_hda_jack_add_kctl(codec, nid, name, false); } EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl); -/* get the unique index number for the given kctl name */ -static int get_unique_index(struct hda_codec *codec, const char *name, int idx) -{ - struct hda_jack_tbl *jack; - int i, len = strlen(name); - again: - jack = codec->jacktbl.list; - for (i = 0; i < codec->jacktbl.used; i++, jack++) { - /* jack->kctl.id contains "XXX Jack" name string with index */ - if (jack->kctl && - !strncmp(name, jack->kctl->id.name, len) && - !strcmp(" Jack", jack->kctl->id.name + len) && - jack->kctl->id.index == idx) { - idx++; - goto again; - } - } - return idx; -} - static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, const struct auto_pin_cfg *cfg, const char *base_name) { unsigned int def_conf, conn; char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - int idx, err; + int err; bool phantom_jack; if (!nid) @@ -482,16 +444,14 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, phantom_jack = (conn != AC_JACK_PORT_COMPLEX) || !is_jack_detectable(codec, nid); - if (base_name) { + if (base_name) strlcpy(name, base_name, sizeof(name)); - idx = 0; - } else - snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx); + else + snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL); if (phantom_jack) /* Example final name: "Internal Mic Phantom Jack" */ strncat(name, " Phantom", sizeof(name) - strlen(name) - 1); - idx = get_unique_index(codec, name, idx); - err = __snd_hda_jack_add_kctl(codec, nid, name, idx, phantom_jack); + err = __snd_hda_jack_add_kctl(codec, nid, name, phantom_jack); if (err < 0) return err; diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index b279e327a23b..387d30984dfe 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -39,11 +39,8 @@ struct hda_jack_tbl { unsigned int block_report:1; /* in a transitional state - do not report to userspace */ hda_nid_t gating_jack; /* valid when gating jack plugged */ hda_nid_t gated_jack; /* gated is dependent on this jack */ - struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */ -#ifdef CONFIG_SND_HDA_INPUT_JACK int type; struct snd_jack *jack; -#endif }; struct hda_jack_tbl * @@ -85,7 +82,7 @@ static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, - const char *name, int idx); + const char *name); int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 3b567f42296b..4a21c2199e02 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -129,8 +129,8 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, /* lowlevel accessor with caching; use carefully */ #define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \ snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx) -#define snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val) \ - snd_hdac_regmap_update_amp(&(codec)->core, nid, ch, dir, idx, mask, val) +int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, + int ch, int dir, int idx, int mask, int val); int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, @@ -330,7 +330,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, /* * generic proc interface */ -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS int snd_hda_codec_proc_new(struct hda_codec *codec); #else static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; } @@ -777,7 +777,7 @@ int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, unsigned char *buf, int *eld_size, bool rev3_or_later); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS void snd_hdmi_print_eld_info(struct hdmi_eld *eld, struct snd_info_buffer *buffer); void snd_hdmi_write_eld_info(struct hdmi_eld *eld, diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 2e4fd5c56d3b..477742cb70a2 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -87,13 +87,13 @@ MODULE_PARM_DESC(power_save, /* * DMA page allocation ops. */ -static int dma_alloc_pages(struct azx *chip, int type, size_t size, +static int dma_alloc_pages(struct hdac_bus *bus, int type, size_t size, struct snd_dma_buffer *buf) { - return snd_dma_alloc_pages(type, chip->card->dev, size, buf); + return snd_dma_alloc_pages(type, bus->dev, size, buf); } -static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf) +static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) { snd_dma_free_pages(buf); } @@ -102,11 +102,6 @@ static int substream_alloc_pages(struct azx *chip, struct snd_pcm_substream *substream, size_t size) { - struct azx_dev *azx_dev = get_azx_dev(substream); - - azx_dev->bufsize = 0; - azx_dev->period_bytes = 0; - azx_dev->format_val = 0; return snd_pcm_lib_malloc_pages(substream, size); } @@ -173,7 +168,7 @@ static u8 hda_tegra_readb(u8 *addr) return (v >> shift) & 0xff; } -static const struct hda_controller_ops hda_tegra_ops = { +static const struct hdac_io_ops hda_tegra_io_ops = { .reg_writel = hda_tegra_writel, .reg_readl = hda_tegra_readl, .reg_writew = hda_tegra_writew, @@ -182,6 +177,9 @@ static const struct hda_controller_ops hda_tegra_ops = { .reg_readb = hda_tegra_readb, .dma_alloc_pages = dma_alloc_pages, .dma_free_pages = dma_free_pages, +}; + +static const struct hda_controller_ops hda_tegra_ops = { .substream_alloc_pages = substream_alloc_pages, .substream_free_pages = substream_free_pages, }; @@ -282,21 +280,29 @@ static const struct dev_pm_ops hda_tegra_pm = { SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume) }; +static int hda_tegra_dev_disconnect(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + chip->bus.shutdown = 1; + return 0; +} + /* * destructor */ static int hda_tegra_dev_free(struct snd_device *device) { - int i; struct azx *chip = device->device_data; - if (chip->initialized) { - for (i = 0; i < chip->num_streams; i++) - azx_stream_stop(chip, &chip->azx_dev[i]); + if (azx_bus(chip)->chip_init) { + azx_stop_all_streams(chip); azx_stop_chip(chip); } azx_free_stream_pages(chip); + azx_free_streams(chip); + snd_hdac_bus_exit(azx_bus(chip)); return 0; } @@ -304,31 +310,40 @@ static int hda_tegra_dev_free(struct snd_device *device) static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) { struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); + struct hdac_bus *bus = azx_bus(chip); struct device *dev = hda->dev; struct resource *res; int err; hda->hda_clk = devm_clk_get(dev, "hda"); - if (IS_ERR(hda->hda_clk)) + if (IS_ERR(hda->hda_clk)) { + dev_err(dev, "failed to get hda clock\n"); return PTR_ERR(hda->hda_clk); + } hda->hda2codec_2x_clk = devm_clk_get(dev, "hda2codec_2x"); - if (IS_ERR(hda->hda2codec_2x_clk)) + if (IS_ERR(hda->hda2codec_2x_clk)) { + dev_err(dev, "failed to get hda2codec_2x clock\n"); return PTR_ERR(hda->hda2codec_2x_clk); + } hda->hda2hdmi_clk = devm_clk_get(dev, "hda2hdmi"); - if (IS_ERR(hda->hda2hdmi_clk)) + if (IS_ERR(hda->hda2hdmi_clk)) { + dev_err(dev, "failed to get hda2hdmi clock\n"); return PTR_ERR(hda->hda2hdmi_clk); + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hda->regs = devm_ioremap_resource(dev, res); if (IS_ERR(hda->regs)) return PTR_ERR(hda->regs); - chip->remap_addr = hda->regs + HDA_BAR0; - chip->addr = res->start + HDA_BAR0; + bus->remap_addr = hda->regs + HDA_BAR0; + bus->addr = res->start + HDA_BAR0; err = hda_tegra_enable_clocks(hda); - if (err) + if (err) { + dev_err(dev, "failed to get enable clocks\n"); return err; + } hda_tegra_init(hda); @@ -337,6 +352,7 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) { + struct hdac_bus *bus = azx_bus(chip); struct snd_card *card = chip->card; int err; unsigned short gcap; @@ -354,9 +370,9 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) irq_id); return err; } - chip->irq = irq_id; + bus->irq = irq_id; - synchronize_irq(chip->irq); + synchronize_irq(bus->irq); gcap = azx_readw(chip, GCAP); dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); @@ -374,23 +390,26 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) chip->capture_index_offset = 0; chip->playback_index_offset = chip->capture_streams; chip->num_streams = chip->playback_streams + chip->capture_streams; - chip->azx_dev = devm_kcalloc(card->dev, chip->num_streams, - sizeof(*chip->azx_dev), GFP_KERNEL); - if (!chip->azx_dev) - return -ENOMEM; - err = azx_alloc_stream_pages(chip); - if (err < 0) + /* initialize streams */ + err = azx_init_streams(chip); + if (err < 0) { + dev_err(card->dev, "failed to initialize streams: %d\n", err); return err; + } - /* initialize streams */ - azx_init_stream(chip); + err = azx_alloc_stream_pages(chip); + if (err < 0) { + dev_err(card->dev, "failed to allocate stream pages: %d\n", + err); + return err; + } /* initialize chip */ azx_init_chip(chip, 1); /* codec detection */ - if (!chip->codec_mask) { + if (!bus->codec_mask) { dev_err(card->dev, "no codecs found!\n"); return -ENODEV; } @@ -399,7 +418,7 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) strcpy(card->shortname, "tegra-hda"); snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", - card->shortname, chip->addr, chip->irq); + card->shortname, bus->addr, bus->irq); return 0; } @@ -409,10 +428,10 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) */ static int hda_tegra_create(struct snd_card *card, unsigned int driver_caps, - const struct hda_controller_ops *hda_ops, struct hda_tegra *hda) { static struct snd_device_ops ops = { + .dev_disconnect = hda_tegra_dev_disconnect, .dev_free = hda_tegra_dev_free, }; struct azx *chip; @@ -420,11 +439,9 @@ static int hda_tegra_create(struct snd_card *card, chip = &hda->chip; - spin_lock_init(&chip->reg_lock); mutex_init(&chip->open_mutex); chip->card = card; - chip->ops = hda_ops; - chip->irq = -1; + chip->ops = &hda_tegra_ops; chip->driver_caps = driver_caps; chip->driver_type = driver_caps & 0xff; chip->dev_index = 0; @@ -435,6 +452,10 @@ static int hda_tegra_create(struct snd_card *card, chip->single_cmd = false; chip->snoop = true; + err = azx_bus_init(chip, NULL, &hda_tegra_io_ops); + if (err < 0) + return err; + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); if (err < 0) { dev_err(card->dev, "Error creating device\n"); @@ -452,11 +473,12 @@ MODULE_DEVICE_TABLE(of, hda_tegra_match); static int hda_tegra_probe(struct platform_device *pdev) { + const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY | + AZX_DCAPS_CORBRP_SELF_CLEAR; struct snd_card *card; struct azx *chip; struct hda_tegra *hda; int err; - const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY; hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); if (!hda) @@ -471,7 +493,7 @@ static int hda_tegra_probe(struct platform_device *pdev) return err; } - err = hda_tegra_create(card, driver_flags, &hda_tegra_ops, hda); + err = hda_tegra_create(card, driver_flags, hda); if (err < 0) goto out_free; card->private_data = chip; @@ -483,10 +505,6 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free; /* create codec instances */ - err = azx_bus_create(chip, NULL); - if (err < 0) - goto out_free; - err = azx_probe_codecs(chip, 0); if (err < 0) goto out_free; @@ -500,7 +518,7 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free; chip->running = 1; - snd_hda_set_power_save(chip->bus, power_save * 1000); + snd_hda_set_power_save(&chip->bus, power_save * 1000); return 0; diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 231f89029779..c033a4ee6547 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -205,8 +205,6 @@ static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp) if (err < 0) return err; - codec->patch_ops = ad198x_auto_patch_ops; - return 0; } @@ -223,6 +221,7 @@ static int alloc_ad_spec(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; snd_hda_gen_spec_init(&spec->gen); + codec->patch_ops = ad198x_auto_patch_ops; return 0; } diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 447302695195..484bbf4134cd 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -63,6 +63,7 @@ static int patch_ca0110(struct hda_codec *codec) return -ENOMEM; snd_hda_gen_spec_init(spec); codec->spec = spec; + codec->patch_ops = ca0110_patch_ops; spec->multi_cap_vol = 1; codec->bus->needs_damn_long_delay = 1; @@ -71,8 +72,6 @@ static int patch_ca0110(struct hda_codec *codec) if (err < 0) goto error; - codec->patch_ops = ca0110_patch_ops; - return 0; error: diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4a4e7b282e4f..0f039abe9673 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -43,8 +43,6 @@ #define FLOAT_TWO 0x40000000 #define FLOAT_MINUS_5 0xc0a00000 -#define UNSOL_TAG_HP 0x10 -#define UNSOL_TAG_AMIC1 0x12 #define UNSOL_TAG_DSP 0x16 #define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18) @@ -703,8 +701,8 @@ struct ca0132_spec { unsigned int num_mixers; const struct hda_verb *base_init_verbs; const struct hda_verb *base_exit_verbs; - const struct hda_verb *init_verbs[5]; - unsigned int num_init_verbs; /* exclude base init verbs */ + const struct hda_verb *chip_init_verbs; + struct hda_verb *spec_init_verbs; struct auto_pin_cfg autocfg; /* Nodes configurations */ @@ -719,6 +717,8 @@ struct ca0132_spec { unsigned int num_inputs; hda_nid_t shared_mic_nid; hda_nid_t shared_out_nid; + hda_nid_t unsol_tag_hp; + hda_nid_t unsol_tag_amic1; /* chip access */ struct mutex chipio_mutex; /* chip access mutex */ @@ -748,6 +748,7 @@ struct ca0132_spec { struct hda_codec *codec; struct delayed_work unsol_hp_work; + int quirk; #ifdef ENABLE_TUNING_CONTROLS long cur_ctl_vals[TUNING_CTLS_COUNT]; @@ -755,6 +756,19 @@ struct ca0132_spec { }; /* + * CA0132 quirks table + */ +enum { + QUIRK_NONE, + QUIRK_ALIENWARE, +}; + +static const struct snd_pci_quirk ca0132_quirks[] = { + SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15", QUIRK_ALIENWARE), + {} +}; + +/* * CA0132 codec access */ static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid, @@ -2052,11 +2066,8 @@ static int dma_convert_to_hda_format(struct hda_codec *codec, { unsigned int format_val; - format_val = snd_hda_calc_stream_format(codec, - sample_rate, - channels, - SNDRV_PCM_FORMAT_S32_LE, - 32, 0); + format_val = snd_hdac_calc_stream_format(sample_rate, + channels, SNDRV_PCM_FORMAT_S32_LE, 32, 0); if (hda_format) *hda_format = (unsigned short)format_val; @@ -3227,7 +3238,7 @@ static void ca0132_unsol_hp_delayed(struct work_struct *work) struct hda_jack_tbl *jack; ca0132_select_out(spec->codec); - jack = snd_hda_jack_tbl_get(spec->codec, UNSOL_TAG_HP); + jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp); if (jack) { jack->block_report = 0; snd_hda_jack_report_sync(spec->codec); @@ -4417,8 +4428,9 @@ static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb) static void ca0132_init_unsol(struct hda_codec *codec) { - snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_HP, hp_callback); - snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_AMIC1, + struct ca0132_spec *spec = codec->spec; + snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_hp, hp_callback); + snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_amic1, amic_callback); snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP, ca0132_process_dsp_response); @@ -4479,17 +4491,6 @@ static struct hda_verb ca0132_init_verbs0[] = { {} }; -static struct hda_verb ca0132_init_verbs1[] = { - {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_HP}, - {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_AMIC1}, - /* config EAPD */ - {0x0b, 0x78D, 0x00}, - /*{0x0b, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/ - /*{0x10, 0x78D, 0x02},*/ - /*{0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/ - {} -}; - static void ca0132_init_chip(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -4569,8 +4570,8 @@ static int ca0132_init(struct hda_codec *codec) init_input(codec, cfg->dig_in_pin, spec->dig_in); - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_sequence_write(codec, spec->spec_init_verbs); ca0132_select_out(codec); ca0132_select_mic(codec); @@ -4591,6 +4592,7 @@ static void ca0132_free(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->base_exit_verbs); ca0132_exit_chip(codec); snd_hda_power_down(codec); + kfree(spec->spec_init_verbs); kfree(codec->spec); } @@ -4617,18 +4619,25 @@ static void ca0132_config(struct hda_codec *codec) spec->num_outputs = 2; spec->out_pins[0] = 0x0b; /* speaker out */ - spec->out_pins[1] = 0x10; /* headphone out */ + if (spec->quirk == QUIRK_ALIENWARE) { + codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n"); + spec->out_pins[1] = 0x0f; + } else{ + spec->out_pins[1] = 0x10; /* headphone out */ + } spec->shared_out_nid = 0x2; + spec->unsol_tag_hp = spec->out_pins[1]; - spec->num_inputs = 3; spec->adcs[0] = 0x7; /* digital mic / analog mic1 */ spec->adcs[1] = 0x8; /* analog mic2 */ spec->adcs[2] = 0xa; /* what u hear */ - spec->shared_mic_nid = 0x7; + spec->num_inputs = 3; spec->input_pins[0] = 0x12; spec->input_pins[1] = 0x11; spec->input_pins[2] = 0x13; + spec->shared_mic_nid = 0x7; + spec->unsol_tag_amic1 = spec->input_pins[0]; /* SPDIF I/O */ spec->dig_out = 0x05; @@ -4641,10 +4650,56 @@ static void ca0132_config(struct hda_codec *codec) cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; } +static int ca0132_prepare_verbs(struct hda_codec *codec) +{ +/* Verbs + terminator (an empty element) */ +#define NUM_SPEC_VERBS 4 + struct ca0132_spec *spec = codec->spec; + + spec->chip_init_verbs = ca0132_init_verbs0; + spec->spec_init_verbs = kzalloc(sizeof(struct hda_verb) * NUM_SPEC_VERBS, GFP_KERNEL); + if (!spec->spec_init_verbs) + return -ENOMEM; + + /* HP jack autodetection */ + spec->spec_init_verbs[0].nid = spec->unsol_tag_hp; + spec->spec_init_verbs[0].param = AC_VERB_SET_UNSOLICITED_ENABLE; + spec->spec_init_verbs[0].verb = AC_USRSP_EN | spec->unsol_tag_hp; + + /* MIC1 jack autodetection */ + spec->spec_init_verbs[1].nid = spec->unsol_tag_amic1; + spec->spec_init_verbs[1].param = AC_VERB_SET_UNSOLICITED_ENABLE; + spec->spec_init_verbs[1].verb = AC_USRSP_EN | spec->unsol_tag_amic1; + + /* config EAPD */ + spec->spec_init_verbs[2].nid = 0x0b; + spec->spec_init_verbs[2].param = 0x78D; + spec->spec_init_verbs[2].verb = 0x00; + + /* Previously commented configuration */ + /* + spec->spec_init_verbs[3].nid = 0x0b; + spec->spec_init_verbs[3].param = AC_VERB_SET_EAPD_BTLENABLE; + spec->spec_init_verbs[3].verb = 0x02; + + spec->spec_init_verbs[4].nid = 0x10; + spec->spec_init_verbs[4].param = 0x78D; + spec->spec_init_verbs[4].verb = 0x02; + + spec->spec_init_verbs[5].nid = 0x10; + spec->spec_init_verbs[5].param = AC_VERB_SET_EAPD_BTLENABLE; + spec->spec_init_verbs[5].verb = 0x02; + */ + + /* Terminator: spec->spec_init_verbs[NUM_SPEC_VERBS-1] */ + return 0; +} + static int patch_ca0132(struct hda_codec *codec) { struct ca0132_spec *spec; int err; + const struct snd_pci_quirk *quirk; codec_dbg(codec, "patch_ca0132\n"); @@ -4654,15 +4709,23 @@ static int patch_ca0132(struct hda_codec *codec) codec->spec = spec; spec->codec = codec; + codec->patch_ops = ca0132_patch_ops; + codec->pcm_format_first = 1; + codec->no_sticky_stream = 1; + + /* Detect codec quirk */ + quirk = snd_pci_quirk_lookup(codec->bus->pci, ca0132_quirks); + if (quirk) + spec->quirk = quirk->value; + else + spec->quirk = QUIRK_NONE; + spec->dsp_state = DSP_DOWNLOAD_INIT; spec->num_mixers = 1; spec->mixers[0] = ca0132_mixer; spec->base_init_verbs = ca0132_base_init_verbs; spec->base_exit_verbs = ca0132_base_exit_verbs; - spec->init_verbs[0] = ca0132_init_verbs0; - spec->init_verbs[1] = ca0132_init_verbs1; - spec->num_init_verbs = 2; INIT_DELAYED_WORK(&spec->unsol_hp_work, ca0132_unsol_hp_delayed); @@ -4670,13 +4733,13 @@ static int patch_ca0132(struct hda_codec *codec) ca0132_config(codec); - err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + err = ca0132_prepare_verbs(codec); if (err < 0) return err; - codec->patch_ops = ca0132_patch_ops; - codec->pcm_format_first = 1; - codec->no_sticky_stream = 1; + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; return 0; } diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 50e9dd675579..25ccf781fbe7 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -584,6 +584,7 @@ static int patch_cs420x(struct hda_codec *codec) if (!spec) return -ENOMEM; + codec->patch_ops = cs_patch_ops; spec->gen.automute_hook = cs_automute; codec->single_adc_amp = 1; @@ -595,8 +596,6 @@ static int patch_cs420x(struct hda_codec *codec) if (err < 0) goto error; - codec->patch_ops = cs_patch_ops; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -738,6 +737,7 @@ static int patch_cs4208(struct hda_codec *codec) if (!spec) return -ENOMEM; + codec->patch_ops = cs_patch_ops; spec->gen.automute_hook = cs_automute; /* exclude NID 0x10 (HP) from output volumes due to different steps */ spec->gen.out_vol_mask = 1ULL << 0x10; @@ -756,8 +756,6 @@ static int patch_cs4208(struct hda_codec *codec) if (err < 0) goto error; - codec->patch_ops = cs_patch_ops; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -1150,6 +1148,7 @@ static int patch_cs4210(struct hda_codec *codec) if (!spec) return -ENOMEM; + codec->patch_ops = cs421x_patch_ops; spec->gen.automute_hook = cs_automute; snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, @@ -1167,8 +1166,6 @@ static int patch_cs4210(struct hda_codec *codec) if (err < 0) goto error; - codec->patch_ops = cs421x_patch_ops; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -1187,11 +1184,12 @@ static int patch_cs4213(struct hda_codec *codec) if (!spec) return -ENOMEM; + codec->patch_ops = cs421x_patch_ops; + err = cs421x_parse_auto_config(codec); if (err < 0) goto error; - codec->patch_ops = cs421x_patch_ops; return 0; error: diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 617d9012e78a..f5ed078710f8 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -57,6 +57,7 @@ static int patch_cmi9880(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; + codec->patch_ops = cmi_auto_patch_ops; cfg = &spec->gen.autocfg; snd_hda_gen_spec_init(&spec->gen); @@ -67,7 +68,6 @@ static int patch_cmi9880(struct hda_codec *codec) if (err < 0) goto error; - codec->patch_ops = cmi_auto_patch_ops; return 0; error: @@ -86,6 +86,7 @@ static int patch_cmi8888(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; + codec->patch_ops = cmi_auto_patch_ops; cfg = &spec->gen.autocfg; snd_hda_gen_spec_init(&spec->gen); @@ -112,7 +113,6 @@ static int patch_cmi8888(struct hda_codec *codec) } } - codec->patch_ops = cmi_auto_patch_ops; return 0; error: diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 78b719b5b34d..f788a91b544a 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -850,6 +850,7 @@ static int patch_conexant_auto(struct hda_codec *codec) return -ENOMEM; snd_hda_gen_spec_init(&spec->gen); codec->spec = spec; + codec->patch_ops = cx_auto_patch_ops; cx_auto_parse_beep(codec); cx_auto_parse_eapd(codec); @@ -908,8 +909,6 @@ static int patch_conexant_auto(struct hda_codec *codec) if (err < 0) goto error; - codec->patch_ops = cx_auto_patch_ops; - /* Some laptops with Conexant chips show stalls in S3 resume, * which falls into the single-cmd mode. * Better to make reset, then. diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 5f44f60a6389..95158914cc6c 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -86,7 +86,7 @@ struct hdmi_spec_per_pin { bool non_pcm; bool chmap_set; /* channel-map override by ALSA API? */ unsigned char chmap[8]; /* ALSA API channel-map */ -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS struct snd_info_entry *proc_entry; #endif }; @@ -548,7 +548,7 @@ static void hdmi_set_channel_count(struct hda_codec *codec, * ELD proc files */ -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS static void print_eld_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -591,8 +591,8 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) static void eld_proc_free(struct hdmi_spec_per_pin *per_pin) { - if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) { - snd_device_free(per_pin->codec->card, per_pin->proc_entry); + if (!per_pin->codec->bus->shutdown) { + snd_info_free_entry(per_pin->proc_entry); per_pin->proc_entry = NULL; } } @@ -2049,9 +2049,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hda_pcm *info; struct hda_pcm_stream *pstr; - struct hdmi_spec_per_pin *per_pin; - per_pin = get_pin(spec, pin_idx); info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx); if (!info) return -ENOMEM; @@ -2081,7 +2079,7 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx) strncat(hdmi_str, " Phantom", sizeof(hdmi_str) - strlen(hdmi_str) - 1); - return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, 0); + return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str); } static int generic_hdmi_build_controls(struct hda_codec *codec) @@ -2335,6 +2333,15 @@ static int patch_generic_hdmi(struct hda_codec *codec) intel_haswell_fixup_enable_dp12(codec); } + /* For Valleyview/Cherryview, only the display codec is in the display + * power well and can use link_power ops to request/release the power. + * For Haswell/Broadwell, the controller is also in the power well and + * can cover the codec power request, and so need not set this flag. + * For previous platforms, there is no such power well feature. + */ + if (is_valleyview_plus(codec) || is_skylake(codec)) + codec->core.link_power_control = 1; + if (is_haswell_plus(codec) || is_valleyview_plus(codec)) codec->depop_delay = 0; @@ -2349,6 +2356,10 @@ static int patch_generic_hdmi(struct hda_codec *codec) codec->dp_mst = true; } + /* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */ + if (is_haswell_plus(codec) || is_valleyview_plus(codec)) + codec->auto_runtime_pm = 1; + generic_hdmi_init_per_pins(codec); init_channel_allocations(); @@ -2923,6 +2934,171 @@ static int patch_nvhdmi(struct hda_codec *codec) } /* + * The HDA codec on NVIDIA Tegra contains two scratch registers that are + * accessed using vendor-defined verbs. These registers can be used for + * interoperability between the HDA and HDMI drivers. + */ + +/* Audio Function Group node */ +#define NVIDIA_AFG_NID 0x01 + +/* + * The SCRATCH0 register is used to notify the HDMI codec of changes in audio + * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to + * be raised in the HDMI codec. The remainder of the bits is arbitrary. This + * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an + * additional bit (at position 30) to signal the validity of the format. + * + * | 31 | 30 | 29 16 | 15 0 | + * +---------+-------+--------+--------+ + * | TRIGGER | VALID | UNUSED | FORMAT | + * +-----------------------------------| + * + * Note that for the trigger bit to take effect it needs to change value + * (i.e. it needs to be toggled). + */ +#define NVIDIA_GET_SCRATCH0 0xfa6 +#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7 +#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8 +#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9 +#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa +#define NVIDIA_SCRATCH_TRIGGER (1 << 7) +#define NVIDIA_SCRATCH_VALID (1 << 6) + +#define NVIDIA_GET_SCRATCH1 0xfab +#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac +#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad +#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae +#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf + +/* + * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0, + * the format is invalidated so that the HDMI codec can be disabled. + */ +static void tegra_hdmi_set_format(struct hda_codec *codec, unsigned int format) +{ + unsigned int value; + + /* bits [31:30] contain the trigger and valid bits */ + value = snd_hda_codec_read(codec, NVIDIA_AFG_NID, 0, + NVIDIA_GET_SCRATCH0, 0); + value = (value >> 24) & 0xff; + + /* bits [15:0] are used to store the HDA format */ + snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + NVIDIA_SET_SCRATCH0_BYTE0, + (format >> 0) & 0xff); + snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + NVIDIA_SET_SCRATCH0_BYTE1, + (format >> 8) & 0xff); + + /* bits [16:24] are unused */ + snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + NVIDIA_SET_SCRATCH0_BYTE2, 0); + + /* + * Bit 30 signals that the data is valid and hence that HDMI audio can + * be enabled. + */ + if (format == 0) + value &= ~NVIDIA_SCRATCH_VALID; + else + value |= NVIDIA_SCRATCH_VALID; + + /* + * Whenever the trigger bit is toggled, an interrupt is raised in the + * HDMI codec. The HDMI driver will use that as trigger to update its + * configuration. + */ + value ^= NVIDIA_SCRATCH_TRIGGER; + + snd_hda_codec_write(codec, NVIDIA_AFG_NID, 0, + NVIDIA_SET_SCRATCH0_BYTE3, value); +} + +static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + int err; + + err = generic_hdmi_playback_pcm_prepare(hinfo, codec, stream_tag, + format, substream); + if (err < 0) + return err; + + /* notify the HDMI codec of the format change */ + tegra_hdmi_set_format(codec, format); + + return 0; +} + +static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + /* invalidate the format in the HDMI codec */ + tegra_hdmi_set_format(codec, 0); + + return generic_hdmi_playback_pcm_cleanup(hinfo, codec, substream); +} + +static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type) +{ + struct hdmi_spec *spec = codec->spec; + unsigned int i; + + for (i = 0; i < spec->num_pins; i++) { + struct hda_pcm *pcm = get_pcm_rec(spec, i); + + if (pcm->pcm_type == type) + return pcm; + } + + return NULL; +} + +static int tegra_hdmi_build_pcms(struct hda_codec *codec) +{ + struct hda_pcm_stream *stream; + struct hda_pcm *pcm; + int err; + + err = generic_hdmi_build_pcms(codec); + if (err < 0) + return err; + + pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI); + if (!pcm) + return -ENODEV; + + /* + * Override ->prepare() and ->cleanup() operations to notify the HDMI + * codec about format changes. + */ + stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; + stream->ops.prepare = tegra_hdmi_pcm_prepare; + stream->ops.cleanup = tegra_hdmi_pcm_cleanup; + + return 0; +} + +static int patch_tegra_hdmi(struct hda_codec *codec) +{ + int err; + + err = patch_generic_hdmi(codec); + if (err) + return err; + + codec->patch_ops.build_pcms = tegra_hdmi_build_pcms; + + return 0; +} + +/* * ATI/AMD-specific implementations */ @@ -3321,7 +3497,10 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi }, -{ .id = 0x10de0028, .name = "Tegra12x HDMI", .patch = patch_nvhdmi }, +{ .id = 0x10de0020, .name = "Tegra30 HDMI", .patch = patch_tegra_hdmi }, +{ .id = 0x10de0022, .name = "Tegra114 HDMI", .patch = patch_tegra_hdmi }, +{ .id = 0x10de0028, .name = "Tegra124 HDMI", .patch = patch_tegra_hdmi }, +{ .id = 0x10de0029, .name = "Tegra210 HDMI/DP", .patch = patch_tegra_hdmi }, { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi }, { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi }, @@ -3348,6 +3527,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862808, .name = "Broadwell HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862809, .name = "Skylake HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x8086280a, .name = "Broxton HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi }, @@ -3412,6 +3592,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862806"); MODULE_ALIAS("snd-hda-codec-id:80862807"); MODULE_ALIAS("snd-hda-codec-id:80862808"); MODULE_ALIAS("snd-hda-codec-id:80862809"); +MODULE_ALIAS("snd-hda-codec-id:8086280a"); MODULE_ALIAS("snd-hda-codec-id:80862880"); MODULE_ALIAS("snd-hda-codec-id:80862882"); MODULE_ALIAS("snd-hda-codec-id:80862883"); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 464168426465..d35cf506a7db 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1003,6 +1003,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) codec->single_adc_amp = 1; /* FIXME: do we need this for all Realtek codec models? */ codec->spdif_status_reset = 1; + codec->patch_ops = alc_patch_ops; err = alc_codec_rename_from_preset(codec); if (err < 0) { @@ -1447,6 +1448,8 @@ static int patch_alc880(struct hda_codec *codec) spec->gen.need_dac_fix = 1; spec->gen.beep_nid = 0x01; + codec->patch_ops.unsol_event = alc880_unsol_event; + snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, alc880_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -1459,10 +1462,6 @@ static int patch_alc880(struct hda_codec *codec) if (!spec->gen.no_analog) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - codec->patch_ops = alc_patch_ops; - codec->patch_ops.unsol_event = alc880_unsol_event; - - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -1699,6 +1698,8 @@ static int patch_alc260(struct hda_codec *codec) spec->gen.prefer_hp_amp = 1; spec->gen.beep_nid = 0x01; + spec->shutup = alc_eapd_shutup; + snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl, alc260_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -1711,9 +1712,6 @@ static int patch_alc260(struct hda_codec *codec) if (!spec->gen.no_analog) set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); - codec->patch_ops = alc_patch_ops; - spec->shutup = alc_eapd_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -2168,6 +2166,7 @@ static const struct hda_fixup alc882_fixups[] = { static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD), SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), + SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD), SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD), SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD), @@ -2298,8 +2297,6 @@ static int patch_alc882(struct hda_codec *codec) if (!spec->gen.no_analog && spec->gen.beep_nid) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - codec->patch_ops = alc_patch_ops; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -2435,6 +2432,8 @@ static int patch_alc262(struct hda_codec *codec) spec = codec->spec; spec->gen.shared_mic_vref_pin = 0x18; + spec->shutup = alc_eapd_shutup; + #if 0 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is * under-run @@ -2460,9 +2459,6 @@ static int patch_alc262(struct hda_codec *codec) if (!spec->gen.no_analog && spec->gen.beep_nid) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - codec->patch_ops = alc_patch_ops; - spec->shutup = alc_eapd_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -2566,6 +2562,8 @@ static int patch_alc268(struct hda_codec *codec) spec = codec->spec; spec->gen.beep_nid = 0x01; + spec->shutup = alc_eapd_shutup; + snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -2587,9 +2585,6 @@ static int patch_alc268(struct hda_codec *codec) (0 << AC_AMPCAP_MUTE_SHIFT)); } - codec->patch_ops = alc_patch_ops; - spec->shutup = alc_eapd_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -3585,6 +3580,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) break; case 0x10ec0286: case 0x10ec0288: + case 0x10ec0298: alc_process_coef_fw(codec, coef0288); break; case 0x10ec0292: @@ -3659,6 +3655,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, break; case 0x10ec0286: case 0x10ec0288: + case 0x10ec0298: alc_update_coef_idx(codec, 0x4f, 0x000c, 0); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); alc_process_coef_fw(codec, coef0288); @@ -3742,6 +3739,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) break; case 0x10ec0286: case 0x10ec0288: + case 0x10ec0298: alc_process_coef_fw(codec, coef0288); break; case 0x10ec0292: @@ -3806,6 +3804,9 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) case 0x10ec0283: alc_process_coef_fw(codec, coef0233); break; + case 0x10ec0298: + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020);/* Headset output enable */ + /* ALC298 jack type setting is the same with ALC286/ALC288 */ case 0x10ec0286: case 0x10ec0288: alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); @@ -3874,6 +3875,9 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) case 0x10ec0283: alc_process_coef_fw(codec, coef0233); break; + case 0x10ec0298: + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);/* Headset output enable */ + /* ALC298 jack type setting is the same with ALC286/ALC288 */ case 0x10ec0286: case 0x10ec0288: alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); @@ -3936,6 +3940,9 @@ static void alc_determine_headset_type(struct hda_codec *codec) val = alc_read_coef_idx(codec, 0x46); is_ctia = (val & 0x0070) == 0x0070; break; + case 0x10ec0298: + alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020); /* Headset output enable */ + /* ALC298 check jack type is the same with ALC286/ALC288 */ case 0x10ec0286: case 0x10ec0288: alc_process_coef_fw(codec, coef0288); @@ -4434,6 +4441,55 @@ static void alc290_fixup_mono_speakers(struct hda_codec *codec, } } +/* Hook to update amp GPIO4 for automute */ +static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + + snd_hda_gen_hp_automute(codec, jack); + /* mute_led_polarity is set to 0, so we pass inverted value here */ + alc_update_gpio_led(codec, 0x10, !spec->gen.hp_jack_present); +} + +/* Manage GPIOs for HP EliteBook Folio 9480m. + * + * GPIO4 is the headphone amplifier power control + * GPIO3 is the audio output mute indicator LED + */ + +static void alc280_fixup_hp_9480m(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; + static const struct hda_verb gpio_init[] = { + { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 }, + { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 }, + {} + }; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + /* Set the hooks to turn the headphone amp on/off + * as needed + */ + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; + spec->gen.hp_automute_hook = alc280_hp_gpio4_automute_hook; + + /* The GPIOs are currently off */ + spec->gpio_led = 0; + + /* GPIO3 is connected to the output mute LED, + * high is on, low is off + */ + spec->mute_led_polarity = 0; + spec->gpio_mute_led_mask = 0x08; + + /* Initialize GPIO configuration */ + snd_hda_add_verbs(codec, gpio_init); + } +} + /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" @@ -4457,6 +4513,7 @@ enum { ALC269_FIXUP_LIFEBOOK, ALC269_FIXUP_LIFEBOOK_EXTMIC, ALC269_FIXUP_LIFEBOOK_HP_PIN, + ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT, ALC269_FIXUP_AMIC, ALC269_FIXUP_DMIC, ALC269VB_FIXUP_AMIC, @@ -4477,6 +4534,7 @@ enum { ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, ALC269_FIXUP_HEADSET_MODE, ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, + ALC269_FIXUP_ASPIRE_HEADSET_MIC, ALC269_FIXUP_ASUS_X101_FUNC, ALC269_FIXUP_ASUS_X101_VERB, ALC269_FIXUP_ASUS_X101, @@ -4504,6 +4562,7 @@ enum { ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC, ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, ALC292_FIXUP_TPT440_DOCK, + ALC292_FIXUP_TPT440_DOCK2, ALC283_FIXUP_BXBT2807_MIC, ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED, ALC282_FIXUP_ASPIRE_V5_PINS, @@ -4511,9 +4570,15 @@ enum { ALC286_FIXUP_HP_GPIO_LED, ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, ALC280_FIXUP_HP_DOCK_PINS, + ALC280_FIXUP_HP_9480M, ALC288_FIXUP_DELL_HEADSET_MODE, ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, ALC288_FIXUP_DELL_XPS_13_GPIO6, + ALC288_FIXUP_DELL_XPS_13, + ALC288_FIXUP_DISABLE_AAMIX, + ALC292_FIXUP_DELL_E7X, + ALC292_FIXUP_DISABLE_AAMIX, + ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, }; static const struct hda_fixup alc269_fixups[] = { @@ -4620,6 +4685,10 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, + [ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_pincfg_no_hp_to_lineout, + }, [ALC269_FIXUP_AMIC] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -4748,6 +4817,15 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode_no_hp_mic, }, + [ALC269_FIXUP_ASPIRE_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* headset mic w/o jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE, + }, [ALC286_FIXUP_SONY_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -4950,6 +5028,12 @@ static const struct hda_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_HEADSET_MODE }, [ALC292_FIXUP_TPT440_DOCK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_pincfg_no_hp_to_lineout, + .chained = true, + .chain_id = ALC292_FIXUP_TPT440_DOCK2 + }, + [ALC292_FIXUP_TPT440_DOCK2] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { { 0x16, 0x21211010 }, /* dock headphone */ @@ -5009,6 +5093,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC280_FIXUP_HP_GPIO4 }, + [ALC280_FIXUP_HP_9480M] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc280_fixup_hp_9480m, + }, [ALC288_FIXUP_DELL_HEADSET_MODE] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode_dell_alc288, @@ -5036,6 +5124,40 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE }, + [ALC288_FIXUP_DISABLE_AAMIX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC288_FIXUP_DELL_XPS_13_GPIO6 + }, + [ALC288_FIXUP_DELL_XPS_13] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_dell_xps13, + .chained = true, + .chain_id = ALC288_FIXUP_DISABLE_AAMIX + }, + [ALC292_FIXUP_DISABLE_AAMIX] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE + }, + [ALC292_FIXUP_DELL_E7X] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_dell_xps13, + .chained = true, + .chain_id = ALC292_FIXUP_DISABLE_AAMIX + }, + [ALC298_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -5043,20 +5165,26 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700), + SND_PCI_QUIRK(0x1025, 0x072d, "Acer Aspire V5-571G", ALC269_FIXUP_ASPIRE_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK), SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), + SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X), + SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER), SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), + SND_PCI_QUIRK(0x1028, 0x062e, "Dell Latitude E7450", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK), SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0665, "Dell XPS 13", ALC288_FIXUP_DELL_XPS_13), SND_PCI_QUIRK(0x1028, 0x06c7, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), @@ -5087,6 +5215,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x22db, "HP", ALC280_FIXUP_HP_9480M), SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED), /* ALC290 */ @@ -5140,6 +5269,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x10cf, 0x159f, "Lifebook E780", ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT), SND_PCI_QUIRK(0x10cf, 0x15dc, "Lifebook T731", ALC269_FIXUP_LIFEBOOK_HP_PIN), SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN), SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), @@ -5243,6 +5373,8 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"}, {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"}, {.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"}, + {.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"}, + {.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"}, {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"}, {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, @@ -5301,6 +5433,13 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {0x1d, 0x40700001}, \ {0x1e, 0x411111f0} +#define ALC298_STANDARD_PINS \ + {0x18, 0x411111f0}, \ + {0x19, 0x411111f0}, \ + {0x1a, 0x411111f0}, \ + {0x1e, 0x411111f0}, \ + {0x1f, 0x411111f0} + static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, ALC255_STANDARD_PINS, @@ -5377,6 +5516,13 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x1d, 0x40700001}, {0x21, 0x02211040}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC255_STANDARD_PINS, + {0x12, 0x90a60160}, + {0x14, 0x90170120}, + {0x17, 0x40000000}, + {0x1d, 0x40700001}, + {0x21, 0x02211030}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, ALC256_STANDARD_PINS, {0x13, 0x40000000}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, @@ -5573,6 +5719,14 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x16, 0x411111f0}, {0x18, 0x411111f0}, {0x19, 0x411111f0}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC298_STANDARD_PINS, + {0x12, 0x90a60130}, + {0x13, 0x40000000}, + {0x14, 0x411111f0}, + {0x17, 0x90170140}, + {0x1d, 0x4068a36d}, + {0x21, 0x03211020}), {} }; @@ -5629,8 +5783,13 @@ static int patch_alc269(struct hda_codec *codec) spec = codec->spec; spec->gen.shared_mic_vref_pin = 0x18; - if (codec->core.vendor_id != 0x10ec0292) - codec->power_save_node = 1; + codec->power_save_node = 1; + +#ifdef CONFIG_PM + codec->patch_ops.suspend = alc269_suspend; + codec->patch_ops.resume = alc269_resume; +#endif + spec->shutup = alc269_shutup; snd_hda_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); @@ -5728,15 +5887,6 @@ static int patch_alc269(struct hda_codec *codec) if (!spec->gen.no_analog && spec->gen.beep_nid && spec->gen.mixer_nid) set_beep_amp(spec, spec->gen.mixer_nid, 0x04, HDA_INPUT); - codec->patch_ops = alc_patch_ops; - codec->patch_ops.stream_pm = snd_hda_gen_stream_pm; -#ifdef CONFIG_PM - codec->patch_ops.suspend = alc269_suspend; - codec->patch_ops.resume = alc269_resume; -#endif - if (!spec->shutup) - spec->shutup = alc269_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -5852,6 +6002,10 @@ static int patch_alc861(struct hda_codec *codec) spec = codec->spec; spec->gen.beep_nid = 0x23; +#ifdef CONFIG_PM + spec->power_hook = alc_power_eapd; +#endif + snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -5863,11 +6017,6 @@ static int patch_alc861(struct hda_codec *codec) if (!spec->gen.no_analog) set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); - codec->patch_ops = alc_patch_ops; -#ifdef CONFIG_PM - spec->power_hook = alc_power_eapd; -#endif - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -5944,6 +6093,8 @@ static int patch_alc861vd(struct hda_codec *codec) spec = codec->spec; spec->gen.beep_nid = 0x23; + spec->shutup = alc_eapd_shutup; + snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -5955,10 +6106,6 @@ static int patch_alc861vd(struct hda_codec *codec) if (!spec->gen.no_analog) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - codec->patch_ops = alc_patch_ops; - - spec->shutup = alc_eapd_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -6559,6 +6706,8 @@ static int patch_alc662(struct hda_codec *codec) spec = codec->spec; + spec->shutup = alc_eapd_shutup; + /* handle multiple HPs as is */ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; @@ -6610,9 +6759,6 @@ static int patch_alc662(struct hda_codec *codec) } } - codec->patch_ops = alc_patch_ops; - spec->shutup = alc_eapd_shutup; - snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -6649,8 +6795,6 @@ static int patch_alc680(struct hda_codec *codec) return err; } - codec->patch_ops = alc_patch_ops; - return 0; } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 6833c74ed6ff..dcc7fe91244c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -100,6 +100,7 @@ enum { STAC_HP_ENVY_BASS, STAC_HP_BNB13_EQ, STAC_HP_ENVY_TS_BASS, + STAC_HP_ENVY_TS_DAC_BIND, STAC_92HD83XXX_GPIO10_EAPD, STAC_92HD83XXX_MODELS }; @@ -2171,6 +2172,22 @@ static void stac92hd83xxx_fixup_gpio10_eapd(struct hda_codec *codec, spec->eapd_switch = 0; } +static void hp_envy_ts_fixup_dac_bind(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct sigmatel_spec *spec = codec->spec; + static hda_nid_t preferred_pairs[] = { + 0xd, 0x13, + 0 + }; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + spec->gen.preferred_dacs = preferred_pairs; +} + static const struct hda_verb hp_bnb13_eq_verbs[] = { /* 44.1KHz base */ { 0x22, 0x7A6, 0x3E }, @@ -2686,6 +2703,12 @@ static const struct hda_fixup stac92hd83xxx_fixups[] = { {} }, }, + [STAC_HP_ENVY_TS_DAC_BIND] = { + .type = HDA_FIXUP_FUNC, + .v.func = hp_envy_ts_fixup_dac_bind, + .chained = true, + .chain_id = STAC_HP_ENVY_TS_BASS, + }, [STAC_92HD83XXX_GPIO10_EAPD] = { .type = HDA_FIXUP_FUNC, .v.func = stac92hd83xxx_fixup_gpio10_eapd, @@ -2764,6 +2787,8 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { "HP bNB13", STAC_HP_BNB13_EQ), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x190e, "HP ENVY TS", STAC_HP_ENVY_TS_BASS), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1967, + "HP ENVY TS", STAC_HP_ENVY_TS_DAC_BIND), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1940, "HP bNB13", STAC_HP_BNB13_EQ), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1941, @@ -4337,7 +4362,7 @@ static void stac_shutup(struct hda_codec *codec) #define stac_free snd_hda_gen_free -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS static void stac92hd_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { @@ -4417,6 +4442,7 @@ static int alloc_stac_spec(struct hda_codec *codec) codec->spec = spec; codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */ spec->gen.dac_min_mute = true; + codec->patch_ops = stac_patch_ops; return 0; } @@ -4433,7 +4459,6 @@ static int patch_stac9200(struct hda_codec *codec) spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; - codec->patch_ops = stac_patch_ops; codec->power_filter = snd_hda_codec_eapd_power_filter; snd_hda_add_verbs(codec, stac9200_eapd_init); @@ -4466,8 +4491,6 @@ static int patch_stac925x(struct hda_codec *codec) spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; - codec->patch_ops = stac_patch_ops; - snd_hda_add_verbs(codec, stac925x_core_init); snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl, @@ -4537,8 +4560,6 @@ static int patch_stac92hd73xx(struct hda_codec *codec) spec->gen.own_eapd_ctl = 1; spec->gen.power_down_unused = 1; - codec->patch_ops = stac_patch_ops; - snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl, stac92hd73xx_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -4614,8 +4635,6 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); spec->default_polarity = -1; /* no default cfg */ - codec->patch_ops = stac_patch_ops; - snd_hda_add_verbs(codec, stac92hd83xxx_core_init); snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl, @@ -4664,8 +4683,6 @@ static int patch_stac92hd95(struct hda_codec *codec) spec->num_pwrs = ARRAY_SIZE(stac92hd95_pwr_nids); spec->default_polarity = 0; - codec->patch_ops = stac_patch_ops; - snd_hda_pick_fixup(codec, stac92hd95_models, stac92hd95_fixup_tbl, stac92hd95_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -4704,8 +4721,6 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec->gen.mixer_nid = 0x17; spec->have_spdif_mux = 1; - codec->patch_ops = stac_patch_ops; - /* GPIO0 = EAPD */ spec->gpio_mask = 0x01; spec->gpio_dir = 0x01; @@ -4784,8 +4799,6 @@ static int patch_stac922x(struct hda_codec *codec) spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; - codec->patch_ops = stac_patch_ops; - snd_hda_add_verbs(codec, stac922x_core_init); /* Fix Mux capture level; max to 2 */ @@ -4841,8 +4854,6 @@ static int patch_stac927x(struct hda_codec *codec) spec->aloopback_shift = 0; spec->eapd_switch = 1; - codec->patch_ops = stac_patch_ops; - snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl, stac927x_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -4904,8 +4915,6 @@ static int patch_stac9205(struct hda_codec *codec) /* Turn on/off EAPD per HP plugging */ spec->eapd_switch = 1; - codec->patch_ops = stac_patch_ops; - snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl, stac9205_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -4977,8 +4986,6 @@ static int patch_stac9872(struct hda_codec *codec) spec->linear_tone_beep = 1; spec->gen.own_eapd_ctl = 1; - codec->patch_ops = stac_patch_ops; - snd_hda_add_verbs(codec, stac9872_core_init); snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl, diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 31a95cca015d..da5366405eda 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -117,6 +117,8 @@ static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream, int action); +static const struct hda_codec_ops via_patch_ops; /* defined below */ + static struct via_spec *via_new_spec(struct hda_codec *codec) { struct via_spec *spec; @@ -137,6 +139,7 @@ static struct via_spec *via_new_spec(struct hda_codec *codec) spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; codec->power_save_node = 1; spec->gen.power_down_unused = 1; + codec->patch_ops = via_patch_ops; return spec; } @@ -238,7 +241,9 @@ static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = codec->power_save_node; + struct via_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused; return 0; } @@ -249,9 +254,9 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, struct via_spec *spec = codec->spec; bool val = !!ucontrol->value.enumerated.item[0]; - if (val == codec->power_save_node) + if (val == spec->gen.power_down_unused) return 0; - codec->power_save_node = val; + /* codec->power_save_node = val; */ /* widget PM seems yet broken */ spec->gen.power_down_unused = val; analog_low_current_mode(codec); return 1; @@ -449,6 +454,15 @@ static int via_suspend(struct hda_codec *codec) return 0; } + +static int via_resume(struct hda_codec *codec) +{ + /* some delay here to make jack detection working (bko#98921) */ + msleep(10); + codec->patch_ops.init(codec); + regcache_sync(codec->core.regmap); + return 0; +} #endif #ifdef CONFIG_PM @@ -472,9 +486,9 @@ static const struct hda_codec_ops via_patch_ops = { .init = via_init, .free = via_free, .unsol_event = snd_hda_jack_unsol_event, - .stream_pm = snd_hda_gen_stream_pm, #ifdef CONFIG_PM .suspend = via_suspend, + .resume = via_resume, .check_power_status = via_check_power_status, #endif }; @@ -651,6 +665,9 @@ static int patch_vt1708(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + /* override some patch_ops */ + codec->patch_ops.build_controls = vt1708_build_controls; + codec->patch_ops.build_pcms = vt1708_build_pcms; spec->gen.mixer_nid = 0x17; /* set jackpoll_interval while parsing the codec */ @@ -679,10 +696,6 @@ static int patch_vt1708(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; - codec->patch_ops = via_patch_ops; - codec->patch_ops.build_controls = vt1708_build_controls; - codec->patch_ops.build_pcms = vt1708_build_pcms; - /* clear jackpoll_interval again; it's set dynamically */ codec->jackpoll_interval = 0; @@ -707,8 +720,6 @@ static int patch_vt1709(struct hda_codec *codec) return err; } - codec->patch_ops = via_patch_ops; - return 0; } @@ -735,7 +746,6 @@ static int patch_vt1708B(struct hda_codec *codec) return err; } - codec->patch_ops = via_patch_ops; return 0; } @@ -800,7 +810,6 @@ static int patch_vt1708S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; - codec->patch_ops = via_patch_ops; return 0; } @@ -842,7 +851,6 @@ static int patch_vt1702(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; - codec->patch_ops = via_patch_ops; return 0; } @@ -915,7 +923,6 @@ static int patch_vt1718S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; - codec->patch_ops = via_patch_ops; return 0; } @@ -1015,7 +1022,6 @@ static int patch_vt1716S(struct hda_codec *codec) spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; - codec->patch_ops = via_patch_ops; return 0; } @@ -1123,7 +1129,6 @@ static int patch_vt2002P(struct hda_codec *codec) else spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; - codec->patch_ops = via_patch_ops; return 0; } @@ -1162,7 +1167,6 @@ static int patch_vt1812(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; - codec->patch_ops = via_patch_ops; return 0; } @@ -1200,7 +1204,6 @@ static int patch_vt3476(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; - codec->patch_ops = via_patch_ops; return 0; } diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index f7b1523e8a82..8ae3bb7975d1 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2530,8 +2530,8 @@ static int snd_ice1712_create(struct snd_card *card, if (err < 0) return err; /* check, if we can restrict PCI DMA transfers to 28 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) { dev_err(card->dev, "architecture does not support 28bit PCI busmaster DMA\n"); pci_disable_device(pci); diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c index 6f55e02e5c84..7c387b04067e 100644 --- a/sound/pci/ice1712/quartet.c +++ b/sound/pci/ice1712/quartet.c @@ -203,7 +203,6 @@ static const char * const ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS", #define AK4620_DEEMVOL_REG 0x03 #define AK4620_SMUTE (1<<7) -#ifdef CONFIG_PROC_FS /* * Conversion from int value to its binary form. Used for debugging. * The output buffer must be allocated prior to calling the function. @@ -228,7 +227,6 @@ static char *get_binary(char *buffer, int value) buffer[pos] = '\0'; return buffer; } -#endif /* CONFIG_PROC_FS */ /* * Initial setup of the conversion array GPIO <-> rate @@ -486,7 +484,7 @@ static void set_cpld(struct snd_ice1712 *ice, unsigned int val) reg_write(ice, GPIO_CPLD_CSN, val); spec->cpld = val; } -#ifdef CONFIG_PROC_FS + static void proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -507,9 +505,6 @@ static void proc_init(struct snd_ice1712 *ice) if (!snd_card_proc_new(ice->card, "quartet", &entry)) snd_info_set_text_ops(entry, ice, proc_regs_read); } -#else /* !CONFIG_PROC_FS */ -static void proc_init(struct snd_ice1712 *ice) {} -#endif static int qtet_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index b120925223ae..42bcbac801a3 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2900,7 +2900,6 @@ static int intel8x0_in_clock_list(struct intel8x0 *chip) return 1; } -#ifdef CONFIG_PROC_FS static void snd_intel8x0_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffer) { @@ -2942,9 +2941,6 @@ static void snd_intel8x0_proc_init(struct intel8x0 *chip) if (! snd_card_proc_new(chip->card, "intel8x0", &entry)) snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read); } -#else -#define snd_intel8x0_proc_init(x) -#endif static int snd_intel8x0_dev_free(struct snd_device *device) { diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 7577f31cd504..1bc98c867133 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -1065,7 +1065,6 @@ static SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume); #define INTEL8X0M_PM_OPS NULL #endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PROC_FS static void snd_intel8x0m_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffer) { @@ -1093,10 +1092,6 @@ static void snd_intel8x0m_proc_init(struct intel8x0m *chip) if (! snd_card_proc_new(chip->card, "intel8x0m", &entry)) snd_info_set_text_ops(entry, chip, snd_intel8x0m_proc_read); } -#else /* !CONFIG_PROC_FS */ -#define snd_intel8x0m_proc_init(chip) -#endif /* CONFIG_PROC_FS */ - static int snd_intel8x0m_dev_free(struct snd_device *device) { diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 601315a1f58f..cba89beb2b38 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -57,13 +57,13 @@ static const char card_name[] = "LX6464ES"; #define PCI_DEVICE_ID_PLX_LX6464ES PCI_DEVICE_ID_PLX_9056 static const struct pci_device_id snd_lx6464es_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES), - .subvendor = PCI_VENDOR_ID_DIGIGRAM, - .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES, + PCI_VENDOR_ID_DIGIGRAM, + PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM), }, /* LX6464ES */ - { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES), - .subvendor = PCI_VENDOR_ID_DIGIGRAM, - .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES, + PCI_VENDOR_ID_DIGIGRAM, + PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM), }, /* LX6464ES-CAE */ { 0, }, }; @@ -412,9 +412,9 @@ static int lx_pcm_hw_free(struct snd_pcm_substream *substream) err = snd_pcm_lib_free_pages(substream); if (is_capture) - chip->capture_stream.stream = 0; + chip->capture_stream.stream = NULL; else - chip->playback_stream.stream = 0; + chip->playback_stream.stream = NULL; exit: mutex_unlock(&chip->setup_mutex); @@ -981,7 +981,7 @@ static int snd_lx6464es_create(struct snd_card *card, pci_set_master(pci); /* check if we can restrict PCI DMA transfers to 32 bits */ - err = pci_set_dma_mask(pci, DMA_BIT_MASK(32)); + err = dma_set_mask(&pci->dev, DMA_BIT_MASK(32)); if (err < 0) { dev_err(card->dev, "architecture does not support 32bit PCI busmaster DMA\n"); diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 9be660993bd0..72e89cedc52d 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2537,8 +2537,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, return -EIO; /* check, if we can restrict PCI DMA transfers to 28 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(28)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(28)) < 0) { dev_err(card->dev, "architecture does not support 28bit PCI busmaster DMA\n"); pci_disable_device(pci); diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index c3a9f39f8d61..bc81b9f75ed0 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1269,7 +1269,7 @@ static int snd_mixart_probe(struct pci_dev *pci, pci_set_master(pci); /* check if we can restrict PCI DMA transfers to 32 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) { dev_err(&pci->dev, "architecture does not support 32bit PCI busmaster DMA\n"); pci_disable_device(pci); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index ffff3b25fd73..b4ef5804212d 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -196,7 +196,6 @@ static void oxygen_gpio_changed(struct work_struct *work) chip->model.gpio_changed(chip); } -#ifdef CONFIG_PROC_FS static void oxygen_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -250,9 +249,6 @@ static void oxygen_proc_init(struct oxygen *chip) if (!snd_card_proc_new(chip->card, "oxygen", &entry)) snd_info_set_text_ops(entry, chip, oxygen_proc_read); } -#else -#define oxygen_proc_init(chip) -#endif static const struct pci_device_id * oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[]) diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index 6ce68604c25e..90ac479f389f 100644 --- a/sound/pci/oxygen/xonar_wm87x6.c +++ b/sound/pci/oxygen/xonar_wm87x6.c @@ -286,7 +286,7 @@ static void xonar_ds_init(struct oxygen *chip) xonar_enable_output(chip); snd_jack_new(chip->card, "Headphone", - SND_JACK_HEADPHONE, &data->hp_jack); + SND_JACK_HEADPHONE, &data->hp_jack, false, false); xonar_ds_handle_hp_jack(chip); snd_component_add(chip->card, "WM8776"); diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index c6092e48ceb6..9293235281dc 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1537,7 +1537,7 @@ static int pcxhr_probe(struct pci_dev *pci, pci_set_master(pci); /* check if we can restrict PCI DMA transfers to 32 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(32)) < 0) { dev_err(&pci->dev, "architecture does not support 32bit PCI busmaster DMA\n"); pci_disable_device(pci); diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index efe669b80256..f3860b850210 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -383,9 +383,9 @@ static void __sis_map_silence(struct sis7019 *sis) { /* Helper function: must hold sis->voice_lock on entry */ if (!sis->silence_users) - sis->silence_dma_addr = pci_map_single(sis->pci, + sis->silence_dma_addr = dma_map_single(&sis->pci->dev, sis->suspend_state[0], - 4096, PCI_DMA_TODEVICE); + 4096, DMA_TO_DEVICE); sis->silence_users++; } @@ -394,8 +394,8 @@ static void __sis_unmap_silence(struct sis7019 *sis) /* Helper function: must hold sis->voice_lock on entry */ sis->silence_users--; if (!sis->silence_users) - pci_unmap_single(sis->pci, sis->silence_dma_addr, 4096, - PCI_DMA_TODEVICE); + dma_unmap_single(&sis->pci->dev, sis->silence_dma_addr, 4096, + DMA_TO_DEVICE); } static void sis_free_voice(struct sis7019 *sis, struct voice *voice) @@ -1325,7 +1325,7 @@ static int sis_chip_create(struct snd_card *card, if (rc) goto error_out; - rc = pci_set_dma_mask(pci, DMA_BIT_MASK(30)); + rc = dma_set_mask(&pci->dev, DMA_BIT_MASK(30)); if (rc < 0) { dev_err(&pci->dev, "architecture does not support 30-bit PCI busmaster DMA"); goto error_out_enabled; diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 0f40624a4275..1b6fad7d4d56 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -1259,8 +1259,8 @@ static int snd_sonicvibes_create(struct snd_card *card, if ((err = pci_enable_device(pci)) < 0) return err; /* check, if we can restrict PCI DMA transfers to 24 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) { dev_err(card->dev, "architecture does not support 24bit PCI busmaster DMA\n"); pci_disable_device(pci); diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index b72be035f785..599d2b7eb5b8 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3551,8 +3551,8 @@ int snd_trident_create(struct snd_card *card, if ((err = pci_enable_device(pci)) < 0) return err; /* check, if we can restrict PCI DMA transfers to 30 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(30)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(30)) < 0) { + if (dma_set_mask(&pci->dev, DMA_BIT_MASK(30)) < 0 || + dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(30)) < 0) { dev_err(card->dev, "architecture does not support 30bit PCI busmaster DMA\n"); pci_disable_device(pci); diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c index 0d1c27e911b8..6120a067494a 100644 --- a/sound/ppc/keywest.c +++ b/sound/ppc/keywest.c @@ -31,10 +31,15 @@ */ static struct pmac_keywest *keywest_ctx; +static bool keywest_probed; static int keywest_probe(struct i2c_client *client, const struct i2c_device_id *id) { + keywest_probed = true; + /* If instantiated via i2c-powermac, we still need to set the client */ + if (!keywest_ctx->client) + keywest_ctx->client = client; i2c_set_clientdata(client, keywest_ctx); return 0; } @@ -52,7 +57,7 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter) return -EINVAL; if (strncmp(adapter->name, "mac-io", 6)) - return 0; /* ignored */ + return -EINVAL; /* ignored */ memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "keywest", I2C_NAME_SIZE); @@ -92,7 +97,8 @@ static int keywest_remove(struct i2c_client *client) static const struct i2c_device_id keywest_i2c_id[] = { - { "keywest", 0 }, + { "MAC,tas3004", 0 }, /* instantiated by i2c-powermac */ + { "keywest", 0 }, /* instantiated by us if needed */ { } }; @@ -100,7 +106,6 @@ static struct i2c_driver keywest_driver = { .driver = { .name = "PMac Keywest Audio", }, - .attach_adapter = keywest_attach_adapter, .probe = keywest_probe, .remove = keywest_remove, .id_table = keywest_i2c_id, @@ -132,16 +137,37 @@ int snd_pmac_tumbler_post_init(void) /* exported */ int snd_pmac_keywest_init(struct pmac_keywest *i2c) { - int err; + struct i2c_adapter *adap; + int err, i = 0; if (keywest_ctx) return -EBUSY; + adap = i2c_get_adapter(0); + if (!adap) + return -EPROBE_DEFER; + keywest_ctx = i2c; if ((err = i2c_add_driver(&keywest_driver))) { snd_printk(KERN_ERR "cannot register keywest i2c driver\n"); + i2c_put_adapter(adap); return err; } - return 0; + + /* There was already a device from i2c-powermac. Great, let's return */ + if (keywest_probed) + return 0; + + /* We assume Macs have consecutive I2C bus numbers starting at 0 */ + while (adap) { + /* Scan for devices to be bound to */ + err = keywest_attach_adapter(adap); + if (!err) + return 0; + i2c_put_adapter(adap); + adap = i2c_get_adapter(++i); + } + + return -ENODEV; } diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index e2828e101433..2ae9619443d1 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -45,6 +45,7 @@ source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" source "sound/soc/intel/Kconfig" +source "sound/soc/mediatek/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/qcom/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index a0e1ee6b507d..e189903fabf4 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += intel/ +obj-$(CONFIG_SND_SOC) += mediatek/ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index c3152072d682..1489cd461aec 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -9,21 +9,32 @@ config SND_ATMEL_SOC if SND_ATMEL_SOC config SND_ATMEL_SOC_PDC - bool + tristate + default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m + default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y) + +config SND_ATMEL_SOC_SSC_PDC + tristate config SND_ATMEL_SOC_DMA - bool + tristate select SND_SOC_GENERIC_DMAENGINE_PCM + default m if SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=m + default y if SND_ATMEL_SOC_SSC_DMA=y || (SND_ATMEL_SOC_SSC_DMA=m && SND_ATMEL_SOC_SSC=y) + +config SND_ATMEL_SOC_SSC_DMA + tristate config SND_ATMEL_SOC_SSC tristate + default y if SND_ATMEL_SOC_SSC_DMA=y || SND_ATMEL_SOC_SSC_PDC=y + default m if SND_ATMEL_SOC_SSC_DMA=m || SND_ATMEL_SOC_SSC_PDC=m config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" depends on ARCH_AT91 || COMPILE_TEST depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI - select SND_ATMEL_SOC_PDC - select SND_ATMEL_SOC_SSC + select SND_ATMEL_SOC_SSC_PDC select SND_SOC_WM8731 help Say Y if you want to add support for SoC audio on WM8731-based @@ -33,8 +44,7 @@ config SND_ATMEL_SOC_WM8904 tristate "Atmel ASoC driver for boards using WM8904 codec" depends on ARCH_AT91 || COMPILE_TEST depends on ATMEL_SSC && I2C - select SND_ATMEL_SOC_SSC - select SND_ATMEL_SOC_DMA + select SND_ATMEL_SOC_SSC_DMA select SND_SOC_WM8904 help Say Y if you want to add support for Atmel ASoC driver for boards using @@ -44,8 +54,7 @@ config SND_AT91_SOC_SAM9X5_WM8731 tristate "SoC Audio support for WM8731-based at91sam9x5 board" depends on ARCH_AT91 || COMPILE_TEST depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI - select SND_ATMEL_SOC_SSC - select SND_ATMEL_SOC_DMA + select SND_ATMEL_SOC_SSC_DMA select SND_SOC_WM8731 help Say Y if you want to add support for audio SoC on an diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 4fa7ac91f972..b327e5cc8de3 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -1,8 +1,10 @@ # AT91 Platform Support -snd-soc-atmel-pcm-$(CONFIG_SND_ATMEL_SOC_PDC) := atmel-pcm-pdc.o -snd-soc-atmel-pcm-$(CONFIG_SND_ATMEL_SOC_DMA) += atmel-pcm-dma.o -snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o $(snd-soc-atmel-pcm-y) +snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o +snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o +snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o +obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o +obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o # AT91 Machine Support diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9b36011a814e..efaafce8ba38 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -509,6 +509,11 @@ config SND_SOC_RL6231 default m if SND_SOC_RT5670=m default m if SND_SOC_RT5677=m +config SND_SOC_RL6347A + tristate + default y if SND_SOC_RT286=y + default m if SND_SOC_RT286=m + config SND_SOC_RT286 tristate depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 3dcf5ac85e89..cf160d972cb3 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -77,6 +77,7 @@ snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-rl6231-objs := rl6231.o +snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt286-objs := rt286.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o @@ -263,6 +264,7 @@ obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o +obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 88f6df21ad95..802e05eae3e9 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -2286,6 +2286,33 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff) } EXPORT_SYMBOL_GPL(arizona_set_output_mode); +static const struct soc_enum arizona_adsp2_rate_enum[] = { + SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1, + ARIZONA_DSP1_RATE_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1, + ARIZONA_DSP1_RATE_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1, + ARIZONA_DSP1_RATE_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1, + ARIZONA_DSP1_RATE_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), +}; + +const struct snd_kcontrol_new arizona_adsp2_rate_controls[] = { + SOC_ENUM("DSP1 Rate", arizona_adsp2_rate_enum[0]), + SOC_ENUM("DSP2 Rate", arizona_adsp2_rate_enum[1]), + SOC_ENUM("DSP3 Rate", arizona_adsp2_rate_enum[2]), + SOC_ENUM("DSP4 Rate", arizona_adsp2_rate_enum[3]), +}; +EXPORT_SYMBOL_GPL(arizona_adsp2_rate_controls); + MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 84e119a56515..43deb0462309 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -217,6 +217,8 @@ extern const struct soc_enum arizona_ng_hold; extern const struct soc_enum arizona_in_hpf_cut_enum; extern const struct soc_enum arizona_in_dmic_osr[]; +extern const struct snd_kcontrol_new arizona_adsp2_rate_controls[]; + extern int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 679f0a0f7039..78268f0514e9 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -27,7 +27,7 @@ #include "max98090.h" /* Allows for sparsely populated register maps */ -static struct reg_default max98090_reg[] = { +static const struct reg_default max98090_reg[] = { { 0x00, 0x00 }, /* 00 Software Reset */ { 0x03, 0x04 }, /* 03 Interrupt Masks */ { 0x04, 0x00 }, /* 04 System Clock Quick */ @@ -2704,7 +2704,7 @@ static const struct of_device_id max98090_of_match[] = { MODULE_DEVICE_TABLE(of, max98090_of_match); #ifdef CONFIG_ACPI -static struct acpi_device_id max98090_acpi_match[] = { +static const struct acpi_device_id max98090_acpi_match[] = { { "193C9890", MAX98090 }, { } }; diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 62dda2488f14..b74118e019fb 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -341,6 +341,7 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec); int i = get_coeff(priv->mclk, params_rate(hw_params)); + int srate; if (i < 0) return i; @@ -370,53 +371,16 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream, BIT(0) | BIT(1), 0); } - switch (params_rate(hw_params)) { - case 16000: - snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, - get_srate(params_rate(hw_params))); - snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, - coeff_div[i].pllnl); - snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, - coeff_div[i].pllnh); - snd_soc_update_bits(codec, ML26124_PLLML, 0xff, - coeff_div[i].pllml); - snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, - coeff_div[i].pllmh); - snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, - coeff_div[i].plldiv); - break; - case 32000: - snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, - get_srate(params_rate(hw_params))); - snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, - coeff_div[i].pllnl); - snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, - coeff_div[i].pllnh); - snd_soc_update_bits(codec, ML26124_PLLML, 0xff, - coeff_div[i].pllml); - snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, - coeff_div[i].pllmh); - snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, - coeff_div[i].plldiv); - break; - case 48000: - snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, - get_srate(params_rate(hw_params))); - snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, - coeff_div[i].pllnl); - snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, - coeff_div[i].pllnh); - snd_soc_update_bits(codec, ML26124_PLLML, 0xff, - coeff_div[i].pllml); - snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, - coeff_div[i].pllmh); - snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, - coeff_div[i].plldiv); - break; - default: - pr_err("%s:this rate is no support for ml26124\n", __func__); - return -EINVAL; - } + srate = get_srate(params_rate(hw_params)); + if (srate < 0) + return srate; + + snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf, srate); + snd_soc_update_bits(codec, ML26124_PLLNL, 0xff, coeff_div[i].pllnl); + snd_soc_update_bits(codec, ML26124_PLLNH, 0x1, coeff_div[i].pllnh); + snd_soc_update_bits(codec, ML26124_PLLML, 0xff, coeff_div[i].pllml); + snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f, coeff_div[i].pllmh); + snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f, coeff_div[i].plldiv); return 0; } diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 477e13d30971..e7ba557979cb 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -102,7 +102,7 @@ static int pcm1681_set_deemph(struct snd_soc_codec *codec) if (val != -1) { regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL, - PCM1681_DEEMPH_RATE_MASK, val); + PCM1681_DEEMPH_RATE_MASK, val << 3); enable = 1; } else enable = 0; diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c new file mode 100644 index 000000000000..91d5166bd3a1 --- /dev/null +++ b/sound/soc/codecs/rl6347a.c @@ -0,0 +1,128 @@ +/* + * rl6347a.c - RL6347A class device shared support + * + * Copyright 2015 Realtek Semiconductor Corp. + * + * Author: Oder Chiou <oder_chiou@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/dmi.h> +#include <linux/acpi.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/jack.h> +#include <linux/workqueue.h> +#include <sound/hda_verbs.h> + +#include "rl6347a.h" + +int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value) +{ + struct i2c_client *client = context; + struct rl6347a_priv *rl6347a = i2c_get_clientdata(client); + u8 data[4]; + int ret, i; + + /* handle index registers */ + if (reg <= 0xff) { + rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg); + for (i = 0; i < rl6347a->index_cache_size; i++) { + if (reg == rl6347a->index_cache[i].reg) { + rl6347a->index_cache[i].def = value; + break; + } + + } + reg = RL6347A_PROC_COEF; + } + + data[0] = (reg >> 24) & 0xff; + data[1] = (reg >> 16) & 0xff; + /* + * 4 bit VID: reg should be 0 + * 12 bit VID: value should be 0 + * So we use an OR operator to handle it rather than use if condition. + */ + data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff); + data[3] = value & 0xff; + + ret = i2c_master_send(client, data, 4); + + if (ret == 4) + return 0; + else + pr_err("ret=%d\n", ret); + if (ret < 0) + return ret; + else + return -EIO; +} +EXPORT_SYMBOL_GPL(rl6347a_hw_write); + +int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value) +{ + struct i2c_client *client = context; + struct i2c_msg xfer[2]; + int ret; + __be32 be_reg; + unsigned int index, vid, buf = 0x0; + + /* handle index registers */ + if (reg <= 0xff) { + rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg); + reg = RL6347A_PROC_COEF; + } + + reg = reg | 0x80000; + vid = (reg >> 8) & 0xfff; + + if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) { + index = (reg >> 8) & 0xf; + reg = (reg & ~0xf0f) | index; + } + be_reg = cpu_to_be32(reg); + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 4; + xfer[0].buf = (u8 *)&be_reg; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 4; + xfer[1].buf = (u8 *)&buf; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + + *value = be32_to_cpu(buf); + + return 0; +} +EXPORT_SYMBOL_GPL(rl6347a_hw_read); + +MODULE_DESCRIPTION("RL6347A class device shared support"); +MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h new file mode 100644 index 000000000000..1cb56e50b7f3 --- /dev/null +++ b/sound/soc/codecs/rl6347a.h @@ -0,0 +1,32 @@ +/* + * rl6347a.h - RL6347A class device shared support + * + * Copyright 2015 Realtek Semiconductor Corp. + * + * Author: Oder Chiou <oder_chiou@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __RL6347A_H__ +#define __RL6347A_H__ + +#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) + +#define RL6347A_VENDOR_REGISTERS 0x20 + +#define RL6347A_COEF_INDEX\ + VERB_CMD(AC_VERB_SET_COEF_INDEX, RL6347A_VENDOR_REGISTERS, 0) +#define RL6347A_PROC_COEF\ + VERB_CMD(AC_VERB_SET_PROC_COEF, RL6347A_VENDOR_REGISTERS, 0) + +struct rl6347a_priv { + struct reg_default *index_cache; + int index_cache_size; +}; + +int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value); +int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value); + +#endif /* __RL6347A_H__ */ diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index c6cca0639e0d..5c43e263b2c1 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -31,12 +31,15 @@ #include <sound/rt286.h> #include <sound/hda_verbs.h> +#include "rl6347a.h" #include "rt286.h" #define RT286_VENDOR_ID 0x10ec0286 #define RT288_VENDOR_ID 0x10ec0288 struct rt286_priv { + struct reg_default *index_cache; + int index_cache_size; struct regmap *regmap; struct snd_soc_codec *codec; struct rt286_platform_data pdata; @@ -45,7 +48,6 @@ struct rt286_priv { struct delayed_work jack_detect_work; int sys_clk; int clk_id; - struct reg_default *index_cache; }; static struct reg_default rt286_index_def[] = { @@ -185,94 +187,6 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg) } } -static int rt286_hw_write(void *context, unsigned int reg, unsigned int value) -{ - struct i2c_client *client = context; - struct rt286_priv *rt286 = i2c_get_clientdata(client); - u8 data[4]; - int ret, i; - - /* handle index registers */ - if (reg <= 0xff) { - rt286_hw_write(client, RT286_COEF_INDEX, reg); - for (i = 0; i < INDEX_CACHE_SIZE; i++) { - if (reg == rt286->index_cache[i].reg) { - rt286->index_cache[i].def = value; - break; - } - - } - reg = RT286_PROC_COEF; - } - - data[0] = (reg >> 24) & 0xff; - data[1] = (reg >> 16) & 0xff; - /* - * 4 bit VID: reg should be 0 - * 12 bit VID: value should be 0 - * So we use an OR operator to handle it rather than use if condition. - */ - data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff); - data[3] = value & 0xff; - - ret = i2c_master_send(client, data, 4); - - if (ret == 4) - return 0; - else - pr_err("ret=%d\n", ret); - if (ret < 0) - return ret; - else - return -EIO; -} - -static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value) -{ - struct i2c_client *client = context; - struct i2c_msg xfer[2]; - int ret; - __be32 be_reg; - unsigned int index, vid, buf = 0x0; - - /* handle index registers */ - if (reg <= 0xff) { - rt286_hw_write(client, RT286_COEF_INDEX, reg); - reg = RT286_PROC_COEF; - } - - reg = reg | 0x80000; - vid = (reg >> 8) & 0xfff; - - if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) { - index = (reg >> 8) & 0xf; - reg = (reg & ~0xf0f) | index; - } - be_reg = cpu_to_be32(reg); - - /* Write register */ - xfer[0].addr = client->addr; - xfer[0].flags = 0; - xfer[0].len = 4; - xfer[0].buf = (u8 *)&be_reg; - - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = 4; - xfer[1].buf = (u8 *)&buf; - - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret < 0) - return ret; - else if (ret != 2) - return -EIO; - - *value = be32_to_cpu(buf); - - return 0; -} - #ifdef CONFIG_PM static void rt286_index_sync(struct snd_soc_codec *codec) { @@ -1174,8 +1088,8 @@ static const struct regmap_config rt286_regmap = { .max_register = 0x02370100, .volatile_reg = rt286_volatile_register, .readable_reg = rt286_readable_register, - .reg_write = rt286_hw_write, - .reg_read = rt286_hw_read, + .reg_write = rl6347a_hw_write, + .reg_read = rl6347a_hw_read, .cache_type = REGCACHE_RBTREE, .reg_defaults = rt286_reg, .num_reg_defaults = ARRAY_SIZE(rt286_reg), @@ -1248,6 +1162,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c, } rt286->index_cache = rt286_index_def; + rt286->index_cache_size = INDEX_CACHE_SIZE; rt286->i2c = i2c; i2c_set_clientdata(i2c, rt286); diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index f40752a6c242..9bc78e57513d 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -51,7 +51,7 @@ static const struct regmap_range_cfg rt5640_ranges[] = { .window_len = 0x1, }, }; -static struct reg_default init_list[] = { +static const struct reg_default init_list[] = { {RT5640_PR_BASE + 0x3d, 0x3600}, {RT5640_PR_BASE + 0x12, 0x0aa8}, {RT5640_PR_BASE + 0x14, 0x0aaa}, @@ -59,7 +59,6 @@ static struct reg_default init_list[] = { {RT5640_PR_BASE + 0x21, 0xe0e0}, {RT5640_PR_BASE + 0x23, 0x1804}, }; -#define RT5640_INIT_REG_LEN ARRAY_SIZE(init_list) static const struct reg_default rt5640_reg[] = { { 0x00, 0x000e }, @@ -2122,7 +2121,7 @@ MODULE_DEVICE_TABLE(of, rt5640_of_match); #endif #ifdef CONFIG_ACPI -static struct acpi_device_id rt5640_acpi_match[] = { +static const struct acpi_device_id rt5640_acpi_match[] = { { "INT33CA", 0 }, { "10EC5640", 0 }, { "10EC5642", 0 }, diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index d5f0f5680d3b..e9cc3aae5366 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -349,6 +349,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg) case RT5645_TDM_CTRL_1: case RT5645_TDM_CTRL_2: case RT5645_TDM_CTRL_3: + case RT5650_TDM_CTRL_4: case RT5645_GLB_CLK: case RT5645_PLL_CTRL1: case RT5645_PLL_CTRL2: @@ -1705,15 +1706,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_MUX("RT5645 IF1 ADC Mux", SND_SOC_NOPM, 0, 0, &rt5645_if1_adc_in_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 ADC1 Swap Mux", SND_SOC_NOPM, - 0, 0, &rt5650_if1_adc1_in_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 ADC2 Swap Mux", SND_SOC_NOPM, - 0, 0, &rt5650_if1_adc2_in_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 ADC3 Swap Mux", SND_SOC_NOPM, - 0, 0, &rt5650_if1_adc3_in_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 ADC Mux", SND_SOC_NOPM, - 0, 0, &rt5650_if1_adc_in_mux), - SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM, 0, 0, &rt5645_if2_adc_in_mux), @@ -1732,14 +1724,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { &rt5645_if1_dac2_tdm_sel_mux), SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_if1_dac3_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0, - &rt5650_if1_dac0_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0, - &rt5650_if1_dac1_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0, - &rt5650_if1_dac2_tdm_sel_mux), - SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0, - &rt5650_if1_dac3_tdm_sel_mux), SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -1881,6 +1865,24 @@ static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = { 0, 0, &rt5650_a_dac2_l_mux), SND_SOC_DAPM_MUX("A DAC2 R Mux", SND_SOC_NOPM, 0, 0, &rt5650_a_dac2_r_mux), + + SND_SOC_DAPM_MUX("RT5650 IF1 ADC1 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc1_in_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 ADC2 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc2_in_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 ADC3 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc3_in_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 ADC Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc_in_mux), + + SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac0_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac1_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac2_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac3_tdm_sel_mux), }; static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { @@ -2761,6 +2763,7 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); if (enable) { + snd_soc_dapm_mutex_lock(&codec->dapm); snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, "ADC L power"); snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, @@ -2770,6 +2773,8 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, "Mic Det Power"); snd_soc_dapm_sync_unlocked(&codec->dapm); + snd_soc_dapm_mutex_unlock(&codec->dapm); + snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x8); snd_soc_update_bits(codec, @@ -2780,6 +2785,8 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, } else { snd_soc_update_bits(codec, RT5650_4BTN_IL_CMD2, 0x8000, 0x0); snd_soc_update_bits(codec, RT5645_INT_IRQ_ST, 0x8, 0x0); + + snd_soc_dapm_mutex_lock(&codec->dapm); snd_soc_dapm_disable_pin_unlocked(&codec->dapm, "ADC L power"); snd_soc_dapm_disable_pin_unlocked(&codec->dapm, @@ -2790,6 +2797,7 @@ static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, snd_soc_dapm_disable_pin_unlocked(&codec->dapm, "Mic Det Power"); snd_soc_dapm_sync_unlocked(&codec->dapm); + snd_soc_dapm_mutex_unlock(&codec->dapm); } } @@ -2935,19 +2943,16 @@ static int rt5645_irq_detection(struct rt5645_priv *rt5645) { int val, btn_type, gpio_state = 0, report = 0; + if (!rt5645->codec) + return -EINVAL; + switch (rt5645->pdata.jd_mode) { case 0: /* Not using rt5645 JD */ - if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) { - gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio); - dev_dbg(rt5645->codec->dev, "gpio = %d(%d)\n", - rt5645->pdata.hp_det_gpio, gpio_state); - } - if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) || - (!rt5645->pdata.gpio_hp_det_active_high && - !gpio_state)) { - report = rt5645_jack_detect(rt5645->codec, 1); - } else { - report = rt5645_jack_detect(rt5645->codec, 0); + if (rt5645->gpiod_hp_det) { + gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); + dev_dbg(rt5645->codec->dev, "gpio_state = %d\n", + gpio_state); + report = rt5645_jack_detect(rt5645->codec, gpio_state); } snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); @@ -3230,6 +3235,20 @@ static struct dmi_system_id dmi_platform_intel_braswell[] = { { } }; +static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev) +{ + rt5645->pdata.in2_diff = device_property_read_bool(dev, + "realtek,in2-differential"); + device_property_read_u32(dev, + "realtek,dmic1-data-pin", &rt5645->pdata.dmic1_data_pin); + device_property_read_u32(dev, + "realtek,dmic2-data-pin", &rt5645->pdata.dmic2_data_pin); + device_property_read_u32(dev, + "realtek,jd-mode", &rt5645->pdata.jd_mode); + + return 0; +} + static int rt5645_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -3237,7 +3256,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, struct rt5645_priv *rt5645; int ret; unsigned int val; - struct gpio_desc *gpiod; rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv), GFP_KERNEL); @@ -3247,22 +3265,19 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, rt5645->i2c = i2c; i2c_set_clientdata(i2c, rt5645); - if (pdata) { + if (pdata) rt5645->pdata = *pdata; - } else { - if (dmi_check_system(dmi_platform_intel_braswell)) { - rt5645->pdata = *rt5645_pdata; - gpiod = devm_gpiod_get_index(&i2c->dev, "rt5645", 0); + else if (dmi_check_system(dmi_platform_intel_braswell)) + rt5645->pdata = *rt5645_pdata; + else + rt5645_parse_dt(rt5645, &i2c->dev); - if (IS_ERR(gpiod) || gpiod_direction_input(gpiod)) { - rt5645->pdata.hp_det_gpio = -1; - dev_err(&i2c->dev, "failed to initialize gpiod\n"); - } else { - rt5645->pdata.hp_det_gpio = desc_to_gpio(gpiod); - rt5645->pdata.gpio_hp_det_active_high - = !gpiod_is_active_low(gpiod); - } - } + rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect", + GPIOD_IN); + + if (IS_ERR(rt5645->gpiod_hp_det)) { + dev_err(&i2c->dev, "failed to initialize gpiod\n"); + return PTR_ERR(rt5645->gpiod_hp_det); } rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap); @@ -3426,16 +3441,6 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); } - if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) { - ret = gpio_request(rt5645->pdata.hp_det_gpio, "rt5645"); - if (ret) - dev_err(&i2c->dev, "Fail gpio_request hp_det_gpio\n"); - - ret = gpio_direction_input(rt5645->pdata.hp_det_gpio); - if (ret) - dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n"); - } - return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645, rt5645_dai, ARRAY_SIZE(rt5645_dai)); } @@ -3449,9 +3454,6 @@ static int rt5645_i2c_remove(struct i2c_client *i2c) cancel_delayed_work_sync(&rt5645->jack_detect_work); - if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) - gpio_free(rt5645->pdata.hp_det_gpio); - snd_soc_unregister_codec(&i2c->dev); return 0; diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index 9ec4e899795d..0353a6a273ab 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -2182,6 +2182,7 @@ struct rt5645_priv { struct rt5645_platform_data pdata; struct regmap *regmap; struct i2c_client *i2c; + struct gpio_desc *gpiod_hp_det; struct snd_soc_jack *hp_jack; struct snd_soc_jack *mic_jack; struct snd_soc_jack *btn_jack; diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 840dd6d0003a..a9123d414178 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -51,12 +51,11 @@ static const struct regmap_range_cfg rt5670_ranges[] = { .window_len = 0x1, }, }; -static struct reg_default init_list[] = { +static const struct reg_default init_list[] = { { RT5670_PR_BASE + 0x14, 0x9a8a }, { RT5670_PR_BASE + 0x38, 0x3ba1 }, { RT5670_PR_BASE + 0x3d, 0x3640 }, }; -#define RT5670_INIT_REG_LEN ARRAY_SIZE(init_list) static const struct reg_default rt5670_reg[] = { { 0x00, 0x0000 }, @@ -2809,7 +2808,7 @@ static const struct i2c_device_id rt5670_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id); #ifdef CONFIG_ACPI -static struct acpi_device_id rt5670_acpi_match[] = { +static const struct acpi_device_id rt5670_acpi_match[] = { { "10EC5670", 0}, { }, }; diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h index bd7a344bf8c5..1c317de26176 100644 --- a/sound/soc/codecs/sgtl5000.h +++ b/sound/soc/codecs/sgtl5000.h @@ -275,7 +275,7 @@ #define SGTL5000_BIAS_CTRL_MASK 0x000e #define SGTL5000_BIAS_CTRL_SHIFT 1 #define SGTL5000_BIAS_CTRL_WIDTH 3 -#define SGTL5000_SMALL_POP 0 +#define SGTL5000_SMALL_POP 1 /* * SGTL5000_CHIP_MIC_CTRL diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index 938d2cb6d78b..84a4f5ad8064 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -315,7 +315,13 @@ static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) if (invert_fclk) ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC; - return regmap_write(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1, ctrl1); + return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1, + SSM4567_SAI_CTRL_1_BCLK | + SSM4567_SAI_CTRL_1_FSYNC | + SSM4567_SAI_CTRL_1_LJ | + SSM4567_SAI_CTRL_1_TDM | + SSM4567_SAI_CTRL_1_PDM, + ctrl1); } static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable) diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 891e2c529df3..4f25a7d0efa2 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -45,7 +45,7 @@ static struct reg_default tas2552_reg_defs[] = { {TAS2552_OUTPUT_DATA, 0xc0}, {TAS2552_PDM_CFG, 0x01}, {TAS2552_PGA_GAIN, 0x00}, - {TAS2552_BOOST_PT_CTRL, 0x0f}, + {TAS2552_BOOST_APT_CTRL, 0x0f}, {TAS2552_RESERVED_0D, 0xbe}, {TAS2552_LIMIT_RATE_HYS, 0x08}, {TAS2552_CFG_2, 0xef}, @@ -77,7 +77,9 @@ struct tas2552_data { struct gpio_desc *enable_gpio; unsigned char regs[TAS2552_VBAT_DATA]; unsigned int pll_clkin; + int pll_clk_id; unsigned int pdm_clk; + int pdm_clk_id; unsigned int dai_fmt; unsigned int tdm_delay; @@ -143,31 +145,105 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = { }; #ifdef CONFIG_PM -static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown) +static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown) { u8 cfg1_reg = 0; - if (!tas_data->codec) + if (!tas2552->codec) return; if (sw_shutdown) cfg1_reg = TAS2552_SWS; - snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS, + snd_soc_update_bits(tas2552->codec, TAS2552_CFG_1, TAS2552_SWS, cfg1_reg); } #endif +static int tas2552_setup_pll(struct snd_soc_codec *codec, + struct snd_pcm_hw_params *params) +{ + struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); + bool bypass_pll = false; + unsigned int pll_clk = params_rate(params) * 512; + unsigned int pll_clkin = tas2552->pll_clkin; + u8 pll_enable; + + if (!pll_clkin) { + if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK) + return -EINVAL; + + pll_clkin = snd_soc_params_to_bclk(params); + pll_clkin += tas2552->tdm_delay; + } + + pll_enable = snd_soc_read(codec, TAS2552_CFG_2) & TAS2552_PLL_ENABLE; + snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); + + if (pll_clkin == pll_clk) + bypass_pll = true; + + if (bypass_pll) { + /* By pass the PLL configuration */ + snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, + TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS); + } else { + /* Fill in the PLL control registers for J & D + * pll_clk = (.5 * pll_clkin * J.D) / 2^p + * Need to fill in J and D here based on incoming freq + */ + unsigned int d; + u8 j; + u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK; + u8 p = snd_soc_read(codec, TAS2552_PLL_CTRL_1); + + p = (p >> 7); + +recalc: + j = (pll_clk * 2 * (1 << p)) / pll_clkin; + d = (pll_clk * 2 * (1 << p)) % pll_clkin; + d /= (pll_clkin / 10000); + + if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) { + if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) { + pll_clkin = 1800000; + pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) & + TAS2552_PLL_SRC_MASK; + } else { + pll_clkin = snd_soc_params_to_bclk(params); + pll_clkin += tas2552->tdm_delay; + pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) & + TAS2552_PLL_SRC_MASK; + } + goto recalc; + } + + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK, + pll_sel); + + snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, + TAS2552_PLL_J_MASK, j); + /* Will clear the PLL_BYPASS bit */ + snd_soc_write(codec, TAS2552_PLL_CTRL_2, + TAS2552_PLL_D_UPPER(d)); + snd_soc_write(codec, TAS2552_PLL_CTRL_3, + TAS2552_PLL_D_LOWER(d)); + } + + /* Restore PLL status */ + snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, + pll_enable); + + return 0; +} + static int tas2552_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); - int sample_rate, pll_clk; - int d; int cpf; - u8 p, j; u8 ser_ctrl1_reg, wclk_rate; switch (params_width(params)) { @@ -245,49 +321,7 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK, wclk_rate); - if (!tas2552->pll_clkin) - return -EINVAL; - - snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); - - if (tas2552->pll_clkin == TAS2552_245MHZ_CLK || - tas2552->pll_clkin == TAS2552_225MHZ_CLK) { - /* By pass the PLL configuration */ - snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2, - TAS2552_PLL_BYPASS_MASK, - TAS2552_PLL_BYPASS); - } else { - /* Fill in the PLL control registers for J & D - * PLL_CLK = (.5 * freq * J.D) / 2^p - * Need to fill in J and D here based on incoming freq - */ - p = snd_soc_read(codec, TAS2552_PLL_CTRL_1); - p = (p >> 7); - sample_rate = params_rate(params); - - if (sample_rate == 48000) - pll_clk = TAS2552_245MHZ_CLK; - else if (sample_rate == 44100) - pll_clk = TAS2552_225MHZ_CLK; - else { - dev_vdbg(codec->dev, "Substream sample rate is not found %i\n", - params_rate(params)); - return -EINVAL; - } - - j = (pll_clk * 2 * (1 << p)) / tas2552->pll_clkin; - d = (pll_clk * 2 * (1 << p)) % tas2552->pll_clkin; - - snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1, - TAS2552_PLL_J_MASK, j); - snd_soc_write(codec, TAS2552_PLL_CTRL_2, - (d >> 7) & TAS2552_PLL_D_UPPER_MASK); - snd_soc_write(codec, TAS2552_PLL_CTRL_3, - d & TAS2552_PLL_D_LOWER_MASK); - - } - - return 0; + return tas2552_setup_pll(codec, params); } #define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ @@ -370,12 +404,21 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, switch (clk_id) { case TAS2552_PLL_CLKIN_MCLK: - case TAS2552_PLL_CLKIN_BCLK: case TAS2552_PLL_CLKIN_IVCLKIN: + if (freq < 512000 || freq > 24576000) { + /* out of range PLL_CLKIN, fall back to use BCLK */ + dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n", + freq); + clk_id = TAS2552_PLL_CLKIN_BCLK; + freq = 0; + } + /* fall through */ + case TAS2552_PLL_CLKIN_BCLK: case TAS2552_PLL_CLKIN_1_8_FIXED: mask = TAS2552_PLL_SRC_MASK; val = (clk_id << 3) & mask; /* bit 4:5 in the register */ reg = TAS2552_CFG_1; + tas2552->pll_clk_id = clk_id; tas2552->pll_clkin = freq; break; case TAS2552_PDM_CLK_PLL: @@ -385,6 +428,7 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, mask = TAS2552_PDM_CLK_SEL_MASK; val = (clk_id >> 1) & mask; /* bit 0:1 in the register */ reg = TAS2552_PDM_CFG; + tas2552->pdm_clk_id = clk_id; tas2552->pdm_clk = freq; break; default: @@ -509,9 +553,20 @@ static struct snd_soc_dai_driver tas2552_dai[] = { */ static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0); +static const char * const tas2552_din_source_select[] = { + "Muted", + "Left", + "Right", + "Left + Right average", +}; +static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum, + TAS2552_CFG_3, 3, + tas2552_din_source_select); + static const struct snd_kcontrol_new tas2552_snd_controls[] = { SOC_SINGLE_TLV("Speaker Driver Playback Volume", TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv), + SOC_ENUM("DIN source", tas2552_din_source_enum), }; static int tas2552_codec_probe(struct snd_soc_codec *codec) @@ -543,13 +598,14 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE); snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL | TAS2552_DIN_SRC_SEL_AVG_L_R); - snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I); - snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8); - snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 | - TAS2552_APT_THRESH_2_1_7); + snd_soc_write(codec, TAS2552_OUTPUT_DATA, + TAS2552_PDM_DATA_SEL_V_I | + TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA)); + snd_soc_write(codec, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 | + TAS2552_APT_THRESH_20_17); - snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | - TAS2552_APT_EN | TAS2552_LIM_EN); + snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN | + TAS2552_LIM_EN); return 0; @@ -647,13 +703,10 @@ static int tas2552_probe(struct i2c_client *client, if (data == NULL) return -ENOMEM; - data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(data->enable_gpio)) { - if (PTR_ERR(data->enable_gpio) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - data->enable_gpio = NULL;; - } + data->enable_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(data->enable_gpio)) + return PTR_ERR(data->enable_gpio); data->tas2552_client = client; data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); @@ -695,6 +748,7 @@ static int tas2552_probe(struct i2c_client *client, static int tas2552_i2c_remove(struct i2c_client *client) { snd_soc_unregister_codec(&client->dev); + pm_runtime_disable(&client->dev); return 0; } diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h index bbb820495516..5746f8fd0afd 100644 --- a/sound/soc/codecs/tas2552.h +++ b/sound/soc/codecs/tas2552.h @@ -19,7 +19,7 @@ #define __TAS2552_H__ /* Register Address Map */ -#define TAS2552_DEVICE_STATUS 0x00 +#define TAS2552_DEVICE_STATUS 0x00 #define TAS2552_CFG_1 0x01 #define TAS2552_CFG_2 0x02 #define TAS2552_CFG_3 0x03 @@ -33,13 +33,13 @@ #define TAS2552_BTIP 0x0b #define TAS2552_BTS_CTRL 0x0c #define TAS2552_RESERVED_0D 0x0d -#define TAS2552_LIMIT_RATE_HYS 0x0e -#define TAS2552_LIMIT_RELEASE 0x0f -#define TAS2552_LIMIT_INT_COUNT 0x10 +#define TAS2552_LIMIT_RATE_HYS 0x0e +#define TAS2552_LIMIT_RELEASE 0x0f +#define TAS2552_LIMIT_INT_COUNT 0x10 #define TAS2552_PDM_CFG 0x11 #define TAS2552_PGA_GAIN 0x12 -#define TAS2552_EDGE_RATE_CTRL 0x13 -#define TAS2552_BOOST_PT_CTRL 0x14 +#define TAS2552_EDGE_RATE_CTRL 0x13 +#define TAS2552_BOOST_APT_CTRL 0x14 #define TAS2552_VER_NUM 0x16 #define TAS2552_VBAT_DATA 0x19 #define TAS2552_MAX_REG 0x20 @@ -103,10 +103,21 @@ #define TAS2552_WCLKDIR (1 << 7) /* OUTPUT_DATA register */ -#define TAS2552_PDM_DATA_I 0x00 -#define TAS2552_PDM_DATA_V (1 << 6) -#define TAS2552_PDM_DATA_I_V (1 << 7) -#define TAS2552_PDM_DATA_V_I (0x11 << 6) +#define TAS2552_DATA_OUT_I_DATA (0x0) +#define TAS2552_DATA_OUT_V_DATA (0x1) +#define TAS2552_DATA_OUT_VBAT_DATA (0x2) +#define TAS2552_DATA_OUT_VBOOST_DATA (0x3) +#define TAS2552_DATA_OUT_PGA_GAIN (0x4) +#define TAS2552_DATA_OUT_IV_DATA (0x5) +#define TAS2552_DATA_OUT_VBAT_VBOOST_GAIN (0x6) +#define TAS2552_DATA_OUT_DISABLED (0x7) +#define TAS2552_L_DATA_OUT(x) ((x) << 0) +#define TAS2552_R_DATA_OUT(x) ((x) << 3) +#define TAS2552_PDM_DATA_SEL_I (0x0 << 6) +#define TAS2552_PDM_DATA_SEL_V (0x1 << 6) +#define TAS2552_PDM_DATA_SEL_I_V (0x2 << 6) +#define TAS2552_PDM_DATA_SEL_V_I (0x3 << 6) +#define TAS2552_PDM_DATA_SEL_MASK TAS2552_PDM_DATA_SEL_V_I /* PDM CFG Register */ #define TAS2552_PDM_CLK_SEL_PLL (0x0 << 0) @@ -116,24 +127,20 @@ #define TAS2552_PDM_CLK_SEL_MASK TAS2552_PDM_CLK_SEL_MCLK #define TAS2552_PDM_DATA_ES (1 << 2) -/* Boost pass-through register */ -#define TAS2552_APT_DELAY_50 0x00 -#define TAS2552_APT_DELAY_75 (1 << 1) -#define TAS2552_APT_DELAY_125 (1 << 2) -#define TAS2552_APT_DELAY_200 (1 << 3) - -#define TAS2552_APT_THRESH_2_5 0x00 -#define TAS2552_APT_THRESH_1_7 (1 << 3) -#define TAS2552_APT_THRESH_1_4_1_1 (1 << 4) -#define TAS2552_APT_THRESH_2_1_7 (0x11 << 2) +/* Boost Auto-pass through register */ +#define TAS2552_APT_DELAY_50 (0x0 << 0) +#define TAS2552_APT_DELAY_75 (0x1 << 0) +#define TAS2552_APT_DELAY_125 (0x2 << 0) +#define TAS2552_APT_DELAY_200 (0x3 << 0) +#define TAS2552_APT_THRESH_05_02 (0x0 << 2) +#define TAS2552_APT_THRESH_10_07 (0x1 << 2) +#define TAS2552_APT_THRESH_14_11 (0x2 << 2) +#define TAS2552_APT_THRESH_20_17 (0x3 << 2) /* PLL Control Register */ -#define TAS2552_245MHZ_CLK 24576000 -#define TAS2552_225MHZ_CLK 22579200 -#define TAS2552_PLL_J_MASK 0x7f -#define TAS2552_PLL_D_UPPER_MASK 0x3f -#define TAS2552_PLL_D_LOWER_MASK 0xff -#define TAS2552_PLL_BYPASS_MASK 0x80 -#define TAS2552_PLL_BYPASS 0x80 +#define TAS2552_PLL_J_MASK 0x7f +#define TAS2552_PLL_D_UPPER(x) (((x) >> 8) & 0x3f) +#define TAS2552_PLL_D_LOWER(x) ((x) & 0xff) +#define TAS2552_PLL_BYPASS (1 << 7) #endif diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 5a9da28f4f33..c83083285e53 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1555,7 +1555,7 @@ static int wm2200_probe(struct snd_soc_codec *codec) wm2200->codec = codec; - ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2); + ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 2); if (ret != 0) return ret; diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index d8959e31853d..d097f09e50f2 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1876,8 +1876,13 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); int ret; - ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 2); - if (ret != 0) + ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec); + if (ret) + return ret; + + ret = snd_soc_add_codec_controls(codec, + arizona_adsp2_rate_controls, 1); + if (ret) return ret; arizona_init_spk(codec); @@ -1894,6 +1899,8 @@ static int wm5102_codec_remove(struct snd_soc_codec *codec) { struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); + wm_adsp2_codec_remove(&priv->core.adsp[0], codec); + priv->core.arizona->dapm = NULL; return 0; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 14a7739d6c09..709fcc6169d8 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -1600,7 +1600,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec); - int ret; + int i, ret; priv->core.arizona->dapm = dapm; @@ -1608,8 +1608,16 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) arizona_init_gpio(codec); arizona_init_mono(codec); - ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 8); - if (ret != 0) + for (i = 0; i < WM5110_NUM_ADSP; ++i) { + ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec); + if (ret) + return ret; + } + + ret = snd_soc_add_codec_controls(codec, + arizona_adsp2_rate_controls, + WM5110_NUM_ADSP); + if (ret) return ret; snd_soc_dapm_disable_pin(dapm, "HAPTICS"); @@ -1620,6 +1628,10 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) static int wm5110_codec_remove(struct snd_soc_codec *codec) { struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec); + int i; + + for (i = 0; i < WM5110_NUM_ADSP; ++i) + wm_adsp2_codec_remove(&priv->core.adsp[i], codec); priv->core.arizona->dapm = NULL; diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 8c5b9df3e542..43ea8ae5f871 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -113,6 +113,15 @@ static struct { { 7, 1152 }, }; +static struct { + int value; + int ratio; +} bclk_ratios[WM8523_NUM_RATES] = { + { 2, 32 }, + { 3, 64 }, + { 4, 128 }, +}; + static int wm8523_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -162,6 +171,23 @@ static int wm8523_hw_params(struct snd_pcm_substream *substream, aifctrl2 &= ~WM8523_SR_MASK; aifctrl2 |= lrclk_ratios[i].value; + if (aifctrl1 & WM8523_AIF_MSTR) { + /* Find a fs->bclk ratio */ + for (i = 0; i < ARRAY_SIZE(bclk_ratios); i++) + if (params_width(params) * 2 <= bclk_ratios[i].ratio) + break; + + if (i == ARRAY_SIZE(bclk_ratios)) { + dev_err(codec->dev, + "No matching BCLK/fs ratio for word length %d\n", + params_width(params)); + return -EINVAL; + } + + aifctrl2 &= ~WM8523_BCLKDIV_MASK; + aifctrl2 |= bclk_ratios[i].value << WM8523_BCLKDIV_SHIFT; + } + aifctrl1 &= ~WM8523_WL_MASK; switch (params_width(params)) { case 16: diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 09ff01f2fc1e..b34623786e35 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -125,18 +125,6 @@ static const struct snd_soc_dapm_route wm8741_dapm_routes[] = { { "VOUTRN", NULL, "DACR" }, }; -static struct { - int value; - int ratio; -} lrclk_ratios[WM8741_NUM_RATES] = { - { 1, 128 }, - { 2, 192 }, - { 3, 256 }, - { 4, 384 }, - { 5, 512 }, - { 6, 768 }, -}; - static const unsigned int rates_11289[] = { 44100, 88200, }; @@ -209,25 +197,16 @@ static const struct snd_pcm_hw_constraint_list constraints_36864 = { .list = rates_36864, }; - static int wm8741_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); - /* The set of sample rates that can be supported depends on the - * MCLK supplied to the CODEC - enforce this. - */ - if (!wm8741->sysclk) { - dev_err(codec->dev, - "No MCLK configured, call set_sysclk() on init\n"); - return -EINVAL; - } - - snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - wm8741->sysclk_constraints); + if (wm8741->sysclk) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + wm8741->sysclk_constraints); return 0; } @@ -241,17 +220,24 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC; int i; - /* Find a supported LRCLK ratio */ - for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { - if (wm8741->sysclk / params_rate(params) == - lrclk_ratios[i].ratio) + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + if (!wm8741->sysclk) { + dev_err(codec->dev, + "No MCLK configured, call set_sysclk() on init or in hw_params\n"); + return -EINVAL; + } + + /* Find a supported LRCLK rate */ + for (i = 0; i < wm8741->sysclk_constraints->count; i++) { + if (wm8741->sysclk_constraints->list[i] == params_rate(params)) break; } - /* Should never happen, should be handled by constraints */ - if (i == ARRAY_SIZE(lrclk_ratios)) { - dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n", - wm8741->sysclk / params_rate(params)); + if (i == wm8741->sysclk_constraints->count) { + dev_err(codec->dev, "LRCLK %d unsupported with MCLK %d\n", + params_rate(params), wm8741->sysclk); return -EINVAL; } @@ -274,8 +260,8 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d", - params_width(params)); + dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d, rate param = %d", + params_width(params), params_rate(params)); snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface); return 0; @@ -290,6 +276,11 @@ static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai, dev_dbg(codec->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq); switch (freq) { + case 0: + wm8741->sysclk_constraints = NULL; + wm8741->sysclk = freq; + return 0; + case 11289600: wm8741->sysclk_constraints = &constraints_11289; wm8741->sysclk = freq; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 761418f05d59..94c5c4681ce5 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -247,7 +247,7 @@ SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), SOC_ENUM("ADC Polarity", wm8960_enum[0]), SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), -SOC_ENUM("DAC Polarity", wm8960_enum[2]), +SOC_ENUM("DAC Polarity", wm8960_enum[1]), SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, wm8960_get_deemph, wm8960_put_deemph), diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 687c4dd7ec99..505b65f5734f 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -1930,7 +1930,7 @@ static int wm8995_set_dai_sysclk(struct snd_soc_dai *dai, dai->id + 1, freq); break; case WM8995_SYSCLK_MCLK2: - wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK1; + wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK2; wm8995->mclk[1] = freq; dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n", dai->id + 1, freq); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index b62ffd0c133e..0bb415a28723 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -23,6 +23,7 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/workqueue.h> +#include <linux/debugfs.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -248,6 +249,175 @@ struct wm_coeff_ctl { unsigned int flags; }; +#ifdef CONFIG_DEBUG_FS +static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) +{ + char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); + + mutex_lock(&dsp->debugfs_lock); + kfree(dsp->wmfw_file_name); + dsp->wmfw_file_name = tmp; + mutex_unlock(&dsp->debugfs_lock); +} + +static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) +{ + char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); + + mutex_lock(&dsp->debugfs_lock); + kfree(dsp->bin_file_name); + dsp->bin_file_name = tmp; + mutex_unlock(&dsp->debugfs_lock); +} + +static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) +{ + mutex_lock(&dsp->debugfs_lock); + kfree(dsp->wmfw_file_name); + kfree(dsp->bin_file_name); + dsp->wmfw_file_name = NULL; + dsp->bin_file_name = NULL; + mutex_unlock(&dsp->debugfs_lock); +} + +static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wm_adsp *dsp = file->private_data; + ssize_t ret; + + mutex_lock(&dsp->debugfs_lock); + + if (!dsp->wmfw_file_name || !dsp->running) + ret = 0; + else + ret = simple_read_from_buffer(user_buf, count, ppos, + dsp->wmfw_file_name, + strlen(dsp->wmfw_file_name)); + + mutex_unlock(&dsp->debugfs_lock); + return ret; +} + +static ssize_t wm_adsp_debugfs_bin_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wm_adsp *dsp = file->private_data; + ssize_t ret; + + mutex_lock(&dsp->debugfs_lock); + + if (!dsp->bin_file_name || !dsp->running) + ret = 0; + else + ret = simple_read_from_buffer(user_buf, count, ppos, + dsp->bin_file_name, + strlen(dsp->bin_file_name)); + + mutex_unlock(&dsp->debugfs_lock); + return ret; +} + +static const struct { + const char *name; + const struct file_operations fops; +} wm_adsp_debugfs_fops[] = { + { + .name = "wmfw_file_name", + .fops = { + .open = simple_open, + .read = wm_adsp_debugfs_wmfw_read, + }, + }, + { + .name = "bin_file_name", + .fops = { + .open = simple_open, + .read = wm_adsp_debugfs_bin_read, + }, + }, +}; + +static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, + struct snd_soc_codec *codec) +{ + struct dentry *root = NULL; + char *root_name; + int i; + + if (!codec->component.debugfs_root) { + adsp_err(dsp, "No codec debugfs root\n"); + goto err; + } + + root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!root_name) + goto err; + + snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); + root = debugfs_create_dir(root_name, codec->component.debugfs_root); + kfree(root_name); + + if (!root) + goto err; + + if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) + goto err; + + if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id)) + goto err; + + if (!debugfs_create_x32("fw_version", S_IRUGO, 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, + &wm_adsp_debugfs_fops[i].fops)) + goto err; + } + + dsp->debugfs_root = root; + return; + +err: + debugfs_remove_recursive(root); + adsp_err(dsp, "Failed to create debugfs\n"); +} + +static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) +{ + wm_adsp_debugfs_clear(dsp); + debugfs_remove_recursive(dsp->debugfs_root); +} +#else +static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, + struct snd_soc_codec *codec) +{ +} + +static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) +{ +} + +static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, + const char *s) +{ +} + +static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, + const char *s) +{ +} + +static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) +{ +} +#endif + static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -288,52 +458,17 @@ static const struct soc_enum wm_adsp_fw_enum[] = { SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), }; -const struct snd_kcontrol_new wm_adsp1_fw_controls[] = { +const struct snd_kcontrol_new wm_adsp_fw_controls[] = { SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], wm_adsp_fw_get, wm_adsp_fw_put), SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], wm_adsp_fw_get, wm_adsp_fw_put), SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], wm_adsp_fw_get, wm_adsp_fw_put), -}; -EXPORT_SYMBOL_GPL(wm_adsp1_fw_controls); - -#if IS_ENABLED(CONFIG_SND_SOC_ARIZONA) -static const struct soc_enum wm_adsp2_rate_enum[] = { - SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1, - ARIZONA_DSP1_RATE_SHIFT, 0xf, - ARIZONA_RATE_ENUM_SIZE, - arizona_rate_text, arizona_rate_val), - SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1, - ARIZONA_DSP1_RATE_SHIFT, 0xf, - ARIZONA_RATE_ENUM_SIZE, - arizona_rate_text, arizona_rate_val), - SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1, - ARIZONA_DSP1_RATE_SHIFT, 0xf, - ARIZONA_RATE_ENUM_SIZE, - arizona_rate_text, arizona_rate_val), - SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1, - ARIZONA_DSP1_RATE_SHIFT, 0xf, - ARIZONA_RATE_ENUM_SIZE, - arizona_rate_text, arizona_rate_val), -}; - -const struct snd_kcontrol_new wm_adsp2_fw_controls[] = { - SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM("DSP1 Rate", wm_adsp2_rate_enum[0]), - SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM("DSP2 Rate", wm_adsp2_rate_enum[1]), - SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], - wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM("DSP3 Rate", wm_adsp2_rate_enum[2]), SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], wm_adsp_fw_get, wm_adsp_fw_put), - SOC_ENUM("DSP4 Rate", wm_adsp2_rate_enum[3]), }; -EXPORT_SYMBOL_GPL(wm_adsp2_fw_controls); -#endif +EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, int type) @@ -1128,6 +1263,8 @@ static int wm_adsp_load(struct wm_adsp *dsp) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, regions, pos - firmware->size); + wm_adsp_debugfs_save_wmfwname(dsp, file); + out_fw: regmap_async_complete(regmap); wm_adsp_buf_free(&buf_list); @@ -1345,11 +1482,12 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp) n_algs = be32_to_cpu(adsp2_id.n_algs); dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); + dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver); adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", dsp->fw_id, - (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, - be32_to_cpu(adsp2_id.fw.ver) & 0xff, + (dsp->fw_id_version & 0xff0000) >> 16, + (dsp->fw_id_version & 0xff00) >> 8, + dsp->fw_id_version & 0xff, n_algs); alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, @@ -1625,6 +1763,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, blocks, pos - firmware->size); + wm_adsp_debugfs_save_binname(dsp, file); + out_fw: regmap_async_complete(regmap); release_firmware(firmware); @@ -1638,6 +1778,9 @@ int wm_adsp1_init(struct wm_adsp *dsp) { INIT_LIST_HEAD(&dsp->alg_regions); +#ifdef CONFIG_DEBUG_FS + mutex_init(&dsp->debugfs_lock); +#endif return 0; } EXPORT_SYMBOL_GPL(wm_adsp1_init); @@ -1896,6 +2039,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, /* Log firmware state, it can be useful for analysis */ wm_adsp2_show_fw_status(dsp); + wm_adsp_debugfs_clear(dsp); + + dsp->fw_id = 0; + dsp->fw_id_version = 0; dsp->running = false; regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, @@ -1933,6 +2080,24 @@ err: } EXPORT_SYMBOL_GPL(wm_adsp2_event); +int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec) +{ + wm_adsp2_init_debugfs(dsp, codec); + + return snd_soc_add_codec_controls(codec, + &wm_adsp_fw_controls[dsp->num - 1], + 1); +} +EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe); + +int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec) +{ + wm_adsp2_cleanup_debugfs(dsp); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove); + int wm_adsp2_init(struct wm_adsp *dsp) { int ret; @@ -1952,6 +2117,9 @@ int wm_adsp2_init(struct wm_adsp *dsp) INIT_LIST_HEAD(&dsp->ctl_list); INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); +#ifdef CONFIG_DEBUG_FS + mutex_init(&dsp->debugfs_lock); +#endif return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_init); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 0e5f07c35d50..579a6350fb01 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -46,17 +46,26 @@ struct wm_adsp { struct list_head alg_regions; int fw_id; + int fw_id_version; const struct wm_adsp_region *mem; int num_mems; int fw; int fw_ver; - bool running; + u32 running; struct list_head ctl_list; struct work_struct boot_work; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_root; + struct mutex debugfs_lock; + char *wmfw_file_name; + char *bin_file_name; +#endif + }; #define WM_ADSP1(wname, num) \ @@ -74,11 +83,12 @@ struct wm_adsp { #define WM_ADSP2(wname, num) \ WM_ADSP2_E(wname, num, wm_adsp2_early_event) -extern const struct snd_kcontrol_new wm_adsp1_fw_controls[]; -extern const struct snd_kcontrol_new wm_adsp2_fw_controls[]; +extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; int wm_adsp1_init(struct wm_adsp *dsp); int wm_adsp2_init(struct wm_adsp *dsp); +int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec); +int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index d79349434a9a..b960e626dad9 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -686,6 +686,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, if (mcasp->serial_dir[i] == TX_MODE && tx_ser < max_active_serializers) { mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + DISMOD_LOW, DISMOD_MASK); tx_ser++; } else if (mcasp->serial_dir[i] == RX_MODE && rx_ser < max_active_serializers) { @@ -1565,6 +1567,49 @@ static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp) return ret; } +enum { + PCM_EDMA, + PCM_SDMA, +}; +static const char *sdma_prefix = "ti,omap"; + +static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) +{ + struct dma_chan *chan; + const char *tmp; + int ret = PCM_EDMA; + + if (!mcasp->dev->of_node) + return PCM_EDMA; + + tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data; + chan = dma_request_slave_channel_reason(mcasp->dev, tmp); + if (IS_ERR(chan)) { + if (PTR_ERR(chan) != -EPROBE_DEFER) + dev_err(mcasp->dev, + "Can't verify DMA configuration (%ld)\n", + PTR_ERR(chan)); + return PTR_ERR(chan); + } + BUG_ON(!chan->device || !chan->device->dev); + + if (chan->device->dev->of_node) + ret = of_property_read_string(chan->device->dev->of_node, + "compatible", &tmp); + else + dev_dbg(mcasp->dev, "DMA controller has no of-node\n"); + + dma_release_channel(chan); + if (ret) + return ret; + + dev_dbg(mcasp->dev, "DMA controller compatible = \"%s\"\n", tmp); + if (!strncmp(tmp, sdma_prefix, strlen(sdma_prefix))) + return PCM_SDMA; + + return PCM_EDMA; +} + static int davinci_mcasp_probe(struct platform_device *pdev) { struct snd_dmaengine_dai_dma_data *dma_data; @@ -1763,27 +1808,34 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (ret != 0) goto err; - switch (mcasp->version) { + ret = davinci_mcasp_get_dma_type(mcasp); + switch (ret) { + case PCM_EDMA: #if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \ (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ IS_MODULE(CONFIG_SND_EDMA_SOC)) - case MCASP_VERSION_1: - case MCASP_VERSION_2: - case MCASP_VERSION_3: ret = edma_pcm_platform_register(&pdev->dev); - break; +#else + dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n"); + ret = -EINVAL; + goto err; #endif + break; + case PCM_SDMA: #if IS_BUILTIN(CONFIG_SND_OMAP_SOC) || \ (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ IS_MODULE(CONFIG_SND_OMAP_SOC)) - case MCASP_VERSION_4: ret = omap_pcm_platform_register(&pdev->dev); - break; +#else + dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n"); + ret = -EINVAL; + goto err; #endif + break; default: - dev_err(&pdev->dev, "Invalid McASP version: %d\n", - mcasp->version); - ret = -EINVAL; + dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret); + case -EPROBE_DEFER: + goto err; break; } diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index 79dc511180bf..a3be108a8c17 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -215,7 +215,10 @@ * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits */ #define MODE(val) (val) -#define DISMOD (val)(val<<2) +#define DISMOD_3STATE (0x0) +#define DISMOD_LOW (0x2 << 2) +#define DISMOD_HIGH (0x3 << 2) +#define DISMOD_MASK DISMOD_HIGH #define TXSTATE BIT(4) #define RXSTATE BIT(5) #define SRMOD_MASK 3 diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index c7647e066cfd..c0b940e2019f 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -633,7 +633,7 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, sub *= 100000; do_div(sub, freq); - if (sub < savesub) { + if (sub < savesub && !(i == 0 && psr == 0 && div2 == 0)) { baudrate = tmprate; savesub = sub; pm = i; diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index cd146d4fa805..b38b98cae855 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -190,7 +190,7 @@ static int imx_wm8962_probe(struct platform_device *pdev) dev_err(&pdev->dev, "audmux internal port setup failed\n"); return ret; } - imx_audmux_v2_configure_port(ext_port, + ret = imx_audmux_v2_configure_port(ext_port, IMX_AUDMUX_V2_PTCR_SYN, IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); if (ret) { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index c87e58504a62..d5554939146e 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -26,6 +26,7 @@ struct simple_card_data { struct simple_dai_props { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; + unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; int gpio_hp_det; @@ -76,11 +77,18 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - unsigned int mclk; + struct simple_dai_props *dai_props = + &priv->dai_props[rtd - rtd->card->rtd]; + unsigned int mclk, mclk_fs = 0; int ret = 0; - if (priv->mclk_fs) { - mclk = params_rate(params) * priv->mclk_fs; + if (priv->mclk_fs) + mclk_fs = priv->mclk_fs; + else if (dai_props->mclk_fs) + mclk_fs = dai_props->mclk_fs; + + if (mclk_fs) { + mclk = params_rate(params) * mclk_fs; ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); } @@ -313,6 +321,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, char prop[128]; char *prefix = ""; int ret, cpu_args; + u32 val; /* For single DAI link & old style of DT node */ if (is_top_level_node) @@ -338,6 +347,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, if (ret < 0) goto dai_link_of_err; + if (!of_property_read_u32(node, "mclk-fs", &val)) + dai_props->mclk_fs = val; + ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai, &dai_link->cpu_of_node, &dai_link->cpu_dai_name, diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 791953ffbc41..f3060a4ca040 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -112,7 +112,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH config SND_SOC_INTEL_CHT_BSW_RT5645_MACH tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec" - depends on X86_INTEL_LPSS + depends on X86_INTEL_LPSS && I2C select SND_SOC_RT5645 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI @@ -123,7 +123,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec" - depends on X86_INTEL_LPSS + depends on X86_INTEL_LPSS && I2C select SND_SOC_MAX98090 select SND_SOC_TS3A227E select SND_SST_MFLD_PLATFORM diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 3853ec2ddbc7..6de5d5cd3280 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/ obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += atom/ # Machine support -obj-$(CONFIG_SND_SOC_INTEL_SST) += boards/ +obj-$(CONFIG_SND_SOC) += boards/ diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 61e240935451..31e9b9ecbb8a 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -1401,36 +1401,32 @@ static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w, down_read(&card->controls_rwsem); list_for_each_entry(kctl, &card->controls, list) { - idx = strstr(kctl->id.name, " "); + idx = strchr(kctl->id.name, ' '); if (idx == NULL) continue; - index = strlen(kctl->id.name) - strlen(idx); + index = idx - (char*)kctl->id.name; + if (strncmp(kctl->id.name, w->name, index)) + continue; - if (strstr(kctl->id.name, "Volume") && - !strncmp(kctl->id.name, w->name, index)) + if (strstr(kctl->id.name, "Volume")) ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN); - else if (strstr(kctl->id.name, "params") && - !strncmp(kctl->id.name, w->name, index)) + else if (strstr(kctl->id.name, "params")) ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO); else if (strstr(kctl->id.name, "Switch") && - !strncmp(kctl->id.name, w->name, index) && strstr(kctl->id.name, "Gain")) { struct sst_gain_mixer_control *mc = (void *)kctl->private_value; mc->w = w; - } else if (strstr(kctl->id.name, "interleaver") && - !strncmp(kctl->id.name, w->name, index)) { + } else if (strstr(kctl->id.name, "interleaver")) { struct sst_enum *e = (void *)kctl->private_value; e->w = w; - } else if (strstr(kctl->id.name, "deinterleaver") && - !strncmp(kctl->id.name, w->name, index)) { - + } else if (strstr(kctl->id.name, "deinterleaver")) { struct sst_enum *e = (void *)kctl->private_value; e->w = w; diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index 96c2e420cce6..a4b458e77089 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -368,8 +368,8 @@ static inline void sst_restore_shim64(struct intel_sst_drv *ctx, * initialize by FW or driver when firmware is loaded */ spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); - sst_shim_write64(shim, SST_IMRX, shim_regs->imrx), - sst_shim_write64(shim, SST_CSR, shim_regs->csr), + sst_shim_write64(shim, SST_IMRX, shim_regs->imrx); + sst_shim_write64(shim, SST_CSR, shim_regs->csr); spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); } diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index 7b50a9d17ec1..0e0e4d9c021f 100644 --- a/sound/soc/intel/atom/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -42,6 +42,11 @@ #define MIN_FRAGMENT_SIZE (50 * 1024) #define MAX_FRAGMENT_SIZE (1024 * 1024) #define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1) +#ifdef CONFIG_PM +#define GET_USAGE_COUNT(dev) (atomic_read(&dev->power.usage_count)) +#else +#define GET_USAGE_COUNT(dev) 1 +#endif int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id) { @@ -141,15 +146,9 @@ static int sst_power_control(struct device *dev, bool state) int ret = 0; int usage_count = 0; -#ifdef CONFIG_PM - usage_count = atomic_read(&dev->power.usage_count); -#else - usage_count = 1; -#endif - if (state == true) { ret = pm_runtime_get_sync(dev); - + usage_count = GET_USAGE_COUNT(dev); dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count); if (ret < 0) { dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret); @@ -164,6 +163,7 @@ static int sst_power_control(struct device *dev, bool state) } } } else { + usage_count = GET_USAGE_COUNT(dev); dev_dbg(ctx->dev, "Disable: pm usage count: %d\n", usage_count); return sst_pm_runtime_put(ctx); } @@ -533,7 +533,7 @@ static inline int sst_calc_tstamp(struct intel_sst_drv *ctx, info->buffer_ptr = pointer_samples / substream->runtime->channels; - info->pcm_delay = delay_frames / substream->runtime->channels; + info->pcm_delay = delay_frames; dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n", info->buffer_ptr, info->pcm_delay); return 0; diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 1be079423d1e..70f832114a5a 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -69,12 +69,12 @@ static const struct snd_soc_dapm_route cht_audio_map[] = { {"Headphone", NULL, "HPR"}, {"Ext Spk", NULL, "SPKL"}, {"Ext Spk", NULL, "SPKR"}, - {"AIF1 Playback", NULL, "ssp2 Tx"}, + {"HiFi Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, - {"ssp2 Rx", NULL, "AIF1 Capture"}, + {"ssp2 Rx", NULL, "HiFi Capture"}, }; static const struct snd_kcontrol_new cht_mc_controls[] = { @@ -101,6 +101,33 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream, return 0; } +static int cht_ti_jack_event(struct notifier_block *nb, + unsigned long event, void *data) +{ + + struct snd_soc_jack *jack = (struct snd_soc_jack *)data; + struct snd_soc_dai *codec_dai = jack->card->rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + + if (event & SND_JACK_MICROPHONE) { + + snd_soc_dapm_force_enable_pin(&codec->dapm, "SHDN"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "MICBIAS"); + snd_soc_dapm_sync(&codec->dapm); + } else { + + snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS"); + snd_soc_dapm_disable_pin(&codec->dapm, "SHDN"); + snd_soc_dapm_sync(&codec->dapm); + } + + return 0; +} + +static struct notifier_block cht_jack_nb = { + .notifier_call = cht_ti_jack_event, +}; + static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { int ret; @@ -130,6 +157,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) return ret; } + if (ctx->ts3a227e_present) + snd_soc_jack_notifier_register(jack, &cht_jack_nb); + return ret; } diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 42f293f9c6e2..67b6d3d52f57 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -263,7 +263,7 @@ static struct sst_acpi_desc sst_acpi_baytrail_desc = { .resindex_dma_base = -1, }; -static struct acpi_device_id sst_acpi_match[] = { +static const struct acpi_device_id sst_acpi_match[] = { { "INT33C8", (unsigned long)&sst_acpi_haswell_desc }, { "INT3438", (unsigned long)&sst_acpi_broadwell_desc }, { "80860F28", (unsigned long)&sst_acpi_baytrail_desc }, diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index e593e7a4b7a7..1aa819c7e09b 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -1346,7 +1346,6 @@ static void hsw_pcm_complete(struct device *dev) static int hsw_pcm_prepare(struct device *dev) { struct hsw_priv_data *pdata = dev_get_drvdata(dev); - struct sst_hsw *hsw = pdata->hsw; struct hsw_pcm_data *pcm_data; int i, err; diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig new file mode 100644 index 000000000000..15c04e2eae34 --- /dev/null +++ b/sound/soc/mediatek/Kconfig @@ -0,0 +1,30 @@ +config SND_SOC_MEDIATEK + tristate "ASoC support for Mediatek chip" + depends on ARCH_MEDIATEK + help + This adds ASoC platform driver support for Mediatek chip + that can be used with other codecs. + Select Y if you have such device. + Ex: MT8173 + +config SND_SOC_MT8173_MAX98090 + tristate "ASoC Audio driver for MT8173 with MAX98090 codec" + depends on SND_SOC_MEDIATEK + select SND_SOC_MAX98090 + help + This adds ASoC driver for Mediatek MT8173 boards + with the MAX98090 audio codec. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MT8173_RT5650_RT5676 + tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs" + depends on SND_SOC_MEDIATEK + select SND_SOC_RT5645 + select SND_SOC_RT5677 + help + This adds ASoC driver for Mediatek MT8173 boards + with the RT5650 and RT5676 codecs. + Select Y if you have such device. + If unsure select "N". + diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile new file mode 100644 index 000000000000..75effbec438d --- /dev/null +++ b/sound/soc/mediatek/Makefile @@ -0,0 +1,5 @@ +# MTK Platform Support +obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o +# Machine support +obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173-max98090.c new file mode 100644 index 000000000000..2d2536af141f --- /dev/null +++ b/sound/soc/mediatek/mt8173-max98090.c @@ -0,0 +1,222 @@ +/* + * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen <koro.chen@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <linux/gpio.h> +#include "../codecs/max98090.h" + +static struct snd_soc_jack mt8173_max98090_jack; + +static struct snd_soc_jack_pin mt8173_max98090_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_soc_dapm_widget mt8173_max98090_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_max98090_routes[] = { + {"Speaker", NULL, "SPKL"}, + {"Speaker", NULL, "SPKR"}, + {"DMICL", NULL, "Int Mic"}, + {"Headphone", NULL, "HPL"}, + {"Headphone", NULL, "HPR"}, + {"Headset Mic", NULL, "MICBIAS"}, + {"IN34", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new mt8173_max98090_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return snd_soc_dai_set_sysclk(codec_dai, 0, params_rate(params) * 256, + SND_SOC_CLOCK_IN); +} + +static struct snd_soc_ops mt8173_max98090_ops = { + .hw_params = mt8173_max98090_hw_params, +}; + +static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec; + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE, + &mt8173_max98090_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret); + return ret; + } + + ret = snd_soc_jack_add_pins(&mt8173_max98090_jack, + ARRAY_SIZE(mt8173_max98090_jack_pins), + mt8173_max98090_jack_pins); + if (ret) { + dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret); + return ret; + } + + return max98090_mic_detect(codec, &mt8173_max98090_jack); +} + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_max98090_dais[] = { + /* Front End DAI links */ + { + .name = "MAX98090 Playback", + .stream_name = "MAX98090 Playback", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "MAX98090 Capture", + .stream_name = "MAX98090 Capture", + .cpu_dai_name = "VUL", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + /* Back End DAI links */ + { + .name = "Codec", + .cpu_dai_name = "I2S", + .no_pcm = 1, + .codec_dai_name = "HiFi", + .init = mt8173_max98090_init, + .ops = &mt8173_max98090_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_card mt8173_max98090_card = { + .name = "mt8173-max98090", + .dai_link = mt8173_max98090_dais, + .num_links = ARRAY_SIZE(mt8173_max98090_dais), + .controls = mt8173_max98090_controls, + .num_controls = ARRAY_SIZE(mt8173_max98090_controls), + .dapm_widgets = mt8173_max98090_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_max98090_widgets), + .dapm_routes = mt8173_max98090_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_max98090_routes), +}; + +static int mt8173_max98090_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_max98090_card; + struct device_node *codec_node, *platform_node; + int ret, i; + + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + for (i = 0; i < card->num_links; i++) { + if (mt8173_max98090_dais[i].platform_name) + continue; + mt8173_max98090_dais[i].platform_of_node = platform_node; + } + + codec_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,audio-codec", 0); + if (!codec_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + for (i = 0; i < card->num_links; i++) { + if (mt8173_max98090_dais[i].codec_name) + continue; + mt8173_max98090_dais[i].codec_of_node = codec_node; + } + card->dev = &pdev->dev; + + ret = snd_soc_register_card(card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static int mt8173_max98090_dev_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + return 0; +} + +static const struct of_device_id mt8173_max98090_dt_match[] = { + { .compatible = "mediatek,mt8173-max98090", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_max98090_dt_match); + +static struct platform_driver mt8173_max98090_driver = { + .driver = { + .name = "mt8173-max98090", + .owner = THIS_MODULE, + .of_match_table = mt8173_max98090_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_max98090_dev_probe, + .remove = mt8173_max98090_dev_remove, +}; + +module_platform_driver(mt8173_max98090_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 MAX98090 ALSA SoC machine driver"); +MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mt8173-max98090"); + diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c new file mode 100644 index 000000000000..6f52eca05e26 --- /dev/null +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -0,0 +1,289 @@ +/* + * mt8173-rt5650-rt5676.c -- MT8173 machine driver with RT5650/5676 codecs + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen <koro.chen@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../codecs/rt5645.h" +#include "../codecs/rt5677.h" + +#define MCLK_FOR_CODECS 12288000 + +static const struct snd_soc_dapm_widget mt8173_rt5650_rt5676_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_rt5650_rt5676_routes[] = { + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ + {"Sub DMIC L1", NULL, "Int Mic"}, /* DMIC from 5676 */ + {"Sub DMIC R1", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headphone", NULL, "Sub AIF2TX"}, /* IF2 ADC to 5650 */ + {"Headset Mic", NULL, "micbias1"}, + {"Headset Mic", NULL, "micbias2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, + {"Sub AIF2RX", NULL, "Headset Mic"}, /* IF2 DAC from 5650 */ +}; + +static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + /* pll from mclk 12.288M */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, + params_rate(params) * 512); + if (ret) + return ret; + + /* sysclk from pll */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + return 0; +} + +static struct snd_soc_ops mt8173_rt5650_rt5676_ops = { + .hw_params = mt8173_rt5650_rt5676_hw_params, +}; + +static struct snd_soc_jack mt8173_rt5650_rt5676_jack; + +static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; + struct snd_soc_codec *codec_sub = runtime->codec_dais[1]->codec; + int ret; + + rt5645_sel_asrc_clk_src(codec, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + rt5677_sel_asrc_clk_src(codec_sub, + RT5677_DA_STEREO_FILTER | + RT5677_AD_STEREO1_FILTER, + RT5677_CLK_SEL_I2S1_ASRC); + rt5677_sel_asrc_clk_src(codec_sub, + RT5677_AD_STEREO2_FILTER | + RT5677_I2S2_SOURCE, + RT5677_CLK_SEL_I2S2_ASRC); + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5676_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new Headset Jack %d\n", ret); + return ret; + } + + return rt5645_set_jack_detect(codec, + &mt8173_rt5650_rt5676_jack, + &mt8173_rt5650_rt5676_jack, + &mt8173_rt5650_rt5676_jack); +} + +static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { + { + .dai_name = "rt5645-aif1", + }, + { + .dai_name = "rt5677-aif1", + }, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { + /* Front End DAI links */ + { + .name = "rt5650_rt5676 Playback", + .stream_name = "rt5650_rt5676 Playback", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "rt5650_rt5676 Capture", + .stream_name = "rt5650_rt5676 Capture", + .cpu_dai_name = "VUL", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + + /* Back End DAI links */ + { + .name = "Codec", + .cpu_dai_name = "I2S", + .no_pcm = 1, + .codecs = mt8173_rt5650_rt5676_codecs, + .num_codecs = 2, + .init = mt8173_rt5650_rt5676_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &mt8173_rt5650_rt5676_ops, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ + .name = "rt5650_rt5676 intercodec", + .stream_name = "rt5650_rt5676 intercodec", + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_dai_name = "rt5677-aif2", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + }, + +}; + +static struct snd_soc_codec_conf mt8173_rt5650_rt5676_codec_conf[] = { + { + .name_prefix = "Sub", + }, +}; + +static struct snd_soc_card mt8173_rt5650_rt5676_card = { + .name = "mtk-rt5650-rt5676", + .dai_link = mt8173_rt5650_rt5676_dais, + .num_links = ARRAY_SIZE(mt8173_rt5650_rt5676_dais), + .codec_conf = mt8173_rt5650_rt5676_codec_conf, + .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5676_codec_conf), + .controls = mt8173_rt5650_rt5676_controls, + .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5676_controls), + .dapm_widgets = mt8173_rt5650_rt5676_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5676_widgets), + .dapm_routes = mt8173_rt5650_rt5676_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5676_routes), +}; + +static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_rt5650_rt5676_card; + struct device_node *platform_node; + int i, ret; + + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + + for (i = 0; i < card->num_links; i++) { + if (mt8173_rt5650_rt5676_dais[i].platform_name) + continue; + mt8173_rt5650_rt5676_dais[i].platform_of_node = platform_node; + } + + mt8173_rt5650_rt5676_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); + if (!mt8173_rt5650_rt5676_codecs[0].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5676_codecs[1].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); + if (!mt8173_rt5650_rt5676_codecs[1].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5676_codec_conf[0].of_node = + mt8173_rt5650_rt5676_codecs[1].of_node; + + mt8173_rt5650_rt5676_dais[3].codec_of_node = + mt8173_rt5650_rt5676_codecs[1].of_node; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = snd_soc_register_card(card); + if (ret) + dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", + __func__, ret); + return ret; +} + +static int mt8173_rt5650_rt5676_dev_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + return 0; +} + +static const struct of_device_id mt8173_rt5650_rt5676_dt_match[] = { + { .compatible = "mediatek,mt8173-rt5650-rt5676", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5676_dt_match); + +static struct platform_driver mt8173_rt5650_rt5676_driver = { + .driver = { + .name = "mtk-rt5650-rt5676", + .owner = THIS_MODULE, + .of_match_table = mt8173_rt5650_rt5676_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_rt5650_rt5676_dev_probe, + .remove = mt8173_rt5650_rt5676_dev_remove, +}; + +module_platform_driver(mt8173_rt5650_rt5676_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 RT5650 and RT5676 SoC machine driver"); +MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-rt5650-rt5676"); + diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h new file mode 100644 index 000000000000..a88b17511fdf --- /dev/null +++ b/sound/soc/mediatek/mtk-afe-common.h @@ -0,0 +1,109 @@ +/* + * mtk_afe_common.h -- Mediatek audio driver common definitions + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen <koro.chen@mediatek.com> + * Sascha Hauer <s.hauer@pengutronix.de> + * Hidalgo Huang <hidalgo.huang@mediatek.com> + * Ir Lian <ir.lian@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MTK_AFE_COMMON_H_ +#define _MTK_AFE_COMMON_H_ + +#include <linux/clk.h> +#include <linux/regmap.h> + +enum { + MTK_AFE_MEMIF_DL1, + MTK_AFE_MEMIF_DL2, + MTK_AFE_MEMIF_VUL, + MTK_AFE_MEMIF_DAI, + MTK_AFE_MEMIF_AWB, + MTK_AFE_MEMIF_MOD_DAI, + MTK_AFE_MEMIF_HDMI, + MTK_AFE_MEMIF_NUM, + MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM, + MTK_AFE_IO_MOD_PCM2, + MTK_AFE_IO_PMIC, + MTK_AFE_IO_I2S, + MTK_AFE_IO_2ND_I2S, + MTK_AFE_IO_HW_GAIN1, + MTK_AFE_IO_HW_GAIN2, + MTK_AFE_IO_MRG_O, + MTK_AFE_IO_MRG_I, + MTK_AFE_IO_DAIBT, + MTK_AFE_IO_HDMI, +}; + +enum { + MTK_AFE_IRQ_1, + MTK_AFE_IRQ_2, + MTK_AFE_IRQ_3, + MTK_AFE_IRQ_4, + MTK_AFE_IRQ_5, + MTK_AFE_IRQ_6, + MTK_AFE_IRQ_7, + MTK_AFE_IRQ_8, + MTK_AFE_IRQ_NUM, +}; + +enum { + MTK_CLK_INFRASYS_AUD, + MTK_CLK_TOP_PDN_AUD, + MTK_CLK_TOP_PDN_AUD_BUS, + MTK_CLK_I2S0_M, + MTK_CLK_I2S1_M, + MTK_CLK_I2S2_M, + MTK_CLK_I2S3_M, + MTK_CLK_I2S3_B, + MTK_CLK_BCK0, + MTK_CLK_BCK1, + MTK_CLK_NUM +}; + +struct mtk_afe; +struct snd_pcm_substream; + +struct mtk_afe_memif_data { + int id; + const char *name; + int reg_ofs_base; + int reg_ofs_cur; + int fs_shift; + int mono_shift; + int enable_shift; + int irq_reg_cnt; + int irq_cnt_shift; + int irq_en_shift; + int irq_fs_shift; + int irq_clr_shift; +}; + +struct mtk_afe_memif { + unsigned int phys_buf_addr; + int buffer_size; + unsigned int hw_ptr; /* Previous IRQ's HW ptr */ + struct snd_pcm_substream *substream; + const struct mtk_afe_memif_data *data; + const struct mtk_afe_irq_data *irqdata; +}; + +struct mtk_afe { + /* address for ioremap audio hardware register */ + void __iomem *base_addr; + struct device *dev; + struct regmap *regmap; + struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM]; + struct clk *clocks[MTK_CLK_NUM]; +}; +#endif diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c new file mode 100644 index 000000000000..9863da73dfe0 --- /dev/null +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -0,0 +1,1235 @@ +/* + * Mediatek ALSA SoC AFE platform driver + * + * Copyright (c) 2015 MediaTek Inc. + * Author: Koro Chen <koro.chen@mediatek.com> + * Sascha Hauer <s.hauer@pengutronix.de> + * Hidalgo Huang <hidalgo.huang@mediatek.com> + * Ir Lian <ir.lian@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/pm_runtime.h> +#include <sound/soc.h> +#include "mtk-afe-common.h" + +/***************************************************************************** + * R E G I S T E R D E F I N I T I O N + *****************************************************************************/ +#define AUDIO_TOP_CON0 0x0000 +#define AUDIO_TOP_CON1 0x0004 +#define AFE_DAC_CON0 0x0010 +#define AFE_DAC_CON1 0x0014 +#define AFE_I2S_CON1 0x0034 +#define AFE_I2S_CON2 0x0038 +#define AFE_CONN_24BIT 0x006c + +#define AFE_CONN1 0x0024 +#define AFE_CONN2 0x0028 +#define AFE_CONN7 0x0460 +#define AFE_CONN8 0x0464 +#define AFE_HDMI_CONN0 0x0390 + +/* Memory interface */ +#define AFE_DL1_BASE 0x0040 +#define AFE_DL1_CUR 0x0044 +#define AFE_DL2_BASE 0x0050 +#define AFE_DL2_CUR 0x0054 +#define AFE_AWB_BASE 0x0070 +#define AFE_AWB_CUR 0x007c +#define AFE_VUL_BASE 0x0080 +#define AFE_VUL_CUR 0x008c +#define AFE_DAI_BASE 0x0090 +#define AFE_DAI_CUR 0x009c +#define AFE_MOD_PCM_BASE 0x0330 +#define AFE_MOD_PCM_CUR 0x033c +#define AFE_HDMI_OUT_BASE 0x0374 +#define AFE_HDMI_OUT_CUR 0x0378 + +#define AFE_ADDA2_TOP_CON0 0x0600 + +#define AFE_HDMI_OUT_CON0 0x0370 + +#define AFE_IRQ_MCU_CON 0x03a0 +#define AFE_IRQ_STATUS 0x03a4 +#define AFE_IRQ_CLR 0x03a8 +#define AFE_IRQ_CNT1 0x03ac +#define AFE_IRQ_CNT2 0x03b0 +#define AFE_IRQ_MCU_EN 0x03b4 +#define AFE_IRQ_CNT5 0x03bc +#define AFE_IRQ_CNT7 0x03dc + +#define AFE_TDM_CON1 0x0548 +#define AFE_TDM_CON2 0x054c + +#define AFE_BASE_END_OFFSET 8 +#define AFE_IRQ_STATUS_BITS 0xff + +/* AUDIO_TOP_CON0 (0x0000) */ +#define AUD_TCON0_PDN_SPDF (0x1 << 21) +#define AUD_TCON0_PDN_HDMI (0x1 << 20) +#define AUD_TCON0_PDN_24M (0x1 << 9) +#define AUD_TCON0_PDN_22M (0x1 << 8) +#define AUD_TCON0_PDN_AFE (0x1 << 2) + +/* AFE_I2S_CON1 (0x0034) */ +#define AFE_I2S_CON1_LOW_JITTER_CLK (0x1 << 12) +#define AFE_I2S_CON1_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON1_FORMAT_I2S (0x1 << 3) +#define AFE_I2S_CON1_EN (0x1 << 0) + +/* AFE_I2S_CON2 (0x0038) */ +#define AFE_I2S_CON2_LOW_JITTER_CLK (0x1 << 12) +#define AFE_I2S_CON2_RATE(x) (((x) & 0xf) << 8) +#define AFE_I2S_CON2_FORMAT_I2S (0x1 << 3) +#define AFE_I2S_CON2_EN (0x1 << 0) + +/* AFE_CONN_24BIT (0x006c) */ +#define AFE_CONN_24BIT_O04 (0x1 << 4) +#define AFE_CONN_24BIT_O03 (0x1 << 3) + +/* AFE_HDMI_CONN0 (0x0390) */ +#define AFE_HDMI_CONN0_O37_I37 (0x7 << 21) +#define AFE_HDMI_CONN0_O36_I36 (0x6 << 18) +#define AFE_HDMI_CONN0_O35_I33 (0x3 << 15) +#define AFE_HDMI_CONN0_O34_I32 (0x2 << 12) +#define AFE_HDMI_CONN0_O33_I35 (0x5 << 9) +#define AFE_HDMI_CONN0_O32_I34 (0x4 << 6) +#define AFE_HDMI_CONN0_O31_I31 (0x1 << 3) +#define AFE_HDMI_CONN0_O30_I30 (0x0 << 0) + +/* AFE_TDM_CON1 (0x0548) */ +#define AFE_TDM_CON1_LRCK_WIDTH(x) (((x) - 1) << 24) +#define AFE_TDM_CON1_32_BCK_CYCLES (0x2 << 12) +#define AFE_TDM_CON1_WLEN_32BIT (0x2 << 8) +#define AFE_TDM_CON1_MSB_ALIGNED (0x1 << 4) +#define AFE_TDM_CON1_1_BCK_DELAY (0x1 << 3) +#define AFE_TDM_CON1_BCK_INV (0x1 << 1) +#define AFE_TDM_CON1_EN (0x1 << 0) + +enum afe_tdm_ch_start { + AFE_TDM_CH_START_O30_O31 = 0, + AFE_TDM_CH_START_O32_O33, + AFE_TDM_CH_START_O34_O35, + AFE_TDM_CH_START_O36_O37, + AFE_TDM_CH_ZERO, +}; + +static const struct snd_pcm_hardware mtk_afe_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .buffer_bytes_max = 256 * 1024, + .period_bytes_min = 512, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 256, + .fifo_size = 0, +}; + +static snd_pcm_uframes_t mtk_afe_pcm_pointer + (struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + + return bytes_to_frames(substream->runtime, memif->hw_ptr); +} + +static const struct snd_pcm_ops mtk_afe_pcm_ops = { + .ioctl = snd_pcm_lib_ioctl, + .pointer = mtk_afe_pcm_pointer, +}; + +static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + size_t size; + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + + size = mtk_afe_hardware.buffer_bytes_max; + + return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + card->dev, size, size); +} + +static void mtk_afe_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static const struct snd_soc_platform_driver mtk_afe_pcm_platform = { + .ops = &mtk_afe_pcm_ops, + .pcm_new = mtk_afe_pcm_new, + .pcm_free = mtk_afe_pcm_free, +}; + +struct mtk_afe_rate { + unsigned int rate; + unsigned int regvalue; +}; + +static const struct mtk_afe_rate mtk_afe_i2s_rates[] = { + { .rate = 8000, .regvalue = 0 }, + { .rate = 11025, .regvalue = 1 }, + { .rate = 12000, .regvalue = 2 }, + { .rate = 16000, .regvalue = 4 }, + { .rate = 22050, .regvalue = 5 }, + { .rate = 24000, .regvalue = 6 }, + { .rate = 32000, .regvalue = 8 }, + { .rate = 44100, .regvalue = 9 }, + { .rate = 48000, .regvalue = 10 }, + { .rate = 88000, .regvalue = 11 }, + { .rate = 96000, .regvalue = 12 }, + { .rate = 174000, .regvalue = 13 }, + { .rate = 192000, .regvalue = 14 }, +}; + +static int mtk_afe_i2s_fs(unsigned int sample_rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++) + if (mtk_afe_i2s_rates[i].rate == sample_rate) + return mtk_afe_i2s_rates[i].regvalue; + + return -EINVAL; +} + +static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate) +{ + unsigned int val; + int fs = mtk_afe_i2s_fs(rate); + + if (fs < 0) + return -EINVAL; + + /* from external ADC */ + regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1); + + /* set input */ + val = AFE_I2S_CON2_LOW_JITTER_CLK | + AFE_I2S_CON2_RATE(fs) | + AFE_I2S_CON2_FORMAT_I2S; + + regmap_update_bits(afe->regmap, AFE_I2S_CON2, ~AFE_I2S_CON2_EN, val); + + /* set output */ + val = AFE_I2S_CON1_LOW_JITTER_CLK | + AFE_I2S_CON1_RATE(fs) | + AFE_I2S_CON1_FORMAT_I2S; + + regmap_update_bits(afe->regmap, AFE_I2S_CON1, ~AFE_I2S_CON1_EN, val); + return 0; +} + +static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable) +{ + unsigned int val; + + regmap_read(afe->regmap, AFE_I2S_CON2, &val); + if (!!(val & AFE_I2S_CON2_EN) == enable) + return; /* must skip soft reset */ + + /* I2S soft reset begin */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0x4); + + /* input */ + regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable); + + /* output */ + regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable); + + /* I2S soft reset end */ + udelay(1); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0); +} + +static int mtk_afe_dais_enable_clks(struct mtk_afe *afe, + struct clk *m_ck, struct clk *b_ck) +{ + int ret; + + if (m_ck) { + ret = clk_prepare_enable(m_ck); + if (ret) { + dev_err(afe->dev, "Failed to enable m_ck\n"); + return ret; + } + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0); + } + + if (b_ck) { + ret = clk_prepare_enable(b_ck); + if (ret) { + dev_err(afe->dev, "Failed to enable b_ck\n"); + return ret; + } + } + return 0; +} + +static int mtk_afe_dais_set_clks(struct mtk_afe *afe, + struct clk *m_ck, unsigned int mck_rate, + struct clk *b_ck, unsigned int bck_rate) +{ + int ret; + + if (m_ck) { + ret = clk_set_rate(m_ck, mck_rate); + if (ret) { + dev_err(afe->dev, "Failed to set m_ck rate\n"); + return ret; + } + } + + if (b_ck) { + ret = clk_set_rate(b_ck, bck_rate); + if (ret) { + dev_err(afe->dev, "Failed to set b_ck rate\n"); + return ret; + } + } + return 0; +} + +static void mtk_afe_dais_disable_clks(struct mtk_afe *afe, + struct clk *m_ck, struct clk *b_ck) +{ + if (m_ck) { + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, + AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M); + clk_disable_unprepare(m_ck); + } + if (b_ck) + clk_disable_unprepare(b_ck); +} + +static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + if (dai->active) + return 0; + + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + return 0; +} + +static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + if (dai->active) + return; + + mtk_afe_set_i2s_enable(afe, false); + mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + + /* disable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); +} + +static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + int ret; + + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256, + NULL, 0); + /* config I2S */ + ret = mtk_afe_set_i2s(afe, substream->runtime->rate); + if (ret) + return ret; + + mtk_afe_set_i2s_enable(afe, true); + + return 0; +} + +static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + if (dai->active) + return 0; + + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], + afe->clocks[MTK_CLK_I2S3_B]); + return 0; +} + +static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + if (dai->active) + return; + + mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M], + afe->clocks[MTK_CLK_I2S3_B]); + + /* disable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0); +} + +static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + unsigned int val; + + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128, + afe->clocks[MTK_CLK_I2S3_B], + runtime->rate * runtime->channels * 32); + + val = AFE_TDM_CON1_BCK_INV | + AFE_TDM_CON1_1_BCK_DELAY | + AFE_TDM_CON1_MSB_ALIGNED | /* I2S mode */ + AFE_TDM_CON1_WLEN_32BIT | + AFE_TDM_CON1_32_BCK_CYCLES | + AFE_TDM_CON1_LRCK_WIDTH(32); + regmap_update_bits(afe->regmap, AFE_TDM_CON1, ~AFE_TDM_CON1_EN, val); + + /* set tdm2 config */ + switch (runtime->channels) { + case 1: + case 2: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_ZERO << 4); + val |= (AFE_TDM_CH_ZERO << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 3: + case 4: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_ZERO << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 5: + case 6: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_START_O34_O35 << 8); + val |= (AFE_TDM_CH_ZERO << 12); + break; + case 7: + case 8: + val = AFE_TDM_CH_START_O30_O31; + val |= (AFE_TDM_CH_START_O32_O33 << 4); + val |= (AFE_TDM_CH_START_O34_O35 << 8); + val |= (AFE_TDM_CH_START_O36_O37 << 12); + break; + default: + val = 0; + } + regmap_update_bits(afe->regmap, AFE_TDM_CON2, 0x0000ffff, val); + + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, + 0x000000f0, runtime->channels << 4); + return 0; +} + +static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, 0); + + /* set connections: O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */ + regmap_write(afe->regmap, AFE_HDMI_CONN0, + AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 | + AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 | + AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 | + AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37); + + /* enable Out control */ + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1); + + /* enable tdm */ + regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0x1); + + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + /* disable tdm */ + regmap_update_bits(afe->regmap, AFE_TDM_CON1, 0x1, 0); + + /* disable Out control */ + regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0); + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF, + AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF); + + return 0; + default: + return -EINVAL; + } +} + +static int mtk_afe_dais_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct snd_pcm_runtime *runtime = substream->runtime; + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int ret; + + memif->substream = substream; + + snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware); + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n"); + return ret; +} + +static void mtk_afe_dais_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + + memif->substream = NULL; +} + +static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int ret; + + dev_dbg(afe->dev, + "%s period = %u, rate= %u, channels=%u\n", + __func__, params_period_size(params), params_rate(params), + params_channels(params)); + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) + return ret; + + memif->phys_buf_addr = substream->runtime->dma_addr; + memif->buffer_size = substream->runtime->dma_bytes; + memif->hw_ptr = 0; + + /* start */ + regmap_write(afe->regmap, + memif->data->reg_ofs_base, memif->phys_buf_addr); + /* end */ + regmap_write(afe->regmap, + memif->data->reg_ofs_base + AFE_BASE_END_OFFSET, + memif->phys_buf_addr + memif->buffer_size - 1); + + /* set channel */ + if (memif->data->mono_shift >= 0) { + unsigned int mono = (params_channels(params) == 1) ? 1 : 0; + + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 1 << memif->data->mono_shift, + mono << memif->data->mono_shift); + } + + /* set rate */ + if (memif->data->fs_shift < 0) + return 0; + if (memif->data->id == MTK_AFE_MEMIF_DAI || + memif->data->id == MTK_AFE_MEMIF_MOD_DAI) { + unsigned int val; + + switch (params_rate(params)) { + case 8000: + val = 0; + break; + case 16000: + val = 1; + break; + case 32000: + val = 2; + break; + default: + return -EINVAL; + } + + if (memif->data->id == MTK_AFE_MEMIF_DAI) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 0x3 << memif->data->fs_shift, + val << memif->data->fs_shift); + else + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 0x3 << memif->data->fs_shift, + val << memif->data->fs_shift); + + } else { + int fs = mtk_afe_i2s_fs(params_rate(params)); + + if (fs < 0) + return -EINVAL; + + regmap_update_bits(afe->regmap, AFE_DAC_CON1, + 0xf << memif->data->fs_shift, + fs << memif->data->fs_shift); + } + + return 0; +} + +static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int mtk_afe_dais_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + + /* enable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1); + return 0; +} + +static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime * const runtime = substream->runtime; + struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); + struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + unsigned int counter = runtime->period_size; + + dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (memif->data->enable_shift >= 0) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif->data->enable_shift, + 1 << memif->data->enable_shift); + + /* set irq counter */ + regmap_update_bits(afe->regmap, + memif->data->irq_reg_cnt, + 0x3ffff << memif->data->irq_cnt_shift, + counter << memif->data->irq_cnt_shift); + + /* set irq fs */ + if (memif->data->irq_fs_shift >= 0) { + int fs = mtk_afe_i2s_fs(runtime->rate); + + if (fs < 0) + return -EINVAL; + + regmap_update_bits(afe->regmap, + AFE_IRQ_MCU_CON, + 0xf << memif->data->irq_fs_shift, + fs << memif->data->irq_fs_shift); + } + /* enable interrupt */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, + 1 << memif->data->irq_en_shift, + 1 << memif->data->irq_en_shift); + + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (memif->data->enable_shift >= 0) + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + 1 << memif->data->enable_shift, 0); + /* disable interrupt */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON, + 1 << memif->data->irq_en_shift, + 0 << memif->data->irq_en_shift); + /* and clear pending IRQ */ + regmap_write(afe->regmap, AFE_IRQ_CLR, + 1 << memif->data->irq_clr_shift); + memif->hw_ptr = 0; + return 0; + default: + return -EINVAL; + } +} + +/* FE DAIs */ +static const struct snd_soc_dai_ops mtk_afe_dai_ops = { + .startup = mtk_afe_dais_startup, + .shutdown = mtk_afe_dais_shutdown, + .hw_params = mtk_afe_dais_hw_params, + .hw_free = mtk_afe_dais_hw_free, + .prepare = mtk_afe_dais_prepare, + .trigger = mtk_afe_dais_trigger, +}; + +/* BE DAIs */ +static const struct snd_soc_dai_ops mtk_afe_i2s_ops = { + .startup = mtk_afe_i2s_startup, + .shutdown = mtk_afe_i2s_shutdown, + .prepare = mtk_afe_i2s_prepare, +}; + +static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = { + .startup = mtk_afe_hdmi_startup, + .shutdown = mtk_afe_hdmi_shutdown, + .prepare = mtk_afe_hdmi_prepare, + .trigger = mtk_afe_hdmi_trigger, + +}; + +static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL1", /* downlink 1 */ + .id = MTK_AFE_MEMIF_DL1, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + .name = "VUL", /* voice uplink */ + .id = MTK_AFE_MEMIF_VUL, + .capture = { + .stream_name = "VUL", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + /* BE DAIs */ + .name = "I2S", + .id = MTK_AFE_IO_I2S, + .playback = { + .stream_name = "I2S Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "I2S Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_i2s_ops, + .symmetric_rates = 1, + }, +}; + +static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = { + /* FE DAIs */ + { + .name = "HDMI", + .id = MTK_AFE_MEMIF_HDMI, + .playback = { + .stream_name = "HDMI", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_dai_ops, + }, { + /* BE DAIs */ + .name = "HDMIO", + .id = MTK_AFE_IO_HDMI, + .playback = { + .stream_name = "HDMIO Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &mtk_afe_hdmi_ops, + }, +}; + +static const struct snd_kcontrol_new mtk_afe_o03_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o04_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o09_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_afe_o10_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = { + /* Backend DAIs */ + SND_SOC_DAPM_AIF_IN("I2S Capture", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S Playback", NULL, 0, SND_SOC_NOPM, 0, 0), + + /* inter-connections */ + SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, + mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)), + SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0, + mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)), + SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0, + mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)), + SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0, + mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)), +}; + +static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = { + {"I05", NULL, "DL1"}, + {"I06", NULL, "DL1"}, + {"I2S Playback", NULL, "O03"}, + {"I2S Playback", NULL, "O04"}, + {"VUL", NULL, "O09"}, + {"VUL", NULL, "O10"}, + {"I17", NULL, "I2S Capture"}, + {"I18", NULL, "I2S Capture"}, + { "O03", "I05 Switch", "I05" }, + { "O04", "I06 Switch", "I06" }, + { "O09", "I17 Switch", "I17" }, + { "O10", "I18 Switch", "I18" }, +}; + +static const struct snd_soc_dapm_widget mtk_afe_hdmi_widgets[] = { + /* Backend DAIs */ + SND_SOC_DAPM_AIF_OUT("HDMIO Playback", NULL, 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = { + {"HDMIO Playback", NULL, "HDMI"}, +}; + +static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = { + .name = "mtk-afe-pcm-dai", + .dapm_widgets = mtk_afe_pcm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets), + .dapm_routes = mtk_afe_pcm_routes, + .num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes), +}; + +static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = { + .name = "mtk-afe-hdmi-dai", + .dapm_widgets = mtk_afe_hdmi_widgets, + .num_dapm_widgets = ARRAY_SIZE(mtk_afe_hdmi_widgets), + .dapm_routes = mtk_afe_hdmi_routes, + .num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes), +}; + +static const char *aud_clks[MTK_CLK_NUM] = { + [MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk", + [MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio", + [MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus", + [MTK_CLK_I2S0_M] = "i2s0_m", + [MTK_CLK_I2S1_M] = "i2s1_m", + [MTK_CLK_I2S2_M] = "i2s2_m", + [MTK_CLK_I2S3_M] = "i2s3_m", + [MTK_CLK_I2S3_B] = "i2s3_b", + [MTK_CLK_BCK0] = "bck0", + [MTK_CLK_BCK1] = "bck1", +}; + +static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { + { + .name = "DL1", + .id = MTK_AFE_MEMIF_DL1, + .reg_ofs_base = AFE_DL1_BASE, + .reg_ofs_cur = AFE_DL1_CUR, + .fs_shift = 0, + .mono_shift = 21, + .enable_shift = 1, + .irq_reg_cnt = AFE_IRQ_CNT1, + .irq_cnt_shift = 0, + .irq_en_shift = 0, + .irq_fs_shift = 4, + .irq_clr_shift = 0, + }, { + .name = "DL2", + .id = MTK_AFE_MEMIF_DL2, + .reg_ofs_base = AFE_DL2_BASE, + .reg_ofs_cur = AFE_DL2_CUR, + .fs_shift = 4, + .mono_shift = 22, + .enable_shift = 2, + .irq_reg_cnt = AFE_IRQ_CNT1, + .irq_cnt_shift = 20, + .irq_en_shift = 2, + .irq_fs_shift = 16, + .irq_clr_shift = 2, + }, { + .name = "VUL", + .id = MTK_AFE_MEMIF_VUL, + .reg_ofs_base = AFE_VUL_BASE, + .reg_ofs_cur = AFE_VUL_CUR, + .fs_shift = 16, + .mono_shift = 27, + .enable_shift = 3, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 0, + .irq_en_shift = 1, + .irq_fs_shift = 8, + .irq_clr_shift = 1, + }, { + .name = "DAI", + .id = MTK_AFE_MEMIF_DAI, + .reg_ofs_base = AFE_DAI_BASE, + .reg_ofs_cur = AFE_DAI_CUR, + .fs_shift = 24, + .mono_shift = -1, + .enable_shift = 4, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 20, + .irq_en_shift = 3, + .irq_fs_shift = 20, + .irq_clr_shift = 3, + }, { + .name = "AWB", + .id = MTK_AFE_MEMIF_AWB, + .reg_ofs_base = AFE_AWB_BASE, + .reg_ofs_cur = AFE_AWB_CUR, + .fs_shift = 12, + .mono_shift = 24, + .enable_shift = 6, + .irq_reg_cnt = AFE_IRQ_CNT7, + .irq_cnt_shift = 0, + .irq_en_shift = 14, + .irq_fs_shift = 24, + .irq_clr_shift = 6, + }, { + .name = "MOD_DAI", + .id = MTK_AFE_MEMIF_MOD_DAI, + .reg_ofs_base = AFE_MOD_PCM_BASE, + .reg_ofs_cur = AFE_MOD_PCM_CUR, + .fs_shift = 30, + .mono_shift = 30, + .enable_shift = 7, + .irq_reg_cnt = AFE_IRQ_CNT2, + .irq_cnt_shift = 20, + .irq_en_shift = 3, + .irq_fs_shift = 20, + .irq_clr_shift = 3, + }, { + .name = "HDMI", + .id = MTK_AFE_MEMIF_HDMI, + .reg_ofs_base = AFE_HDMI_OUT_BASE, + .reg_ofs_cur = AFE_HDMI_OUT_CUR, + .fs_shift = -1, + .mono_shift = -1, + .enable_shift = -1, + .irq_reg_cnt = AFE_IRQ_CNT5, + .irq_cnt_shift = 0, + .irq_en_shift = 12, + .irq_fs_shift = -1, + .irq_clr_shift = 4, + }, +}; + +static const struct regmap_config mtk_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = AFE_ADDA2_TOP_CON0, + .cache_type = REGCACHE_NONE, +}; + +static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id) +{ + struct mtk_afe *afe = dev_id; + unsigned int reg_value, hw_ptr; + int i, ret; + + ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, ®_value); + if (ret) { + dev_err(afe->dev, "%s irq status err\n", __func__); + reg_value = AFE_IRQ_STATUS_BITS; + goto err_irq; + } + + for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) { + struct mtk_afe_memif *memif = &afe->memif[i]; + + if (!(reg_value & (1 << memif->data->irq_clr_shift))) + continue; + + ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, + &hw_ptr); + if (ret || hw_ptr == 0) { + dev_err(afe->dev, "%s hw_ptr err\n", __func__); + hw_ptr = memif->phys_buf_addr; + } + memif->hw_ptr = hw_ptr - memif->phys_buf_addr; + snd_pcm_period_elapsed(memif->substream); + } + +err_irq: + /* clear irq */ + regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS); + + return IRQ_HANDLED; +} + +static int mtk_afe_runtime_suspend(struct device *dev) +{ + struct mtk_afe *afe = dev_get_drvdata(dev); + + /* disable AFE clk */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, + AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE); + + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]); + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); + clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); + return 0; +} + +static int mtk_afe_runtime_resume(struct device *dev) +{ + struct mtk_afe *afe = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]); + if (ret) + return ret; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); + if (ret) + goto err_infra; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]); + if (ret) + goto err_top_aud_bus; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]); + if (ret) + goto err_top_aud; + + ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]); + if (ret) + goto err_bck0; + + /* enable AFE clk */ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0); + + /* set O3/O4 16bits */ + regmap_update_bits(afe->regmap, AFE_CONN_24BIT, + AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, 0); + + /* unmask all IRQs */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_EN, 0xff, 0xff); + return 0; + +err_bck0: + clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]); +err_top_aud: + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]); +err_top_aud_bus: + clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]); +err_infra: + clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]); + return ret; +} + +static int mtk_afe_init_audio_clk(struct mtk_afe *afe) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(aud_clks); i++) { + afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]); + if (IS_ERR(afe->clocks[i])) { + dev_err(afe->dev, "%s devm_clk_get %s fail\n", + __func__, aud_clks[i]); + return PTR_ERR(afe->clocks[i]); + } + } + clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */ + clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */ + return 0; +} + +static int mtk_afe_pcm_dev_probe(struct platform_device *pdev) +{ + int ret, i; + unsigned int irq_id; + struct mtk_afe *afe; + struct resource *res; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + + afe->dev = &pdev->dev; + + irq_id = platform_get_irq(pdev, 0); + if (!irq_id) { + dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name); + return -ENXIO; + } + ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler, + 0, "Afe_ISR_Handle", (void *)afe); + if (ret) { + dev_err(afe->dev, "could not request_irq\n"); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + afe->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mtk_afe_regmap_config); + if (IS_ERR(afe->regmap)) + return PTR_ERR(afe->regmap); + + /* initial audio related clock */ + ret = mtk_afe_init_audio_clk(afe); + if (ret) { + dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n"); + return ret; + } + + for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) + afe->memif[i].data = &memif_data[i]; + + platform_set_drvdata(pdev, afe); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = mtk_afe_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform); + if (ret) + goto err_pm_disable; + + ret = snd_soc_register_component(&pdev->dev, + &mtk_afe_pcm_dai_component, + mtk_afe_pcm_dais, + ARRAY_SIZE(mtk_afe_pcm_dais)); + if (ret) + goto err_platform; + + ret = snd_soc_register_component(&pdev->dev, + &mtk_afe_hdmi_dai_component, + mtk_afe_hdmi_dais, + ARRAY_SIZE(mtk_afe_hdmi_dais)); + if (ret) + goto err_comp; + + dev_info(&pdev->dev, "MTK AFE driver initialized.\n"); + return 0; + +err_comp: + snd_soc_unregister_component(&pdev->dev); +err_platform: + snd_soc_unregister_platform(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int mtk_afe_pcm_dev_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + mtk_afe_runtime_suspend(&pdev->dev); + snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id mtk_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt8173-afe-pcm", }, + { } +}; +MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match); + +static const struct dev_pm_ops mtk_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume, + NULL) +}; + +static struct platform_driver mtk_afe_pcm_driver = { + .driver = { + .name = "mtk-afe-pcm", + .owner = THIS_MODULE, + .of_match_table = mtk_afe_pcm_dt_match, + .pm = &mtk_afe_pm_ops, + }, + .probe = mtk_afe_pcm_dev_probe, + .remove = mtk_afe_pcm_dev_remove, +}; + +module_platform_driver(mtk_afe_pcm_driver); + +MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver"); +MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index 4775da4c4db5..aeef25c0cb3d 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -210,16 +210,18 @@ static int hdmi_dai_hw_params(struct snd_pcm_substream *substream, cea->db3 = 0; /* not used, all zeros */ - /* - * The OMAP HDMI IP requires to use the 8-channel channel code when - * transmitting more than two channels. - */ if (params_channels(params) == 2) cea->db4_ca = 0x0; + else if (params_channels(params) == 6) + cea->db4_ca = 0xb; else cea->db4_ca = 0x13; - cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED; + if (cea->db4_ca == 0x00) + cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED; + else + cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED; + /* the expression is trivial but makes clear what we are doing */ cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV); diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index fded99362d39..3bebfb1d3a6f 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -245,6 +245,8 @@ static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { static const struct snd_soc_dapm_route audio_map[] = { {"Ext Spk", NULL, "HPLOUT"}, {"Ext Spk", NULL, "HPROUT"}, + {"Ext Spk", NULL, "HPLCOM"}, + {"Ext Spk", NULL, "HPRCOM"}, {"Headphone Jack", NULL, "LLOUT"}, {"Headphone Jack", NULL, "RLOUT"}, {"FM Transmitter", NULL, "LLOUT"}, @@ -288,15 +290,8 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = rtd->card; struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card); - - struct snd_soc_dapm_context *dapm = &codec->dapm; int err; - /* Set up NC codec pins */ - snd_soc_dapm_nc_pin(dapm, "MIC3L"); - snd_soc_dapm_nc_pin(dapm, "MIC3R"); - snd_soc_dapm_nc_pin(dapm, "LINE1R"); - err = tpa6130a2_add_controls(codec); if (err < 0) { dev_err(card->dev, "Failed to add TPA6130A2 controls\n"); @@ -383,6 +378,7 @@ static struct snd_soc_card rx51_sound_card = { .num_aux_devs = ARRAY_SIZE(rx51_aux_dev), .codec_conf = rx51_codec_conf, .num_configs = ARRAY_SIZE(rx51_codec_conf), + .fully_routed = true, .controls = aic34_rx51_controls, .num_controls = ARRAY_SIZE(aic34_rx51_controls), diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 938144c59e2b..807fedfa1c76 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -32,3 +32,12 @@ config SND_SOC_STORM help Say Y or M if you want add support for SoC audio on the Qualcomm Technologies IPQ806X-based Storm board. + +config SND_SOC_APQ8016_SBC + tristate "SoC Audio support for APQ8016 SBC platforms" + depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST) + select SND_SOC_LPASS_APQ8016 + help + Support for Qualcomm Technologies LPASS audio block in + APQ8016 SOC-based systems. + Say Y if you want to use audio devices on MI2S. diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index ac7630833fe5..79e5c50a8f71 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -11,5 +11,7 @@ obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o # Machine snd-soc-storm-objs := storm.o +snd-soc-apq8016-sbc-objs := apq8016_sbc.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o +obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c new file mode 100644 index 000000000000..1efdf0088ecd --- /dev/null +++ b/sound/soc/qcom/apq8016_sbc.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <dt-bindings/sound/apq8016-lpass.h> + +struct apq8016_sbc_data { + void __iomem *mic_iomux; + void __iomem *spkr_iomux; + struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ +}; + +#define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17) +#define MIC_CTRL_TLMM_SCLK_EN BIT(1) +#define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16)) + +static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *card = rtd->card; + struct apq8016_sbc_data *pdata = snd_soc_card_get_drvdata(card); + int rval = 0; + + switch (cpu_dai->id) { + case MI2S_PRIMARY: + writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11, + pdata->spkr_iomux); + break; + + case MI2S_QUATERNARY: + /* Configure the Quat MI2S to TLMM */ + writel(readl(pdata->mic_iomux) | MIC_CTRL_QUA_WS_SLAVE_SEL_10 | + MIC_CTRL_TLMM_SCLK_EN, + pdata->mic_iomux); + break; + + default: + dev_err(card->dev, "unsupported cpu dai configuration\n"); + rval = -EINVAL; + break; + + } + + return rval; +} + +static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) +{ + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + struct device_node *np, *codec, *cpu, *node = dev->of_node; + struct apq8016_sbc_data *data; + int ret, num_links; + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(dev, "Error parsing card name: %d\n", ret); + return ERR_PTR(ret); + } + + /* Populate links */ + num_links = of_get_child_count(node); + + /* Allocate the private data and the DAI link array */ + data = devm_kzalloc(dev, sizeof(*data) + sizeof(*link) * num_links, + GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + card->dai_link = &data->dai_link[0]; + card->num_links = num_links; + + link = data->dai_link; + + for_each_child_of_node(node, np) { + cpu = of_get_child_by_name(np, "cpu"); + codec = of_get_child_by_name(np, "codec"); + + if (!cpu || !codec) { + dev_err(dev, "Can't find cpu/codec DT node\n"); + return ERR_PTR(-EINVAL); + } + + link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); + if (!link->cpu_of_node) { + dev_err(card->dev, "error getting cpu phandle\n"); + return ERR_PTR(-EINVAL); + } + + link->codec_of_node = of_parse_phandle(codec, "sound-dai", 0); + if (!link->codec_of_node) { + dev_err(card->dev, "error getting codec phandle\n"); + return ERR_PTR(-EINVAL); + } + + ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); + if (ret) { + dev_err(card->dev, "error getting cpu dai name\n"); + return ERR_PTR(ret); + } + + ret = snd_soc_of_get_dai_name(codec, &link->codec_dai_name); + if (ret) { + dev_err(card->dev, "error getting codec dai name\n"); + return ERR_PTR(ret); + } + + link->platform_of_node = link->cpu_of_node; + /* For now we only support playback */ + link->playback_only = true; + + ret = of_property_read_string(np, "link-name", &link->name); + if (ret) { + dev_err(card->dev, "error getting codec dai_link name\n"); + return ERR_PTR(ret); + } + + link->stream_name = link->name; + link->init = apq8016_sbc_dai_init; + link++; + } + + return data; +} + +static int apq8016_sbc_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_soc_card *card; + struct apq8016_sbc_data *data; + struct resource *res; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->dev = dev; + data = apq8016_sbc_parse_of(card); + if (IS_ERR(data)) { + dev_err(&pdev->dev, "Error resolving dai links: %ld\n", + PTR_ERR(data)); + return PTR_ERR(data); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mic-iomux"); + data->mic_iomux = devm_ioremap_resource(dev, res); + if (IS_ERR(data->mic_iomux)) + return PTR_ERR(data->mic_iomux); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spkr-iomux"); + data->spkr_iomux = devm_ioremap_resource(dev, res); + if (IS_ERR(data->spkr_iomux)) + return PTR_ERR(data->spkr_iomux); + + platform_set_drvdata(pdev, data); + snd_soc_card_set_drvdata(card, data); + + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static const struct of_device_id apq8016_sbc_device_id[] = { + { .compatible = "qcom,apq8016-sbc-sndcard" }, + {}, +}; +MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id); + +static struct platform_driver apq8016_sbc_platform_driver = { + .driver = { + .name = "qcom-apq8016-sbc", + .of_match_table = of_match_ptr(apq8016_sbc_device_id), + }, + .probe = apq8016_sbc_platform_probe, +}; +module_platform_driver(apq8016_sbc_platform_driver); + +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); +MODULE_DESCRIPTION("APQ8016 ASoC Machine Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c index b8bd296190ad..2d833bffdba0 100644 --- a/sound/soc/qcom/storm.c +++ b/sound/soc/qcom/storm.c @@ -69,11 +69,6 @@ static struct snd_soc_dai_link storm_dai_link = { .ops = &storm_soc_ops, }; -static struct snd_soc_card storm_soc_card = { - .name = "ipq806x-storm", - .dev = NULL, -}; - static int storm_parse_of(struct snd_soc_card *card) { struct snd_soc_dai_link *dai_link = card->dai_link; @@ -99,14 +94,13 @@ static int storm_parse_of(struct snd_soc_card *card) static int storm_platform_probe(struct platform_device *pdev) { - struct snd_soc_card *card = &storm_soc_card; + struct snd_soc_card *card; int ret; - if (card->dev) { - dev_err(&pdev->dev, "%s() error, existing soundcard\n", - __func__); - return -ENODEV; - } + card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + card->dev = &pdev->dev; platform_set_drvdata(pdev, card); @@ -128,16 +122,12 @@ static int storm_platform_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret == -EPROBE_DEFER) { - card->dev = NULL; - return ret; - } else if (ret) { + if (ret) dev_err(&pdev->dev, "%s() error registering soundcard: %d\n", __func__, ret); - return ret; - } - return 0; + return ret; + } #ifdef CONFIG_OF diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c index 82f582344fe7..672bcd4c252b 100644 --- a/sound/soc/sh/migor.c +++ b/sound/soc/sh/migor.c @@ -162,12 +162,11 @@ static int __init migor_init(void) if (ret < 0) return ret; - siumckb_lookup = clkdev_alloc(&siumckb_clk, "siumckb_clk", NULL); + siumckb_lookup = clkdev_create(&siumckb_clk, "siumckb_clk", NULL); if (!siumckb_lookup) { ret = -ENOMEM; goto eclkdevalloc; } - clkdev_add(siumckb_lookup); /* Port number used on this machine: port B */ migor_snd_device = platform_device_alloc("soc-audio", 1); diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index d460d2aa82ee..f1e5920654f6 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -137,15 +137,17 @@ char *rsnd_mod_name(struct rsnd_mod *mod) return mod->ops->name; } -struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod) +struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { if (!mod || !mod->ops || !mod->ops->dma_req) return NULL; - return mod->ops->dma_req(mod); + return mod->ops->dma_req(io, mod); } -int rsnd_mod_init(struct rsnd_mod *mod, +int rsnd_mod_init(struct rsnd_priv *priv, + struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, enum rsnd_mod_type type, @@ -160,6 +162,7 @@ int rsnd_mod_init(struct rsnd_mod *mod, mod->ops = ops; mod->type = type; mod->clk = clk; + mod->priv = priv; return ret; } @@ -170,10 +173,31 @@ void rsnd_mod_quit(struct rsnd_mod *mod) clk_unprepare(mod->clk); } -int rsnd_mod_is_working(struct rsnd_mod *mod) +void rsnd_mod_interrupt(struct rsnd_mod *mod, + void (*callback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io)) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dai_stream *io; + struct rsnd_dai *rdai; + int i, j; + + for_each_rsnd_dai(rdai, priv, j) { + + for (i = 0; i < RSND_MOD_MAX; i++) { + io = &rdai->playback; + if (mod == io->mod[i]) + callback(mod, io); + + io = &rdai->capture; + if (mod == io->mod[i]) + callback(mod, io); + } + } +} +int rsnd_io_is_working(struct rsnd_dai_stream *io) +{ /* see rsnd_dai_stream_init/quit() */ return !!io->substream; } @@ -181,10 +205,9 @@ int rsnd_mod_is_working(struct rsnd_mod *mod) /* * settting function */ -u32 rsnd_get_adinr(struct rsnd_mod *mod) +u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); u32 adinr = runtime->channels; @@ -207,26 +230,31 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) /* * rsnd_dai functions */ -#define __rsnd_mod_call(mod, func, param...) \ +#define __rsnd_mod_call(mod, io, func, param...) \ ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ - u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \ - u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ + u32 mask = 0xF << __rsnd_mod_shift_##func; \ + u8 val = (mod->status >> __rsnd_mod_shift_##func) & 0xF; \ + u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ - if ((mod->status & mask) == call) { \ - dev_dbg(dev, "%s[%d] %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), #func); \ - ret = (mod)->ops->func(mod, param); \ - mod->status = (mod->status & ~mask) | (~call & mask); \ + int called = 0; \ + if (val == __rsnd_mod_call_##func) { \ + called = 1; \ + ret = (mod)->ops->func(mod, io, param); \ + mod->status = (mod->status & ~mask) + \ + (add << __rsnd_mod_shift_##func); \ } \ + dev_dbg(dev, "%s[%d] 0x%08x %s\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status, \ + called ? #func : ""); \ ret; \ }) -#define rsnd_mod_call(mod, func, param...) \ +#define rsnd_mod_call(mod, io, func, param...) \ (!(mod) ? -ENODEV : \ !((mod)->ops->func) ? 0 : \ - __rsnd_mod_call(mod, func, param)) + __rsnd_mod_call(mod, io, func, param)) #define rsnd_dai_call(fn, io, param...) \ ({ \ @@ -236,7 +264,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) mod = (io)->mod[i]; \ if (!mod) \ continue; \ - ret = rsnd_mod_call(mod, fn, param); \ + ret = rsnd_mod_call(mod, io, fn, param); \ if (ret < 0) \ break; \ } \ @@ -260,7 +288,6 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, } io->mod[mod->type] = mod; - mod->io = io; return 0; } @@ -268,7 +295,6 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, static void rsnd_dai_disconnect(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - mod->io = NULL; io->mod[mod->type] = NULL; } @@ -302,7 +328,7 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional) return pos; } -void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) +bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) { io->byte_pos += byte; @@ -319,8 +345,24 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) io->next_period_byte = io->byte_per_period; } - snd_pcm_period_elapsed(substream); + return true; } + + return false; +} + +void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io) +{ + struct snd_pcm_substream *substream = io->substream; + + /* + * this function should be called... + * + * - if rsnd_dai_pointer_update() returns true + * - without spin lock + */ + + snd_pcm_period_elapsed(substream); } static void rsnd_dai_stream_init(struct rsnd_dai_stream *io, @@ -834,16 +876,18 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, } if (change) - cfg->update(mod); + cfg->update(cfg->io, mod); return change; } static int __rsnd_kctrl_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, struct rsnd_kctrl_cfg *cfg, - void (*update)(struct rsnd_mod *mod)) + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod)) { struct snd_soc_card *soc_card = rtd->card; struct snd_card *card = rtd->card->snd_card; @@ -872,6 +916,7 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod, cfg->update = update; cfg->card = card; cfg->kctrl = kctrl; + cfg->io = io; return 0; } @@ -882,36 +927,42 @@ void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg) } int rsnd_kctrl_new_m(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_m *_cfg, u32 max) { _cfg->cfg.max = max; _cfg->cfg.size = RSND_DVC_CHANNELS; _cfg->cfg.val = _cfg->val; - return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update); + return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); } int rsnd_kctrl_new_s(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_s *_cfg, u32 max) { _cfg->cfg.max = max; _cfg->cfg.size = 1; _cfg->cfg.val = &_cfg->val; - return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update); + return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); } int rsnd_kctrl_new_e(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, struct rsnd_kctrl_cfg_s *_cfg, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), const char * const *texts, u32 max) { @@ -919,7 +970,7 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod, _cfg->cfg.size = 1; _cfg->cfg.val = &_cfg->val; _cfg->cfg.texts = texts; - return __rsnd_kctrl_new(mod, rtd, name, &_cfg->cfg, update); + return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update); } /* diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 144308f15fb3..d306e298c63d 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -32,11 +32,12 @@ struct rsnd_dma_ctrl { /* * Audio DMAC */ -static void rsnd_dmaen_complete(void *data) +static void __rsnd_dmaen_complete(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_dma *dma = (struct rsnd_dma *)data; - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + bool elapsed = false; + unsigned long flags; /* * Renesas sound Gen1 needs 1 DMAC, @@ -49,23 +50,36 @@ static void rsnd_dmaen_complete(void *data) * rsnd_dai_pointer_update() will be called twice, * ant it will breaks io->byte_pos */ + spin_lock_irqsave(&priv->lock, flags); + + if (rsnd_io_is_working(io)) + elapsed = rsnd_dai_pointer_update(io, io->byte_per_period); - rsnd_dai_pointer_update(io, io->byte_per_period); + spin_unlock_irqrestore(&priv->lock, flags); + + if (elapsed) + rsnd_dai_period_elapsed(io); } -static void rsnd_dmaen_stop(struct rsnd_dma *dma) +static void rsnd_dmaen_complete(void *data) +{ + struct rsnd_mod *mod = data; + + rsnd_mod_interrupt(mod, __rsnd_dmaen_complete); +} + +static void rsnd_dmaen_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); dmaengine_terminate_all(dmaen->chan); } -static void rsnd_dmaen_start(struct rsnd_dma *dma) +static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); struct rsnd_mod *mod = rsnd_dma_to_mod(dma); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_substream *substream = io->substream; struct device *dev = rsnd_priv_to_dev(priv); struct dma_async_tx_descriptor *desc; @@ -84,7 +98,7 @@ static void rsnd_dmaen_start(struct rsnd_dma *dma) } desc->callback = rsnd_dmaen_complete; - desc->callback_param = dma; + desc->callback_param = mod; if (dmaengine_submit(desc) < 0) { dev_err(dev, "dmaengine_submit() fail\n"); @@ -115,7 +129,8 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, return chan; } -static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from, +static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { if ((!mod_from && !mod_to) || @@ -123,19 +138,19 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from, return NULL; if (mod_from) - return rsnd_mod_dma_req(mod_from); + return rsnd_mod_dma_req(io, mod_from); else - return rsnd_mod_dma_req(mod_to); + return rsnd_mod_dma_req(io, mod_to); } -static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, +static int rsnd_dmaen_init(struct rsnd_dai_stream *io, + struct rsnd_dma *dma, int id, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + struct rsnd_priv *priv = rsnd_io_to_priv(io); struct device *dev = rsnd_priv_to_dev(priv); struct dma_slave_config cfg = {}; - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_play = rsnd_io_is_play(io); int ret; @@ -145,7 +160,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, } if (dev->of_node) { - dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to); + dmaen->chan = rsnd_dmaen_request_channel(io, mod_from, mod_to); } else { dma_cap_mask_t mask; @@ -177,7 +192,7 @@ static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, return 0; rsnd_dma_init_err: - rsnd_dma_quit(dma); + rsnd_dma_quit(io, dma); rsnd_dma_channel_err: /* @@ -189,7 +204,7 @@ rsnd_dma_channel_err: return -EAGAIN; } -static void rsnd_dmaen_quit(struct rsnd_dma *dma) +static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); @@ -238,9 +253,9 @@ static const u8 gen2_id_table_cmd[] = { 0x38, /* SCU_CMD1 */ }; -static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod) +static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); @@ -268,11 +283,12 @@ static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod) return entry[id]; } -static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from, +static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { - return (rsnd_dmapp_get_id(mod_from) << 24) + - (rsnd_dmapp_get_id(mod_to) << 16); + return (rsnd_dmapp_get_id(io, mod_from) << 24) + + (rsnd_dmapp_get_id(io, mod_to) << 16); } #define rsnd_dmapp_addr(dmac, dma, reg) \ @@ -299,7 +315,7 @@ static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); } -static void rsnd_dmapp_stop(struct rsnd_dma *dma) +static void rsnd_dmapp_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { int i; @@ -312,7 +328,7 @@ static void rsnd_dmapp_stop(struct rsnd_dma *dma) } } -static void rsnd_dmapp_start(struct rsnd_dma *dma) +static void rsnd_dmapp_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); @@ -321,19 +337,21 @@ static void rsnd_dmapp_start(struct rsnd_dma *dma) rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); } -static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, +static int rsnd_dmapp_init(struct rsnd_dai_stream *io, + struct rsnd_dma *dma, int id, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) { struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); + struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); struct device *dev = rsnd_priv_to_dev(priv); dmapp->dmapp_id = dmac->dmapp_num; - dmapp->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE; + dmapp->chcr = rsnd_dmapp_get_chcr(io, mod_from, mod_to) | PDMACHCR_DE; dmac->dmapp_num++; - rsnd_dmapp_stop(dma); + rsnd_dmapp_stop(io, dma); dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); @@ -386,12 +404,12 @@ static struct rsnd_dma_ops rsnd_dmapp_ops = { #define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) static dma_addr_t -rsnd_gen2_dma_addr(struct rsnd_priv *priv, +rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod, int is_play, int is_from) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI); phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU); int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); @@ -438,7 +456,7 @@ rsnd_gen2_dma_addr(struct rsnd_priv *priv, dev_err(dev, "DVC is selected without SRC\n"); /* use SSIU or SSI ? */ - if (is_ssi && rsnd_ssi_use_busif(mod)) + if (is_ssi && rsnd_ssi_use_busif(io, mod)) is_ssi++; return (is_from) ? @@ -446,10 +464,12 @@ rsnd_gen2_dma_addr(struct rsnd_priv *priv, dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; } -static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv, +static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod, int is_play, int is_from) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); + /* * gen1 uses default DMA addr */ @@ -459,17 +479,17 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv, if (!mod) return 0; - return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); + return rsnd_gen2_dma_addr(io, mod, is_play, is_from); } #define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ static void rsnd_dma_of_path(struct rsnd_dma *dma, + struct rsnd_dai_stream *io, int is_play, struct rsnd_mod **mod_from, struct rsnd_mod **mod_to) { struct rsnd_mod *this = rsnd_dma_to_mod(dma); - struct rsnd_dai_stream *io = rsnd_mod_to_io(this); struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); @@ -524,17 +544,17 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma, } } -void rsnd_dma_stop(struct rsnd_dma *dma) +void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { - dma->ops->stop(dma); + dma->ops->stop(io, dma); } -void rsnd_dma_start(struct rsnd_dma *dma) +void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { - dma->ops->start(dma); + dma->ops->start(io, dma); } -void rsnd_dma_quit(struct rsnd_dma *dma) +void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma) { struct rsnd_mod *mod = rsnd_dma_to_mod(dma); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); @@ -543,15 +563,14 @@ void rsnd_dma_quit(struct rsnd_dma *dma) if (!dmac) return; - dma->ops->quit(dma); + dma->ops->quit(io, dma); } -int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) +int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id) { - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); struct rsnd_mod *mod_from; struct rsnd_mod *mod_to; - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); int is_play = rsnd_io_is_play(io); @@ -564,10 +583,10 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) if (!dmac) return -EAGAIN; - rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to); + rsnd_dma_of_path(dma, io, is_play, &mod_from, &mod_to); - dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1); - dma->dst_addr = rsnd_dma_addr(priv, mod_to, is_play, 0); + dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); + dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); /* for Gen2 */ if (mod_from && mod_to) @@ -579,7 +598,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) if (rsnd_is_gen1(priv)) dma->ops = &rsnd_dmaen_ops; - return dma->ops->init(priv, dma, id, mod_from, mod_to); + return dma->ops->init(io, dma, id, mod_from, mod_to); } int rsnd_dma_probe(struct platform_device *pdev, diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index e5fcb062ad77..36fc020cbc18 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -63,7 +63,8 @@ static const char * const dvc_ramp_rate[] = { "0.125 dB/8192 steps", /* 10111 */ }; -static void rsnd_dvc_volume_update(struct rsnd_mod *mod) +static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); u32 val[RSND_DVC_CHANNELS]; @@ -120,6 +121,7 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) } static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); @@ -134,9 +136,9 @@ static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, } static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(dvc_mod); struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); struct device *dev = rsnd_priv_to_dev(priv); int dvc_id = rsnd_mod_id(dvc_mod); @@ -168,10 +170,10 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, rsnd_mod_write(dvc_mod, DVC_DVUIR, 1); - rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod)); + rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod, io)); /* ch0/ch1 Volume */ - rsnd_dvc_volume_update(dvc_mod); + rsnd_dvc_volume_update(io, dvc_mod); rsnd_mod_write(dvc_mod, DVC_DVUIR, 0); @@ -181,6 +183,7 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, } static int rsnd_dvc_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { rsnd_mod_hw_stop(mod); @@ -189,6 +192,7 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod, } static int rsnd_dvc_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { rsnd_mod_write(mod, CMD_CTRL, 0x10); @@ -197,6 +201,7 @@ static int rsnd_dvc_start(struct rsnd_mod *mod, } static int rsnd_dvc_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { rsnd_mod_write(mod, CMD_CTRL, 0); @@ -205,15 +210,15 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod, } static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); int is_play = rsnd_io_is_play(io); int ret; /* Volume */ - ret = rsnd_kctrl_new_m(mod, rtd, + ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? "DVC Out Playback Volume" : "DVC In Capture Volume", rsnd_dvc_volume_update, @@ -222,7 +227,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return ret; /* Mute */ - ret = rsnd_kctrl_new_m(mod, rtd, + ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? "DVC Out Mute Switch" : "DVC In Mute Switch", rsnd_dvc_volume_update, @@ -231,7 +236,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return ret; /* Ramp */ - ret = rsnd_kctrl_new_s(mod, rtd, + ret = rsnd_kctrl_new_s(mod, io, rtd, is_play ? "DVC Out Ramp Switch" : "DVC In Ramp Switch", rsnd_dvc_volume_update, @@ -239,7 +244,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_kctrl_new_e(mod, rtd, + ret = rsnd_kctrl_new_e(mod, io, rtd, is_play ? "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", &dvc->rup, @@ -248,7 +253,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_kctrl_new_e(mod, rtd, + ret = rsnd_kctrl_new_e(mod, io, rtd, is_play ? "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", &dvc->rdown, @@ -261,7 +266,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return 0; } -static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod) +static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); @@ -366,7 +372,7 @@ int rsnd_dvc_probe(struct platform_device *pdev, dvc->info = &info->dvc_info[i]; - ret = rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops, + ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops, clk, RSND_MOD_DVC, i); if (ret) return ret; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 03ff071d012f..09fcc54a8ee0 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -165,18 +165,18 @@ void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 data); void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); -u32 rsnd_get_adinr(struct rsnd_mod *mod); +u32 rsnd_get_adinr(struct rsnd_mod *mod, struct rsnd_dai_stream *io); /* * R-Car DMA */ struct rsnd_dma; struct rsnd_dma_ops { - void (*start)(struct rsnd_dma *dma); - void (*stop)(struct rsnd_dma *dma); - int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, + void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); + void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); + int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); - void (*quit)(struct rsnd_dma *dma); + void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma); }; struct rsnd_dmaen { @@ -200,10 +200,10 @@ struct rsnd_dma { #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) -void rsnd_dma_start(struct rsnd_dma *dma); -void rsnd_dma_stop(struct rsnd_dma *dma); -int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id); -void rsnd_dma_quit(struct rsnd_dma *dma); +void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma); +void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma); +int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id); +void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma); int rsnd_dma_probe(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv); @@ -224,25 +224,35 @@ enum rsnd_mod_type { struct rsnd_mod_ops { char *name; - struct dma_chan* (*dma_req)(struct rsnd_mod *mod); + struct dma_chan* (*dma_req)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod); int (*probe)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*remove)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*init)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*quit)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*start)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*stop)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); int (*pcm_new)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd); int (*hw_params)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params); int (*fallback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv); }; @@ -252,32 +262,43 @@ struct rsnd_mod { enum rsnd_mod_type type; struct rsnd_mod_ops *ops; struct rsnd_dma dma; - struct rsnd_dai_stream *io; + struct rsnd_priv *priv; struct clk *clk; u32 status; }; /* * status * - * bit - * 0 0: probe 1: remove - * 1 0: init 1: quit - * 2 0: start 1: stop - * 3 0: pcm_new - * 4 0: fallback + * 0xH0000CBA + * + * A 0: probe 1: remove + * B 0: init 1: quit + * C 0: start 1: stop * - * 31 bit is always called (see __rsnd_mod_call) - * 31 0: hw_params + * H is always called (see __rsnd_mod_call) + * H 0: pcm_new + * H 0: fallback + * H 0: hw_params */ #define __rsnd_mod_shift_probe 0 #define __rsnd_mod_shift_remove 0 -#define __rsnd_mod_shift_init 1 -#define __rsnd_mod_shift_quit 1 -#define __rsnd_mod_shift_start 2 -#define __rsnd_mod_shift_stop 2 -#define __rsnd_mod_shift_pcm_new 3 -#define __rsnd_mod_shift_fallback 4 -#define __rsnd_mod_shift_hw_params 31 /* always called */ +#define __rsnd_mod_shift_init 4 +#define __rsnd_mod_shift_quit 4 +#define __rsnd_mod_shift_start 8 +#define __rsnd_mod_shift_stop 8 +#define __rsnd_mod_shift_pcm_new 28 /* always called */ +#define __rsnd_mod_shift_fallback 28 /* always called */ +#define __rsnd_mod_shift_hw_params 28 /* always called */ + +#define __rsnd_mod_add_probe 1 +#define __rsnd_mod_add_remove -1 +#define __rsnd_mod_add_init 1 +#define __rsnd_mod_add_quit -1 +#define __rsnd_mod_add_start 1 +#define __rsnd_mod_add_stop -1 +#define __rsnd_mod_add_pcm_new 0 +#define __rsnd_mod_add_fallback 0 +#define __rsnd_mod_add_hw_params 0 #define __rsnd_mod_call_probe 0 #define __rsnd_mod_call_remove 1 @@ -289,22 +310,25 @@ struct rsnd_mod { #define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_hw_params 0 -#define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod))) +#define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_to_dma(mod) (&(mod)->dma) -#define rsnd_mod_to_io(mod) ((mod)->io) #define rsnd_mod_id(mod) ((mod)->id) #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) #define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) -int rsnd_mod_init(struct rsnd_mod *mod, +int rsnd_mod_init(struct rsnd_priv *priv, + struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, enum rsnd_mod_type type, int id); void rsnd_mod_quit(struct rsnd_mod *mod); char *rsnd_mod_name(struct rsnd_mod *mod); -int rsnd_mod_is_working(struct rsnd_mod *mod); -struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod); +struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod); +void rsnd_mod_interrupt(struct rsnd_mod *mod, + void (*callback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io)); /* * R-Car sound DAI @@ -329,7 +353,7 @@ struct rsnd_dai_stream { #define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) #define rsnd_io_to_runtime(io) ((io)->substream ? \ (io)->substream->runtime : NULL) - +int rsnd_io_is_working(struct rsnd_dai_stream *io); struct rsnd_dai { char name[RSND_DAI_NAME_SIZE]; @@ -355,7 +379,8 @@ struct rsnd_dai { struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id); -void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); /* @@ -459,7 +484,8 @@ struct rsnd_kctrl_cfg { unsigned int size; u32 *val; const char * const *texts; - void (*update)(struct rsnd_mod *mod); + void (*update)(struct rsnd_dai_stream *io, struct rsnd_mod *mod); + struct rsnd_dai_stream *io; struct snd_card *card; struct snd_kcontrol *kctrl; }; @@ -479,22 +505,28 @@ void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg); #define rsnd_kctrl_remove(_cfg) _rsnd_kctrl_remove(&((_cfg).cfg)) int rsnd_kctrl_new_m(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_m *_cfg, u32 max); int rsnd_kctrl_new_s(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), struct rsnd_kctrl_cfg_s *_cfg, u32 max); int rsnd_kctrl_new_e(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, const unsigned char *name, struct rsnd_kctrl_cfg_s *_cfg, - void (*update)(struct rsnd_mod *mod), + void (*update)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod), const char * const *texts, u32 max); @@ -511,8 +543,10 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct snd_pcm_runtime *runtime); int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, + struct rsnd_dai_stream *io, int use_busif); -int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod); +int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, + struct rsnd_dai_stream *io); int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod); int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod); @@ -529,7 +563,7 @@ void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); -int rsnd_ssi_use_busif(struct rsnd_mod *mod); +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod); /* * R-Car DVC diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index 050b0dbcee65..84e935711e29 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -45,61 +45,48 @@ static const struct of_device_id rsrc_card_of_match[] = { }; MODULE_DEVICE_TABLE(of, rsrc_card_of_match); +#define DAI_NAME_NUM 32 struct rsrc_card_dai { - const char *name; unsigned int fmt; unsigned int sysclk; struct clk *clk; + char dai_name[DAI_NAME_NUM]; }; -#define RSRC_FB_NUM 2 /* FE/BE */ #define IDX_CPU 0 #define IDX_CODEC 1 struct rsrc_card_priv { struct snd_soc_card snd_card; - struct rsrc_card_dai_props { - struct rsrc_card_dai cpu_dai; - struct rsrc_card_dai codec_dai; - } dai_props[RSRC_FB_NUM]; struct snd_soc_codec_conf codec_conf; - struct snd_soc_dai_link dai_link[RSRC_FB_NUM]; + struct rsrc_card_dai *dai_props; + struct snd_soc_dai_link *dai_link; + int dai_num; u32 convert_rate; }; #define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) -#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i) -#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + i) +#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) +#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i)) #define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data) static int rsrc_card_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct rsrc_card_dai_props *dai_props = - &priv->dai_props[rtd - rtd->card->rtd]; - int ret; - - ret = clk_prepare_enable(dai_props->cpu_dai.clk); - if (ret) - return ret; + struct rsrc_card_dai *dai_props = + rsrc_priv_to_props(priv, rtd - rtd->card->rtd); - ret = clk_prepare_enable(dai_props->codec_dai.clk); - if (ret) - clk_disable_unprepare(dai_props->cpu_dai.clk); - - return ret; + return clk_prepare_enable(dai_props->clk); } static void rsrc_card_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct rsrc_card_dai_props *dai_props = - &priv->dai_props[rtd - rtd->card->rtd]; - - clk_disable_unprepare(dai_props->cpu_dai.clk); + struct rsrc_card_dai *dai_props = + rsrc_priv_to_props(priv, rtd - rtd->card->rtd); - clk_disable_unprepare(dai_props->codec_dai.clk); + clk_disable_unprepare(dai_props->clk); } static struct snd_soc_ops rsrc_card_ops = { @@ -107,21 +94,31 @@ static struct snd_soc_ops rsrc_card_ops = { .shutdown = rsrc_card_shutdown, }; -static int __rsrc_card_dai_init(struct snd_soc_dai *dai, - struct rsrc_card_dai *set) +static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) { + struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai; + struct snd_soc_dai_link *dai_link; + struct rsrc_card_dai *dai_props; + int num = rtd - rtd->card->rtd; int ret; - if (set->fmt) { - ret = snd_soc_dai_set_fmt(dai, set->fmt); + dai_link = rsrc_priv_to_link(priv, num); + dai_props = rsrc_priv_to_props(priv, num); + dai = dai_link->dynamic ? + rtd->cpu_dai : + rtd->codec_dai; + + if (dai_props->fmt) { + ret = snd_soc_dai_set_fmt(dai, dai_props->fmt); if (ret && ret != -ENOTSUPP) { dev_err(dai->dev, "set_fmt error\n"); goto err; } } - if (set->sysclk) { - ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); + if (dai_props->sysclk) { + ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0); if (ret && ret != -ENOTSUPP) { dev_err(dai->dev, "set_sysclk error\n"); goto err; @@ -134,27 +131,6 @@ err: return ret; } -static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *codec = rtd->codec_dai; - struct snd_soc_dai *cpu = rtd->cpu_dai; - struct rsrc_card_dai_props *dai_props; - int num, ret; - - num = rtd - rtd->card->rtd; - dai_props = &priv->dai_props[num]; - ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai); - if (ret < 0) - return ret; - - ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai); - if (ret < 0) - return ret; - - return 0; -} - static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -170,40 +146,47 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int -rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, - struct device_node *np, - struct rsrc_card_dai *dai, - struct snd_soc_dai_link *dai_link, - int *args_count) +static int rsrc_card_parse_daifmt(struct device_node *node, + struct device_node *np, + struct rsrc_card_priv *priv, + int idx, bool is_fe) { - struct device *dev = rsrc_priv_to_dev(priv); - const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); - struct of_phandle_args args; - struct device_node **p_node; - struct clk *clk; - const char **dai_name; - const char **name; - u32 val; - int ret; + struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); + struct device_node *bitclkmaster = NULL; + struct device_node *framemaster = NULL; + struct device_node *codec = is_fe ? NULL : np; + unsigned int daifmt; - if (args_count) { - p_node = &dai_link->cpu_of_node; - dai_name = &dai_link->cpu_dai_name; - name = &dai_link->cpu_name; - } else { - p_node = &dai_link->codec_of_node; - dai_name = &dai_link->codec_dai_name; - name = &dai_link->codec_name; - } + daifmt = snd_soc_of_parse_daifmt(node, NULL, + &bitclkmaster, &framemaster); + daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - if (!np) { - /* use snd-soc-dummy */ - *p_node = NULL; - *dai_name = "snd-soc-dummy-dai"; - *name = "snd-soc-dummy"; - return 0; - } + if (!bitclkmaster && !framemaster) + return -EINVAL; + + if (codec == bitclkmaster) + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; + else + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; + + dai_props->fmt = daifmt; + + of_node_put(bitclkmaster); + of_node_put(framemaster); + + return 0; +} + +static int rsrc_card_parse_links(struct device_node *np, + struct rsrc_card_priv *priv, + int idx, bool is_fe) +{ + struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); + struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); + struct of_phandle_args args; + int ret; /* * Get node via "sound-dai = <&phandle port>" @@ -214,31 +197,82 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, if (ret) return ret; - *p_node = args.np; + if (is_fe) { + /* BE is dummy */ + dai_link->codec_of_node = NULL; + dai_link->codec_dai_name = "snd-soc-dummy-dai"; + dai_link->codec_name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + dai_link->cpu_of_node = args.np; + snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name); + + /* set dai_name */ + snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s", + dai_link->cpu_dai_name); + + /* + * In soc_bind_dai_link() will check cpu name after + * of_node matching if dai_link has cpu_dai_name. + * but, it will never match if name was created by + * fmt_single_name() remove cpu_dai_name if cpu_args + * was 0. See: + * fmt_single_name() + * fmt_multiple_name() + */ + if (!args.args_count) + dai_link->cpu_dai_name = NULL; + } else { + struct device *dev = rsrc_priv_to_dev(priv); + const struct rsrc_card_of_data *of_data; - /* Get dai->name */ - ret = snd_soc_of_get_dai_name(np, dai_name); - if (ret < 0) - return ret; + of_data = rsrc_dev_to_of_data(dev); - /* - * FIXME - * - * rsrc assumes DPCM playback/capture - */ - dai_link->dpcm_playback = 1; - dai_link->dpcm_capture = 1; + /* FE is dummy */ + dai_link->cpu_of_node = NULL; + dai_link->cpu_dai_name = "snd-soc-dummy-dai"; + dai_link->cpu_name = "snd-soc-dummy"; - if (args_count) { - *args_count = args.args_count; - dai_link->dynamic = 1; - dai_link->dpcm_merged_format = 1; - } else { - dai_link->no_pcm = 1; - priv->codec_conf.of_node = (*p_node); - priv->codec_conf.name_prefix = of_data->prefix; + /* BE settings */ + dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; + dai_link->codec_of_node = args.np; + snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name); + + /* additional name prefix */ + priv->codec_conf.of_node = dai_link->codec_of_node; + priv->codec_conf.name_prefix = of_data->prefix; + + /* set dai_name */ + snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s", + dai_link->codec_dai_name); } + /* Simple Card assumes platform == cpu */ + dai_link->platform_of_node = dai_link->cpu_of_node; + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->name = dai_props->dai_name; + dai_link->stream_name = dai_props->dai_name; + dai_link->ops = &rsrc_card_ops; + dai_link->init = rsrc_card_dai_init; + + return 0; +} + +static int rsrc_card_parse_clk(struct device_node *np, + struct rsrc_card_priv *priv, + int idx, bool is_fe) +{ + struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); + struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); + struct clk *clk; + struct device_node *of_np = is_fe ? dai_link->cpu_of_node : + dai_link->codec_of_node; + u32 val; + /* * Parse dai->sysclk come from "clocks = <&xxx>" * (if system has common clock) @@ -247,173 +281,92 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, */ if (of_property_read_bool(np, "clocks")) { clk = of_clk_get(np, 0); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - return ret; - } + if (IS_ERR(clk)) + return PTR_ERR(clk); - dai->sysclk = clk_get_rate(clk); - dai->clk = clk; + dai_props->sysclk = clk_get_rate(clk); + dai_props->clk = clk; } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { - dai->sysclk = val; + dai_props->sysclk = val; } else { - clk = of_clk_get(args.np, 0); + clk = of_clk_get(of_np, 0); if (!IS_ERR(clk)) - dai->sysclk = clk_get_rate(clk); + dai_props->sysclk = clk_get_rate(clk); } return 0; } -static int rsrc_card_parse_daifmt(struct device_node *node, - struct rsrc_card_priv *priv, - struct device_node *codec, - int idx) -{ - struct device_node *bitclkmaster = NULL; - struct device_node *framemaster = NULL; - struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); - struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai; - struct rsrc_card_dai *codec_dai = &dai_props->codec_dai; - unsigned int daifmt; - - daifmt = snd_soc_of_parse_daifmt(node, NULL, - &bitclkmaster, &framemaster); - daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; - - if (!bitclkmaster && !framemaster) - return -EINVAL; - - if (codec == bitclkmaster) - daifmt |= (codec == framemaster) ? - SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; - else - daifmt |= (codec == framemaster) ? - SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; - - cpu_dai->fmt = daifmt; - codec_dai->fmt = daifmt; - - of_node_put(bitclkmaster); - of_node_put(framemaster); - - return 0; -} - static int rsrc_card_dai_link_of(struct device_node *node, + struct device_node *np, struct rsrc_card_priv *priv, int idx) { struct device *dev = rsrc_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); - struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); - struct device_node *cpu = NULL; - struct device_node *codec = NULL; - char *name; - char prop[128]; - int ret, cpu_args; - - cpu = of_get_child_by_name(node, "cpu"); - codec = of_get_child_by_name(node, "codec"); - - if (!cpu || !codec) { - ret = -EINVAL; - dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); - goto dai_link_of_err; - } + struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx); + bool is_fe = false; + int ret; - ret = rsrc_card_parse_daifmt(node, priv, codec, idx); - if (ret < 0) - goto dai_link_of_err; + if (0 == strcmp(np->name, "cpu")) + is_fe = true; - ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL, - &dai_props->cpu_dai, - dai_link, - &cpu_args); + ret = rsrc_card_parse_daifmt(node, np, priv, idx, is_fe); if (ret < 0) - goto dai_link_of_err; + return ret; - ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL, - &dai_props->codec_dai, - dai_link, - NULL); + ret = rsrc_card_parse_links(np, priv, idx, is_fe); if (ret < 0) - goto dai_link_of_err; - - if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { - ret = -EINVAL; - goto dai_link_of_err; - } - - /* Simple Card assumes platform == cpu */ - dai_link->platform_of_node = dai_link->cpu_of_node; - - /* DAI link name is created from CPU/CODEC dai name */ - name = devm_kzalloc(dev, - strlen(dai_link->cpu_dai_name) + - strlen(dai_link->codec_dai_name) + 2, - GFP_KERNEL); - if (!name) { - ret = -ENOMEM; - goto dai_link_of_err; - } - - sprintf(name, "%s-%s", dai_link->cpu_dai_name, - dai_link->codec_dai_name); - dai_link->name = dai_link->stream_name = name; - dai_link->ops = &rsrc_card_ops; - dai_link->init = rsrc_card_dai_init; - - if (idx == IDX_CODEC) - dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; - - dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); - dev_dbg(dev, "\tcpu : %s / %04x / %d\n", - dai_link->cpu_dai_name, - dai_props->cpu_dai.fmt, - dai_props->cpu_dai.sysclk); - dev_dbg(dev, "\tcodec : %s / %04x / %d\n", - dai_link->codec_dai_name, - dai_props->codec_dai.fmt, - dai_props->codec_dai.sysclk); + return ret; - /* - * In soc_bind_dai_link() will check cpu name after - * of_node matching if dai_link has cpu_dai_name. - * but, it will never match if name was created by - * fmt_single_name() remove cpu_dai_name if cpu_args - * was 0. See: - * fmt_single_name() - * fmt_multiple_name() - */ - if (!cpu_args) - dai_link->cpu_dai_name = NULL; + ret = rsrc_card_parse_clk(np, priv, idx, is_fe); + if (ret < 0) + return ret; -dai_link_of_err: - of_node_put(cpu); - of_node_put(codec); + dev_dbg(dev, "\t%s / %04x / %d\n", + dai_props->dai_name, + dai_props->fmt, + dai_props->sysclk); return ret; } static int rsrc_card_parse_of(struct device_node *node, - struct rsrc_card_priv *priv) + struct rsrc_card_priv *priv, + struct device *dev) { - struct device *dev = rsrc_priv_to_dev(priv); const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); + struct rsrc_card_dai *props; + struct snd_soc_dai_link *links; + struct device_node *np; int ret; - int i; + int i, num; if (!node) return -EINVAL; - /* Parse the card name from DT */ - snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); + num = of_get_child_count(node); + props = devm_kzalloc(dev, sizeof(*props) * num, GFP_KERNEL); + links = devm_kzalloc(dev, sizeof(*links) * num, GFP_KERNEL); + if (!props || !links) + return -ENOMEM; + + priv->dai_props = props; + priv->dai_link = links; + priv->dai_num = num; - /* DAPM routes */ + /* Init snd_soc_card */ + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + priv->snd_card.dai_link = priv->dai_link; + priv->snd_card.num_links = num; + priv->snd_card.codec_conf = &priv->codec_conf; + priv->snd_card.num_configs = 1; priv->snd_card.of_dapm_routes = of_data->routes; priv->snd_card.num_of_dapm_routes = of_data->num_routes; + /* Parse the card name from DT */ + snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); + /* sampling rate convert */ of_property_read_u32(node, "convert-rate", &priv->convert_rate); @@ -421,11 +374,12 @@ static int rsrc_card_parse_of(struct device_node *node, priv->snd_card.name ? priv->snd_card.name : "", priv->convert_rate); - /* FE/BE */ - for (i = 0; i < RSRC_FB_NUM; i++) { - ret = rsrc_card_dai_link_of(node, priv, i); + i = 0; + for_each_child_of_node(node, np) { + ret = rsrc_card_dai_link_of(node, np, priv, i); if (ret < 0) return ret; + i++; } if (!priv->snd_card.name) @@ -452,7 +406,6 @@ static int rsrc_card_unref(struct snd_soc_card *card) static int rsrc_card_probe(struct platform_device *pdev) { struct rsrc_card_priv *priv; - struct snd_soc_dai_link *dai_link; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; int ret; @@ -462,16 +415,7 @@ static int rsrc_card_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - /* Init snd_soc_card */ - priv->snd_card.owner = THIS_MODULE; - priv->snd_card.dev = dev; - dai_link = priv->dai_link; - priv->snd_card.dai_link = dai_link; - priv->snd_card.num_links = RSRC_FB_NUM; - priv->snd_card.codec_conf = &priv->codec_conf; - priv->snd_card.num_configs = 1; - - ret = rsrc_card_parse_of(np, priv); + ret = rsrc_card_parse_of(np, priv, dev); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(dev, "parse error %d\n", ret); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index fbe9166e26d1..c61c17180142 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -117,10 +117,10 @@ struct rsnd_src { /* * Gen1/Gen2 common functions */ -static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod) +static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_play = rsnd_io_is_play(io); return rsnd_dma_request_channel(rsnd_src_of_node(priv), @@ -129,9 +129,9 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod) } int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, + struct rsnd_dai_stream *io, int use_busif) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); int ssi_id = rsnd_mod_id(ssi_mod); @@ -174,7 +174,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, u32 mask = ~0; rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR, - rsnd_get_adinr(ssi_mod)); + rsnd_get_adinr(ssi_mod, io)); rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1); rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1); @@ -196,7 +196,8 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, return 0; } -int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod) +int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod, + struct rsnd_dai_stream *io) { /* * DMA settings for SSIU @@ -235,10 +236,9 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod) return 0; } -static u32 rsnd_src_convert_rate(struct rsnd_src *src) +static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, + struct rsnd_src *src) { - struct rsnd_mod *mod = &src->mod; - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 convert_rate; @@ -274,7 +274,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, * return convert rate if SRC is used, * otherwise, return runtime->rate as usual */ - rate = rsnd_src_convert_rate(src); + rate = rsnd_src_convert_rate(io, src); } if (!rate) @@ -283,12 +283,12 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, return rate; } -static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) +static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 fsrate = 0; if (convert_rate) @@ -299,7 +299,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) rsnd_mod_write(mod, SRC_SWRSR, 1); /* Set channel number and output bit length */ - rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod)); + rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod, io)); /* Enable the initial value of IFS */ if (fsrate) { @@ -316,6 +316,7 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) } static int rsnd_src_hw_params(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *fe_params) { @@ -372,6 +373,7 @@ static int rsnd_src_init(struct rsnd_mod *mod, } static int rsnd_src_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); @@ -411,9 +413,9 @@ static int rsnd_src_stop(struct rsnd_mod *mod) /* * Gen1 functions */ -static int rsnd_src_set_route_gen1(struct rsnd_mod *mod) +static int rsnd_src_set_route_gen1(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct src_route_config { u32 mask; int shift; @@ -448,13 +450,13 @@ static int rsnd_src_set_route_gen1(struct rsnd_mod *mod) return 0; } -static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod) +static int rsnd_src_set_convert_timing_gen1(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct rsnd_src *src = rsnd_mod_to_src(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - u32 convert_rate = rsnd_src_convert_rate(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 mask; u32 val; int shift; @@ -506,12 +508,13 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod) return 0; } -static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) +static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; - ret = rsnd_src_set_convert_rate(mod); + ret = rsnd_src_set_convert_rate(mod, io); if (ret < 0) return ret; @@ -523,7 +526,7 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); /* Gen1/Gen2 are not compatible */ - if (rsnd_src_convert_rate(src)) + if (rsnd_src_convert_rate(io, src)) rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ @@ -532,6 +535,7 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) } static int rsnd_src_init_gen1(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int ret; @@ -540,15 +544,15 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_src_set_route_gen1(mod); + ret = rsnd_src_set_route_gen1(io, mod); if (ret < 0) return ret; - ret = rsnd_src_set_convert_rate_gen1(mod); + ret = rsnd_src_set_convert_rate_gen1(mod, io); if (ret < 0) return ret; - ret = rsnd_src_set_convert_timing_gen1(mod); + ret = rsnd_src_set_convert_timing_gen1(io, mod); if (ret < 0) return ret; @@ -556,6 +560,7 @@ static int rsnd_src_init_gen1(struct rsnd_mod *mod, } static int rsnd_src_start_gen1(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int id = rsnd_mod_id(mod); @@ -566,6 +571,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod, } static int rsnd_src_stop_gen1(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int id = rsnd_mod_id(mod); @@ -643,9 +649,9 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod) return ret; } -static int _rsnd_src_start_gen2(struct rsnd_mod *mod) +static int _rsnd_src_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; rsnd_mod_write(mod, SRC_CTRL, val); @@ -670,15 +676,15 @@ static int _rsnd_src_stop_gen2(struct rsnd_mod *mod) return rsnd_src_stop(mod); } -static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) +static void __rsnd_src_interrupt_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_mod *mod = data; struct rsnd_priv *priv = rsnd_mod_to_priv(mod); spin_lock(&priv->lock); /* ignore all cases if not working */ - if (!rsnd_mod_is_working(mod)) + if (!rsnd_io_is_working(io)) goto rsnd_src_interrupt_gen2_out; if (rsnd_src_error_record_gen2(mod)) { @@ -691,24 +697,32 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) _rsnd_src_stop_gen2(mod); if (src->err < 1024) - _rsnd_src_start_gen2(mod); + _rsnd_src_start_gen2(mod, io); else dev_warn(dev, "no more SRC restart\n"); } + rsnd_src_interrupt_gen2_out: spin_unlock(&priv->lock); +} + +static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) +{ + struct rsnd_mod *mod = data; + + rsnd_mod_interrupt(mod, __rsnd_src_interrupt_gen2); return IRQ_HANDLED; } -static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) +static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 cr, route; uint ratio; int ret; @@ -726,7 +740,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) return -EINVAL; } - ret = rsnd_src_set_convert_rate(mod); + ret = rsnd_src_set_convert_rate(mod, io); if (ret < 0) return ret; @@ -762,12 +776,12 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) return 0; } -static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod) +static int rsnd_src_set_convert_timing_gen2(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); int ret; if (convert_rate) @@ -781,6 +795,7 @@ static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod) } static int rsnd_src_probe_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); @@ -802,7 +817,7 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, return ret; } - ret = rsnd_dma_init(priv, + ret = rsnd_dma_init(io, rsnd_mod_to_dma(mod), src->info->dma_id); @@ -810,14 +825,16 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, } static int rsnd_src_remove_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_dma_quit(rsnd_mod_to_dma(mod)); + rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); return 0; } static int rsnd_src_init_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int ret; @@ -826,11 +843,11 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_src_set_convert_rate_gen2(mod); + ret = rsnd_src_set_convert_rate_gen2(mod, io); if (ret < 0) return ret; - ret = rsnd_src_set_convert_timing_gen2(mod); + ret = rsnd_src_set_convert_timing_gen2(io, mod); if (ret < 0) return ret; @@ -838,31 +855,33 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, } static int rsnd_src_start_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - rsnd_dma_start(rsnd_mod_to_dma(mod)); + rsnd_dma_start(io, rsnd_mod_to_dma(mod)); - return _rsnd_src_start_gen2(mod); + return _rsnd_src_start_gen2(mod, io); } static int rsnd_src_stop_gen2(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { int ret; ret = _rsnd_src_stop_gen2(mod); - rsnd_dma_stop(rsnd_mod_to_dma(mod)); + rsnd_dma_stop(io, rsnd_mod_to_dma(mod)); return ret; } -static void rsnd_src_reconvert_update(struct rsnd_mod *mod) +static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(src); + u32 convert_rate = rsnd_src_convert_rate(io, src); u32 fsrate; if (!runtime) @@ -878,10 +897,10 @@ static void rsnd_src_reconvert_update(struct rsnd_mod *mod) } static int rsnd_src_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -912,7 +931,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, /* * enable sync convert */ - ret = rsnd_kctrl_new_s(mod, rtd, + ret = rsnd_kctrl_new_s(mod, io, rtd, rsnd_io_is_play(io) ? "SRC Out Rate Switch" : "SRC In Rate Switch", @@ -921,7 +940,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, if (ret < 0) return ret; - ret = rsnd_kctrl_new_s(mod, rtd, + ret = rsnd_kctrl_new_s(mod, io, rtd, rsnd_io_is_play(io) ? "SRC Out Rate" : "SRC In Rate", @@ -1046,7 +1065,7 @@ int rsnd_src_probe(struct platform_device *pdev, src->info = &info->src_info[i]; - ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); + ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i); if (ret) return ret; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 50fa3928a003..2fbe59f7f9b5 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -87,10 +87,9 @@ struct rsnd_ssi { #define rsnd_ssi_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") -int rsnd_ssi_use_busif(struct rsnd_mod *mod) +int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int use_busif = 0; if (!rsnd_ssi_is_dma_mode(mod)) @@ -199,15 +198,17 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, } } - cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ? - DMEN : /* DMA : enable DMA */ - DIEN; /* PIO : enable Data interrupt */ - + if (rsnd_ssi_is_dma_mode(&ssi->mod)) { + cr_mode = UIEN | OIEN | /* over/under run */ + DMEN; /* DMA : enable DMA */ + } else { + cr_mode = DIEN; /* PIO : enable Data interrupt */ + } cr = ssi->cr_own | ssi->cr_clk | cr_mode | - UIEN | OIEN | EN; + EN; rsnd_mod_write(&ssi->mod, SSICR, cr); @@ -224,10 +225,9 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod)); } -static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) +static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi) { struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(&ssi->mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct device *dev = rsnd_priv_to_dev(priv); u32 cr; @@ -261,7 +261,7 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); if (ssi_parent) - rsnd_ssi_hw_stop(ssi_parent); + rsnd_ssi_hw_stop(io, ssi_parent); else rsnd_ssi_master_clk_stop(ssi); } @@ -279,10 +279,10 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) * SSI mod common functions */ static int rsnd_ssi_init(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); u32 cr; @@ -330,6 +330,7 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, } static int rsnd_ssi_quit(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -346,6 +347,7 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, } static int rsnd_ssi_hw_params(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -369,7 +371,8 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, /* It will be removed on rsnd_ssi_hw_stop */ ssi->chan = chan; if (ssi_parent) - return rsnd_ssi_hw_params(&ssi_parent->mod, substream, params); + return rsnd_ssi_hw_params(&ssi_parent->mod, io, + substream, params); return 0; } @@ -386,12 +389,12 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) } static int rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - rsnd_src_ssiu_start(mod, rsnd_ssi_use_busif(mod)); + rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io, mod)); rsnd_ssi_hw_start(ssi, io); @@ -401,6 +404,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod, } static int rsnd_ssi_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -409,26 +413,26 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); - rsnd_ssi_hw_stop(ssi); + rsnd_ssi_hw_stop(io, ssi); - rsnd_src_ssiu_stop(mod); + rsnd_src_ssiu_stop(mod, io); return 0; } -static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) +static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { - struct rsnd_ssi *ssi = data; - struct rsnd_mod *mod = &ssi->mod; + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_dma = rsnd_ssi_is_dma_mode(mod); u32 status; + bool elapsed = false; spin_lock(&priv->lock); /* ignore all cases if not working */ - if (!rsnd_mod_is_working(mod)) + if (!rsnd_io_is_working(io)) goto rsnd_ssi_interrupt_out; status = rsnd_mod_read(mod, SSISR); @@ -449,11 +453,11 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) else *buf = rsnd_mod_read(mod, SSIRDR); - rsnd_dai_pointer_update(io, sizeof(*buf)); + elapsed = rsnd_dai_pointer_update(io, sizeof(*buf)); } - /* PIO / DMA */ - if (status & (UIRQ | OIRQ)) { + /* DMA only */ + if (is_dma && (status & (UIRQ | OIRQ))) { struct device *dev = rsnd_priv_to_dev(priv); /* @@ -462,9 +466,9 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) dev_dbg(dev, "%s[%d] restart\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - rsnd_ssi_stop(mod, priv); + rsnd_ssi_stop(mod, io, priv); if (ssi->err < 1024) - rsnd_ssi_start(mod, priv); + rsnd_ssi_start(mod, io, priv); else dev_warn(dev, "no more SSI restart\n"); } @@ -474,6 +478,16 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) rsnd_ssi_interrupt_out: spin_unlock(&priv->lock); + if (elapsed) + rsnd_dai_period_elapsed(io); +} + +static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) +{ + struct rsnd_mod *mod = data; + + rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt); + return IRQ_HANDLED; } @@ -481,6 +495,7 @@ rsnd_ssi_interrupt_out: * SSI PIO */ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); @@ -490,7 +505,7 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, ret = devm_request_irq(dev, ssi->info->irq, rsnd_ssi_interrupt, IRQF_SHARED, - dev_name(dev), ssi); + dev_name(dev), mod); return ret; } @@ -506,6 +521,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { }; static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); @@ -516,25 +532,26 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, ret = devm_request_irq(dev, ssi->info->irq, rsnd_ssi_interrupt, IRQF_SHARED, - dev_name(dev), ssi); + dev_name(dev), mod); if (ret) return ret; ret = rsnd_dma_init( - priv, rsnd_mod_to_dma(mod), + io, rsnd_mod_to_dma(mod), dma_id); return ret; } static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); int irq = ssi->info->irq; - rsnd_dma_quit(rsnd_mod_to_dma(mod)); + rsnd_dma_quit(io, rsnd_mod_to_dma(mod)); /* PIO will request IRQ again */ devm_free_irq(dev, irq, ssi); @@ -543,6 +560,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, } static int rsnd_ssi_fallback(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); @@ -563,37 +581,39 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod, } static int rsnd_ssi_dma_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - rsnd_dma_start(dma); + rsnd_dma_start(io, dma); - rsnd_ssi_start(mod, priv); + rsnd_ssi_start(mod, io, priv); return 0; } static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, struct rsnd_priv *priv) { struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - rsnd_ssi_stop(mod, priv); + rsnd_ssi_stop(mod, io, priv); - rsnd_dma_stop(dma); + rsnd_dma_stop(io, dma); return 0; } -static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod) +static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); int is_play = rsnd_io_is_play(io); char *name; - if (rsnd_ssi_use_busif(mod)) + if (rsnd_ssi_use_busif(io, mod)) name = is_play ? "rxu" : "txu"; else name = is_play ? "rx" : "tx"; @@ -776,7 +796,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, else if (rsnd_ssi_pio_available(ssi)) ops = &rsnd_ssi_pio_ops; - ret = rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i); + ret = rsnd_mod_init(priv, &ssi->mod, ops, clk, RSND_MOD_SSI, i); if (ret) return ret; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3a4a5c0e3f97..0e1e69c7abd5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1716,6 +1716,7 @@ card_probe_error: if (card->remove) card->remove(card); + snd_soc_dapm_free(&card->dapm); soc_cleanup_card_debugfs(card); snd_card_free(card->snd_card); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index aa327c92480c..e0de8072c514 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -358,9 +358,10 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, data->widget = snd_soc_dapm_new_control_unlocked(widget->dapm, &template); + kfree(name); if (!data->widget) { ret = -ENOMEM; - goto err_name; + goto err_data; } } break; @@ -389,11 +390,12 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, data->value = template.on_val; - data->widget = snd_soc_dapm_new_control(widget->dapm, - &template); + data->widget = snd_soc_dapm_new_control_unlocked( + widget->dapm, &template); + kfree(name); if (!data->widget) { ret = -ENOMEM; - goto err_name; + goto err_data; } snd_soc_dapm_add_path(widget->dapm, data->widget, @@ -408,8 +410,6 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, return 0; -err_name: - kfree(name); err_data: kfree(data); return ret; @@ -418,8 +418,6 @@ err_data: static void dapm_kcontrol_free(struct snd_kcontrol *kctl) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); - if (data->widget) - kfree(data->widget->name); kfree(data->wlist); kfree(data); } @@ -1952,6 +1950,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, size_t count, loff_t *ppos) { struct snd_soc_dapm_widget *w = file->private_data; + struct snd_soc_card *card = w->dapm->card; char *buf; int in, out; ssize_t ret; @@ -1961,6 +1960,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (!buf) return -ENOMEM; + mutex_lock(&card->dapm_mutex); + /* Supply widgets are not handled by is_connected_{input,output}_ep() */ if (w->is_supply) { in = 0; @@ -2007,6 +2008,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file, p->sink->name); } + mutex_unlock(&card->dapm_mutex); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); kfree(buf); @@ -2281,11 +2284,15 @@ static ssize_t dapm_widget_show(struct device *dev, struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); int i, count = 0; + mutex_lock(&rtd->card->dapm_mutex); + for (i = 0; i < rtd->num_codecs; i++) { struct snd_soc_codec *codec = rtd->codec_dais[i]->codec; count += dapm_widget_show_codec(codec, buf + count); } + mutex_unlock(&rtd->card->dapm_mutex); + return count; } @@ -3334,16 +3341,10 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, } prefix = soc_dapm_prefix(dapm); - if (prefix) { + if (prefix) w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name); - if (widget->sname) - w->sname = kasprintf(GFP_KERNEL, "%s %s", prefix, - widget->sname); - } else { + else w->name = kasprintf(GFP_KERNEL, "%s", widget->name); - if (widget->sname) - w->sname = kasprintf(GFP_KERNEL, "%s", widget->sname); - } if (w->name == NULL) { kfree(w); return NULL; @@ -3792,7 +3793,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) break; } - if (!w->sname || !strstr(w->sname, dai_w->name)) + if (!w->sname || !strstr(w->sname, dai_w->sname)) continue; if (dai_w->id == snd_soc_dapm_dai_in) { diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 171c4291ea21..fbaa1bb41102 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -48,7 +48,7 @@ int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); - ret = snd_jack_new(card->snd_card, id, type, &jack->jack); + ret = snd_jack_new(card->snd_card, id, type, &jack->jack, false, false); if (ret) return ret; @@ -197,6 +197,7 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, INIT_LIST_HEAD(&pins[i].list); list_add(&(pins[i].list), &jack->pins); + snd_jack_add_new_kctl(jack->jack, pins[i].pin, pins[i].mask); } /* Update to reflect the last reported status; canned jack diff --git a/sound/soc/zte/zx296702-i2s.c b/sound/soc/zte/zx296702-i2s.c index 98d96e1b17e0..1930c42e1f55 100644 --- a/sound/soc/zte/zx296702-i2s.c +++ b/sound/soc/zte/zx296702-i2s.c @@ -393,9 +393,9 @@ static int zx_i2s_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); zx_i2s->mapbase = res->start; zx_i2s->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (!zx_i2s->reg_base) { + if (IS_ERR(zx_i2s->reg_base)) { dev_err(&pdev->dev, "ioremap failed!\n"); - return -EIO; + return PTR_ERR(zx_i2s->reg_base); } writel_relaxed(0, zx_i2s->reg_base + ZX_I2S_FIFO_CTRL); diff --git a/sound/soc/zte/zx296702-spdif.c b/sound/soc/zte/zx296702-spdif.c index 11a0e46a1156..26265ce4caca 100644 --- a/sound/soc/zte/zx296702-spdif.c +++ b/sound/soc/zte/zx296702-spdif.c @@ -322,9 +322,9 @@ static int zx_spdif_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); zx_spdif->mapbase = res->start; zx_spdif->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (!zx_spdif->reg_base) { + if (IS_ERR(zx_spdif->reg_base)) { dev_err(&pdev->dev, "ioremap failed!\n"); - return -EIO; + return PTR_ERR(zx_spdif->reg_base); } zx_spdif_dev_init(zx_spdif->reg_base); diff --git a/sound/sound_firmware.c b/sound/sound_firmware.c index b155137ee312..026347643c81 100644 --- a/sound/sound_firmware.c +++ b/sound/sound_firmware.c @@ -12,7 +12,6 @@ static int do_mod_firmware_load(const char *fn, char **fp) struct file* filp; long l; char *dp; - loff_t pos; filp = filp_open(fn, 0, 0); if (IS_ERR(filp)) @@ -34,8 +33,7 @@ static int do_mod_firmware_load(const char *fn, char **fp) fput(filp); return 0; } - pos = 0; - if (vfs_read(filp, dp, l, &pos) != l) + if (kernel_read(filp, 0, dp, l) != l) { printk(KERN_INFO "Failed to read '%s'.\n", fn); vfree(dp); diff --git a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile index 328594e6152d..fb761c2c2b50 100644 --- a/sound/synth/emux/Makefile +++ b/sound/synth/emux/Makefile @@ -4,8 +4,9 @@ # snd-emux-synth-objs := emux.o emux_synth.o emux_seq.o emux_nrpn.o \ - emux_effect.o emux_proc.o emux_hwdep.o soundfont.o \ - $(if $(CONFIG_SND_SEQUENCER_OSS),emux_oss.o) + emux_effect.o emux_hwdep.o soundfont.o +snd-emux-synth-$(CONFIG_SND_PROC_FS) += emux_proc.o +snd-emux-synth-$(CONFIG_SND_SEQUENCER_OSS) += emux_oss.o # Toplevel Module Dependencies obj-$(CONFIG_SND_SBAWE_SEQ) += snd-emux-synth.o diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index 49195325fdf6..9312cd8a6fdd 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -128,9 +128,7 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch #endif snd_emux_init_virmidi(emu, card); -#ifdef CONFIG_PROC_FS snd_emux_proc_init(emu, card, index); -#endif return 0; } @@ -150,9 +148,7 @@ int snd_emux_free(struct snd_emux *emu) del_timer(&emu->tlist); spin_unlock_irqrestore(&emu->voice_lock, flags); -#ifdef CONFIG_PROC_FS snd_emux_proc_free(emu); -#endif snd_emux_delete_virmidi(emu); #ifdef CONFIG_SND_SEQUENCER_OSS snd_emux_detach_seq_oss(emu); diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c index 58a32a10d115..a82b4053bee8 100644 --- a/sound/synth/emux/emux_proc.c +++ b/sound/synth/emux/emux_proc.c @@ -24,8 +24,6 @@ #include <sound/info.h> #include "emux_voice.h" -#ifdef CONFIG_PROC_FS - static void snd_emux_proc_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buf) @@ -128,5 +126,3 @@ void snd_emux_proc_free(struct snd_emux *emu) snd_info_free_entry(emu->proc); emu->proc = NULL; } - -#endif /* CONFIG_PROC_FS */ diff --git a/sound/synth/emux/emux_voice.h b/sound/synth/emux/emux_voice.h index 09711f84ed30..a7073c371bcc 100644 --- a/sound/synth/emux/emux_voice.h +++ b/sound/synth/emux/emux_voice.h @@ -82,9 +82,13 @@ void snd_emux_init_seq_oss(struct snd_emux *emu); void snd_emux_detach_seq_oss(struct snd_emux *emu); /* emux_proc.c */ -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_SND_PROC_FS void snd_emux_proc_init(struct snd_emux *emu, struct snd_card *card, int device); void snd_emux_proc_free(struct snd_emux *emu); +#else +static inline void snd_emux_proc_init(struct snd_emux *emu, + struct snd_card *card, int device) {} +static inline void snd_emux_proc_free(struct snd_emux *emu) {} #endif #define STATE_IS_PLAYING(s) ((s) & SNDRV_EMUX_ST_ON) diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index 820d6ca8c458..d060dddcc52d 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -70,7 +70,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static DEFINE_MUTEX(devices_mutex); -DECLARE_BITMAP(devices_used, SNDRV_CARDS); +static DECLARE_BITMAP(devices_used, SNDRV_CARDS); static struct usb_driver bcd2000_driver; #ifdef CONFIG_SND_DEBUG diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c index 8461d6bf992f..204cc074adb9 100644 --- a/sound/usb/line6/pcm.c +++ b/sound/usb/line6/pcm.c @@ -186,12 +186,8 @@ static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction, int ret = 0; spin_lock_irqsave(&pstr->lock, flags); - if (!test_and_set_bit(type, &pstr->running)) { - if (pstr->active_urbs || pstr->unlink_urbs) { - ret = -EBUSY; - goto error; - } - + if (!test_and_set_bit(type, &pstr->running) && + !(pstr->active_urbs || pstr->unlink_urbs)) { pstr->count = 0; /* Submit all currently available URBs */ if (direction == SNDRV_PCM_STREAM_PLAYBACK) @@ -199,7 +195,6 @@ static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction, else ret = line6_submit_audio_in_all_urbs(line6pcm); } - error: if (ret < 0) clear_bit(type, &pstr->running); spin_unlock_irqrestore(&pstr->lock, flags); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 3e2ef61c627b..6b3acba5da7a 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -809,12 +809,12 @@ static struct usb_feature_control_info audio_feature_info[] = { { "Tone Control - Treble", USB_MIXER_S8 }, { "Graphic Equalizer", USB_MIXER_S8 }, /* FIXME: not implemeted yet */ { "Auto Gain Control", USB_MIXER_BOOLEAN }, - { "Delay Control", USB_MIXER_U16 }, + { "Delay Control", USB_MIXER_U16 }, /* FIXME: U32 in UAC2 */ { "Bass Boost", USB_MIXER_BOOLEAN }, { "Loudness", USB_MIXER_BOOLEAN }, /* UAC2 specific */ - { "Input Gain Control", USB_MIXER_U16 }, - { "Input Gain Pad Control", USB_MIXER_BOOLEAN }, + { "Input Gain Control", USB_MIXER_S16 }, + { "Input Gain Pad Control", USB_MIXER_S16 }, { "Phase Inverter Control", USB_MIXER_BOOLEAN }, }; @@ -918,6 +918,7 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, case USB_ID(0x046d, 0x081d): /* HD Webcam c510 */ case USB_ID(0x046d, 0x0825): /* HD Webcam c270 */ case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */ + case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */ case USB_ID(0x046d, 0x0991): /* Most audio usb devices lie about volume resolution. * Most Logitech webcams have res = 384. @@ -1582,12 +1583,6 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unitid); return -EINVAL; } - /* no bmControls field (e.g. Maya44) -> ignore */ - if (desc->bLength <= 10 + input_pins) { - usb_audio_dbg(state->chip, "MU %d has no bmControls field\n", - unitid); - return 0; - } num_ins = 0; ich = 0; @@ -1595,6 +1590,9 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, err = parse_audio_unit(state, desc->baSourceID[pin]); if (err < 0) continue; + /* no bmControls field (e.g. Maya44) -> ignore */ + if (desc->bLength <= 10 + input_pins) + continue; err = check_input_term(state, desc->baSourceID[pin], &iterm); if (err < 0) return err; diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index b703cb3cda19..e5000da9e9d7 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -437,6 +437,11 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = ebox44_map, }, { + /* MAYA44 USB+ */ + .id = USB_ID(0x2573, 0x0008), + .map = maya44_map, + }, + { /* KEF X300A */ .id = USB_ID(0x27ac, 0x1000), .map = scms_usb3318_map, diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 2f6d3e9a1bcd..e4756651a52c 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2512,6 +2512,74 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, +/* Steinberg devices */ +{ + /* Steinberg MI2 */ + USB_DEVICE_VENDOR_SPEC(0x0a4e, 0x2040), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 3, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = &(const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* Steinberg MI4 */ + USB_DEVICE_VENDOR_SPEC(0x0a4e, 0x4040), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 3, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = &(const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, + /* TerraTec devices */ { USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0012), diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 29175346cc4f..754e689596a2 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1120,6 +1120,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */ case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */ case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ + case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */ return true; } return false; @@ -1266,8 +1267,9 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, if (fp->altsetting == 2) return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; - /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */ - case USB_ID(0x20b1, 0x2009): + + case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */ + case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */ if (fp->altsetting == 3) return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; |