diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-09-08 01:01:14 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-09-08 01:01:14 -0300 |
commit | e5fd91f1ef340da553f7a79da9540c3db711c937 (patch) | |
tree | b11842027dc6641da63f4bcc524f8678263304a3 /sound | |
parent | 2a9b0348e685a63d97486f6749622b61e9e3292f (diff) |
Linux-libre 4.2-gnu
Diffstat (limited to 'sound')
371 files changed, 17456 insertions, 6622 deletions
diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c index 7487eb76e..3edf73631 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 adecbf36f..21e756cf2 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 e0980b5c2..5b2d51d99 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 313f22e9d..6c96feeaf 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 4daf2f582..3354f91e0 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 e4b38fbe5..84a3cd683 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 886be7da9..f845ecf7e 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 51692c8a3..36d2416f9 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 9f404e965..895362a69 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 83c29dbff..1478c8dfd 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 04734e047..20f37fb38 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 8658578eb..7237acbdc 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 082509eb8..f05cb6a8c 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 056f8e274..a99f7200f 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 dfed728d8..02bd96954 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -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 000000000..e70379fb6 --- /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 000000000..36b2d7aca --- /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 941f64a85..b65fa5a19 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 72873a46a..7354b8bed 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 2de3feff7..b1221b297 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 96e8395ae..aaff9ee32 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 c080c73ce..ccd893566 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 48e4fe1b6..0f3b38184 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 edbdab85f..b64f20deb 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 d99f99d61..c4acf17e9 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 acf776941..97015447b 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 4892a7f35..f8549f81a 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 a0cda3820..7dfd0f429 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 186f16111..82b220c76 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 5fc93d005..175f9e4e0 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 573a65eb2..0ca9d72b2 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 a9a1a047c..31f40f03e 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 7f9126efc..54f348a4f 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 d11baaf0f..016e451ed 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 b94009b0b..c8eaa433d 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 3b0ee42a5..89c7aa04b 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 470e5a758..9a41bdebc 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 9b824bfc9..cd2c07fa2 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 d9647bd84..27e25bb78 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 ecec54778..8850b7de1 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 bf20593d3..2a153d260 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; @@ -737,49 +747,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, @@ -788,6 +795,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. @@ -798,8 +809,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); } @@ -808,32 +826,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; @@ -873,7 +914,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; @@ -1014,8 +1055,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 25c905537..b2cf9e756 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 611b7dae7..27a04ac8f 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 dfbcd2331..d23caca7f 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 fc67c1b7c..a1a39494e 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 9ee25a63f..057495d54 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 63343d578..568114392 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 4a55561ed..7a2c1f53b 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 335da6450..301cc6a93 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 98e4fc812..5be5242e1 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 ad635004d..9242e33d2 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 ef1fe3823..581017024 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 e6757cd85..873d40fc4 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 001c6588a..312954639 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 7a359f5b7..7e999c995 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 000000000..9b6f641c7 --- /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 000000000..0aa5d9eb6 --- /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 000000000..358f16195 --- /dev/null +++ b/sound/hda/ext/hdac_ext_controller.c @@ -0,0 +1,282 @@ +/* + * 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); + + /* Lets walk the linked capabilities list */ + do { + cur_cap = _snd_hdac_chip_read(l, bus, offset); + + 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 000000000..3de47dd1a --- /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 519914a12..89c2711ba 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 8e262da74..27c447e4f 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 000000000..b5a17cb51 --- /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 f75bf5622..cdee7103f 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 000000000..5676b8493 --- /dev/null +++ b/sound/hda/hdac_i915.c @@ -0,0 +1,199 @@ +/* + * 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); + snd_hdac_set_codec_wakeup(bus, true); + snd_hdac_set_codec_wakeup(bus, false); + } + } 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_stream.c b/sound/hda/hdac_stream.c new file mode 100644 index 000000000..4c15d0acc --- /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 33a7eb557..e27e2c0b7 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 c65731088..bf377dc19 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 0dd434140..3b5d9a7a6 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/oss/ad1848.c b/sound/oss/ad1848.c index ec1ee07df..10c8de1f8 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 a8ceef8d1..a8bb4a06b 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 048439a16..dc91072f4 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 41fa322f0..526175333 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 c276a5e3f..941a5062c 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 66ddd981d..1fc6d8bc0 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 3bf0dc533..2fb1fbba3 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 c8d499575..36470af7e 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 57e034f20..add317639 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 a3dea4641..ff39a0c72 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 924560d60..c2ae18fc5 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 /*(DEBLOBBED)*/ diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 42a20c806..1028fc8bd 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 0a38e0816..27ed678a4 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 996369134..32092184b 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 8d2fee7b3..167714303 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 33b2a0af1..07a4acc99 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 dcbae7b31..c1455fc55 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 dd75b7536..d3cd95633 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 2c5c28adb..9b2b8b381 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 6cf464d90..24cdcba06 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 b69b3e27c..9c8037e1a 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2812,7 +2812,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 */ @@ -2861,7 +2861,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 86f14620f..bdf411416 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 5c99efb00..d2951ed4b 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 2c90c0bde..7488e1b7a 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 963b91255..de409cda5 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 1cac55fd1..9667cbfb0 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 955ad871e..9dc2950e1 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 fc5591e77..29b44ca27 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 2f28334f8..77e847737 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1906,8 +1906,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); @@ -2058,7 +2058,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 53745f4c2..cf05229b5 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 e1858d9d2..8963d7688 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 059f3846d..e0d9363dc 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 4f5209634..694fb0e7a 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 af78fb33a..6d83c6e03 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 3364dc0fd..c397e7da0 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 46524ff7e..1052ad380 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 00aa31c5f..d5ac25cc7 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 5645481af..d1a2cb65e 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; } @@ -437,7 +437,7 @@ static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid) return 0; parm = snd_hdac_read_parm_uncached(&codec->core, nid, AC_PAR_DEVLIST_LEN); - if (parm == -1 && codec->bus->rirb_error) + 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; } @@ -3223,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; } @@ -3230,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); @@ -3259,7 +3172,7 @@ static int add_std_chmaps(struct hda_codec *codec) struct snd_pcm_chmap *chmap; const struct snd_pcm_chmap_elem *elem; - if (!pcm || pcm->own_chmap || + if (!pcm || !pcm->pcm || pcm->own_chmap || !hinfo->substreams) continue; elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps; @@ -3313,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, @@ -3829,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); @@ -3849,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", @@ -3916,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); @@ -4519,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; @@ -4537,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 9075ac28d..12837abbb 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 26ce99059..944455997 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 0efdb094d..314105cd5 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 000000000..3e18d99bf --- /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 0e6d7534f..c746cd9a4 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 ac0db1679..24f91114a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -671,7 +671,8 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, } for (i = 0; i < path->depth; i++) { if (path->path[i] == nid) { - if (dir == HDA_OUTPUT || path->idx[i] == idx) + if (dir == HDA_OUTPUT || idx == -1 || + path->idx[i] == idx) return true; break; } @@ -682,7 +683,7 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, /* check whether the NID is referred by any active paths */ #define is_active_nid_for_any(codec, nid) \ - is_active_nid(codec, nid, HDA_OUTPUT, 0) + is_active_nid(codec, nid, HDA_OUTPUT, -1) /* get the default amp value for the target state */ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, @@ -883,8 +884,7 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, struct hda_gen_spec *spec = codec->spec; int i; - if (!enable) - path->active = false; + path->active = enable; /* make sure the widget is powered up */ if (enable && (spec->power_down_unused || codec->power_save_node)) @@ -902,9 +902,6 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, if (has_amp_out(codec, path, i)) activate_amp_out(codec, path, i, enable); } - - if (enable) - path->active = true; } EXPORT_SYMBOL_GPL(snd_hda_activate_path); @@ -5175,7 +5172,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 3052a2b09..000000000 --- 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 44dfc7b92..c38c68f57 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, }; @@ -496,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; @@ -510,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; @@ -548,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. @@ -559,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]) @@ -576,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 { @@ -585,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 */ } @@ -606,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, @@ -617,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); @@ -645,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)) { @@ -665,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; } @@ -678,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; } @@ -688,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 */ @@ -705,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; } @@ -760,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; @@ -772,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 @@ -781,28 +860,33 @@ 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; chip = card->private_data; hda = container_of(chip, struct hda_intel, chip); - if (chip->disabled || hda->init_failed) + if (chip->disabled || hda->init_failed || !chip->running) 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; } @@ -818,11 +902,12 @@ static int azx_resume(struct device *dev) chip = card->private_data; hda = container_of(chip, struct hda_intel, chip); - if (chip->disabled || hda->init_failed) + if (chip->disabled || hda->init_failed || !chip->running) 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) @@ -832,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 */ @@ -864,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; } @@ -875,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; @@ -891,19 +980,25 @@ static int azx_runtime_resume(struct device *dev) return 0; if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { - hda_display_power(hda, true); - haswell_set_bclk(hda); + bus = azx_bus(chip); + if (hda->need_i915_power) { + snd_hdac_display_power(bus, true); + haswell_set_bclk(hda); + } else { + /* 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); @@ -913,6 +1008,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; } @@ -931,7 +1027,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 || !chip->running) return -EBUSY; return 0; @@ -969,7 +1065,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, @@ -990,11 +1086,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); @@ -1011,11 +1107,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; } @@ -1048,7 +1144,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; @@ -1071,7 +1167,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); @@ -1082,42 +1178,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); @@ -1284,9 +1392,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); } } @@ -1373,12 +1481,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; @@ -1398,12 +1509,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); @@ -1435,6 +1544,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"); @@ -1455,6 +1571,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; @@ -1474,9 +1591,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; } @@ -1494,7 +1611,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); @@ -1536,11 +1653,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 @@ -1571,17 +1688,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); @@ -1593,10 +1708,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; } @@ -1606,7 +1721,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; } @@ -1675,10 +1790,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); @@ -1689,15 +1805,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; @@ -1705,8 +1822,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); } @@ -1719,9 +1838,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; @@ -1748,20 +1864,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, @@ -1788,8 +1908,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; @@ -1851,14 +1970,24 @@ 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); + /* 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; @@ -1870,18 +1999,16 @@ static int azx_probe_continue(struct azx *chip) else goto skip_i915; } - err = hda_display_power(hda, true); + + 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 } -#ifdef CONFIG_SND_HDA_I915 skip_i915: -#endif err = azx_first_init(chip); if (err < 0) goto out_free; @@ -1891,17 +2018,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; @@ -1923,11 +2046,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); diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h index d5231f721..354f0bbed 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 7b5e4c2cf..0922d8b1b 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 d7cfe7b8c..366efbf87 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 b279e327a..387d30984 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 bed66c314..4a21c2199 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -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 2e4fd5c56..477742cb7 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 231f89029..c033a4ee6 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 447302695..484bbf413 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 5c0416254..04aeed6d1 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 3a24f7739..584a0343a 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; @@ -1148,6 +1146,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, @@ -1165,8 +1164,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; @@ -1185,11 +1182,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 617d9012e..f5ed07871 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 78b719b5b..ca03c4060 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -200,12 +200,33 @@ static int cx_auto_init(struct hda_codec *codec) return 0; } -#define cx_auto_free snd_hda_gen_free +static void cx_auto_reboot_notify(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + + if (codec->core.vendor_id != 0x14f150f2) + return; + + /* Turn the CX20722 codec into D3 to avoid spurious noises + from the internal speaker during (and after) reboot */ + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); + + snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); +} + +static void cx_auto_free(struct hda_codec *codec) +{ + cx_auto_reboot_notify(codec); + snd_hda_gen_free(codec); +} static const struct hda_codec_ops cx_auto_patch_ops = { .build_controls = cx_auto_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = cx_auto_init, + .reboot_notify = cx_auto_reboot_notify, .free = cx_auto_free, .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM @@ -850,6 +871,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 +930,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 225b78b4e..a97db5fc8 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 }, @@ -3349,6 +3528,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 }, @@ -3414,6 +3594,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 1e99f075a..374ea5328 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; @@ -2299,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; @@ -2436,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 @@ -2461,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; @@ -2567,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); @@ -2588,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; @@ -3586,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: @@ -3660,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); @@ -3743,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: @@ -3807,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); @@ -3875,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); @@ -3937,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); @@ -4435,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" @@ -4515,6 +4570,7 @@ 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, @@ -4522,6 +4578,7 @@ enum { 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[] = { @@ -5036,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, @@ -5087,6 +5148,16 @@ static const struct hda_fixup alc269_fixups[] = { .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[] = { @@ -5119,6 +5190,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC292_FIXUP_DISABLE_AAMIX), + SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC292_FIXUP_DISABLE_AAMIX), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), @@ -5146,6 +5218,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 */ @@ -5219,6 +5292,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -5303,6 +5377,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"}, @@ -5326,8 +5402,6 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {0x19, 0x411111f0}, \ {0x1a, 0x411111f0}, \ {0x1b, 0x411111f0}, \ - {0x1d, 0x40700001}, \ - {0x1e, 0x411111f0}, \ {0x21, 0x02211020} #define ALC282_STANDARD_PINS \ @@ -5358,8 +5432,14 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {0x15, 0x0221401f}, \ {0x1a, 0x411111f0}, \ {0x1b, 0x411111f0}, \ - {0x1d, 0x40700001}, \ - {0x1e, 0x411111f0} + {0x1d, 0x40700001} + +#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, @@ -5395,6 +5475,28 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x1e, 0x411111f0}, {0x21, 0x0221103f}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x40000000}, + {0x14, 0x90170150}, + {0x17, 0x411111f0}, + {0x18, 0x411111f0}, + {0x19, 0x411111f0}, + {0x1a, 0x411111f0}, + {0x1b, 0x02011020}, + {0x1d, 0x4054c029}, + {0x1e, 0x411111f0}, + {0x21, 0x0221105f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x40000000}, + {0x14, 0x90170110}, + {0x17, 0x411111f0}, + {0x18, 0x411111f0}, + {0x19, 0x411111f0}, + {0x1a, 0x411111f0}, + {0x1b, 0x01014020}, + {0x1d, 0x4054c029}, + {0x1e, 0x411111f0}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60160}, {0x14, 0x90170120}, {0x17, 0x90170140}, @@ -5456,10 +5558,19 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x21, 0x02211030}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, ALC256_STANDARD_PINS, - {0x13, 0x40000000}), + {0x13, 0x40000000}, + {0x1d, 0x40700001}, + {0x1e, 0x411111f0}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC256_STANDARD_PINS, + {0x13, 0x411111f0}, + {0x1d, 0x40700001}, + {0x1e, 0x411111f0}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, ALC256_STANDARD_PINS, - {0x13, 0x411111f0}), + {0x13, 0x411111f0}, + {0x1d, 0x4077992d}, + {0x1e, 0x411111ff}), SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4, {0x12, 0x90a60130}, {0x13, 0x40000000}, @@ -5622,35 +5733,56 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x13, 0x411111f0}, {0x16, 0x01014020}, {0x18, 0x411111f0}, - {0x19, 0x01a19030}), + {0x19, 0x01a19030}, + {0x1e, 0x411111f0}), SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, {0x12, 0x90a60140}, {0x13, 0x411111f0}, {0x16, 0x01014020}, {0x18, 0x02a19031}, - {0x19, 0x01a1903e}), + {0x19, 0x01a1903e}, + {0x1e, 0x411111f0}), SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, {0x12, 0x90a60140}, {0x13, 0x411111f0}, {0x16, 0x411111f0}, {0x18, 0x411111f0}, - {0x19, 0x411111f0}), + {0x19, 0x411111f0}, + {0x1e, 0x411111f0}), SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, {0x12, 0x40000000}, {0x13, 0x90a60140}, {0x16, 0x21014020}, {0x18, 0x411111f0}, - {0x19, 0x21a19030}), + {0x19, 0x21a19030}, + {0x1e, 0x411111f0}), SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, {0x12, 0x40000000}, {0x13, 0x90a60140}, {0x16, 0x411111f0}, {0x18, 0x411111f0}, - {0x19, 0x411111f0}), + {0x19, 0x411111f0}, + {0x1e, 0x411111f0}), + SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC292_STANDARD_PINS, + {0x12, 0x40000000}, + {0x13, 0x90a60140}, + {0x16, 0x21014020}, + {0x18, 0x411111f0}, + {0x19, 0x21a19030}, + {0x1e, 0x411111ff}), + 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}), {} }; @@ -5709,6 +5841,12 @@ static int patch_alc269(struct hda_codec *codec) spec->gen.shared_mic_vref_pin = 0x18; 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); snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups); @@ -5805,15 +5943,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; @@ -5929,6 +6058,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); @@ -5940,11 +6073,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; @@ -6021,6 +6149,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); @@ -6032,10 +6162,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; @@ -6636,6 +6762,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; @@ -6687,9 +6815,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; @@ -6726,8 +6851,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 25f0f45e6..9d947aef2 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -4363,7 +4363,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) { @@ -4443,6 +4443,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; } @@ -4459,7 +4460,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); @@ -4492,8 +4492,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, @@ -4563,8 +4561,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); @@ -4640,8 +4636,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, @@ -4690,8 +4684,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); @@ -4730,8 +4722,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; @@ -4810,8 +4800,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 */ @@ -4867,8 +4855,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); @@ -4930,8 +4916,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); @@ -5003,8 +4987,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 0baeecc22..da5366405 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; } @@ -483,7 +486,6 @@ 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, @@ -663,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 */ @@ -691,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; @@ -719,8 +720,6 @@ static int patch_vt1709(struct hda_codec *codec) return err; } - codec->patch_ops = via_patch_ops; - return 0; } @@ -747,7 +746,6 @@ static int patch_vt1708B(struct hda_codec *codec) return err; } - codec->patch_ops = via_patch_ops; return 0; } @@ -812,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; } @@ -854,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; } @@ -927,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; } @@ -1027,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; } @@ -1135,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; } @@ -1174,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; } @@ -1212,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 f7b1523e8..8ae3bb797 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 6f55e02e5..7c387b040 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 b12092522..42bcbac80 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 7577f31cd..1bc98c867 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 601315a1f..cba89beb2 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 452908b8a..0a287ce70 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2536,8 +2536,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 c3a9f39f8..bc81b9f75 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 ffff3b25f..b4ef58042 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/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 6492bca8c..4ca12665f 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -88,7 +88,7 @@ static int dac_mute_put(struct snd_kcontrol *ctl, int changed; mutex_lock(&chip->mutex); - changed = !value->value.integer.value[0] != chip->dac_mute; + changed = (!value->value.integer.value[0]) != chip->dac_mute; if (changed) { chip->dac_mute = !value->value.integer.value[0]; chip->model.update_dac_mute(chip); diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c index 6ce68604c..90ac479f3 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 c6092e48c..929323528 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 efe669b80..f3860b850 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 0f40624a4..1b6fad7d4 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 b72be035f..599d2b7eb 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 0d1c27e91..6120a0674 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 3ba52da18..1d651b8a8 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -30,6 +30,9 @@ config SND_SOC_GENERIC_DMAENGINE_PCM bool select SND_DMAENGINE_PCM +config SND_SOC_TOPOLOGY + bool + # All the supported SoCs source "sound/soc/adi/Kconfig" source "sound/soc/atmel/Kconfig" @@ -45,6 +48,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" @@ -57,6 +61,7 @@ source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/ux500/Kconfig" source "sound/soc/xtensa/Kconfig" +source "sound/soc/zte/Kconfig" # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 974ba708b..669648b41 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,6 +1,10 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o +ifneq ($(CONFIG_SND_SOC_TOPOLOGY),) +snd-soc-core-objs += soc-topology.o +endif + ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-objs += soc-generic-dmaengine-pcm.o endif @@ -23,6 +27,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/ @@ -38,3 +43,4 @@ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += ux500/ obj-$(CONFIG_SND_SOC) += xtensa/ +obj-$(CONFIG_SND_SOC) += zte/ diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index e7d08806f..1489cd461 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -6,29 +6,35 @@ config SND_ATMEL_SOC the ATMEL SSC interface. You will also need to select the audio interfaces to support below. +if SND_ATMEL_SOC + config SND_ATMEL_SOC_PDC tristate - depends on SND_ATMEL_SOC + 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 tristate - depends on SND_ATMEL_SOC 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 - depends on SND_ATMEL_SOC - help - Say Y or M if you want to add support for codecs the - ATMEL SSC interface. You will also needs to select the individual - machine drivers to support below. + 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_ATMEL_SOC && SND_SOC_I2C_AND_SPI - select SND_ATMEL_SOC_PDC - select SND_ATMEL_SOC_SSC + depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI + 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 @@ -37,9 +43,8 @@ config SND_AT91_SOC_SAM9G20_WM8731 config SND_ATMEL_SOC_WM8904 tristate "Atmel ASoC driver for boards using WM8904 codec" depends on ARCH_AT91 || COMPILE_TEST - depends on ATMEL_SSC && SND_ATMEL_SOC && I2C - select SND_ATMEL_SOC_SSC - select SND_ATMEL_SOC_DMA + depends on ATMEL_SSC && I2C + 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 @@ -48,10 +53,10 @@ config SND_ATMEL_SOC_WM8904 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_ATMEL_SOC && SND_SOC_I2C_AND_SPI - select SND_ATMEL_SOC_SSC - select SND_ATMEL_SOC_DMA + depends on ATMEL_SSC && SND_SOC_I2C_AND_SPI + select SND_ATMEL_SOC_SSC_DMA select SND_SOC_WM8731 help Say Y if you want to add support for audio SoC on an at91sam9x5 based board that is using WM8731 codec. +endif diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index b6625c8c4..dd57a9eac 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -124,8 +124,7 @@ static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = { int atmel_pcm_dma_platform_register(struct device *dev) { - return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config, - SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); + return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config, 0); } EXPORT_SYMBOL(atmel_pcm_dma_platform_register); diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 8de836165..d7469cdd9 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -95,8 +95,9 @@ static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = { static const struct snd_soc_dapm_route intercon[] = { - /* speaker connected to LHPOUT */ + /* speaker connected to LHPOUT/RHPOUT */ {"Ext Spk", NULL, "LHPOUT"}, + {"Ext Spk", NULL, "RHPOUT"}, /* mic is connected to Mic Jack, with WM8731 Mic Bias */ {"MICIN", NULL, "Mic Bias"}, @@ -108,9 +109,7 @@ static const struct snd_soc_dapm_route intercon[] = { */ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; printk(KERN_DEBUG @@ -124,10 +123,6 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) return ret; } - /* not connected */ - snd_soc_dapm_nc_pin(dapm, "RLINEIN"); - snd_soc_dapm_nc_pin(dapm, "LLINEIN"); - #ifndef ENABLE_MIC_INPUT snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic"); #endif @@ -158,6 +153,7 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = { .num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets), .dapm_routes = intercon, .num_dapm_routes = ARRAY_SIZE(intercon), + .fully_routed = true, }; static int at91sam9g20ek_audio_probe(struct platform_device *pdev) diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index c75995f27..58c316480 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -21,7 +21,7 @@ #include "../codecs/wm8731.h" #include "psc.h" -static struct platform_device_id db1200_pids[] = { +static const struct platform_device_id db1200_pids[] = { { .name = "db1200-ac97", .driver_data = 0, diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c index 5f664471d..67a73330d 100644 --- a/sound/soc/cirrus/ep93xx-pcm.c +++ b/sound/soc/cirrus/ep93xx-pcm.c @@ -60,7 +60,6 @@ int devm_ep93xx_pcm_platform_register(struct device *dev) { return devm_snd_dmaengine_pcm_register(dev, &ep93xx_dmaengine_pcm_config, - SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | SND_DMAENGINE_PCM_FLAG_NO_DT | SND_DMAENGINE_PCM_FLAG_COMPAT); } diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index a0f265327..38b3dad9d 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -1140,7 +1140,7 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Enable Audio PLL & Audio section */ data = AUDIO_PLL | AUDIO_SECTION_ON; pm860x_reg_write(pm860x->i2c, REG_MISC2, data); @@ -1156,7 +1156,6 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec, pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0); break; } - codec->dapm.bias_level = level; return 0; } @@ -1187,16 +1186,16 @@ static struct snd_soc_dai_driver pm860x_dai[] = { .channels_min = 2, .channels_max = 2, .rates = PM860X_RATES, - .formats = SNDRV_PCM_FORMAT_S16_LE | \ - SNDRV_PCM_FORMAT_S18_3LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, }, .capture = { .stream_name = "PCM Capture", .channels_min = 2, .channels_max = 2, .rates = PM860X_RATES, - .formats = SNDRV_PCM_FORMAT_S16_LE | \ - SNDRV_PCM_FORMAT_S18_3LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, }, .ops = &pm860x_pcm_dai_ops, }, { @@ -1208,16 +1207,16 @@ static struct snd_soc_dai_driver pm860x_dai[] = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FORMAT_S16_LE | \ - SNDRV_PCM_FORMAT_S18_3LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, }, .capture = { .stream_name = "I2S Capture", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FORMAT_S16_LE | \ - SNDRV_PCM_FORMAT_S18_3LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, }, .ops = &pm860x_i2s_dai_ops, }, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 061c46587..efaafce8b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -16,7 +16,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_88PM860X if MFD_88PM860X select SND_SOC_L3 select SND_SOC_AB8500_CODEC if ABX500_CORE - select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS + select SND_SOC_AC97_CODEC select SND_SOC_AD1836 if SPI_MASTER select SND_SOC_AD193X_SPI if SPI_MASTER select SND_SOC_AD193X_I2C if I2C @@ -54,7 +54,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4271_SPI if SPI_MASTER select SND_SOC_CS42XX8_I2C if I2C select SND_SOC_CX20442 if TTY - select SND_SOC_DA7210 if I2C + select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI select SND_SOC_DA7213 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C @@ -104,6 +104,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C + select SND_SOC_TAS571X if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -211,8 +212,9 @@ config SND_SOC_AB8500_CODEC tristate config SND_SOC_AC97_CODEC - tristate + tristate "Build generic ASoC AC97 CODEC driver" select SND_AC97_CODEC + select SND_SOC_AC97_BUS config SND_SOC_AD1836 tristate @@ -507,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 @@ -611,6 +618,10 @@ config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C +config SND_SOC_TAS571X + tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers" + depends on I2C + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index abe2d7edf..cf160d972 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 @@ -106,6 +107,7 @@ snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o +snd-soc-tas571x-objs := tas571x.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -262,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 @@ -288,6 +291,7 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o +obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 88ca9cb0c..c7d243db0 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -1209,6 +1209,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); struct device *dev = codec->dev; bool apply_fir, apply_iir; @@ -1234,15 +1235,14 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR; apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR; - status = snd_soc_dapm_force_enable_pin(&codec->dapm, - "ANC Configure Input"); + status = snd_soc_dapm_force_enable_pin(dapm, "ANC Configure Input"); if (status < 0) { dev_err(dev, "%s: ERROR: Failed to enable power (status = %d)!\n", __func__, status); goto cleanup; } - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); anc_configure(codec, apply_fir, apply_iir); @@ -1259,8 +1259,8 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, drvdata->anc_status = ANC_IIR_CONFIGURED; } - status = snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input"); - snd_soc_dapm_sync(&codec->dapm); + status = snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); + snd_soc_dapm_sync(dapm); cleanup: mutex_unlock(&drvdata->ctrl_lock); @@ -1947,6 +1947,7 @@ static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec) static int ab8500_audio_setup_mics(struct snd_soc_codec *codec, struct amic_settings *amics) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); u8 value8; unsigned int value; int status; @@ -1973,15 +1974,15 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec, dev_dbg(codec->dev, "%s: Mic 1a regulator: %s\n", __func__, amic_micbias_str(amics->mic1a_micbias)); route = &ab8500_dapm_routes_mic1a_vamicx[amics->mic1a_micbias]; - status = snd_soc_dapm_add_routes(&codec->dapm, route, 1); + status = snd_soc_dapm_add_routes(dapm, route, 1); dev_dbg(codec->dev, "%s: Mic 1b regulator: %s\n", __func__, amic_micbias_str(amics->mic1b_micbias)); route = &ab8500_dapm_routes_mic1b_vamicx[amics->mic1b_micbias]; - status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1); + status |= snd_soc_dapm_add_routes(dapm, route, 1); dev_dbg(codec->dev, "%s: Mic 2 regulator: %s\n", __func__, amic_micbias_str(amics->mic2_micbias)); route = &ab8500_dapm_routes_mic2_vamicx[amics->mic2_micbias]; - status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1); + status |= snd_soc_dapm_add_routes(dapm, route, 1); if (status < 0) { dev_err(codec->dev, "%s: Failed to add AMic-regulator DAPM-routes (%d).\n", @@ -2461,6 +2462,7 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np, static int ab8500_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct device *dev = codec->dev; struct device_node *np = dev->of_node; struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); @@ -2541,7 +2543,7 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) &ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value; drvdata->sid_fir_values = (long *)fc->value; - (void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input"); + snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); mutex_init(&drvdata->ctrl_lock); diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index d0ac723ee..5b3224c63 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -44,10 +44,6 @@ static int ac97_prepare(struct snd_pcm_substream *substream, return snd_ac97_set_rate(ac97, reg, substream->runtime->rate); } -#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ - SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\ - SNDRV_PCM_RATE_48000) - static const struct snd_soc_dai_ops ac97_dai_ops = { .prepare = ac97_prepare, }; @@ -58,13 +54,13 @@ static struct snd_soc_dai_driver ac97_dai = { .stream_name = "AC97 Playback", .channels_min = 1, .channels_max = 2, - .rates = STD_AC97_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = SND_SOC_STD_AC97_FMTS,}, .capture = { .stream_name = "AC97 Capture", .channels_min = 1, .channels_max = 2, - .rates = STD_AC97_RATES, + .rates = SNDRV_PCM_RATE_KNOT, .formats = SND_SOC_STD_AC97_FMTS,}, .ops = &ac97_dai_ops, }; diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 685998dd0..95f0bec26 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -251,7 +251,7 @@ static int ad1836_resume(struct snd_soc_codec *codec) static int ad1836_probe(struct snd_soc_codec *codec) { struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int num_dacs, num_adcs; int ret = 0; int i; diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 783dcb570..a43160254 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1444,7 +1444,6 @@ static int adau1373_set_bias_level(struct snd_soc_codec *codec, ADAU1373_PWDN_CTRL3_PWR_EN, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 899f17126..0c8df9c6d 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -16,6 +16,7 @@ #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/of_device.h> +#include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> @@ -101,6 +102,10 @@ #define ADAU1701_FIRMWARE "/*(DEBLOBBED)*/" +static const char * const supply_names[] = { + "dvdd", "avdd" +}; + struct adau1701 { int gpio_nreset; int gpio_pll_mode[2]; @@ -112,6 +117,7 @@ struct adau1701 { u8 pin_config[12]; struct sigmadsp *sigmadsp; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; }; static const struct snd_kcontrol_new adau1701_controls[] = { @@ -565,7 +571,6 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -669,6 +674,13 @@ static int adau1701_probe(struct snd_soc_codec *codec) if (ret) return ret; + ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(codec->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + /* * Let the pll_clkdiv variable default to something that won't happen * at runtime. That way, we can postpone the firmware download from @@ -680,7 +692,7 @@ static int adau1701_probe(struct snd_soc_codec *codec) /* initalize with pre-configured pll mode settings */ ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0); if (ret < 0) - return ret; + goto exit_regulators_disable; /* set up pin config */ val = 0; @@ -696,10 +708,60 @@ static int adau1701_probe(struct snd_soc_codec *codec) regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val); return 0; + +exit_regulators_disable: + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); + return ret; } +static int adau1701_remove(struct snd_soc_codec *codec) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + + if (gpio_is_valid(adau1701->gpio_nreset)) + gpio_set_value_cansleep(adau1701->gpio_nreset, 0); + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); + + return 0; +} + +#ifdef CONFIG_PM +static int adau1701_suspend(struct snd_soc_codec *codec) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + + return 0; +} + +static int adau1701_resume(struct snd_soc_codec *codec) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(codec->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + return adau1701_reset(codec, adau1701->pll_clkdiv, 0); +} +#else +#define adau1701_resume NULL +#define adau1701_suspend NULL +#endif /* CONFIG_PM */ + static struct snd_soc_codec_driver adau1701_codec_drv = { .probe = adau1701_probe, + .remove = adau1701_remove, + .resume = adau1701_resume, + .suspend = adau1701_suspend, .set_bias_level = adau1701_set_bias_level, .idle_bias_off = true, @@ -730,32 +792,58 @@ static int adau1701_i2c_probe(struct i2c_client *client, struct device *dev = &client->dev; int gpio_nreset = -EINVAL; int gpio_pll_mode[2] = { -EINVAL, -EINVAL }; - int ret; + int ret, i; adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL); if (!adau1701) return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + adau1701->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + adau1701->client = client; adau1701->regmap = devm_regmap_init(dev, NULL, client, &adau1701_regmap); - if (IS_ERR(adau1701->regmap)) - return PTR_ERR(adau1701->regmap); + if (IS_ERR(adau1701->regmap)) { + ret = PTR_ERR(adau1701->regmap); + goto exit_regulators_disable; + } + if (dev->of_node) { gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0); - if (gpio_nreset < 0 && gpio_nreset != -ENOENT) - return gpio_nreset; + if (gpio_nreset < 0 && gpio_nreset != -ENOENT) { + ret = gpio_nreset; + goto exit_regulators_disable; + } gpio_pll_mode[0] = of_get_named_gpio(dev->of_node, "adi,pll-mode-gpios", 0); - if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) - return gpio_pll_mode[0]; + if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) { + ret = gpio_pll_mode[0]; + goto exit_regulators_disable; + } gpio_pll_mode[1] = of_get_named_gpio(dev->of_node, "adi,pll-mode-gpios", 1); - if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) - return gpio_pll_mode[1]; + if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) { + ret = gpio_pll_mode[1]; + goto exit_regulators_disable; + } of_property_read_u32(dev->of_node, "adi,pll-clkdiv", &adau1701->pll_clkdiv); @@ -769,7 +857,7 @@ static int adau1701_i2c_probe(struct i2c_client *client, ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW, "ADAU1701 Reset"); if (ret < 0) - return ret; + goto exit_regulators_disable; } if (gpio_is_valid(gpio_pll_mode[0]) && @@ -778,13 +866,13 @@ static int adau1701_i2c_probe(struct i2c_client *client, GPIOF_OUT_INIT_LOW, "ADAU1701 PLL mode 0"); if (ret < 0) - return ret; + goto exit_regulators_disable; ret = devm_gpio_request_one(dev, gpio_pll_mode[1], GPIOF_OUT_INIT_LOW, "ADAU1701 PLL mode 1"); if (ret < 0) - return ret; + goto exit_regulators_disable; } adau1701->gpio_nreset = gpio_nreset; @@ -795,11 +883,17 @@ static int adau1701_i2c_probe(struct i2c_client *client, adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, &adau1701_sigmadsp_ops, ADAU1701_FIRMWARE); - if (IS_ERR(adau1701->sigmadsp)) - return PTR_ERR(adau1701->sigmadsp); + if (IS_ERR(adau1701->sigmadsp)) { + ret = PTR_ERR(adau1701->sigmadsp); + goto exit_regulators_disable; + } ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv, &adau1701_dai, 1); + +exit_regulators_disable: + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); return ret; } diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index b8d7e32cc..a3a6e638f 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -466,7 +466,6 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -483,6 +482,7 @@ static enum adau1761_output_mode adau1761_get_lineout_mode( static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau1761_platform_data *pdata = codec->dev->platform_data; struct adau *adau = snd_soc_codec_get_drvdata(codec); enum adau1761_digmic_jackdet_pin_mode mode; @@ -515,21 +515,18 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec) if (ret) return ret; case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */ - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau1761_no_dmic_routes, + ret = snd_soc_dapm_add_routes(dapm, adau1761_no_dmic_routes, ARRAY_SIZE(adau1761_no_dmic_routes)); if (ret) return ret; break; case ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC: - ret = snd_soc_dapm_new_controls(&codec->dapm, - adau1761_dmic_widgets, + ret = snd_soc_dapm_new_controls(dapm, adau1761_dmic_widgets, ARRAY_SIZE(adau1761_dmic_widgets)); if (ret) return ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau1761_dmic_routes, + ret = snd_soc_dapm_add_routes(dapm, adau1761_dmic_routes, ARRAY_SIZE(adau1761_dmic_routes)); if (ret) return ret; @@ -547,6 +544,7 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec) static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau *adau = snd_soc_codec_get_drvdata(codec); struct adau1761_platform_data *pdata = codec->dev->platform_data; enum adau1761_output_mode mode; @@ -577,12 +575,12 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec) } if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) { - ret = snd_soc_dapm_new_controls(&codec->dapm, + ret = snd_soc_dapm_new_controls(dapm, adau1761_capless_dapm_widgets, ARRAY_SIZE(adau1761_capless_dapm_widgets)); if (ret) return ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, + ret = snd_soc_dapm_add_routes(dapm, adau1761_capless_dapm_routes, ARRAY_SIZE(adau1761_capless_dapm_routes)); } else { @@ -590,12 +588,12 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec) ARRAY_SIZE(adau1761_mono_controls)); if (ret) return ret; - ret = snd_soc_dapm_new_controls(&codec->dapm, + ret = snd_soc_dapm_new_controls(dapm, adau1761_mono_dapm_widgets, ARRAY_SIZE(adau1761_mono_dapm_widgets)); if (ret) return ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, + ret = snd_soc_dapm_add_routes(dapm, adau1761_mono_dapm_routes, ARRAY_SIZE(adau1761_mono_dapm_routes)); } @@ -640,6 +638,7 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg) static int adau1761_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau1761_platform_data *pdata = codec->dev->platform_data; struct adau *adau = snd_soc_codec_get_drvdata(codec); int ret; @@ -692,14 +691,12 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec) return ret; if (adau->type == ADAU1761) { - ret = snd_soc_dapm_new_controls(&codec->dapm, - adau1761_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets, ARRAY_SIZE(adau1761_dapm_widgets)); if (ret) return ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau1761_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_routes, ARRAY_SIZE(adau1761_dapm_routes)); if (ret) return ret; diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index faa905a2d..4f6844510 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -339,7 +339,6 @@ static int adau1781_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -383,6 +382,7 @@ static int adau1781_set_input_mode(struct adau *adau, unsigned int reg, static int adau1781_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev); struct adau *adau = snd_soc_codec_get_drvdata(codec); int ret; @@ -403,19 +403,17 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec) } if (pdata && pdata->use_dmic) { - ret = snd_soc_dapm_new_controls(&codec->dapm, + ret = snd_soc_dapm_new_controls(dapm, adau1781_dmic_dapm_widgets, ARRAY_SIZE(adau1781_dmic_dapm_widgets)); if (ret) return ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau1781_dmic_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau1781_dmic_dapm_routes, ARRAY_SIZE(adau1781_dmic_dapm_routes)); if (ret) return ret; } else { - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau1781_adc_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau1781_adc_dapm_routes, ARRAY_SIZE(adau1781_adc_dapm_routes)); if (ret) return ret; diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index fa2e690e5..fcf05b254 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -155,6 +155,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau *adau = snd_soc_codec_get_drvdata(codec); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct snd_soc_dapm_update update; @@ -188,7 +189,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol, update.reg = reg; update.val = val; - snd_soc_dapm_mux_update_power(&codec->dapm, kcontrol, + snd_soc_dapm_mux_update_power(dapm, kcontrol, ucontrol->value.enumerated.item[0], e, &update); } @@ -444,8 +445,8 @@ static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id, static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec); struct adau *adau = snd_soc_codec_get_drvdata(dai->codec); - struct snd_soc_dapm_context *dapm = &dai->codec->dapm; switch (clk_id) { case ADAU17X1_CLK_SRC_MCLK: @@ -804,6 +805,7 @@ EXPORT_SYMBOL_GPL(adau17x1_setup_firmware); int adau17x1_add_widgets(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau *adau = snd_soc_codec_get_drvdata(codec); int ret; @@ -811,14 +813,13 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec) ARRAY_SIZE(adau17x1_controls)); if (ret) return ret; - ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, adau17x1_dapm_widgets, ARRAY_SIZE(adau17x1_dapm_widgets)); if (ret) return ret; if (adau17x1_has_dsp(adau)) { - ret = snd_soc_dapm_new_controls(&codec->dapm, - adau17x1_dsp_dapm_widgets, + ret = snd_soc_dapm_new_controls(dapm, adau17x1_dsp_dapm_widgets, ARRAY_SIZE(adau17x1_dsp_dapm_widgets)); if (ret) return ret; @@ -840,21 +841,20 @@ EXPORT_SYMBOL_GPL(adau17x1_add_widgets); int adau17x1_add_routes(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau *adau = snd_soc_codec_get_drvdata(codec); int ret; - ret = snd_soc_dapm_add_routes(&codec->dapm, adau17x1_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau17x1_dapm_routes, ARRAY_SIZE(adau17x1_dapm_routes)); if (ret) return ret; if (adau17x1_has_dsp(adau)) { - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau17x1_dsp_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau17x1_dsp_dapm_routes, ARRAY_SIZE(adau17x1_dsp_dapm_routes)); } else { - ret = snd_soc_dapm_add_routes(&codec->dapm, - adau17x1_no_dsp_dapm_routes, + ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes, ARRAY_SIZE(adau17x1_no_dsp_dapm_routes)); } return ret; diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index 7ad8e156e..9bdd15f40 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -202,7 +202,7 @@ static const struct snd_soc_dapm_route adau1977_dapm_routes[] = { ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0) #define ADAU1977_DC_SUB_SWITCH(x) \ - SOC_SINGLE("ADC" #x " DC Substraction Capture Switch", \ + SOC_SINGLE("ADC" #x " DC Subtraction Capture Switch", \ ADAU1977_REG_DC_HPF_CAL, (x) + 3, 1, 0) static const struct snd_kcontrol_new adau1977_snd_controls[] = { @@ -485,7 +485,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) ret = adau1977_power_enable(adau1977); break; case SND_SOC_BIAS_OFF: @@ -493,12 +493,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec, break; } - if (ret) - return ret; - - codec->dapm.bias_level = level; - - return 0; + return ret; } static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, @@ -853,12 +848,13 @@ static int adau1977_set_sysclk(struct snd_soc_codec *codec, static int adau1977_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec); int ret; switch (adau1977->type) { case ADAU1977: - ret = snd_soc_dapm_new_controls(&codec->dapm, + ret = snd_soc_dapm_new_controls(dapm, adau1977_micbias_dapm_widgets, ARRAY_SIZE(adau1977_micbias_dapm_widgets)); if (ret < 0) diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 4373ada95..36d842570 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -539,7 +539,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec, unsigned int freq, int dir) { struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); if (dir == SND_SOC_CLOCK_IN) { switch (clk_id) { @@ -622,6 +622,7 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec, static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); unsigned int pll_ctrl1 = 0; unsigned int pll_ctrl2 = 0; @@ -687,7 +688,7 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id, adav80x->pll_src = source; - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); } return 0; @@ -714,7 +715,6 @@ static int adav80x_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -801,11 +801,12 @@ static struct snd_soc_dai_driver adav80x_dais[] = { static int adav80x_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec); /* Force PLLs on for SYSCLK output */ - snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2"); + snd_soc_dapm_force_enable_pin(dapm, "PLL1"); + snd_soc_dapm_force_enable_pin(dapm, "PLL2"); /* Power down S/PDIF receiver, since it is currently not supported */ regmap_write(adav80x->regmap, ADAV80X_PLL_OUTE, 0x20); diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 9130d916f..8670861e5 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -341,7 +341,6 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 81b54a270..2d0ff4595 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -412,7 +412,7 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { if (pdata && gpio_is_valid(pdata->gpio_power)) gpio_set_value(pdata->gpio_power, 1); mdelay(1); @@ -439,7 +439,6 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec, regcache_mark_dirty(ak4641->regmap); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 13585e88f..7c0f6552c 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -482,7 +482,6 @@ static int ak4642_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, PW_MGMT1, PMVCM, PMVCM); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index 2a58b1dcc..0e59063ae 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -577,7 +577,6 @@ static int ak4671_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 0e3579968..0fc24e0d5 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -826,7 +826,6 @@ static int alc5623_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, 0); break; } - codec->dapm.bias_level = level; return 0; } @@ -894,7 +893,7 @@ static int alc5623_resume(struct snd_soc_codec *codec) static int alc5623_probe(struct snd_soc_codec *codec) { struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); alc5623_reset(codec); diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index db3283abb..607a63b97 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1000,7 +1000,6 @@ static int alc5632_set_bias_level(struct snd_soc_codec *codec, ALC5632_PWR_MANAG_ADD1_MASK, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index eff4b4d51..802e05eae 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -208,11 +208,12 @@ static const struct snd_soc_dapm_widget arizona_spkr = int arizona_init_spk(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; int ret; - ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkl, 1); + ret = snd_soc_dapm_new_controls(dapm, &arizona_spkl, 1); if (ret != 0) return ret; @@ -220,8 +221,7 @@ int arizona_init_spk(struct snd_soc_codec *codec) case WM8997: break; default: - ret = snd_soc_dapm_new_controls(&codec->dapm, - &arizona_spkr, 1); + ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1); if (ret != 0) return ret; break; @@ -258,13 +258,14 @@ static const struct snd_soc_dapm_route arizona_mono_routes[] = { int arizona_init_mono(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; int i; for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) { if (arizona->pdata.out_mono[i]) - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, &arizona_mono_routes[i], 1); } @@ -274,6 +275,7 @@ EXPORT_SYMBOL_GPL(arizona_init_mono); int arizona_init_gpio(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; int i; @@ -281,23 +283,21 @@ int arizona_init_gpio(struct snd_soc_codec *codec) switch (arizona->type) { case WM5110: case WM8280: - snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity"); + snd_soc_dapm_disable_pin(dapm, "DRC2 Signal Activity"); break; default: break; } - snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity"); + snd_soc_dapm_disable_pin(dapm, "DRC1 Signal Activity"); for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) { case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT: - snd_soc_dapm_enable_pin(&codec->dapm, - "DRC1 Signal Activity"); + snd_soc_dapm_enable_pin(dapm, "DRC1 Signal Activity"); break; case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT: - snd_soc_dapm_enable_pin(&codec->dapm, - "DRC2 Signal Activity"); + snd_soc_dapm_enable_pin(dapm, "DRC2 Signal Activity"); break; default: break; @@ -851,6 +851,134 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w, } EXPORT_SYMBOL_GPL(arizona_hp_ev); +static int arizona_dvfs_enable(struct snd_soc_codec *codec) +{ + const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + int ret; + + ret = regulator_set_voltage(arizona->dcvdd, 1800000, 1800000); + if (ret) { + dev_err(codec->dev, "Failed to boost DCVDD: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, + ARIZONA_SUBSYS_MAX_FREQ, + ARIZONA_SUBSYS_MAX_FREQ); + if (ret) { + dev_err(codec->dev, "Failed to enable subsys max: %d\n", ret); + regulator_set_voltage(arizona->dcvdd, 1200000, 1800000); + return ret; + } + + return 0; +} + +static int arizona_dvfs_disable(struct snd_soc_codec *codec) +{ + const struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + int ret; + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, + ARIZONA_SUBSYS_MAX_FREQ, 0); + if (ret) { + dev_err(codec->dev, "Failed to disable subsys max: %d\n", ret); + return ret; + } + + ret = regulator_set_voltage(arizona->dcvdd, 1200000, 1800000); + if (ret) { + dev_err(codec->dev, "Failed to unboost DCVDD: %d\n", ret); + return ret; + } + + return 0; +} + +int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + mutex_lock(&priv->dvfs_lock); + + if (!priv->dvfs_cached && !priv->dvfs_reqs) { + ret = arizona_dvfs_enable(codec); + if (ret) + goto err; + } + + priv->dvfs_reqs |= flags; +err: + mutex_unlock(&priv->dvfs_lock); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dvfs_up); + +int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + unsigned int old_reqs; + int ret = 0; + + mutex_lock(&priv->dvfs_lock); + + old_reqs = priv->dvfs_reqs; + priv->dvfs_reqs &= ~flags; + + if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs) + ret = arizona_dvfs_disable(codec); + + mutex_unlock(&priv->dvfs_lock); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dvfs_down); + +int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + mutex_lock(&priv->dvfs_lock); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (priv->dvfs_reqs) + ret = arizona_dvfs_enable(codec); + + priv->dvfs_cached = false; + break; + case SND_SOC_DAPM_PRE_PMD: + /* We must ensure DVFS is disabled before the codec goes into + * suspend so that we are never in an illegal state of DVFS + * enabled without enough DCVDD + */ + priv->dvfs_cached = true; + + if (priv->dvfs_reqs) + ret = arizona_dvfs_disable(codec); + break; + default: + break; + } + + mutex_unlock(&priv->dvfs_lock); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev); + +void arizona_init_dvfs(struct arizona_priv *priv) +{ + mutex_init(&priv->dvfs_lock); +} +EXPORT_SYMBOL_GPL(arizona_init_dvfs); + static unsigned int arizona_sysclk_48k_rates[] = { 6144000, 12288000, @@ -1266,7 +1394,7 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream, struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; int base = dai->driver->base; - int i, sr_val; + int i, sr_val, ret; /* * We will need to be more flexible than this in future, @@ -1282,6 +1410,23 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream, } sr_val = i; + switch (priv->arizona->type) { + case WM5102: + case WM8997: + if (arizona_sr_vals[sr_val] >= 88200) + ret = arizona_dvfs_up(codec, ARIZONA_DVFS_SR1_RQ); + else + ret = arizona_dvfs_down(codec, ARIZONA_DVFS_SR1_RQ); + + if (ret) { + arizona_aif_err(dai, "Failed to change DVFS %d\n", ret); + return ret; + } + break; + default: + break; + } + switch (dai_priv->clk) { case ARIZONA_CLK_SYSCLK: switch (priv->arizona->type) { @@ -1474,6 +1619,7 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = dai->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; struct snd_soc_dapm_route routes[2]; @@ -1504,15 +1650,15 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, routes[0].source = arizona_dai_clk_str(dai_priv->clk); routes[1].source = arizona_dai_clk_str(dai_priv->clk); - snd_soc_dapm_del_routes(&codec->dapm, routes, ARRAY_SIZE(routes)); + snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes)); routes[0].source = arizona_dai_clk_str(clk_id); routes[1].source = arizona_dai_clk_str(clk_id); - snd_soc_dapm_add_routes(&codec->dapm, routes, ARRAY_SIZE(routes)); + snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes)); dai_priv->clk = clk_id; - return snd_soc_dapm_sync(&codec->dapm); + return snd_soc_dapm_sync(dapm); } static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate) @@ -2140,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 11ff899b0..43deb0462 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -60,6 +60,9 @@ #define ARIZONA_MAX_DAI 6 #define ARIZONA_MAX_ADSP 4 +#define ARIZONA_DVFS_SR1_RQ 0x001 +#define ARIZONA_DVFS_ADSP1_RQ 0x100 + struct arizona; struct wm_adsp; @@ -84,6 +87,10 @@ struct arizona_priv { unsigned int spk_ena:2; unsigned int spk_ena_pending:1; + + unsigned int dvfs_reqs; + struct mutex dvfs_lock; + bool dvfs_cached; }; #define ARIZONA_NUM_MIXER_INPUTS 103 @@ -107,8 +114,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; arizona_mixer_tlv) #define ARIZONA_MUX_ENUM_DECL(name, reg) \ - SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff, \ - arizona_mixer_texts, arizona_mixer_values) + SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL( \ + name, reg, 0, 0xff, arizona_mixer_texts, arizona_mixer_values) #define ARIZONA_MUX_CTL_DECL(name) \ const struct snd_kcontrol_new name##_mux = \ @@ -210,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); @@ -245,6 +254,12 @@ struct arizona_fll { char clock_ok_name[ARIZONA_FLL_NAME_LEN]; }; +extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags); +extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags); +extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +extern void arizona_init_dvfs(struct arizona_priv *priv); + extern int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, int ok_irq, struct arizona_fll *fll); extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source, diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index e7238b890..b084ad113 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -63,7 +63,7 @@ static int bt_sco_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id bt_sco_driver_ids[] = { +static const struct platform_device_id bt_sco_driver_ids[] = { { .name = "dfbmcs320", }, @@ -74,9 +74,18 @@ static struct platform_device_id bt_sco_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); +#if defined(CONFIG_OF) +static const struct of_device_id bt_sco_codec_of_match[] = { + { .compatible = "delta,dfbmcs320", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match); +#endif + static struct platform_driver bt_sco_driver = { .driver = { .name = "bt-sco", + .of_match_table = of_match_ptr(bt_sco_codec_of_match), }, .probe = bt_sco_probe, .remove = bt_sco_remove, diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index d6dedd4ea..1c895a530 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -92,7 +92,6 @@ static int cq93vc_set_bias_level(struct snd_soc_codec *codec, DAVINCI_VC_REG12_POWER_ALL_OFF); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 60598b230..8f40025b7 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -13,7 +13,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index cac48ddf3..8e3619847 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -457,14 +457,14 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream, case SND_SOC_DAIFMT_RIGHT_J: if (params_width(params) == 16) { snd_soc_update_bits(codec, CS4265_DAC_CTL, - CS4265_DAC_CTL_DIF, (1 << 5)); + CS4265_DAC_CTL_DIF, (2 << 4)); snd_soc_update_bits(codec, CS4265_SPDIF_CTL2, - CS4265_SPDIF_CTL2_DIF, (1 << 7)); + CS4265_SPDIF_CTL2_DIF, (2 << 6)); } else { snd_soc_update_bits(codec, CS4265_DAC_CTL, - CS4265_DAC_CTL_DIF, (3 << 5)); + CS4265_DAC_CTL_DIF, (3 << 4)); snd_soc_update_bits(codec, CS4265_SPDIF_CTL2, - CS4265_SPDIF_CTL2_DIF, (1 << 7)); + CS4265_SPDIF_CTL2_DIF, (3 << 6)); } break; case SND_SOC_DAIFMT_LEFT_J: @@ -473,7 +473,7 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, CS4265_ADC_CTL, CS4265_ADC_DIF, 0); snd_soc_update_bits(codec, CS4265_SPDIF_CTL2, - CS4265_SPDIF_CTL2_DIF, (1 << 6)); + CS4265_SPDIF_CTL2_DIF, 0); break; default: @@ -503,7 +503,6 @@ static int cs4265_set_bias_level(struct snd_soc_codec *codec, CS4265_PWRCTL_PDN); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 1589e7a88..4de52c995 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -897,7 +897,7 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec, CS42L52_PWRCTL1_PDN_CODEC, 0); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_cache_only(cs42l52->regmap, false); regcache_sync(cs42l52->regmap); } @@ -908,7 +908,6 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec, regcache_cache_only(cs42l52->regmap, true); break; } - codec->dapm.bias_level = level; return 0; } @@ -956,7 +955,7 @@ static void cs42l52_beep_work(struct work_struct *work) struct cs42l52_private *cs42l52 = container_of(work, struct cs42l52_private, beep_work); struct snd_soc_codec *codec = cs42l52->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int i; int val = 0; int best = 0; diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index cbc654fe4..1e11ba45a 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -953,7 +953,7 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec, CS42L56_PDN_ALL_MASK, 0); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_cache_only(cs42l56->regmap, false); regcache_sync(cs42l56->regmap); ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies), @@ -978,7 +978,6 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec, cs42l56->supplies); break; } - codec->dapm.bias_level = level; return 0; } @@ -1026,7 +1025,7 @@ static void cs42l56_beep_work(struct work_struct *work) struct cs42l56_private *cs42l56 = container_of(work, struct cs42l56_private, beep_work); struct snd_soc_codec *codec = cs42l56->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int i; int val = 0; int best = 0; diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 8ecedba79..b7853b9d3 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1208,7 +1208,7 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_cache_only(cs42l73->regmap, false); regcache_sync(cs42l73->regmap); } @@ -1228,7 +1228,6 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 1); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index 670ebfe12..e1d46862e 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -380,7 +380,7 @@ EXPORT_SYMBOL_GPL(cs42xx8_regmap_config); static int cs42xx8_codec_probe(struct snd_soc_codec *codec) { struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); switch (cs42xx8->drvdata->num_adcs) { case 3: diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 0f334bc1b..d6f4abbbf 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -333,7 +333,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level != SND_SOC_BIAS_STANDBY) + if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_STANDBY) break; if (IS_ERR(cx20442->por)) err = PTR_ERR(cx20442->por); @@ -341,7 +341,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec, err = regulator_enable(cx20442->por); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level != SND_SOC_BIAS_PREPARE) + if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_PREPARE) break; if (IS_ERR(cx20442->por)) err = PTR_ERR(cx20442->por); @@ -351,8 +351,6 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec, default: break; } - if (!err) - codec->dapm.bias_level = level; return err; } diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 9ec577f0e..238e48a3a 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1374,7 +1374,7 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Enable VMID reference & master bias */ snd_soc_update_bits(codec, DA7213_REFERENCES, DA7213_VMID_EN | DA7213_BIAS_EN, @@ -1387,7 +1387,6 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec, DA7213_VMID_EN | DA7213_BIAS_EN, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 911c26c70..207523686 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -1432,7 +1432,7 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Init Codec */ snd_soc_write(codec, DA732X_REG_REF1, DA732X_VMID_FASTCHG); @@ -1502,8 +1502,6 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index ad19cc567..66bb44647 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1364,7 +1364,7 @@ static int da9055_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Enable VMID reference & master bias */ snd_soc_update_bits(codec, DA9055_REFERENCES, DA9055_VMID_EN | DA9055_BIAS_EN, @@ -1377,7 +1377,6 @@ static int da9055_set_bias_level(struct snd_soc_codec *codec, DA9055_VMID_EN | DA9055_BIAS_EN, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index c5f35a07e..6a091016e 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -536,7 +536,7 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, ES8328_CONTROL1, ES8328_CONTROL1_VMIDSEL_MASK | ES8328_CONTROL1_ENREF, @@ -566,7 +566,6 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c index 3a89ce66d..ebd90283c 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -909,8 +909,6 @@ static int isabelle_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index 933f4476d..9363fdbca 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -258,7 +258,7 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: /* The only way to clear the suspend flag is to reset the codec */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) jz4740_codec_wakeup(regmap); mask = JZ4740_CODEC_1_VREF_DISABLE | @@ -281,8 +281,6 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index a924bb9d7..99ffc49aa 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -23,11 +23,6 @@ #include <sound/soc.h> #include <sound/tlv.h> -struct lm4857 { - struct regmap *regmap; - uint8_t mode; -}; - static const struct reg_default lm4857_default_regs[] = { { 0x0, 0x00 }, { 0x1, 0x00 }, @@ -46,66 +41,33 @@ static const struct reg_default lm4857_default_regs[] = { #define LM4857_WAKEUP 5 #define LM4857_EPGAIN 4 -static int lm4857_get_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.integer.value[0] = lm4857->mode; - - return 0; -} - -static int lm4857_set_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); - uint8_t value = ucontrol->value.integer.value[0]; - - lm4857->mode = value; - - if (codec->dapm.bias_level == SND_SOC_BIAS_ON) - regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, value + 6); - - return 1; -} - -static int lm4857_set_bias_level(struct snd_soc_codec *codec, - enum snd_soc_bias_level level) -{ - struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); - - switch (level) { - case SND_SOC_BIAS_ON: - regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, - lm4857->mode + 6); - break; - case SND_SOC_BIAS_STANDBY: - regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, 0); - break; - default: - break; - } - - codec->dapm.bias_level = level; - - return 0; -} +static const unsigned int lm4857_mode_values[] = { + 0, + 6, + 7, + 8, + 9, +}; -static const char *lm4857_mode[] = { +static const char * const lm4857_mode_texts[] = { + "Off", "Earpiece", "Loudspeaker", "Loudspeaker + Headphone", "Headphone", }; -static SOC_ENUM_SINGLE_EXT_DECL(lm4857_mode_enum, lm4857_mode); +static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(lm4857_mode_enum, + LM4857_CTRL, 0, 0xf, lm4857_mode_texts, lm4857_mode_values); + +static const struct snd_kcontrol_new lm4857_mode_ctrl = + SOC_DAPM_ENUM("Mode", lm4857_mode_enum); static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_DEMUX("Mode", SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl), + SND_SOC_DAPM_OUTPUT("LS"), SND_SOC_DAPM_OUTPUT("HP"), SND_SOC_DAPM_OUTPUT("EP"), @@ -127,24 +89,18 @@ static const struct snd_kcontrol_new lm4857_controls[] = { LM4857_WAKEUP, 1, 0), SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL, LM4857_EPGAIN, 1, 0), - - SOC_ENUM_EXT("Mode", lm4857_mode_enum, - lm4857_get_mode, lm4857_set_mode), }; -/* There is a demux between the input signal and the output signals. - * Currently there is no easy way to model it in ASoC and since it does not make - * much of a difference in practice simply connect the input direclty to the - * outputs. */ static const struct snd_soc_dapm_route lm4857_routes[] = { - {"LS", NULL, "IN"}, - {"HP", NULL, "IN"}, - {"EP", NULL, "IN"}, + { "Mode", NULL, "IN" }, + { "LS", "Loudspeaker", "Mode" }, + { "LS", "Loudspeaker + Headphone", "Mode" }, + { "HP", "Headphone", "Mode" }, + { "HP", "Loudspeaker + Headphone", "Mode" }, + { "EP", "Earpiece", "Mode" }, }; -static struct snd_soc_codec_driver soc_codec_dev_lm4857 = { - .set_bias_level = lm4857_set_bias_level, - +static struct snd_soc_component_driver lm4857_component_driver = { .controls = lm4857_controls, .num_controls = ARRAY_SIZE(lm4857_controls), .dapm_widgets = lm4857_dapm_widgets, @@ -167,25 +123,14 @@ static const struct regmap_config lm4857_regmap_config = { static int lm4857_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct lm4857 *lm4857; - - lm4857 = devm_kzalloc(&i2c->dev, sizeof(*lm4857), GFP_KERNEL); - if (!lm4857) - return -ENOMEM; - - i2c_set_clientdata(i2c, lm4857); - - lm4857->regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); - if (IS_ERR(lm4857->regmap)) - return PTR_ERR(lm4857->regmap); + struct regmap *regmap; - return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0); -} + regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); -static int lm4857_i2c_remove(struct i2c_client *i2c) -{ - snd_soc_unregister_codec(&i2c->dev); - return 0; + return devm_snd_soc_register_component(&i2c->dev, + &lm4857_component_driver, NULL, 0); } static const struct i2c_device_id lm4857_i2c_id[] = { @@ -200,7 +145,6 @@ static struct i2c_driver lm4857_i2c_driver = { .owner = THIS_MODULE, }, .probe = lm4857_i2c_probe, - .remove = lm4857_i2c_remove, .id_table = lm4857_i2c_id, }; diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index c4dfde9bd..6600aa0a3 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1271,7 +1271,7 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) regcache_sync(lm49453->regmap); snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG, @@ -1284,8 +1284,6 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 805b3f8cd..d0f45348b 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1571,7 +1571,7 @@ static int max98088_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) regcache_sync(max98088->regmap); snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN, @@ -1584,7 +1584,6 @@ static int max98088_set_bias_level(struct snd_soc_codec *codec, regcache_mark_dirty(max98088->regmap); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 3e33ef2ac..78268f051 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 */ @@ -1500,7 +1500,7 @@ static const struct snd_soc_dapm_route max98091_dapm_routes[] = { static int max98090_add_widgets(struct snd_soc_codec *codec) { struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); snd_soc_add_codec_controls(codec, max98090_snd_controls, ARRAY_SIZE(max98090_snd_controls)); @@ -1798,16 +1798,17 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec, * away from ON. Disable the clock in that case, otherwise * enable it. */ - if (!IS_ERR(max98090->mclk)) { - if (codec->dapm.bias_level == SND_SOC_BIAS_ON) - clk_disable_unprepare(max98090->mclk); - else - clk_prepare_enable(max98090->mclk); - } + if (IS_ERR(max98090->mclk)) + break; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) + clk_disable_unprepare(max98090->mclk); + else + clk_prepare_enable(max98090->mclk); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(max98090->regmap); if (ret != 0) { dev_err(codec->dev, @@ -1824,7 +1825,6 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec, regcache_mark_dirty(max98090->regmap); break; } - codec->dapm.bias_level = level; return 0; } @@ -2187,7 +2187,6 @@ static void max98090_jack_work(struct work_struct *work) struct max98090_priv, jack_work.work); struct snd_soc_codec *codec = max98090->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; int status = 0; int reg; @@ -2266,8 +2265,6 @@ static void max98090_jack_work(struct work_struct *work) snd_soc_jack_report(max98090->jack, status, SND_JACK_HEADSET | SND_JACK_BTN_0); - - snd_soc_dapm_sync(dapm); } static irqreturn_t max98090_interrupt(int irq, void *data) @@ -2422,6 +2419,8 @@ static int max98090_probe(struct snd_soc_codec *codec) struct max98090_cdata *cdata; enum max98090_type devtype; int ret = 0; + int err; + unsigned int micbias; dev_dbg(codec->dev, "max98090_probe\n"); @@ -2506,8 +2505,17 @@ static int max98090_probe(struct snd_soc_codec *codec) snd_soc_write(codec, M98090_REG_BIAS_CONTROL, M98090_VCM_MODE_MASK); + err = device_property_read_u32(codec->dev, "maxim,micbias", &micbias); + if (err) { + micbias = M98090_MBVSEL_2V8; + dev_info(codec->dev, "use default 2.8v micbias\n"); + } else if (micbias < M98090_MBVSEL_2V2 || micbias > M98090_MBVSEL_2V8) { + dev_err(codec->dev, "micbias out of range 0x%x\n", micbias); + micbias = M98090_MBVSEL_2V8; + } + snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE, - M98090_MBVSEL_MASK, M98090_MBVSEL_2V8); + M98090_MBVSEL_MASK, micbias); max98090_add_widgets(codec); @@ -2696,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/max98095.c b/sound/soc/codecs/max98095.c index 8fba0c3db..9a46d3dcf 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1650,16 +1650,17 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec, * away from ON. Disable the clock in that case, otherwise * enable it. */ - if (!IS_ERR(max98095->mclk)) { - if (codec->dapm.bias_level == SND_SOC_BIAS_ON) - clk_disable_unprepare(max98095->mclk); - else - clk_prepare_enable(max98095->mclk); - } + if (IS_ERR(max98095->mclk)) + break; + + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) + clk_disable_unprepare(max98095->mclk); + else + clk_prepare_enable(max98095->mclk); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(max98095->regmap); if (ret != 0) { @@ -1678,7 +1679,6 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec, regcache_mark_dirty(max98095->regmap); break; } - codec->dapm.bias_level = level; return 0; } @@ -2198,7 +2198,7 @@ static int max98095_suspend(struct snd_soc_codec *codec) if (max98095->headphone_jack || max98095->mic_jack) max98095_jack_detect_disable(codec); - max98095_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -2208,7 +2208,7 @@ static int max98095_resume(struct snd_soc_codec *codec) struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec); struct i2c_client *client = to_i2c_client(codec->dev); - max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); if (max98095->headphone_jack || max98095->mic_jack) { max98095_jack_detect_enable(codec); @@ -2301,8 +2301,8 @@ static int max98095_probe(struct snd_soc_codec *codec) /* register an audio interrupt */ ret = request_threaded_irq(client->irq, NULL, max98095_report_jack, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "max98095", codec); + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "max98095", codec); if (ret) { dev_err(codec->dev, "Failed to request IRQ: %d\n", ret); goto err_access; diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c index bf3e933ee..3a2fda08a 100644 --- a/sound/soc/codecs/max98357a.c +++ b/sound/soc/codecs/max98357a.c @@ -60,13 +60,12 @@ static int max98357a_codec_probe(struct snd_soc_codec *codec) { struct gpio_desc *sdmode; - sdmode = devm_gpiod_get(codec->dev, "sdmode"); + sdmode = devm_gpiod_get(codec->dev, "sdmode", GPIOD_OUT_LOW); if (IS_ERR(sdmode)) { dev_err(codec->dev, "%s() unable to get sdmode GPIO: %ld\n", __func__, PTR_ERR(sdmode)); return PTR_ERR(sdmode); } - gpiod_direction_output(sdmode, 0); snd_soc_codec_set_drvdata(codec, sdmode); return 0; diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index 10f8e47ce..481d58f1c 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -252,7 +252,7 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(max9850->regmap); if (ret) { dev_err(codec->dev, @@ -264,7 +264,6 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_OFF: break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 711f55039..b74118e01 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; } @@ -523,7 +487,7 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: /* VMID ON */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG, ML26124_VMID, ML26124_VMID); msleep(500); @@ -536,7 +500,6 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec, ML26124_VMID, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index e12764d15..de16429f0 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -242,7 +242,7 @@ static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_STANDBY: break; @@ -270,7 +270,7 @@ static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_STANDBY: break; @@ -298,7 +298,7 @@ static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_STANDBY: break; @@ -641,8 +641,6 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c new file mode 100644 index 000000000..91d5166bd --- /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 000000000..1cb56e50b --- /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 0fcda35a3..5c43e263b 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) { @@ -301,6 +215,7 @@ static int rt286_support_power_controls[] = { static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) { + struct snd_soc_dapm_context *dapm; unsigned int val, buf; *hp = false; @@ -308,6 +223,9 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) if (!rt286->codec) return -EINVAL; + + dapm = snd_soc_codec_get_dapm(rt286->codec); + if (rt286->pdata.cbj_en) { regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf); *hp = buf & 0x80000000; @@ -316,14 +234,11 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) regmap_update_bits(rt286->regmap, RT286_DC_GAIN, 0x200, 0x200); - snd_soc_dapm_force_enable_pin(&rt286->codec->dapm, - "HV"); - snd_soc_dapm_force_enable_pin(&rt286->codec->dapm, - "VREF"); + snd_soc_dapm_force_enable_pin(dapm, "HV"); + snd_soc_dapm_force_enable_pin(dapm, "VREF"); /* power LDO1 */ - snd_soc_dapm_force_enable_pin(&rt286->codec->dapm, - "LDO1"); - snd_soc_dapm_sync(&rt286->codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24); msleep(50); @@ -360,11 +275,11 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) *mic = buf & 0x80000000; } - snd_soc_dapm_disable_pin(&rt286->codec->dapm, "HV"); - snd_soc_dapm_disable_pin(&rt286->codec->dapm, "VREF"); + snd_soc_dapm_disable_pin(dapm, "HV"); + snd_soc_dapm_disable_pin(dapm, "VREF"); if (!*hp) - snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1"); - snd_soc_dapm_sync(&rt286->codec->dapm); + snd_soc_dapm_disable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); return 0; } @@ -391,6 +306,7 @@ static void rt286_jack_detect_work(struct work_struct *work) int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); rt286->jack = jack; @@ -398,7 +314,7 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) if (jack) { /* enable IRQ */ if (rt286->jack->status & SND_JACK_HEADPHONE) - snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1"); + snd_soc_dapm_force_enable_pin(dapm, "LDO1"); regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2); /* Send an initial empty report */ snd_soc_jack_report(rt286->jack, rt286->jack->status, @@ -406,9 +322,9 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) } else { /* disable IRQ */ regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x0); - snd_soc_dapm_disable_pin(&codec->dapm, "LDO1"); + snd_soc_dapm_disable_pin(dapm, "LDO1"); } - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); return 0; } @@ -985,7 +901,7 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec, { switch (level) { case SND_SOC_BIAS_PREPARE: - if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { + if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) { snd_soc_write(codec, RT286_SET_AUDIO_POWER, AC_PWRST_D0); snd_soc_update_bits(codec, @@ -1012,7 +928,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } @@ -1173,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), @@ -1247,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/rt5631.c b/sound/soc/codecs/rt5631.c index 2c10d7772..058167c80 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1546,7 +1546,7 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3, RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS, RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS); @@ -1569,7 +1569,6 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } @@ -1615,7 +1614,7 @@ static int rt5631_probe(struct snd_soc_codec *codec) RT5631_DMIC_R_CH_LATCH_RISING); } - codec->dapm.bias_level = SND_SOC_BIAS_STANDBY; + snd_soc_codec_init_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 178e55d4d..9bc78e575 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 }, @@ -1870,7 +1869,7 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec, { switch (level) { case SND_SOC_BIAS_STANDBY: - if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) { + if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) { snd_soc_update_bits(codec, RT5640_PWR_ANLG1, RT5640_PWR_VREF1 | RT5640_PWR_MB | RT5640_PWR_BG | RT5640_PWR_VREF2, @@ -1902,7 +1901,6 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } @@ -1935,11 +1933,12 @@ EXPORT_SYMBOL_GPL(rt5640_dmic_enable); static int rt5640_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); rt5640->codec = codec; - rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301); snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030); @@ -1951,18 +1950,18 @@ static int rt5640_probe(struct snd_soc_codec *codec) snd_soc_add_codec_controls(codec, rt5640_specific_snd_controls, ARRAY_SIZE(rt5640_specific_snd_controls)); - snd_soc_dapm_new_controls(&codec->dapm, + snd_soc_dapm_new_controls(dapm, rt5640_specific_dapm_widgets, ARRAY_SIZE(rt5640_specific_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5640_specific_dapm_routes, ARRAY_SIZE(rt5640_specific_dapm_routes)); break; case RT5640_ID_5639: - snd_soc_dapm_new_controls(&codec->dapm, + snd_soc_dapm_new_controls(dapm, rt5639_specific_dapm_widgets, ARRAY_SIZE(rt5639_specific_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5639_specific_dapm_routes, ARRAY_SIZE(rt5639_specific_dapm_routes)); break; @@ -1991,7 +1990,7 @@ static int rt5640_suspend(struct snd_soc_codec *codec) { struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); - rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); rt5640_reset(codec); regcache_cache_only(rt5640->regmap, true); regcache_mark_dirty(rt5640->regmap); @@ -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 2ee44abd5..961bd7e58 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -18,7 +18,9 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/acpi.h> +#include <linux/dmi.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -347,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: @@ -415,9 +418,9 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg) } static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); -static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); -static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); /* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ @@ -432,30 +435,6 @@ static unsigned int bst_tlv[] = { 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), }; -static const char * const rt5645_tdm_data_swap_select[] = { - "L/R", "R/L", "L/L", "R/R" -}; - -static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum, - RT5645_TDM_CTRL_1, 6, rt5645_tdm_data_swap_select); - -static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum, - RT5645_TDM_CTRL_1, 4, rt5645_tdm_data_swap_select); - -static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum, - RT5645_TDM_CTRL_1, 2, rt5645_tdm_data_swap_select); - -static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot6_7_enum, - RT5645_TDM_CTRL_1, 0, rt5645_tdm_data_swap_select); - -static const char * const rt5645_tdm_adc_data_select[] = { - "1/2/R", "2/1/R", "R/1/2", "R/2/1" -}; - -static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_sel_enum, - RT5645_TDM_CTRL_1, 8, - rt5645_tdm_adc_data_select); - static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* Speaker Output Volume */ SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, @@ -464,9 +443,9 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), /* Headphone Output Volume */ - SOC_DOUBLE("HP Channel Switch", RT5645_HP_VOL, + SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL, RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), - SOC_DOUBLE_TLV("HP Playback Volume", RT5645_HP_VOL, + SOC_DOUBLE_TLV("Headphone Playback Volume", RT5645_HP_VOL, RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), /* OUTPUT Control */ @@ -481,9 +460,9 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { SOC_DOUBLE("DAC2 Playback Switch", RT5645_DAC_CTRL, RT5645_M_DAC_L2_VOL_SFT, RT5645_M_DAC_R2_VOL_SFT, 1, 1), SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5645_DAC1_DIG_VOL, - RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv), + RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 87, 0, dac_vol_tlv), SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5645_DAC2_DIG_VOL, - RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv), + RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 87, 0, dac_vol_tlv), /* IN1/IN2 Control */ SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1, @@ -499,11 +478,11 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { SOC_DOUBLE("ADC Capture Switch", RT5645_STO1_ADC_DIG_VOL, RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1), SOC_DOUBLE_TLV("ADC Capture Volume", RT5645_STO1_ADC_DIG_VOL, - RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv), + RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv), SOC_DOUBLE("Mono ADC Capture Switch", RT5645_MONO_ADC_DIG_VOL, RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1), SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5645_MONO_ADC_DIG_VOL, - RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv), + RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv), /* ADC Boost Volume Control */ SOC_DOUBLE_TLV("STO1 ADC Boost Gain", RT5645_ADC_BST_VOL1, @@ -516,17 +495,6 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* I2S2 function select */ SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT, 1, 1), - - /* TDM */ - SOC_ENUM("TDM Adc Slot0 1 Data", rt5645_tdm_adc_slot0_1_enum), - SOC_ENUM("TDM Adc Slot2 3 Data", rt5645_tdm_adc_slot2_3_enum), - SOC_ENUM("TDM Adc Slot4 5 Data", rt5645_tdm_adc_slot4_5_enum), - SOC_ENUM("TDM Adc Slot6 7 Data", rt5645_tdm_adc_slot6_7_enum), - SOC_ENUM("TDM IF1 ADC DATA Sel", rt5645_tdm_adc_sel_enum), - SOC_SINGLE("TDM IF1_DAC1_L Sel", RT5645_TDM_CTRL_3, 12, 7, 0), - SOC_SINGLE("TDM IF1_DAC1_R Sel", RT5645_TDM_CTRL_3, 8, 7, 0), - SOC_SINGLE("TDM IF1_DAC2_L Sel", RT5645_TDM_CTRL_3, 4, 7, 0), - SOC_SINGLE("TDM IF1_DAC2_R Sel", RT5645_TDM_CTRL_3, 0, 7, 0), }; /** @@ -1093,7 +1061,8 @@ static const struct snd_kcontrol_new rt5645_mono_adc_r2_mux = /* MX-77 [9:8] */ static const char * const rt5645_if1_adc_in_src[] = { - "IF_ADC1", "IF_ADC2", "VAD_ADC" + "IF_ADC1/IF_ADC2/VAD_ADC", "IF_ADC2/IF_ADC1/VAD_ADC", + "VAD_ADC/IF_ADC1/IF_ADC2", "VAD_ADC/IF_ADC2/IF_ADC1" }; static SOC_ENUM_SINGLE_DECL( @@ -1103,6 +1072,140 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5645_if1_adc_in_mux = SOC_DAPM_ENUM("IF1 ADC IN source", rt5645_if1_adc_in_enum); +/* MX-78 [4:0] */ +static const char * const rt5650_if1_adc_in_src[] = { + "IF_ADC1/IF_ADC2/DAC_REF/Null", + "IF_ADC1/IF_ADC2/Null/DAC_REF", + "IF_ADC1/DAC_REF/IF_ADC2/Null", + "IF_ADC1/DAC_REF/Null/IF_ADC2", + "IF_ADC1/Null/DAC_REF/IF_ADC2", + "IF_ADC1/Null/IF_ADC2/DAC_REF", + + "IF_ADC2/IF_ADC1/DAC_REF/Null", + "IF_ADC2/IF_ADC1/Null/DAC_REF", + "IF_ADC2/DAC_REF/IF_ADC1/Null", + "IF_ADC2/DAC_REF/Null/IF_ADC1", + "IF_ADC2/Null/DAC_REF/IF_ADC1", + "IF_ADC2/Null/IF_ADC1/DAC_REF", + + "DAC_REF/IF_ADC1/IF_ADC2/Null", + "DAC_REF/IF_ADC1/Null/IF_ADC2", + "DAC_REF/IF_ADC2/IF_ADC1/Null", + "DAC_REF/IF_ADC2/Null/IF_ADC1", + "DAC_REF/Null/IF_ADC1/IF_ADC2", + "DAC_REF/Null/IF_ADC2/IF_ADC1", + + "Null/IF_ADC1/IF_ADC2/DAC_REF", + "Null/IF_ADC1/DAC_REF/IF_ADC2", + "Null/IF_ADC2/IF_ADC1/DAC_REF", + "Null/IF_ADC2/DAC_REF/IF_ADC1", + "Null/DAC_REF/IF_ADC1/IF_ADC2", + "Null/DAC_REF/IF_ADC2/IF_ADC1", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5650_if1_adc_in_enum, RT5645_TDM_CTRL_2, + 0, rt5650_if1_adc_in_src); + +static const struct snd_kcontrol_new rt5650_if1_adc_in_mux = + SOC_DAPM_ENUM("IF1 ADC IN source", rt5650_if1_adc_in_enum); + +/* MX-78 [15:14][13:12][11:10] */ +static const char * const rt5645_tdm_adc_swap_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot0_1_enum, + RT5645_TDM_CTRL_2, 14, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_adc1_in_mux = + SOC_DAPM_ENUM("IF1 ADC1 IN source", rt5650_tdm_adc_slot0_1_enum); + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot2_3_enum, + RT5645_TDM_CTRL_2, 12, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_adc2_in_mux = + SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5650_tdm_adc_slot2_3_enum); + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot4_5_enum, + RT5645_TDM_CTRL_2, 10, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_adc3_in_mux = + SOC_DAPM_ENUM("IF1 ADC3 IN source", rt5650_tdm_adc_slot4_5_enum); + +/* MX-77 [7:6][5:4][3:2] */ +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum, + RT5645_TDM_CTRL_1, 6, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_adc1_in_mux = + SOC_DAPM_ENUM("IF1 ADC1 IN source", rt5645_tdm_adc_slot0_1_enum); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum, + RT5645_TDM_CTRL_1, 4, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_adc2_in_mux = + SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5645_tdm_adc_slot2_3_enum); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum, + RT5645_TDM_CTRL_1, 2, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_adc3_in_mux = + SOC_DAPM_ENUM("IF1 ADC3 IN source", rt5645_tdm_adc_slot4_5_enum); + +/* MX-79 [14:12][10:8][6:4][2:0] */ +static const char * const rt5645_tdm_dac_swap_select[] = { + "Slot0", "Slot1", "Slot2", "Slot3" +}; + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac0_enum, + RT5645_TDM_CTRL_3, 12, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_dac0_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC0 source", rt5645_tdm_dac0_enum); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac1_enum, + RT5645_TDM_CTRL_3, 8, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_dac1_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC1 source", rt5645_tdm_dac1_enum); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac2_enum, + RT5645_TDM_CTRL_3, 4, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_dac2_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC2 source", rt5645_tdm_dac2_enum); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac3_enum, + RT5645_TDM_CTRL_3, 0, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_dac3_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC3 source", rt5645_tdm_dac3_enum); + +/* MX-7a [14:12][10:8][6:4][2:0] */ +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac0_enum, + RT5650_TDM_CTRL_4, 12, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_dac0_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC0 source", rt5650_tdm_dac0_enum); + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac1_enum, + RT5650_TDM_CTRL_4, 8, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_dac1_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC1 source", rt5650_tdm_dac1_enum); + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac2_enum, + RT5650_TDM_CTRL_4, 4, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_dac2_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC2 source", rt5650_tdm_dac2_enum); + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac3_enum, + RT5650_TDM_CTRL_4, 0, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_dac3_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC3 source", rt5650_tdm_dac3_enum); + /* MX-2d [3] [2] */ static const char * const rt5650_a_dac1_src[] = { "DAC1", "Stereo DAC Mixer" @@ -1227,52 +1330,79 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on) if (on) { if (hp_amp_power_count <= 0) { - /* depop parameters */ - snd_soc_update_bits(codec, RT5645_DEPOP_M2, - RT5645_DEPOP_MASK, RT5645_DEPOP_MAN); - snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d); - regmap_write(rt5645->regmap, RT5645_PR_BASE + - RT5645_HP_DCC_INT1, 0x9f01); - mdelay(150); - /* headphone amp power on */ - snd_soc_update_bits(codec, RT5645_PWR_ANLG1, - RT5645_PWR_FV1 | RT5645_PWR_FV2 , 0); - snd_soc_update_bits(codec, RT5645_PWR_VOL, - RT5645_PWR_HV_L | RT5645_PWR_HV_R, - RT5645_PWR_HV_L | RT5645_PWR_HV_R); - snd_soc_update_bits(codec, RT5645_PWR_ANLG1, - RT5645_PWR_HP_L | RT5645_PWR_HP_R | - RT5645_PWR_HA, - RT5645_PWR_HP_L | RT5645_PWR_HP_R | - RT5645_PWR_HA); - mdelay(5); - snd_soc_update_bits(codec, RT5645_PWR_ANLG1, - RT5645_PWR_FV1 | RT5645_PWR_FV2, - RT5645_PWR_FV1 | RT5645_PWR_FV2); - - snd_soc_update_bits(codec, RT5645_DEPOP_M1, - RT5645_HP_CO_MASK | RT5645_HP_SG_MASK, - RT5645_HP_CO_EN | RT5645_HP_SG_EN); - regmap_write(rt5645->regmap, RT5645_PR_BASE + - 0x14, 0x1aaa); - regmap_write(rt5645->regmap, RT5645_PR_BASE + - 0x24, 0x0430); + if (rt5645->codec_type == CODEC_TYPE_RT5650) { + snd_soc_write(codec, RT5645_CHARGE_PUMP, + 0x0e06); + snd_soc_write(codec, RT5645_DEPOP_M1, 0x001d); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + 0x3e, 0x7400); + snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_MAMP_INT_REG2, 0xfc00); + snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); + } else { + /* depop parameters */ + snd_soc_update_bits(codec, RT5645_DEPOP_M2, + RT5645_DEPOP_MASK, RT5645_DEPOP_MAN); + snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_HP_DCC_INT1, 0x9f01); + mdelay(150); + /* headphone amp power on */ + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_FV1 | RT5645_PWR_FV2, 0); + snd_soc_update_bits(codec, RT5645_PWR_VOL, + RT5645_PWR_HV_L | RT5645_PWR_HV_R, + RT5645_PWR_HV_L | RT5645_PWR_HV_R); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R | + RT5645_PWR_HA, + RT5645_PWR_HP_L | RT5645_PWR_HP_R | + RT5645_PWR_HA); + mdelay(5); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_FV1 | RT5645_PWR_FV2, + RT5645_PWR_FV1 | RT5645_PWR_FV2); + + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_HP_CO_MASK | RT5645_HP_SG_MASK, + RT5645_HP_CO_EN | RT5645_HP_SG_EN); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + 0x14, 0x1aaa); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + 0x24, 0x0430); + } } hp_amp_power_count++; } else { hp_amp_power_count--; if (hp_amp_power_count <= 0) { - snd_soc_update_bits(codec, RT5645_DEPOP_M1, - RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK | - RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS | - RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS); - /* headphone amp power down */ - snd_soc_write(codec, RT5645_DEPOP_M1, 0x0000); - snd_soc_update_bits(codec, RT5645_PWR_ANLG1, - RT5645_PWR_HP_L | RT5645_PWR_HP_R | - RT5645_PWR_HA, 0); - snd_soc_update_bits(codec, RT5645_DEPOP_M2, - RT5645_DEPOP_MASK, 0); + if (rt5645->codec_type == CODEC_TYPE_RT5650) { + regmap_write(rt5645->regmap, RT5645_PR_BASE + + 0x3e, 0x7400); + snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_MAMP_INT_REG2, 0xfc00); + snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); + msleep(100); + snd_soc_write(codec, RT5645_DEPOP_M1, 0x0001); + + } else { + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_HP_SG_MASK | + RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, + RT5645_HP_SG_DIS | + RT5645_HP_L_SMT_DIS | + RT5645_HP_R_SMT_DIS); + /* headphone amp power down */ + snd_soc_write(codec, RT5645_DEPOP_M1, 0x0000); + snd_soc_update_bits(codec, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R | + RT5645_PWR_HA, 0); + snd_soc_update_bits(codec, RT5645_DEPOP_M2, + RT5645_DEPOP_MASK, 0); + } } } } @@ -1287,56 +1417,52 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_POST_PMU: hp_amp_power(codec, 1); /* headphone unmute sequence */ - if (rt5645->codec_type == CODEC_TYPE_RT5650) { - snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737); - } else { + if (rt5645->codec_type == CODEC_TYPE_RT5645) { snd_soc_update_bits(codec, RT5645_DEPOP_M3, RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK | RT5645_CP_FQ3_MASK, (RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ1_SFT) | (RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) | (RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ3_SFT)); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_MAMP_INT_REG2, 0xfc00); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_RSTN_MASK, RT5645_RSTN_EN); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS | + RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN); + msleep(40); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS | + RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS); } - regmap_write(rt5645->regmap, - RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); - snd_soc_update_bits(codec, RT5645_DEPOP_M1, - RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN); - snd_soc_update_bits(codec, RT5645_DEPOP_M1, - RT5645_RSTN_MASK, RT5645_RSTN_EN); - snd_soc_update_bits(codec, RT5645_DEPOP_M1, - RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK | - RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS | - RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN); - msleep(40); - snd_soc_update_bits(codec, RT5645_DEPOP_M1, - RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK | - RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS | - RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS); break; case SND_SOC_DAPM_PRE_PMD: /* headphone mute sequence */ - if (rt5645->codec_type == CODEC_TYPE_RT5650) { - snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737); - } else { + if (rt5645->codec_type == CODEC_TYPE_RT5645) { snd_soc_update_bits(codec, RT5645_DEPOP_M3, RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK | RT5645_CP_FQ3_MASK, (RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ1_SFT) | (RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) | (RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ3_SFT)); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_MAMP_INT_REG2, 0xfc00); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_HP_SG_MASK, RT5645_HP_SG_EN); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_RSTP_MASK, RT5645_RSTP_EN); + snd_soc_update_bits(codec, RT5645_DEPOP_M1, + RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS | + RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN); + msleep(30); } - regmap_write(rt5645->regmap, - RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); - snd_soc_update_bits(codec, RT5645_DEPOP_M1, - RT5645_HP_SG_MASK, RT5645_HP_SG_EN); - snd_soc_update_bits(codec, RT5645_DEPOP_M1, - RT5645_RSTP_MASK, RT5645_RSTP_EN); - snd_soc_update_bits(codec, RT5645_DEPOP_M1, - RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK | - RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS | - RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN); - msleep(30); hp_amp_power(codec, 0); break; @@ -1571,20 +1697,33 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), /* IF1 2 Mux */ - SND_SOC_DAPM_MUX("IF1 ADC Mux", SND_SOC_NOPM, + SND_SOC_DAPM_MUX("RT5645 IF1 ADC1 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc1_in_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC2 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc2_in_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC3 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc3_in_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC Mux", SND_SOC_NOPM, 0, 0, &rt5645_if1_adc_in_mux), + SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM, 0, 0, &rt5645_if2_adc_in_mux), /* Digital Interface */ SND_SOC_DAPM_SUPPLY("I2S1", RT5645_PWR_DIG1, RT5645_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0, + &rt5645_if1_dac0_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0, + &rt5645_if1_dac1_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0, + &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_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), @@ -1726,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[] = { @@ -1848,42 +2005,32 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { { "IF_ADC2", NULL, "Mono ADC MIXR" }, { "VAD_ADC", NULL, "VAD ADC Mux" }, - { "IF1 ADC Mux", "IF_ADC1", "IF_ADC1" }, - { "IF1 ADC Mux", "IF_ADC2", "IF_ADC2" }, - { "IF1 ADC Mux", "VAD_ADC", "VAD_ADC" }, - { "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" }, { "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" }, { "IF2 ADC Mux", "VAD_ADC", "VAD_ADC" }, { "IF1 ADC", NULL, "I2S1" }, - { "IF1 ADC", NULL, "IF1 ADC Mux" }, { "IF2 ADC", NULL, "I2S2" }, { "IF2 ADC", NULL, "IF2 ADC Mux" }, - { "AIF1TX", NULL, "IF1 ADC" }, - { "AIF1TX", NULL, "IF2 ADC" }, { "AIF2TX", NULL, "IF2 ADC" }, + { "IF1 DAC0", NULL, "AIF1RX" }, { "IF1 DAC1", NULL, "AIF1RX" }, { "IF1 DAC2", NULL, "AIF1RX" }, + { "IF1 DAC3", NULL, "AIF1RX" }, { "IF2 DAC", NULL, "AIF2RX" }, + { "IF1 DAC0", NULL, "I2S1" }, { "IF1 DAC1", NULL, "I2S1" }, { "IF1 DAC2", NULL, "I2S1" }, + { "IF1 DAC3", NULL, "I2S1" }, { "IF2 DAC", NULL, "I2S2" }, - { "IF1 DAC2 L", NULL, "IF1 DAC2" }, - { "IF1 DAC2 R", NULL, "IF1 DAC2" }, - { "IF1 DAC1 L", NULL, "IF1 DAC1" }, - { "IF1 DAC1 R", NULL, "IF1 DAC1" }, { "IF2 DAC L", NULL, "IF2 DAC" }, { "IF2 DAC R", NULL, "IF2 DAC" }, - { "DAC1 L Mux", "IF1 DAC", "IF1 DAC1 L" }, { "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" }, - - { "DAC1 R Mux", "IF1 DAC", "IF1 DAC1 R" }, { "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" }, { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" }, @@ -1893,14 +2040,12 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" }, { "DAC1 MIXR", NULL, "dac stereo1 filter" }, - { "DAC L2 Mux", "IF1 DAC", "IF1 DAC2 L" }, { "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" }, { "DAC L2 Mux", "Mono ADC", "Mono ADC MIXL" }, { "DAC L2 Mux", "VAD_ADC", "VAD_ADC" }, { "DAC L2 Volume", NULL, "DAC L2 Mux" }, { "DAC L2 Volume", NULL, "dac mono left filter" }, - { "DAC R2 Mux", "IF1 DAC", "IF1 DAC2 R" }, { "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" }, { "DAC R2 Mux", "Mono ADC", "Mono ADC MIXR" }, { "DAC R2 Mux", "Haptic", "Haptic Generator" }, @@ -2038,6 +2183,80 @@ static const struct snd_soc_dapm_route rt5650_specific_dapm_routes[] = { { "DAC R1", NULL, "A DAC1 R Mux" }, { "DAC L2", NULL, "A DAC2 L Mux" }, { "DAC R2", NULL, "A DAC2 R Mux" }, + + { "RT5650 IF1 ADC1 Swap Mux", "L/R", "IF_ADC1" }, + { "RT5650 IF1 ADC1 Swap Mux", "R/L", "IF_ADC1" }, + { "RT5650 IF1 ADC1 Swap Mux", "L/L", "IF_ADC1" }, + { "RT5650 IF1 ADC1 Swap Mux", "R/R", "IF_ADC1" }, + + { "RT5650 IF1 ADC2 Swap Mux", "L/R", "IF_ADC2" }, + { "RT5650 IF1 ADC2 Swap Mux", "R/L", "IF_ADC2" }, + { "RT5650 IF1 ADC2 Swap Mux", "L/L", "IF_ADC2" }, + { "RT5650 IF1 ADC2 Swap Mux", "R/R", "IF_ADC2" }, + + { "RT5650 IF1 ADC3 Swap Mux", "L/R", "VAD_ADC" }, + { "RT5650 IF1 ADC3 Swap Mux", "R/L", "VAD_ADC" }, + { "RT5650 IF1 ADC3 Swap Mux", "L/L", "VAD_ADC" }, + { "RT5650 IF1 ADC3 Swap Mux", "R/R", "VAD_ADC" }, + + { "IF1 ADC", NULL, "RT5650 IF1 ADC1 Swap Mux" }, + { "IF1 ADC", NULL, "RT5650 IF1 ADC2 Swap Mux" }, + { "IF1 ADC", NULL, "RT5650 IF1 ADC3 Swap Mux" }, + + { "RT5650 IF1 ADC Mux", "IF_ADC1/IF_ADC2/DAC_REF/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC1/IF_ADC2/Null/DAC_REF", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC1/DAC_REF/IF_ADC2/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC1/DAC_REF/Null/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC1/Null/DAC_REF/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC1/Null/IF_ADC2/DAC_REF", "IF1 ADC" }, + + { "RT5650 IF1 ADC Mux", "IF_ADC2/IF_ADC1/DAC_REF/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC2/IF_ADC1/Null/DAC_REF", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC2/DAC_REF/IF_ADC1/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC2/DAC_REF/Null/IF_ADC1", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC2/Null/DAC_REF/IF_ADC1", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC2/Null/IF_ADC1/DAC_REF", "IF1 ADC" }, + + { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC1/IF_ADC2/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC1/Null/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC2/IF_ADC1/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC2/Null/IF_ADC1", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "DAC_REF/Null/IF_ADC1/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "DAC_REF/Null/IF_ADC2/IF_ADC1", "IF1 ADC" }, + + { "RT5650 IF1 ADC Mux", "Null/IF_ADC1/IF_ADC2/DAC_REF", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "Null/IF_ADC1/DAC_REF/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "Null/IF_ADC2/IF_ADC1/DAC_REF", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "Null/IF_ADC2/DAC_REF/IF_ADC1", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "Null/DAC_REF/IF_ADC1/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "Null/DAC_REF/IF_ADC2/IF_ADC1", "IF1 ADC" }, + { "AIF1TX", NULL, "RT5650 IF1 ADC Mux" }, + + { "RT5650 IF1 DAC1 L Mux", "Slot0", "IF1 DAC0" }, + { "RT5650 IF1 DAC1 L Mux", "Slot1", "IF1 DAC1" }, + { "RT5650 IF1 DAC1 L Mux", "Slot2", "IF1 DAC2" }, + { "RT5650 IF1 DAC1 L Mux", "Slot3", "IF1 DAC3" }, + + { "RT5650 IF1 DAC1 R Mux", "Slot0", "IF1 DAC0" }, + { "RT5650 IF1 DAC1 R Mux", "Slot1", "IF1 DAC1" }, + { "RT5650 IF1 DAC1 R Mux", "Slot2", "IF1 DAC2" }, + { "RT5650 IF1 DAC1 R Mux", "Slot3", "IF1 DAC3" }, + + { "RT5650 IF1 DAC2 L Mux", "Slot0", "IF1 DAC0" }, + { "RT5650 IF1 DAC2 L Mux", "Slot1", "IF1 DAC1" }, + { "RT5650 IF1 DAC2 L Mux", "Slot2", "IF1 DAC2" }, + { "RT5650 IF1 DAC2 L Mux", "Slot3", "IF1 DAC3" }, + + { "RT5650 IF1 DAC2 R Mux", "Slot0", "IF1 DAC0" }, + { "RT5650 IF1 DAC2 R Mux", "Slot1", "IF1 DAC1" }, + { "RT5650 IF1 DAC2 R Mux", "Slot2", "IF1 DAC2" }, + { "RT5650 IF1 DAC2 R Mux", "Slot3", "IF1 DAC3" }, + + { "DAC1 L Mux", "IF1 DAC", "RT5650 IF1 DAC1 L Mux" }, + { "DAC1 R Mux", "IF1 DAC", "RT5650 IF1 DAC1 R Mux" }, + + { "DAC L2 Mux", "IF1 DAC", "RT5650 IF1 DAC2 L Mux" }, + { "DAC R2 Mux", "IF1 DAC", "RT5650 IF1 DAC2 R Mux" }, }; static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = { @@ -2045,6 +2264,57 @@ static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = { { "DAC R1", NULL, "Stereo DAC MIXR" }, { "DAC L2", NULL, "Mono DAC MIXL" }, { "DAC R2", NULL, "Mono DAC MIXR" }, + + { "RT5645 IF1 ADC1 Swap Mux", "L/R", "IF_ADC1" }, + { "RT5645 IF1 ADC1 Swap Mux", "R/L", "IF_ADC1" }, + { "RT5645 IF1 ADC1 Swap Mux", "L/L", "IF_ADC1" }, + { "RT5645 IF1 ADC1 Swap Mux", "R/R", "IF_ADC1" }, + + { "RT5645 IF1 ADC2 Swap Mux", "L/R", "IF_ADC2" }, + { "RT5645 IF1 ADC2 Swap Mux", "R/L", "IF_ADC2" }, + { "RT5645 IF1 ADC2 Swap Mux", "L/L", "IF_ADC2" }, + { "RT5645 IF1 ADC2 Swap Mux", "R/R", "IF_ADC2" }, + + { "RT5645 IF1 ADC3 Swap Mux", "L/R", "VAD_ADC" }, + { "RT5645 IF1 ADC3 Swap Mux", "R/L", "VAD_ADC" }, + { "RT5645 IF1 ADC3 Swap Mux", "L/L", "VAD_ADC" }, + { "RT5645 IF1 ADC3 Swap Mux", "R/R", "VAD_ADC" }, + + { "IF1 ADC", NULL, "RT5645 IF1 ADC1 Swap Mux" }, + { "IF1 ADC", NULL, "RT5645 IF1 ADC2 Swap Mux" }, + { "IF1 ADC", NULL, "RT5645 IF1 ADC3 Swap Mux" }, + + { "RT5645 IF1 ADC Mux", "IF_ADC1/IF_ADC2/VAD_ADC", "IF1 ADC" }, + { "RT5645 IF1 ADC Mux", "IF_ADC2/IF_ADC1/VAD_ADC", "IF1 ADC" }, + { "RT5645 IF1 ADC Mux", "VAD_ADC/IF_ADC1/IF_ADC2", "IF1 ADC" }, + { "RT5645 IF1 ADC Mux", "VAD_ADC/IF_ADC2/IF_ADC1", "IF1 ADC" }, + { "AIF1TX", NULL, "RT5645 IF1 ADC Mux" }, + + { "RT5645 IF1 DAC1 L Mux", "Slot0", "IF1 DAC0" }, + { "RT5645 IF1 DAC1 L Mux", "Slot1", "IF1 DAC1" }, + { "RT5645 IF1 DAC1 L Mux", "Slot2", "IF1 DAC2" }, + { "RT5645 IF1 DAC1 L Mux", "Slot3", "IF1 DAC3" }, + + { "RT5645 IF1 DAC1 R Mux", "Slot0", "IF1 DAC0" }, + { "RT5645 IF1 DAC1 R Mux", "Slot1", "IF1 DAC1" }, + { "RT5645 IF1 DAC1 R Mux", "Slot2", "IF1 DAC2" }, + { "RT5645 IF1 DAC1 R Mux", "Slot3", "IF1 DAC3" }, + + { "RT5645 IF1 DAC2 L Mux", "Slot0", "IF1 DAC0" }, + { "RT5645 IF1 DAC2 L Mux", "Slot1", "IF1 DAC1" }, + { "RT5645 IF1 DAC2 L Mux", "Slot2", "IF1 DAC2" }, + { "RT5645 IF1 DAC2 L Mux", "Slot3", "IF1 DAC3" }, + + { "RT5645 IF1 DAC2 R Mux", "Slot0", "IF1 DAC0" }, + { "RT5645 IF1 DAC2 R Mux", "Slot1", "IF1 DAC1" }, + { "RT5645 IF1 DAC2 R Mux", "Slot2", "IF1 DAC2" }, + { "RT5645 IF1 DAC2 R Mux", "Slot3", "IF1 DAC3" }, + + { "DAC1 L Mux", "IF1 DAC", "RT5645 IF1 DAC1 L Mux" }, + { "DAC1 R Mux", "IF1 DAC", "RT5645 IF1 DAC1 R Mux" }, + + { "DAC L2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 L Mux" }, + { "DAC R2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 R Mux" }, }; static int rt5645_hw_params(struct snd_pcm_substream *substream, @@ -2102,9 +2372,8 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, switch (dai->id) { case RT5645_AIF1: - mask_clk = RT5645_I2S_BCLK_MS1_MASK | RT5645_I2S_PD1_MASK; - val_clk = bclk_ms << RT5645_I2S_BCLK_MS1_SFT | - pre_div << RT5645_I2S_PD1_SFT; + mask_clk = RT5645_I2S_PD1_MASK; + val_clk = pre_div << RT5645_I2S_PD1_SFT; snd_soc_update_bits(codec, RT5645_I2S1_SDP, (0x3 << dl_sft), (val_len << dl_sft)); snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk); @@ -2369,6 +2638,8 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, static int rt5645_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { @@ -2399,8 +2670,9 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_OFF: snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100); - snd_soc_update_bits(codec, RT5645_GEN_CTRL1, - RT5645_DIG_GATE_CTRL, 0); + if (!rt5645->en_button_func) + snd_soc_update_bits(codec, RT5645_GEN_CTRL1, + RT5645_DIG_GATE_CTRL, 0); snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_VREF1 | RT5645_PWR_MB | RT5645_PWR_BG | RT5645_PWR_VREF2 | @@ -2410,72 +2682,228 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } -static int rt5645_jack_detect(struct snd_soc_codec *codec) +static int rt5650_calibration(struct rt5645_priv *rt5645) { - struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); - int gpio_state, jack_type = 0; - unsigned int val; + int val, i; + int ret = -1; - if (!gpio_is_valid(rt5645->pdata.hp_det_gpio)) { - dev_err(codec->dev, "invalid gpio\n"); - return -EINVAL; + regcache_cache_bypass(rt5645->regmap, true); + regmap_write(rt5645->regmap, RT5645_RESET, 0); + regmap_write(rt5645->regmap, RT5645_GEN_CTRL3, 0x0800); + regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_CHOP_DAC_ADC, + 0x3600); + regmap_write(rt5645->regmap, RT5645_PR_BASE + 0x25, 0x7000); + regmap_write(rt5645->regmap, RT5645_I2S1_SDP, 0x8008); + /* headset type */ + regmap_write(rt5645->regmap, RT5645_GEN_CTRL1, 0x2061); + regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0006); + regmap_write(rt5645->regmap, RT5645_PWR_ANLG1, 0x2012); + regmap_write(rt5645->regmap, RT5645_PWR_MIXER, 0x0002); + regmap_write(rt5645->regmap, RT5645_PWR_VOL, 0x0020); + regmap_write(rt5645->regmap, RT5645_JD_CTRL3, 0x00f0); + regmap_write(rt5645->regmap, RT5645_IN1_CTRL1, 0x0006); + regmap_write(rt5645->regmap, RT5645_IN1_CTRL2, 0x1827); + regmap_write(rt5645->regmap, RT5645_IN1_CTRL2, 0x0827); + msleep(400); + /* Inline command */ + regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x0001); + regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD2, 0xc000); + regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD1, 0x0008); + /* Calbration */ + regmap_write(rt5645->regmap, RT5645_GLB_CLK, 0x8000); + regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x0000); + regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD2, 0xc000); + regmap_write(rt5645->regmap, RT5650_4BTN_IL_CMD1, 0x0008); + regmap_write(rt5645->regmap, RT5645_PWR_DIG2, 0x8800); + regmap_write(rt5645->regmap, RT5645_PWR_ANLG1, 0xe8fa); + regmap_write(rt5645->regmap, RT5645_PWR_ANLG2, 0x8c04); + regmap_write(rt5645->regmap, RT5645_DEPOP_M2, 0x3100); + regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06); + regmap_write(rt5645->regmap, RT5645_BASS_BACK, 0x8a13); + regmap_write(rt5645->regmap, RT5645_GEN_CTRL3, 0x0820); + regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x000d); + /* Power on and Calbration */ + regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_HP_DCC_INT1, + 0x9f01); + msleep(200); + for (i = 0; i < 5; i++) { + regmap_read(rt5645->regmap, RT5645_PR_BASE + 0x7a, &val); + if (val != 0 && val != 0x3f3f) { + ret = 0; + break; + } + msleep(50); } - gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio); + pr_debug("%s: PR-7A = 0x%x\n", __func__, val); + + /* mute */ + regmap_write(rt5645->regmap, RT5645_PR_BASE + 0x3e, 0x7400); + regmap_write(rt5645->regmap, RT5645_DEPOP_M3, 0x0737); + regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, + 0xfc00); + regmap_write(rt5645->regmap, RT5645_DEPOP_M2, 0x1140); + regmap_write(rt5645->regmap, RT5645_DEPOP_M1, 0x0000); + regmap_write(rt5645->regmap, RT5645_GEN_CTRL2, 0x4020); + regmap_write(rt5645->regmap, RT5645_PWR_ANLG2, 0x0006); + regmap_write(rt5645->regmap, RT5645_PWR_DIG2, 0x0000); + msleep(350); + + regcache_cache_bypass(rt5645->regmap, false); + + return ret; +} - dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio, - gpio_state); +static void rt5645_enable_push_button_irq(struct snd_soc_codec *codec, + bool enable) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); - if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) || - (!rt5645->pdata.gpio_hp_det_active_high && !gpio_state)) { - snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias1"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias2"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2"); - snd_soc_dapm_force_enable_pin(&codec->dapm, "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); + 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, + "ADC R power"); + snd_soc_dapm_force_enable_pin_unlocked(&codec->dapm, + "LDO2"); + 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, + RT5650_4BTN_IL_CMD2, 0x8000, 0x8000); + snd_soc_read(codec, RT5650_4BTN_IL_CMD1); + pr_debug("%s read %x = %x\n", __func__, RT5650_4BTN_IL_CMD1, + snd_soc_read(codec, RT5650_4BTN_IL_CMD1)); + } 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, + "ADC R power"); + if (rt5645->pdata.jd_mode == 0) + snd_soc_dapm_disable_pin_unlocked(&codec->dapm, + "LDO2"); + 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); + } +} + +static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert) +{ + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + unsigned int val; - snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006); - snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0); + if (jack_insert) { + regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0006); - snd_soc_update_bits(codec, RT5645_IN1_CTRL2, - RT5645_CBJ_MN_JD, 0); - snd_soc_update_bits(codec, RT5645_IN1_CTRL2, - RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD); + if (codec->component.card->instantiated) { + /* for jack type detect */ + snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2"); + snd_soc_dapm_force_enable_pin(&codec->dapm, + "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + } else { + /* Power up necessary bits for JD if dapm is + not ready yet */ + regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1, + RT5645_PWR_MB | RT5645_PWR_VREF2, + RT5645_PWR_MB | RT5645_PWR_VREF2); + regmap_update_bits(rt5645->regmap, RT5645_PWR_MIXER, + RT5645_PWR_LDO2, RT5645_PWR_LDO2); + regmap_update_bits(rt5645->regmap, RT5645_PWR_VOL, + RT5645_PWR_MIC_DET, RT5645_PWR_MIC_DET); + } - msleep(400); - val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7; + regmap_write(rt5645->regmap, RT5645_JD_CTRL3, 0x00f0); + regmap_write(rt5645->regmap, RT5645_IN1_CTRL1, 0x0006); + regmap_update_bits(rt5645->regmap, + RT5645_IN1_CTRL2, 0x1000, 0x1000); + msleep(100); + regmap_update_bits(rt5645->regmap, + RT5645_IN1_CTRL2, 0x1000, 0x0000); + + msleep(450); + regmap_read(rt5645->regmap, RT5645_IN1_CTRL3, &val); + val &= 0x7; dev_dbg(codec->dev, "val = %d\n", val); - if (val == 1 || val == 2) - jack_type = SND_JACK_HEADSET; - else - jack_type = SND_JACK_HEADPHONE; + if (val == 1 || val == 2) { + rt5645->jack_type = SND_JACK_HEADSET; + if (rt5645->en_button_func) { + rt5645_enable_push_button_irq(codec, true); + } + } else { + if (codec->component.card->instantiated) { + snd_soc_dapm_disable_pin(&codec->dapm, + "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + } else + regmap_update_bits(rt5645->regmap, + RT5645_PWR_VOL, RT5645_PWR_MIC_DET, 0); + rt5645->jack_type = SND_JACK_HEADPHONE; + } - snd_soc_dapm_disable_pin(&codec->dapm, "micbias1"); - snd_soc_dapm_disable_pin(&codec->dapm, "micbias2"); - if (rt5645->pdata.jd_mode == 0) - snd_soc_dapm_disable_pin(&codec->dapm, "LDO2"); - snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); + } else { /* jack out */ + rt5645->jack_type = 0; + if (rt5645->en_button_func) + rt5645_enable_push_button_irq(codec, false); + else { + if (codec->component.card->instantiated) { + if (rt5645->pdata.jd_mode == 0) + snd_soc_dapm_disable_pin(&codec->dapm, + "LDO2"); + snd_soc_dapm_disable_pin(&codec->dapm, + "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + } else { + if (rt5645->pdata.jd_mode == 0) + regmap_update_bits(rt5645->regmap, + RT5645_PWR_MIXER, + RT5645_PWR_LDO2, 0); + regmap_update_bits(rt5645->regmap, + RT5645_PWR_VOL, RT5645_PWR_MIC_DET, 0); + } + } } - snd_soc_jack_report(rt5645->hp_jack, jack_type, SND_JACK_HEADPHONE); - snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE); - return 0; + return rt5645->jack_type; } +static int rt5645_irq_detection(struct rt5645_priv *rt5645); +static irqreturn_t rt5645_irq(int irq, void *data); + int rt5645_set_jack_detect(struct snd_soc_codec *codec, - struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack) + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack, + struct snd_soc_jack *btn_jack) { struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); rt5645->hp_jack = hp_jack; rt5645->mic_jack = mic_jack; - rt5645_jack_detect(codec); + rt5645->btn_jack = btn_jack; + if (rt5645->btn_jack && rt5645->codec_type == CODEC_TYPE_RT5650) { + rt5645->en_button_func = true; + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ); + regmap_update_bits(rt5645->regmap, RT5645_DEPOP_M1, + RT5645_HP_CB_MASK, RT5645_HP_CB_PU); + regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL1, + RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL); + } + rt5645_irq(0, rt5645); return 0; } @@ -2486,7 +2914,7 @@ static void rt5645_jack_detect_work(struct work_struct *work) struct rt5645_priv *rt5645 = container_of(work, struct rt5645_priv, jack_detect_work.work); - rt5645_jack_detect(rt5645->codec); + rt5645_irq_detection(rt5645); } static irqreturn_t rt5645_irq(int irq, void *data) @@ -2499,6 +2927,123 @@ static irqreturn_t rt5645_irq(int irq, void *data) return IRQ_HANDLED; } +static int rt5645_button_detect(struct snd_soc_codec *codec) +{ + int btn_type, val; + + val = snd_soc_read(codec, RT5650_4BTN_IL_CMD1); + pr_debug("val=0x%x\n", val); + btn_type = val & 0xfff0; + snd_soc_write(codec, RT5650_4BTN_IL_CMD1, val); + + return btn_type; +} + +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 (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); + snd_soc_jack_report(rt5645->mic_jack, + report, SND_JACK_MICROPHONE); + return report; + case 1: /* 2 port */ + val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0070; + break; + default: /* 1 port */ + val = snd_soc_read(rt5645->codec, RT5645_A_JD_CTRL1) & 0x0020; + break; + + } + + switch (val) { + /* jack in */ + case 0x30: /* 2 port */ + case 0x0: /* 1 port or 2 port */ + if (rt5645->jack_type == 0) { + report = rt5645_jack_detect(rt5645->codec, 1); + /* for push button and jack out */ + break; + } + btn_type = 0; + if (snd_soc_read(rt5645->codec, RT5645_INT_IRQ_ST) & 0x4) { + /* button pressed */ + report = SND_JACK_HEADSET; + btn_type = rt5645_button_detect(rt5645->codec); + /* rt5650 can report three kinds of button behavior, + one click, double click and hold. However, + currently we will report button pressed/released + event. So all the three button behaviors are + treated as button pressed. */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + report |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + report |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + report |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + report |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + dev_err(rt5645->codec->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + if (btn_type == 0)/* button release */ + report = rt5645->jack_type; + + break; + /* jack out */ + case 0x70: /* 2 port */ + case 0x10: /* 2 port */ + case 0x20: /* 1 port */ + report = 0; + snd_soc_update_bits(rt5645->codec, + RT5645_INT_IRQ_ST, 0x1, 0x0); + rt5645_jack_detect(rt5645->codec, 0); + break; + default: + break; + } + + snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); + snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE); + if (rt5645->en_button_func) + snd_soc_jack_report(rt5645->btn_jack, + report, SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + return report; +} + static int rt5645_probe(struct snd_soc_codec *codec) { struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); @@ -2521,12 +3066,10 @@ static int rt5645_probe(struct snd_soc_codec *codec) break; } - rt5645_set_bias_level(codec, SND_SOC_BIAS_OFF); - - snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); /* for JD function */ - if (rt5645->pdata.en_jd_func) { + if (rt5645->pdata.jd_mode) { snd_soc_dapm_force_enable_pin(&codec->dapm, "JD Power"); snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2"); snd_soc_dapm_sync(&codec->dapm); @@ -2666,6 +3209,46 @@ static struct acpi_device_id rt5645_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match); #endif +static struct rt5645_platform_data *rt5645_pdata; + +static struct rt5645_platform_data strago_platform_data = { + .dmic1_data_pin = RT5645_DMIC1_DISABLE, + .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, + .jd_mode = 3, +}; + +static int strago_quirk_cb(const struct dmi_system_id *id) +{ + rt5645_pdata = &strago_platform_data; + + return 1; +} + +static struct dmi_system_id dmi_platform_intel_braswell[] = { + { + .ident = "Intel Strago", + .callback = strago_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Strago"), + }, + }, + { } +}; + +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) { @@ -2684,6 +3267,18 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, if (pdata) rt5645->pdata = *pdata; + else if (dmi_check_system(dmi_platform_intel_braswell)) + rt5645->pdata = *rt5645_pdata; + else + rt5645_parse_dt(rt5645, &i2c->dev); + + 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); if (IS_ERR(rt5645->regmap)) { @@ -2709,6 +3304,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, return -ENODEV; } + if (rt5645->codec_type == CODEC_TYPE_RT5650) { + ret = rt5650_calibration(rt5645); + + if (ret < 0) + pr_err("calibration failed!\n"); + } + regmap_write(rt5645->regmap, RT5645_RESET, 0); ret = regmap_register_patch(rt5645->regmap, init_list, @@ -2728,84 +3330,78 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL, RT5645_IN_DF2, RT5645_IN_DF2); - if (rt5645->pdata.dmic_en) { + if (rt5645->pdata.dmic1_data_pin || rt5645->pdata.dmic2_data_pin) { regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, RT5645_GP2_PIN_MASK, RT5645_GP2_PIN_DMIC1_SCL); + } + switch (rt5645->pdata.dmic1_data_pin) { + case RT5645_DMIC_DATA_IN2N: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_IN2N); + break; - switch (rt5645->pdata.dmic1_data_pin) { - case RT5645_DMIC_DATA_IN2N: - regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, - RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_IN2N); - break; - - case RT5645_DMIC_DATA_GPIO5: - regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, - RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO5); - regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, - RT5645_GP5_PIN_MASK, RT5645_GP5_PIN_DMIC1_SDA); - break; - - case RT5645_DMIC_DATA_GPIO11: - regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, - RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO11); - regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, - RT5645_GP11_PIN_MASK, - RT5645_GP11_PIN_DMIC1_SDA); - break; + case RT5645_DMIC_DATA_GPIO5: + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_I2S2_DAC_PIN_MASK, RT5645_I2S2_DAC_PIN_GPIO); + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP5_PIN_MASK, RT5645_GP5_PIN_DMIC1_SDA); + break; - default: - break; - } + case RT5645_DMIC_DATA_GPIO11: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO11); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP11_PIN_MASK, + RT5645_GP11_PIN_DMIC1_SDA); + break; - switch (rt5645->pdata.dmic2_data_pin) { - case RT5645_DMIC_DATA_IN2P: - regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, - RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_IN2P); - break; + default: + break; + } - case RT5645_DMIC_DATA_GPIO6: - regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, - RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO6); - regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, - RT5645_GP6_PIN_MASK, RT5645_GP6_PIN_DMIC2_SDA); - break; + switch (rt5645->pdata.dmic2_data_pin) { + case RT5645_DMIC_DATA_IN2P: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_IN2P); + break; - case RT5645_DMIC_DATA_GPIO10: - regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, - RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO10); - regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, - RT5645_GP10_PIN_MASK, - RT5645_GP10_PIN_DMIC2_SDA); - break; + case RT5645_DMIC_DATA_GPIO6: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO6); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP6_PIN_MASK, RT5645_GP6_PIN_DMIC2_SDA); + break; - case RT5645_DMIC_DATA_GPIO12: - regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, - RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO12); - regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, - RT5645_GP12_PIN_MASK, - RT5645_GP12_PIN_DMIC2_SDA); - break; + case RT5645_DMIC_DATA_GPIO10: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO10); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP10_PIN_MASK, + RT5645_GP10_PIN_DMIC2_SDA); + break; - default: - break; - } + case RT5645_DMIC_DATA_GPIO12: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO12); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP12_PIN_MASK, + RT5645_GP12_PIN_DMIC2_SDA); + break; + default: + break; } - if (rt5645->pdata.en_jd_func) { + if (rt5645->pdata.jd_mode) { regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, - RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU, - RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU); + RT5645_IRQ_CLK_GATE_CTRL, + RT5645_IRQ_CLK_GATE_CTRL); regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, - RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN); - regmap_update_bits(rt5645->regmap, RT5645_JD_CTRL3, - RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL, - RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL); + RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN); regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, - RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT); - } - - if (rt5645->pdata.jd_mode) { + RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT); regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_IRQ_JD_1_1_EN, RT5645_IRQ_JD_1_1_EN); regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, @@ -2847,16 +3443,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)); } @@ -2870,9 +3456,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 db78e9462..278bb9f46 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -105,6 +105,7 @@ #define RT5645_TDM_CTRL_1 0x77 #define RT5645_TDM_CTRL_2 0x78 #define RT5645_TDM_CTRL_3 0x79 +#define RT5650_TDM_CTRL_4 0x7a /* Function - Analog */ #define RT5645_GLB_CLK 0x80 @@ -942,10 +943,6 @@ #define RT5645_I2S2_SDI_I2S2 (0x1 << 6) /* ADC/DAC Clock Control 1 (0x73) */ -#define RT5645_I2S_BCLK_MS1_MASK (0x1 << 15) -#define RT5645_I2S_BCLK_MS1_SFT 15 -#define RT5645_I2S_BCLK_MS1_32 (0x0 << 15) -#define RT5645_I2S_BCLK_MS1_64 (0x1 << 15) #define RT5645_I2S_PD1_MASK (0x7 << 12) #define RT5645_I2S_PD1_SFT 12 #define RT5645_I2S_PD1_1 (0x0 << 12) @@ -1067,13 +1064,14 @@ #define RT5645_SCLK_SRC_SFT 14 #define RT5645_SCLK_SRC_MCLK (0x0 << 14) #define RT5645_SCLK_SRC_PLL1 (0x1 << 14) -#define RT5645_SCLK_SRC_RCCLK (0x2 << 14) /* 15MHz */ -#define RT5645_PLL1_SRC_MASK (0x3 << 12) -#define RT5645_PLL1_SRC_SFT 12 -#define RT5645_PLL1_SRC_MCLK (0x0 << 12) -#define RT5645_PLL1_SRC_BCLK1 (0x1 << 12) -#define RT5645_PLL1_SRC_BCLK2 (0x2 << 12) -#define RT5645_PLL1_SRC_BCLK3 (0x3 << 12) +#define RT5645_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5645_PLL1_SRC_MASK (0x7 << 11) +#define RT5645_PLL1_SRC_SFT 11 +#define RT5645_PLL1_SRC_MCLK (0x0 << 11) +#define RT5645_PLL1_SRC_BCLK1 (0x1 << 11) +#define RT5645_PLL1_SRC_BCLK2 (0x2 << 11) +#define RT5645_PLL1_SRC_BCLK3 (0x3 << 11) +#define RT5645_PLL1_SRC_RCCLK (0x4 << 11) #define RT5645_PLL1_PD_MASK (0x1 << 3) #define RT5645_PLL1_PD_SFT 3 #define RT5645_PLL1_PD_1 (0x0 << 3) @@ -1695,6 +1693,10 @@ #define RT5645_GP6_PIN_SFT 6 #define RT5645_GP6_PIN_GPIO6 (0x0 << 6) #define RT5645_GP6_PIN_DMIC2_SDA (0x1 << 6) +#define RT5645_I2S2_DAC_PIN_MASK (0x1 << 4) +#define RT5645_I2S2_DAC_PIN_SFT 4 +#define RT5645_I2S2_DAC_PIN_I2S (0x0 << 4) +#define RT5645_I2S2_DAC_PIN_GPIO (0x1 << 4) #define RT5645_GP8_PIN_MASK (0x1 << 3) #define RT5645_GP8_PIN_SFT 3 #define RT5645_GP8_PIN_GPIO8 (0x0 << 3) @@ -2147,6 +2149,7 @@ enum { }; enum { + RT5645_DMIC1_DISABLE, RT5645_DMIC_DATA_IN2P, RT5645_DMIC_DATA_GPIO6, RT5645_DMIC_DATA_GPIO10, @@ -2154,6 +2157,7 @@ enum { }; enum { + RT5645_DMIC2_DISABLE, RT5645_DMIC_DATA_IN2N, RT5645_DMIC_DATA_GPIO5, RT5645_DMIC_DATA_GPIO11, @@ -2182,8 +2186,10 @@ 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; struct delayed_work jack_detect_work; int codec_type; @@ -2196,9 +2202,12 @@ struct rt5645_priv { int pll_src; int pll_in; int pll_out; + + int jack_type; + bool en_button_func; }; int rt5645_set_jack_detect(struct snd_soc_codec *codec, - struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack); - + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack, + struct snd_soc_jack *btn_jack); #endif /* __RT5645_H__ */ diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 9f4c7be6d..a3506e193 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -1571,7 +1571,7 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec, { switch (level) { case SND_SOC_BIAS_PREPARE: - if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { + if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) { snd_soc_update_bits(codec, RT5651_PWR_ANLG1, RT5651_PWR_VREF1 | RT5651_PWR_MB | RT5651_PWR_BG | RT5651_PWR_VREF2, @@ -1604,7 +1604,6 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } @@ -1625,7 +1624,7 @@ static int rt5651_probe(struct snd_soc_codec *codec) RT5651_PWR_FV1 | RT5651_PWR_FV2, RT5651_PWR_FV1 | RT5651_PWR_FV2); - rt5651_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index cc7f84a15..a9123d414 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 }, @@ -416,12 +415,12 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg) static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert) { int val; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); if (jack_insert) { - snd_soc_dapm_force_enable_pin(&codec->dapm, - "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0); snd_soc_update_bits(codec, RT5670_CJ_CTRL2, RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD, @@ -447,15 +446,15 @@ static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert) } else { snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4); rt5670->jack_type = SND_JACK_HEADPHONE; - snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); } } else { snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0); snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4); rt5670->jack_type = 0; - snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); } return rt5670->jack_type; @@ -2603,7 +2602,7 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_PREPARE: - if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) { + if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) { snd_soc_update_bits(codec, RT5670_PWR_ANLG1, RT5670_PWR_VREF1 | RT5670_PWR_MB | RT5670_PWR_BG | RT5670_PWR_VREF2, @@ -2647,30 +2646,30 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } static int rt5670_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) { case RT5670_ID_5670: case RT5670_ID_5671: - snd_soc_dapm_new_controls(&codec->dapm, + snd_soc_dapm_new_controls(dapm, rt5670_specific_dapm_widgets, ARRAY_SIZE(rt5670_specific_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5670_specific_dapm_routes, ARRAY_SIZE(rt5670_specific_dapm_routes)); break; case RT5670_ID_5672: - snd_soc_dapm_new_controls(&codec->dapm, + snd_soc_dapm_new_controls(dapm, rt5672_specific_dapm_widgets, ARRAY_SIZE(rt5672_specific_dapm_widgets)); - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5672_specific_dapm_routes, ARRAY_SIZE(rt5672_specific_dapm_routes)); break; @@ -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/rt5677.c b/sound/soc/codecs/rt5677.c index 6d7f6cb41..92280f844 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -820,7 +820,7 @@ static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol, rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0]; - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en); return 0; @@ -1060,6 +1060,7 @@ int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec, unsigned int asrc5_mask = 0, asrc5_value = 0; unsigned int asrc6_mask = 0, asrc6_value = 0; unsigned int asrc7_mask = 0, asrc7_value = 0; + unsigned int asrc8_mask = 0, asrc8_value = 0; switch (clk_src) { case RT5677_CLK_SEL_SYS: @@ -1196,10 +1197,108 @@ int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec, regmap_update_bits(rt5677->regmap, RT5677_ASRC_7, asrc7_mask, asrc7_value); + /* ASRC 8 */ + if (filter_mask & RT5677_I2S1_SOURCE) { + asrc8_mask |= RT5677_I2S1_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S1_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S1_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_I2S2_SOURCE) { + asrc8_mask |= RT5677_I2S2_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S2_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S2_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_I2S3_SOURCE) { + asrc8_mask |= RT5677_I2S3_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S3_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S3_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_I2S4_SOURCE) { + asrc8_mask |= RT5677_I2S4_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S4_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S4_CLK_SEL_SFT); + } + + if (asrc8_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_8, asrc8_mask, + asrc8_value); + return 0; } EXPORT_SYMBOL_GPL(rt5677_sel_asrc_clk_src); +static int rt5677_dmic_use_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + unsigned int asrc_setting; + + switch (source->shift) { + case 11: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO1_CLK_SEL_MASK) >> + RT5677_AD_STO1_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + case 10: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO2_CLK_SEL_MASK) >> + RT5677_AD_STO2_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + case 9: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO3_CLK_SEL_MASK) >> + RT5677_AD_STO3_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + case 8: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO4_CLK_SEL_MASK) >> + RT5677_AD_STO4_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + case 7: + regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_MONOL_CLK_SEL_MASK) >> + RT5677_AD_MONOL_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + case 6: + regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_MONOR_CLK_SEL_MASK) >> + RT5677_AD_MONOR_CLK_SEL_SFT; + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + break; + + default: + break; + } + + return 0; +} + /* Digital Mixer */ static const struct snd_kcontrol_new rt5677_sto1_adc_l_mix[] = { SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER, @@ -2479,7 +2578,7 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: - if (codec->dapm.bias_level != SND_SOC_BIAS_ON && + if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON && !rt5677->is_vref_slow) { mdelay(20); regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, @@ -3057,12 +3156,12 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { }; static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { - { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc }, - { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc }, - { "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", can_use_asrc }, - { "Stereo4 DMIC Mux", NULL, "DMIC STO4 ASRC", can_use_asrc }, - { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc }, - { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc }, + { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", rt5677_dmic_use_asrc }, + { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", rt5677_dmic_use_asrc }, + { "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", rt5677_dmic_use_asrc }, + { "Stereo4 DMIC Mux", NULL, "DMIC STO4 ASRC", rt5677_dmic_use_asrc }, + { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", rt5677_dmic_use_asrc }, + { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", rt5677_dmic_use_asrc }, { "I2S1", NULL, "I2S1 ASRC", can_use_asrc}, { "I2S2", NULL, "I2S2 ASRC", can_use_asrc}, { "I2S3", NULL, "I2S3 ASRC", can_use_asrc}, @@ -4353,7 +4452,7 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { rt5677_set_dsp_vad(codec, false); regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, @@ -4395,7 +4494,6 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec, default: break; } - codec->dapm.bias_level = level; return 0; } @@ -4606,22 +4704,23 @@ static void rt5677_free_gpio(struct i2c_client *i2c) static int rt5677_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); int i; rt5677->codec = codec; if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) { - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5677_dmic2_clk_2, ARRAY_SIZE(rt5677_dmic2_clk_2)); } else { /*use dmic1 clock by default*/ - snd_soc_dapm_add_routes(&codec->dapm, + snd_soc_dapm_add_routes(dapm, rt5677_dmic2_clk_1, ARRAY_SIZE(rt5677_dmic2_clk_1)); } - rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020); regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00); @@ -4667,6 +4766,8 @@ static int rt5677_remove(struct snd_soc_codec *codec) regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); if (gpio_is_valid(rt5677->pow_ldo2)) gpio_set_value_cansleep(rt5677->pow_ldo2, 0); + if (gpio_is_valid(rt5677->reset_pin)) + gpio_set_value_cansleep(rt5677->reset_pin, 0); return 0; } @@ -4682,6 +4783,8 @@ static int rt5677_suspend(struct snd_soc_codec *codec) if (gpio_is_valid(rt5677->pow_ldo2)) gpio_set_value_cansleep(rt5677->pow_ldo2, 0); + if (gpio_is_valid(rt5677->reset_pin)) + gpio_set_value_cansleep(rt5677->reset_pin, 0); } return 0; @@ -4692,10 +4795,13 @@ static int rt5677_resume(struct snd_soc_codec *codec) struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); if (!rt5677->dsp_vad_en) { - if (gpio_is_valid(rt5677->pow_ldo2)) { + if (gpio_is_valid(rt5677->pow_ldo2)) gpio_set_value_cansleep(rt5677->pow_ldo2, 1); + if (gpio_is_valid(rt5677->reset_pin)) + gpio_set_value_cansleep(rt5677->reset_pin, 1); + if (gpio_is_valid(rt5677->pow_ldo2) || + gpio_is_valid(rt5677->reset_pin)) msleep(10); - } regcache_cache_only(rt5677->regmap, false); regcache_sync(rt5677->regmap); @@ -4933,6 +5039,8 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) rt5677->pow_ldo2 = of_get_named_gpio(np, "realtek,pow-ldo2-gpio", 0); + rt5677->reset_pin = of_get_named_gpio(np, + "realtek,reset-gpio", 0); /* * POW_LDO2 is optional (it may be statically tied on the board). @@ -4943,6 +5051,9 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np) if (!gpio_is_valid(rt5677->pow_ldo2) && (rt5677->pow_ldo2 != -ENOENT)) return rt5677->pow_ldo2; + if (!gpio_is_valid(rt5677->reset_pin) && + (rt5677->reset_pin != -ENOENT)) + return rt5677->reset_pin; of_property_read_u8_array(np, "realtek,gpio-config", rt5677->pdata.gpio_config, RT5677_GPIO_NUM); @@ -5044,6 +5155,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, } } else { rt5677->pow_ldo2 = -EINVAL; + rt5677->reset_pin = -EINVAL; } if (gpio_is_valid(rt5677->pow_ldo2)) { @@ -5055,6 +5167,21 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, rt5677->pow_ldo2, ret); return ret; } + } + + if (gpio_is_valid(rt5677->reset_pin)) { + ret = devm_gpio_request_one(&i2c->dev, rt5677->reset_pin, + GPIOF_OUT_INIT_HIGH, + "RT5677 RESET"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request RESET %d: %d\n", + rt5677->reset_pin, ret); + return ret; + } + } + + if (gpio_is_valid(rt5677->pow_ldo2) || + gpio_is_valid(rt5677->reset_pin)) { /* Wait a while until I2C bus becomes available. The datasheet * does not specify the exact we should wait but startup * sequence mentiones at least a few milliseconds. diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 0903d440a..a15407454 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1446,6 +1446,16 @@ #define RT5677_DSP_OB_4_7_CLK_SEL_MASK (0xf << 8) #define RT5677_DSP_OB_4_7_CLK_SEL_SFT 8 +/* ASRC Control 8 (0x8a) */ +#define RT5677_I2S1_CLK_SEL_MASK (0xf << 12) +#define RT5677_I2S1_CLK_SEL_SFT 12 +#define RT5677_I2S2_CLK_SEL_MASK (0xf << 8) +#define RT5677_I2S2_CLK_SEL_SFT 8 +#define RT5677_I2S3_CLK_SEL_MASK (0xf << 4) +#define RT5677_I2S3_CLK_SEL_SFT 4 +#define RT5677_I2S4_CLK_SEL_MASK (0xf) +#define RT5677_I2S4_CLK_SEL_SFT 0 + /* VAD Function Control 4 (0x9f) */ #define RT5677_VAD_SRC_MASK (0x7 << 8) #define RT5677_VAD_SRC_SFT 8 @@ -1744,6 +1754,10 @@ enum { RT5677_AD_MONO_R_FILTER = (0x1 << 12), RT5677_DSP_OB_0_3_FILTER = (0x1 << 13), RT5677_DSP_OB_4_7_FILTER = (0x1 << 14), + RT5677_I2S1_SOURCE = (0x1 << 15), + RT5677_I2S2_SOURCE = (0x1 << 16), + RT5677_I2S3_SOURCE = (0x1 << 17), + RT5677_I2S4_SOURCE = (0x1 << 18), }; struct rt5677_priv { @@ -1762,6 +1776,7 @@ struct rt5677_priv { int pll_in; int pll_out; int pow_ldo2; /* POW_LDO2 pin */ + int reset_pin; /* RESET pin */ enum rt5677_type type; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio_chip; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 3593a1496..e673f6ceb 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -948,7 +948,7 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable( ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); @@ -979,7 +979,6 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -1092,6 +1091,19 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg) } /* + * This precalculated table contains all (vag_val * 100 / lo_calcntrl) results + * to select an appropriate lo_vol_* in SGTL5000_CHIP_LINE_OUT_VOL + * The calculatation was done for all possible register values which + * is the array index and the following formula: 10^((idx−15)/40) * 100 + */ +static const u8 vol_quot_table[] = { + 42, 45, 47, 50, 53, 56, 60, 63, + 67, 71, 75, 79, 84, 89, 94, 100, + 106, 112, 119, 126, 133, 141, 150, 158, + 168, 178, 188, 200, 211, 224, 237, 251 +}; + +/* * sgtl5000 has 3 internal power supplies: * 1. VAG, normally set to vdda/2 * 2. charge pump, set to different value @@ -1111,6 +1123,10 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) u16 ana_pwr; u16 lreg_ctrl; int vag; + int lo_vag; + int vol_quot; + int lo_vol; + size_t i; struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer); @@ -1198,23 +1214,45 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) SGTL5000_ANA_GND_MASK, vag << SGTL5000_ANA_GND_SHIFT); /* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */ - vag = vddio / 2; - if (vag <= SGTL5000_LINE_OUT_GND_BASE) - vag = 0; - else if (vag >= SGTL5000_LINE_OUT_GND_BASE + + lo_vag = vddio / 2; + if (lo_vag <= SGTL5000_LINE_OUT_GND_BASE) + lo_vag = 0; + else if (lo_vag >= SGTL5000_LINE_OUT_GND_BASE + SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX) - vag = SGTL5000_LINE_OUT_GND_MAX; + lo_vag = SGTL5000_LINE_OUT_GND_MAX; else - vag = (vag - SGTL5000_LINE_OUT_GND_BASE) / + lo_vag = (lo_vag - SGTL5000_LINE_OUT_GND_BASE) / SGTL5000_LINE_OUT_GND_STP; snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL, SGTL5000_LINE_OUT_CURRENT_MASK | SGTL5000_LINE_OUT_GND_MASK, - vag << SGTL5000_LINE_OUT_GND_SHIFT | + lo_vag << SGTL5000_LINE_OUT_GND_SHIFT | SGTL5000_LINE_OUT_CURRENT_360u << SGTL5000_LINE_OUT_CURRENT_SHIFT); + /* + * Set lineout output level in range (0..31) + * the same value is used for right and left channel + * + * Searching for a suitable index solving this formula: + * idx = 40 * log10(vag_val / lo_cagcntrl) + 15 + */ + vol_quot = (vag * 100) / lo_vag; + lo_vol = 0; + for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) { + if (vol_quot >= vol_quot_table[i]) + lo_vol = i; + else + break; + } + + snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_VOL, + SGTL5000_LINE_OUT_VOL_RIGHT_MASK | + SGTL5000_LINE_OUT_VOL_LEFT_MASK, + lo_vol << SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT | + lo_vol << SGTL5000_LINE_OUT_VOL_LEFT_SHIFT); + return 0; } diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h index bd7a344bf..1c317de26 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/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c index 0a8e43c98..29cb44256 100644 --- a/sound/soc/codecs/sirf-audio-codec.c +++ b/sound/soc/codecs/sirf-audio-codec.c @@ -395,7 +395,7 @@ struct snd_soc_dai_driver sirf_audio_codec_dai = { static int sirf_audio_codec_probe(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); pm_runtime_enable(codec->dev); diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 7947c0ebb..3a7de0159 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -194,7 +194,7 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { pr_debug("vaud_bias powering up pll\n"); /* power up the pll */ snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5)); @@ -205,17 +205,22 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + switch (snd_soc_codec_get_bias_level(codec)) { + case SND_SOC_BIAS_OFF: pr_debug("vaud_bias power up rail\n"); /* power up the rail */ snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0)); msleep(1); - } else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) { + break; + case SND_SOC_BIAS_PREPARE: /* turn off pcm */ pr_debug("vaud_bias power dn pcm\n"); snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0); snd_soc_write(codec, SN95031_AUDPLLCTRL, 0); + break; + default: + break; } break; @@ -226,7 +231,6 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 67ea55adb..f30de7639 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -510,7 +510,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) ret = ssm2518_set_power(ssm2518, true); break; case SND_SOC_BIAS_OFF: @@ -518,12 +518,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec, break; } - if (ret) - return ret; - - codec->dapm.bias_level = level; - - return 0; + return ret; } static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 314eaece1..69a773aeb 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -473,7 +473,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -524,8 +523,8 @@ static int ssm2602_resume(struct snd_soc_codec *codec) static int ssm2602_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V, @@ -549,7 +548,7 @@ static int ssm2602_codec_probe(struct snd_soc_codec *codec) static int ssm2604_codec_probe(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int ret; ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets, diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index f7549cc7e..84a4f5ad8 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -359,7 +359,7 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) ret = ssm4567_set_power(ssm4567, true); break; case SND_SOC_BIAS_OFF: @@ -367,12 +367,7 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec, break; } - if (ret) - return ret; - - codec->dapm.bias_level = level; - - return 0; + return ret; } static const struct snd_soc_dai_ops ssm4567_dai_ops = { diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 007a0e3bc..60eff3626 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -819,7 +819,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); if (ret != 0) { @@ -854,7 +854,6 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, sta32x->supplies); break; } - codec->dapm.bias_level = level; return 0; } @@ -970,7 +969,7 @@ static int sta32x_probe(struct snd_soc_codec *codec) if (sta32x->pdata->needs_esd_watchdog) INIT_DELAYED_WORK(&sta32x->watchdog_work, sta32x_watchdog); - sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Bias level configuration will have done an extra enable */ regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); @@ -1096,16 +1095,10 @@ static int sta32x_i2c_probe(struct i2c_client *i2c, #endif /* GPIOs */ - sta32x->gpiod_nreset = devm_gpiod_get(dev, "reset"); - if (IS_ERR(sta32x->gpiod_nreset)) { - ret = PTR_ERR(sta32x->gpiod_nreset); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - sta32x->gpiod_nreset = NULL; - } else { - gpiod_direction_output(sta32x->gpiod_nreset, 0); - } + sta32x->gpiod_nreset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(sta32x->gpiod_nreset)) + return PTR_ERR(sta32x->gpiod_nreset); /* regulators */ for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++) diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index 669e32282..bd819a3f2 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -853,7 +853,7 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable( ARRAY_SIZE(sta350->supplies), sta350->supplies); @@ -890,7 +890,6 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec, sta350->supplies); break; } - codec->dapm.bias_level = level; return 0; } @@ -1037,7 +1036,7 @@ static int sta350_probe(struct snd_soc_codec *codec) sta350->coef_shadow[60] = 0x400000; sta350->coef_shadow[61] = 0x400000; - sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Bias level configuration will have done an extra enable */ regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); @@ -1218,8 +1217,8 @@ static int sta350_i2c_probe(struct i2c_client *i2c, if (IS_ERR(sta350->gpiod_nreset)) return PTR_ERR(sta350->gpiod_nreset); - sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down", - GPIOD_OUT_LOW); + sta350->gpiod_power_down = devm_gpiod_get_optional(dev, "power-down", + GPIOD_OUT_LOW); if (IS_ERR(sta350->gpiod_power_down)) return PTR_ERR(sta350->gpiod_power_down); diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index b0f436d10..4f70378b2 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -165,7 +165,7 @@ static int sta529_set_bias_level(struct snd_soc_codec *codec, enum FFX_CLK_ENB); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) regcache_sync(sta529->regmap); snd_soc_update_bits(codec, STA529_FFXCFG0, POWER_CNTLMSAK, POWER_STDBY); @@ -179,12 +179,6 @@ static int sta529_set_bias_level(struct snd_soc_codec *codec, enum break; } - /* - * store the label for powers down audio subsystem for suspend.This is - * used by soc core layer - */ - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 6464caf72..ed4cca7f6 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -236,7 +236,6 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec, stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->dapm.bias_level = level; return 0; } @@ -321,7 +320,7 @@ static struct snd_soc_dai_driver stac9766_dai[] = { .channels_max = 2, .rates = SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE, }, /* alsa ops */ .ops = &stac9766_dai_ops_digital, diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 18558595b..4f25a7d0e 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -34,6 +34,7 @@ #include <sound/soc-dapm.h> #include <sound/tlv.h> #include <sound/tas2552-plat.h> +#include <dt-bindings/sound/tas2552.h> #include "tas2552.h" @@ -44,8 +45,8 @@ 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_RESERVED_0D, 0x00}, + {TAS2552_BOOST_APT_CTRL, 0x0f}, + {TAS2552_RESERVED_0D, 0xbe}, {TAS2552_LIMIT_RATE_HYS, 0x08}, {TAS2552_CFG_2, 0xef}, {TAS2552_SER_CTRL_1, 0x00}, @@ -75,20 +76,47 @@ struct tas2552_data { struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES]; struct gpio_desc *enable_gpio; unsigned char regs[TAS2552_VBAT_DATA]; - unsigned int mclk; -}; + unsigned int pll_clkin; + int pll_clk_id; + unsigned int pdm_clk; + int pdm_clk_id; -/* Input mux controls */ -static const char *tas2552_input_texts[] = { - "Digital", "Analog" + unsigned int dai_fmt; + unsigned int tdm_delay; }; +static int tas2552_post_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, TAS2552_RESERVED_0D, 0xc0); + snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), + (1 << 5)); + snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 0); + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, 0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_SWS, + TAS2552_SWS); + snd_soc_update_bits(codec, TAS2552_CFG_2, 1, 1); + snd_soc_update_bits(codec, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0); + snd_soc_write(codec, TAS2552_RESERVED_0D, 0xbe); + break; + } + return 0; +} + +/* Input mux controls */ +static const char * const tas2552_input_texts[] = { + "Digital", "Analog" }; static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7, tas2552_input_texts); -static const struct snd_kcontrol_new tas2552_input_mux_control[] = { - SOC_DAPM_ENUM("Input selection", tas2552_input_mux_enum) -}; +static const struct snd_kcontrol_new tas2552_input_mux_control = + SOC_DAPM_ENUM("Route", tas2552_input_mux_enum); static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] = { @@ -96,12 +124,13 @@ static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] = /* MUX Controls */ SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0, - tas2552_input_mux_control), + &tas2552_input_mux_control), SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0), + SND_SOC_DAPM_POST("Post Event", tas2552_post_event), SND_SOC_DAPM_OUTPUT("OUT") }; @@ -116,128 +145,253 @@ 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; + u8 cfg1_reg = 0; - if (!tas_data->codec) + if (!tas2552->codec) return; if (sw_shutdown) - cfg1_reg = 0; - else - cfg1_reg = TAS2552_SWS_MASK; + cfg1_reg = TAS2552_SWS; - snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, - TAS2552_SWS_MASK, cfg1_reg); + snd_soc_update_bits(tas2552->codec, TAS2552_CFG_1, TAS2552_SWS, + cfg1_reg); } #endif -static int tas2552_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int tas2552_setup_pll(struct snd_soc_codec *codec, + struct snd_pcm_hw_params *params) { - struct snd_soc_codec *codec = dai->codec; struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); - int sample_rate, pll_clk; - int d; - u8 p, j; + bool bypass_pll = false; + unsigned int pll_clk = params_rate(params) * 512; + unsigned int pll_clkin = tas2552->pll_clkin; + u8 pll_enable; - if (!tas2552->mclk) - return -EINVAL; + 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 (tas2552->mclk == TAS2552_245MHZ_CLK || - tas2552->mclk == TAS2552_225MHZ_CLK) { + 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_MASK, - TAS2552_PLL_BYPASS); + TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS); } else { /* Fill in the PLL control registers for J & D - * PLL_CLK = (.5 * freq * J.D) / 2^p + * pll_clk = (.5 * pll_clkin * 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); + 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); - 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; + +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; } - j = (pll_clk * 2 * (1 << p)) / tas2552->mclk; - d = (pll_clk * 2 * (1 << p)) % tas2552->mclk; + 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); + TAS2552_PLL_J_MASK, j); + /* Will clear the PLL_BYPASS bit */ snd_soc_write(codec, TAS2552_PLL_CTRL_2, - (d >> 7) & TAS2552_PLL_D_UPPER_MASK); + TAS2552_PLL_D_UPPER(d)); snd_soc_write(codec, TAS2552_PLL_CTRL_3, - d & TAS2552_PLL_D_LOWER_MASK); + 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 cpf; + u8 ser_ctrl1_reg, wclk_rate; + + switch (params_width(params)) { + case 16: + ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT; + cpf = 32 + tas2552->tdm_delay; + break; + case 20: + ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT; + cpf = 64 + tas2552->tdm_delay; + break; + case 24: + ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT; + cpf = 64 + tas2552->tdm_delay; + break; + case 32: + ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT; + cpf = 64 + tas2552->tdm_delay; + break; + default: + dev_err(codec->dev, "Not supported sample size: %d\n", + params_width(params)); + return -EINVAL; } + if (cpf <= 32) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32; + else if (cpf <= 64) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64; + else if (cpf <= 128) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128; + else + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256; + + snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, + TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK, + ser_ctrl1_reg); + + switch (params_rate(params)) { + case 8000: + wclk_rate = TAS2552_WCLK_FREQ_8KHZ; + break; + case 11025: + case 12000: + wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ; + break; + case 16000: + wclk_rate = TAS2552_WCLK_FREQ_16KHZ; + break; + case 22050: + case 24000: + wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ; + break; + case 32000: + wclk_rate = TAS2552_WCLK_FREQ_32KHZ; + break; + case 44100: + case 48000: + wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ; + break; + case 88200: + case 96000: + wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ; + break; + case 176400: + case 192000: + wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ; + break; + default: + dev_err(codec->dev, "Not supported sample rate: %d\n", + params_rate(params)); + return -EINVAL; + } + + snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK, + wclk_rate); + + return tas2552_setup_pll(codec, params); +} + +#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ + TAS2552_WCLKDIR | \ + TAS2552_DATAFORMAT_MASK) +static int tas2552_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + int delay = 0; + + /* TDM slot selection only valid in DSP_A/_B mode */ + if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A) + delay += (tas2552->tdm_delay + 1); + else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B) + delay += tas2552->tdm_delay; + + /* Configure data delay */ + snd_soc_write(codec, TAS2552_SER_CTRL_2, delay); + return 0; } static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); u8 serial_format; - u8 serial_control_mask; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: serial_format = 0x00; break; case SND_SOC_DAIFMT_CBS_CFM: - serial_format = TAS2552_WORD_CLK_MASK; + serial_format = TAS2552_WCLKDIR; break; case SND_SOC_DAIFMT_CBM_CFS: - serial_format = TAS2552_BIT_CLK_MASK; + serial_format = TAS2552_BCLKDIR; break; case SND_SOC_DAIFMT_CBM_CFM: - serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK); + serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR); break; default: dev_vdbg(codec->dev, "DAI Format master is not found\n"); return -EINVAL; } - serial_control_mask = TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK; - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - serial_format &= TAS2552_DAIFMT_I2S_MASK; + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | + SND_SOC_DAIFMT_INV_MASK)) { + case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): break; - case SND_SOC_DAIFMT_DSP_A: - serial_format |= TAS2552_DAIFMT_DSP; + case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF): + case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF): + serial_format |= TAS2552_DATAFORMAT_DSP; break; - case SND_SOC_DAIFMT_RIGHT_J: - serial_format |= TAS2552_DAIFMT_RIGHT_J; + case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF): + serial_format |= TAS2552_DATAFORMAT_RIGHT_J; break; - case SND_SOC_DAIFMT_LEFT_J: - serial_format |= TAS2552_DAIFMT_LEFT_J; + case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): + serial_format |= TAS2552_DATAFORMAT_LEFT_J; break; default: dev_vdbg(codec->dev, "DAI Format is not found\n"); return -EINVAL; } + tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; - if (fmt & SND_SOC_DAIFMT_FORMAT_MASK) - serial_control_mask |= TAS2552_DATA_FORMAT_MASK; - - snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, serial_control_mask, - serial_format); - + snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK, + serial_format); return 0; } @@ -246,23 +400,85 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, { struct snd_soc_codec *codec = dai->codec; struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev); + u8 reg, mask, val; + + switch (clk_id) { + case TAS2552_PLL_CLKIN_MCLK: + 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: + case TAS2552_PDM_CLK_IVCLKIN: + case TAS2552_PDM_CLK_BCLK: + case TAS2552_PDM_CLK_MCLK: + 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: + dev_err(codec->dev, "Invalid clk id: %d\n", clk_id); + return -EINVAL; + } + + snd_soc_update_bits(codec, reg, mask, val); + + return 0; +} - tas2552->mclk = freq; +static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec); + unsigned int lsb; + + if (unlikely(!tx_mask)) { + dev_err(codec->dev, "tx masks need to be non 0\n"); + return -EINVAL; + } + + /* TDM based on DSP mode requires slots to be adjacent */ + lsb = __ffs(tx_mask); + if ((lsb + 1) != __fls(tx_mask)) { + dev_err(codec->dev, "Invalid mask, slots must be adjacent\n"); + return -EINVAL; + } + + tas2552->tdm_delay = lsb * slot_width; + + /* DOUT in high-impedance on inactive bit clocks */ + snd_soc_update_bits(codec, TAS2552_DOUT, + TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE); return 0; } static int tas2552_mute(struct snd_soc_dai *dai, int mute) { - u8 cfg1_reg; + u8 cfg1_reg = 0; struct snd_soc_codec *codec = dai->codec; if (mute) - cfg1_reg = TAS2552_MUTE_MASK; - else - cfg1_reg = ~TAS2552_MUTE_MASK; + cfg1_reg |= TAS2552_MUTE; - snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, cfg1_reg); + snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg); return 0; } @@ -272,7 +488,7 @@ static int tas2552_runtime_suspend(struct device *dev) { struct tas2552_data *tas2552 = dev_get_drvdata(dev); - tas2552_sw_shutdown(tas2552, 0); + tas2552_sw_shutdown(tas2552, 1); regcache_cache_only(tas2552->regmap, true); regcache_mark_dirty(tas2552->regmap); @@ -290,7 +506,7 @@ static int tas2552_runtime_resume(struct device *dev) if (tas2552->enable_gpio) gpiod_set_value(tas2552->enable_gpio, 1); - tas2552_sw_shutdown(tas2552, 1); + tas2552_sw_shutdown(tas2552, 0); regcache_cache_only(tas2552->regmap, false); regcache_sync(tas2552->regmap); @@ -306,8 +522,10 @@ static const struct dev_pm_ops tas2552_pm = { static struct snd_soc_dai_ops tas2552_speaker_dai_ops = { .hw_params = tas2552_hw_params, + .prepare = tas2552_prepare, .set_sysclk = tas2552_set_dai_sysclk, .set_fmt = tas2552_set_dai_fmt, + .set_tdm_slot = tas2552_set_dai_tdm_slot, .digital_mute = tas2552_mute, }; @@ -333,15 +551,22 @@ static struct snd_soc_dai_driver tas2552_dai[] = { /* * DAC digital volumes. From -7 to 24 dB in 1 dB steps */ -static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24); +static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0); -static const struct snd_kcontrol_new tas2552_snd_controls[] = { - SOC_SINGLE_TLV("Speaker Driver Playback Volume", - TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv), +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 reg_default tas2552_init_regs[] = { - { TAS2552_RESERVED_0D, 0xc0 }, +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) @@ -370,31 +595,20 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec) goto probe_fail; } - snd_soc_write(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK | - TAS2552_PLL_SRC_BCLK); + 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 | TAS2552_88_96KHZ); - 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_PDM_CFG, TAS2552_PDM_BCLK_SEL); - snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 | - TAS2552_APT_THRESH_2_1_7); - - ret = regmap_register_patch(tas2552->regmap, tas2552_init_regs, - ARRAY_SIZE(tas2552_init_regs)); - if (ret != 0) { - dev_err(codec->dev, "Failed to write init registers: %d\n", - ret); - goto patch_fail; - } + TAS2552_DIN_SRC_SEL_AVG_L_R); + 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; -patch_fail: - pm_runtime_put(codec->dev); probe_fail: if (tas2552->enable_gpio) gpiod_set_value(tas2552->enable_gpio, 0); @@ -456,6 +670,8 @@ static struct snd_soc_codec_driver soc_codec_dev_tas2552 = { .remove = tas2552_codec_remove, .suspend = tas2552_suspend, .resume = tas2552_resume, + .ignore_pmdown_time = true, + .controls = tas2552_snd_controls, .num_controls = ARRAY_SIZE(tas2552_snd_controls), .dapm_widgets = tas2552_dapm_widgets, @@ -487,7 +703,8 @@ static int tas2552_probe(struct i2c_client *client, if (data == NULL) return -ENOMEM; - data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + data->enable_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_LOW); if (IS_ERR(data->enable_gpio)) return PTR_ERR(data->enable_gpio); @@ -531,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 6cea8f31b..5746f8fd0 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,22 +33,26 @@ #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 /* CFG1 Register Masks */ -#define TAS2552_MUTE_MASK (1 << 2) -#define TAS2552_SWS_MASK (1 << 1) -#define TAS2552_WCLK_MASK 0x07 -#define TAS2552_CLASSD_EN_MASK (1 << 7) +#define TAS2552_DEV_RESET (1 << 0) +#define TAS2552_SWS (1 << 1) +#define TAS2552_MUTE (1 << 2) +#define TAS2552_PLL_SRC_MCLK (0x0 << 4) +#define TAS2552_PLL_SRC_BCLK (0x1 << 4) +#define TAS2552_PLL_SRC_IVCLKIN (0x2 << 4) +#define TAS2552_PLL_SRC_1_8_FIXED (0x3 << 4) +#define TAS2552_PLL_SRC_MASK TAS2552_PLL_SRC_1_8_FIXED /* CFG2 Register Masks */ #define TAS2552_CLASSD_EN (1 << 7) @@ -59,71 +63,84 @@ #define TAS2552_IVSENSE_EN (1 << 1) /* CFG3 Register Masks */ -#define TAS2552_WORD_CLK_MASK (1 << 7) -#define TAS2552_BIT_CLK_MASK (1 << 6) -#define TAS2552_DATA_FORMAT_MASK (0x11 << 2) - -#define TAS2552_DAIFMT_I2S_MASK 0xf3 -#define TAS2552_DAIFMT_DSP (1 << 3) -#define TAS2552_DAIFMT_RIGHT_J (1 << 4) -#define TAS2552_DAIFMT_LEFT_J (0x11 << 3) - -#define TAS2552_PLL_SRC_MCLK 0x00 -#define TAS2552_PLL_SRC_BCLK (1 << 3) -#define TAS2552_PLL_SRC_IVCLKIN (1 << 4) -#define TAS2552_PLL_SRC_1_8_FIXED (0x11 << 3) - -#define TAS2552_DIN_SRC_SEL_MUTED 0x00 -#define TAS2552_DIN_SRC_SEL_LEFT (1 << 4) -#define TAS2552_DIN_SRC_SEL_RIGHT (1 << 5) -#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x11 << 4) - +#define TAS2552_WCLK_FREQ_8KHZ (0x0 << 0) +#define TAS2552_WCLK_FREQ_11_12KHZ (0x1 << 0) +#define TAS2552_WCLK_FREQ_16KHZ (0x2 << 0) +#define TAS2552_WCLK_FREQ_22_24KHZ (0x3 << 0) +#define TAS2552_WCLK_FREQ_32KHZ (0x4 << 0) +#define TAS2552_WCLK_FREQ_44_48KHZ (0x5 << 0) +#define TAS2552_WCLK_FREQ_88_96KHZ (0x6 << 0) +#define TAS2552_WCLK_FREQ_176_192KHZ (0x7 << 0) +#define TAS2552_WCLK_FREQ_MASK TAS2552_WCLK_FREQ_176_192KHZ +#define TAS2552_DIN_SRC_SEL_MUTED (0x0 << 3) +#define TAS2552_DIN_SRC_SEL_LEFT (0x1 << 3) +#define TAS2552_DIN_SRC_SEL_RIGHT (0x2 << 3) +#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x3 << 3) #define TAS2552_PDM_IN_SEL (1 << 5) #define TAS2552_I2S_OUT_SEL (1 << 6) -#define TAS2552_ANALOG_IN_SEL (1 << 7) - -/* CFG3 WCLK Dividers */ -#define TAS2552_8KHZ 0x00 -#define TAS2552_11_12KHZ (1 << 1) -#define TAS2552_16KHZ (1 << 2) -#define TAS2552_22_24KHZ (1 << 3) -#define TAS2552_32KHZ (1 << 4) -#define TAS2552_44_48KHZ (1 << 5) -#define TAS2552_88_96KHZ (1 << 6) -#define TAS2552_176_192KHZ (1 << 7) +#define TAS2552_ANALOG_IN_SEL (1 << 7) + +/* DOUT Register Masks */ +#define TAS2552_SDOUT_TRISTATE (1 << 2) + +/* Serial Interface Control Register Masks */ +#define TAS2552_WORDLENGTH_16BIT (0x0 << 0) +#define TAS2552_WORDLENGTH_20BIT (0x1 << 0) +#define TAS2552_WORDLENGTH_24BIT (0x2 << 0) +#define TAS2552_WORDLENGTH_32BIT (0x3 << 0) +#define TAS2552_WORDLENGTH_MASK TAS2552_WORDLENGTH_32BIT +#define TAS2552_DATAFORMAT_I2S (0x0 << 2) +#define TAS2552_DATAFORMAT_DSP (0x1 << 2) +#define TAS2552_DATAFORMAT_RIGHT_J (0x2 << 2) +#define TAS2552_DATAFORMAT_LEFT_J (0x3 << 2) +#define TAS2552_DATAFORMAT_MASK TAS2552_DATAFORMAT_LEFT_J +#define TAS2552_CLKSPERFRAME_32 (0x0 << 4) +#define TAS2552_CLKSPERFRAME_64 (0x1 << 4) +#define TAS2552_CLKSPERFRAME_128 (0x2 << 4) +#define TAS2552_CLKSPERFRAME_256 (0x3 << 4) +#define TAS2552_CLKSPERFRAME_MASK TAS2552_CLKSPERFRAME_256 +#define TAS2552_BCLKDIR (1 << 6) +#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_DATA_ES_RISE 0x4 - -#define TAS2552_PDM_PLL_CLK_SEL 0x00 -#define TAS2552_PDM_IV_CLK_SEL (1 << 1) -#define TAS2552_PDM_BCLK_SEL (1 << 2) -#define TAS2552_PDM_MCLK_SEL (1 << 3) - -/* 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) +#define TAS2552_PDM_CLK_SEL_PLL (0x0 << 0) +#define TAS2552_PDM_CLK_SEL_IVCLKIN (0x1 << 0) +#define TAS2552_PDM_CLK_SEL_BCLK (0x2 << 0) +#define TAS2552_PDM_CLK_SEL_MCLK (0x3 << 0) +#define TAS2552_PDM_CLK_SEL_MASK TAS2552_PDM_CLK_SEL_MCLK +#define TAS2552_PDM_DATA_ES (1 << 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/tas571x.c b/sound/soc/codecs/tas571x.c new file mode 100644 index 000000000..85bcc374c --- /dev/null +++ b/sound/soc/codecs/tas571x.c @@ -0,0 +1,514 @@ +/* + * TAS571x amplifier audio driver + * + * Copyright (C) 2015 Google, Inc. + * Copyright (c) 2013 Daniel Mack <zonque@gmail.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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/stddef.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "tas571x.h" + +#define TAS571X_MAX_SUPPLIES 6 + +struct tas571x_chip { + const char *const *supply_names; + int num_supply_names; + const struct snd_kcontrol_new *controls; + int num_controls; + const struct regmap_config *regmap_config; + int vol_reg_size; +}; + +struct tas571x_private { + const struct tas571x_chip *chip; + struct regmap *regmap; + struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES]; + struct clk *mclk; + unsigned int format; + struct gpio_desc *reset_gpio; + struct gpio_desc *pdn_gpio; + struct snd_soc_codec_driver codec_driver; +}; + +static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg) +{ + switch (reg) { + case TAS571X_MVOL_REG: + case TAS571X_CH1_VOL_REG: + case TAS571X_CH2_VOL_REG: + return priv->chip->vol_reg_size; + default: + return 1; + } +} + +static int tas571x_reg_write(void *context, unsigned int reg, + unsigned int value) +{ + struct i2c_client *client = context; + struct tas571x_private *priv = i2c_get_clientdata(client); + unsigned int i, size; + uint8_t buf[5]; + int ret; + + size = tas571x_register_size(priv, reg); + buf[0] = reg; + + for (i = size; i >= 1; --i) { + buf[i] = value; + value >>= 8; + } + + ret = i2c_master_send(client, buf, size + 1); + if (ret == size + 1) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int tas571x_reg_read(void *context, unsigned int reg, + unsigned int *value) +{ + struct i2c_client *client = context; + struct tas571x_private *priv = i2c_get_clientdata(client); + uint8_t send_buf, recv_buf[4]; + struct i2c_msg msgs[2]; + unsigned int size; + unsigned int i; + int ret; + + size = tas571x_register_size(priv, reg); + send_buf = reg; + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(send_buf); + msgs[0].buf = &send_buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = size; + msgs[1].buf = recv_buf; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + else if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *value = 0; + + for (i = 0; i < size; i++) { + *value <<= 8; + *value |= recv_buf[i]; + } + + return 0; +} + +static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); + + priv->format = format; + + return 0; +} + +static int tas571x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec); + u32 val; + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + val = 0x00; + break; + case SND_SOC_DAIFMT_I2S: + val = 0x03; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = 0x06; + break; + default: + return -EINVAL; + } + + if (params_width(params) >= 24) + val += 2; + else if (params_width(params) >= 20) + val += 1; + + return regmap_update_bits(priv->regmap, TAS571X_SDI_REG, + TAS571X_SDI_FMT_MASK, val); +} + +static int tas571x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct tas571x_private *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (!IS_ERR(priv->mclk)) { + ret = clk_prepare_enable(priv->mclk); + if (ret) { + dev_err(codec->dev, + "Failed to enable master clock: %d\n", + ret); + return ret; + } + } + + gpiod_set_value(priv->pdn_gpio, 0); + usleep_range(5000, 6000); + + regcache_cache_only(priv->regmap, false); + ret = regcache_sync(priv->regmap); + if (ret) + return ret; + } + break; + case SND_SOC_BIAS_OFF: + regcache_cache_only(priv->regmap, true); + gpiod_set_value(priv->pdn_gpio, 1); + + if (!IS_ERR(priv->mclk)) + clk_disable_unprepare(priv->mclk); + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops tas571x_dai_ops = { + .set_fmt = tas571x_set_dai_fmt, + .hw_params = tas571x_hw_params, +}; + +static const char *const tas5711_supply_names[] = { + "AVDD", + "DVDD", + "PVDD_A", + "PVDD_B", + "PVDD_C", + "PVDD_D", +}; + +static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1); + +static const struct snd_kcontrol_new tas5711_controls[] = { + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, + TAS571X_CH2_VOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), +}; + +static const struct reg_default tas5711_reg_defaults[] = { + { 0x04, 0x05 }, + { 0x05, 0x40 }, + { 0x06, 0x00 }, + { 0x07, 0xff }, + { 0x08, 0x30 }, + { 0x09, 0x30 }, + { 0x1b, 0x82 }, +}; + +static const struct regmap_config tas5711_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5711_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct tas571x_chip tas5711_chip = { + .supply_names = tas5711_supply_names, + .num_supply_names = ARRAY_SIZE(tas5711_supply_names), + .controls = tas5711_controls, + .num_controls = ARRAY_SIZE(tas5711_controls), + .regmap_config = &tas5711_regmap_config, + .vol_reg_size = 1, +}; + +static const char *const tas5717_supply_names[] = { + "AVDD", + "DVDD", + "HPVDD", + "PVDD_AB", + "PVDD_CD", +}; + +static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0); + +static const struct snd_kcontrol_new tas5717_controls[] = { + /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */ + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, 1, 0x1ff, 1, + tas5717_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG, + 1, 0x1ff, 1, tas5717_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), +}; + +static const struct reg_default tas5717_reg_defaults[] = { + { 0x04, 0x05 }, + { 0x05, 0x40 }, + { 0x06, 0x00 }, + { 0x07, 0x03ff }, + { 0x08, 0x00c0 }, + { 0x09, 0x00c0 }, + { 0x1b, 0x82 }, +}; + +static const struct regmap_config tas5717_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5717_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +/* This entry is reused for tas5719 as the software interface is identical. */ +static const struct tas571x_chip tas5717_chip = { + .supply_names = tas5717_supply_names, + .num_supply_names = ARRAY_SIZE(tas5717_supply_names), + .controls = tas5717_controls, + .num_controls = ARRAY_SIZE(tas5717_controls), + .regmap_config = &tas5717_regmap_config, + .vol_reg_size = 2, +}; + +static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("OUT_A"), + SND_SOC_DAPM_OUTPUT("OUT_B"), + SND_SOC_DAPM_OUTPUT("OUT_C"), + SND_SOC_DAPM_OUTPUT("OUT_D"), +}; + +static const struct snd_soc_dapm_route tas571x_dapm_routes[] = { + { "DACL", NULL, "Playback" }, + { "DACR", NULL, "Playback" }, + + { "OUT_A", NULL, "DACL" }, + { "OUT_B", NULL, "DACL" }, + { "OUT_C", NULL, "DACR" }, + { "OUT_D", NULL, "DACR" }, +}; + +static const struct snd_soc_codec_driver tas571x_codec = { + .set_bias_level = tas571x_set_bias_level, + .idle_bias_off = true, + + .dapm_widgets = tas571x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), + .dapm_routes = tas571x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), +}; + +static struct snd_soc_dai_driver tas571x_dai = { + .name = "tas571x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tas571x_dai_ops, +}; + +static const struct of_device_id tas571x_of_match[]; + +static int tas571x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tas571x_private *priv; + struct device *dev = &client->dev; + const struct of_device_id *of_id; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + i2c_set_clientdata(client, priv); + + of_id = of_match_device(tas571x_of_match, dev); + if (!of_id) { + dev_err(dev, "Unknown device type\n"); + return -EINVAL; + } + priv->chip = of_id->data; + + priv->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { + dev_err(dev, "Failed to request mclk: %ld\n", + PTR_ERR(priv->mclk)); + return PTR_ERR(priv->mclk); + } + + BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES); + for (i = 0; i < priv->chip->num_supply_names; i++) + priv->supplies[i].supply = priv->chip->supply_names[i]; + + ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names, + priv->supplies); + if (ret) { + dev_err(dev, "Failed to get supplies: %d\n", ret); + return ret; + } + ret = regulator_bulk_enable(priv->chip->num_supply_names, + priv->supplies); + if (ret) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + priv->regmap = devm_regmap_init(dev, NULL, client, + priv->chip->regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW); + if (IS_ERR(priv->pdn_gpio)) { + dev_err(dev, "error requesting pdn_gpio: %ld\n", + PTR_ERR(priv->pdn_gpio)); + return PTR_ERR(priv->pdn_gpio); + } + + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset_gpio)) { + dev_err(dev, "error requesting reset_gpio: %ld\n", + PTR_ERR(priv->reset_gpio)); + return PTR_ERR(priv->reset_gpio); + } else if (priv->reset_gpio) { + /* pulse the active low reset line for ~100us */ + usleep_range(100, 200); + gpiod_set_value(priv->reset_gpio, 0); + usleep_range(12000, 20000); + } + + ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, TAS571X_SYS_CTRL_2_REG, + TAS571X_SYS_CTRL_2_SDN_MASK, 0); + if (ret) + return ret; + + memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver)); + priv->codec_driver.controls = priv->chip->controls; + priv->codec_driver.num_controls = priv->chip->num_controls; + + if (priv->chip->vol_reg_size == 2) { + /* + * The master volume defaults to 0x3ff (mute), but we ignore + * (zero) the LSB because the hardware step size is 0.125 dB + * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB. + */ + ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0); + if (ret) + return ret; + } + + regcache_cache_only(priv->regmap, true); + gpiod_set_value(priv->pdn_gpio, 1); + + return snd_soc_register_codec(&client->dev, &priv->codec_driver, + &tas571x_dai, 1); +} + +static int tas571x_i2c_remove(struct i2c_client *client) +{ + struct tas571x_private *priv = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); + + return 0; +} + +static const struct of_device_id tas571x_of_match[] = { + { .compatible = "ti,tas5711", .data = &tas5711_chip, }, + { .compatible = "ti,tas5717", .data = &tas5717_chip, }, + { .compatible = "ti,tas5719", .data = &tas5717_chip, }, + { } +}; +MODULE_DEVICE_TABLE(of, tas571x_of_match); + +static const struct i2c_device_id tas571x_i2c_id[] = { + { "tas5711", 0 }, + { "tas5717", 0 }, + { "tas5719", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); + +static struct i2c_driver tas571x_i2c_driver = { + .driver = { + .name = "tas571x", + .of_match_table = of_match_ptr(tas571x_of_match), + }, + .probe = tas571x_i2c_probe, + .remove = tas571x_i2c_remove, + .id_table = tas571x_i2c_id, +}; +module_i2c_driver(tas571x_i2c_driver); + +MODULE_DESCRIPTION("ASoC TAS571x driver"); +MODULE_AUTHOR("Kevin Cernekee <cernekee@chromium.org>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h new file mode 100644 index 000000000..0aee47123 --- /dev/null +++ b/sound/soc/codecs/tas571x.h @@ -0,0 +1,33 @@ +/* + * TAS571x amplifier audio driver + * + * Copyright (C) 2015 Google, Inc. + * + * 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. + */ + +#ifndef _TAS571X_H +#define _TAS571X_H + +/* device registers */ +#define TAS571X_SDI_REG 0x04 +#define TAS571X_SDI_FMT_MASK 0x0f + +#define TAS571X_SYS_CTRL_2_REG 0x05 +#define TAS571X_SYS_CTRL_2_SDN_MASK 0x40 + +#define TAS571X_SOFT_MUTE_REG 0x06 +#define TAS571X_SOFT_MUTE_CH1_SHIFT 0 +#define TAS571X_SOFT_MUTE_CH2_SHIFT 1 +#define TAS571X_SOFT_MUTE_CH3_SHIFT 2 + +#define TAS571X_MVOL_REG 0x07 +#define TAS571X_CH1_VOL_REG 0x08 +#define TAS571X_CH2_VOL_REG 0x09 + +#define TAS571X_OSC_TRIM_REG 0x1b + +#endif /* _TAS571X_H */ diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index cc17e7e51..cd8c02b6e 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -506,7 +506,6 @@ static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, TLV320AIC23_PWR, 0x1ff); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index c86dd9aae..c4c960f59 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -646,7 +646,7 @@ static int aic31xx_add_controls(struct snd_soc_codec *codec) static int aic31xx_add_widgets(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec); int ret = 0; @@ -1027,17 +1027,17 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { dev_dbg(codec->dev, "## %s: %d -> %d\n", __func__, - codec->dapm.bias_level, level); + snd_soc_codec_get_bias_level(codec), level); switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) aic31xx_clk_on(codec); break; case SND_SOC_BIAS_STANDBY: - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_OFF: aic31xx_power_on(codec); break; @@ -1049,11 +1049,10 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec, } break; case SND_SOC_BIAS_OFF: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) aic31xx_power_off(codec); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 015467ed6..ad6cb90e5 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -564,7 +564,6 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_OFF: break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 51c4713ac..a7cf19b53 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -147,6 +147,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -179,7 +180,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, update.mask = mask; update.val = val; - snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect, + snd_soc_dapm_mixer_update_power(dapm, kcontrol, connect, &update); } @@ -979,7 +980,7 @@ static const struct snd_soc_dapm_route intercon_3007[] = { static int aic3x_add_widgets(struct snd_soc_codec *codec) { struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); switch (aic3x->model) { case AIC3X_MODEL_3X: @@ -1384,7 +1385,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY && + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY && aic3x->master) { /* enable pll */ snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, @@ -1394,7 +1395,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: if (!aic3x->power) aic3x_set_power(codec, 1); - if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE && + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE && aic3x->master) { /* disable pll */ snd_soc_update_bits(codec, AIC3X_PLL_PROGA_REG, @@ -1406,7 +1407,6 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, aic3x_set_power(codec, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 4e3e607de..d67a311f0 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -633,7 +633,7 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Coming from OFF, switch on the codec */ ret = dac33_hard_power(codec, 1); if (ret != 0) @@ -644,14 +644,13 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: /* Do not power off, when the codec is already off */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) return 0; ret = dac33_hard_power(codec, 0); if (ret != 0) return ret; break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 9fd80ac18..12232d7db 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -254,12 +254,13 @@ static const struct regmap_config ts3a227e_regmap_config = { .num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults), }; -static int ts3a227e_parse_dt(struct ts3a227e *ts3a227e, struct device_node *np) +static int ts3a227e_parse_device_property(struct ts3a227e *ts3a227e, + struct device *dev) { u32 micbias; int err; - err = of_property_read_u32(np, "ti,micbias", &micbias); + err = device_property_read_u32(dev, "ti,micbias", &micbias); if (!err) { regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_3, MICBIAS_SETTING_MASK, @@ -287,12 +288,10 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c, if (IS_ERR(ts3a227e->regmap)) return PTR_ERR(ts3a227e->regmap); - if (dev->of_node) { - ret = ts3a227e_parse_dt(ts3a227e, dev->of_node); - if (ret) { - dev_err(dev, "Failed to parse device tree: %d\n", ret); - return ret; - } + ret = ts3a227e_parse_device_property(ts3a227e, dev); + if (ret) { + dev_err(dev, "Failed to parse device property: %d\n", ret); + return ret; } ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt, diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index d04693e9c..90f5f04ec 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1588,14 +1588,13 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) twl4030_codec_enable(codec, 1); break; case SND_SOC_BIAS_OFF: twl4030_codec_enable(codec, 0); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index aeec27b6f..4cad8929d 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -533,7 +533,7 @@ static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol, int twl6040_get_dl1_gain(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); if (snd_soc_dapm_get_pin_status(dapm, "EP")) return -1; /* -1dB */ @@ -853,8 +853,6 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } @@ -1123,14 +1121,15 @@ static int twl6040_probe(struct snd_soc_codec *codec) mutex_init(&priv->mutex); ret = request_threaded_irq(priv->plug_irq, NULL, - twl6040_audio_handler, IRQF_NO_SUSPEND, + twl6040_audio_handler, + IRQF_NO_SUSPEND | IRQF_ONESHOT, "twl6040_irq_plug", codec); if (ret) { dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); return ret; } - twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); twl6040_init_chip(codec); return 0; diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index f883308c0..913edf283 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -350,7 +350,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec, pd->power(0); break; } - codec->dapm.bias_level = level; return 0; } @@ -478,6 +477,7 @@ static struct snd_soc_dai_driver uda134x_dai = { static int uda134x_soc_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct uda134x_priv *uda134x; struct uda134x_platform_data *pd = codec->component.card->dev->platform_data; const struct snd_soc_dapm_widget *widgets; @@ -526,7 +526,7 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) num_widgets = ARRAY_SIZE(uda1340_dapm_widgets); } - ret = snd_soc_dapm_new_controls(&codec->dapm, widgets, num_widgets); + ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets); if (ret) { printk(KERN_ERR "%s failed to register dapm controls: %d", __func__, ret); diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index c3c33bd0d..6e159f59d 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -590,9 +590,6 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, int reg; struct uda1380_platform_data *pdata = codec->dev->platform_data; - if (codec->dapm.bias_level == level) - return 0; - switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: @@ -600,7 +597,7 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { if (gpio_is_valid(pdata->gpio_power)) { gpio_set_value(pdata->gpio_power, 1); mdelay(1); @@ -623,7 +620,6 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec, for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++) set_bit(reg - 0x10, &uda1380_cache_dirty); } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index cef274ed6..35b3d919f 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -751,13 +751,13 @@ static int wm0010_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) wm0010_boot(codec); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) { mutex_lock(&wm0010->lock); wm0010_halt(codec); mutex_unlock(&wm0010->lock); @@ -767,8 +767,6 @@ static int wm0010_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index 8011f75fb..048f00568 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -61,8 +61,6 @@ static int wm1250_ev1_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 5a9da28f4..c83083285 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/wm5100.c b/sound/soc/codecs/wm5100.c index 96740379b..4c10cd88c 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2101,7 +2101,7 @@ static void wm5100_micd_irq(struct wm5100_priv *wm5100) int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) { struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); if (jack) { wm5100->jack = jack; @@ -2336,6 +2336,7 @@ static void wm5100_free_gpio(struct i2c_client *i2c) static int wm5100_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct i2c_client *i2c = to_i2c_client(codec->dev); struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec); int ret, i; @@ -2353,8 +2354,7 @@ static int wm5100_probe(struct snd_soc_codec *codec) /* TODO: check if we're symmetric */ if (i2c->irq) - snd_soc_dapm_new_controls(&codec->dapm, - wm5100_dapm_widgets_noirq, + snd_soc_dapm_new_controls(dapm, wm5100_dapm_widgets_noirq, ARRAY_SIZE(wm5100_dapm_widgets_noirq)); if (wm5100->pdata.hp_pol) { @@ -2570,11 +2570,13 @@ static int wm5100_i2c_probe(struct i2c_client *i2c, if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) ret = request_threaded_irq(i2c->irq, NULL, - wm5100_edge_irq, irq_flags, + wm5100_edge_irq, + irq_flags | IRQF_ONESHOT, "wm5100", wm5100); else ret = request_threaded_irq(i2c->irq, NULL, wm5100_irq, - irq_flags, "wm5100", + irq_flags | IRQF_ONESHOT, + "wm5100", wm5100); if (ret != 0) { diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index d476221db..d097f09e5 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -605,12 +605,56 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, regmap_write_async(regmap, patch[i].reg, patch[i].def); break; + case SND_SOC_DAPM_PRE_PMD: + break; + default: + return 0; + } + + return arizona_dvfs_sysclk_ev(w, kcontrol, event); +} + +static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + unsigned int v; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); + if (ret != 0) { + dev_err(codec->dev, + "Failed to read SYSCLK state: %d\n", ret); + return -EIO; + } + + v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; + + if (v >= 3) { + ret = arizona_dvfs_up(codec, ARIZONA_DVFS_ADSP1_RQ); + if (ret) { + dev_err(codec->dev, + "Failed to raise DVFS: %d\n", ret); + return ret; + } + } + break; + + case SND_SOC_DAPM_POST_PMD: + ret = arizona_dvfs_down(codec, ARIZONA_DVFS_ADSP1_RQ); + if (ret) + dev_warn(codec->dev, + "Failed to lower DVFS: %d\n", ret); + break; default: break; } - return 0; + return wm_adsp2_early_event(w, kcontrol, event); } static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, @@ -1036,7 +1080,8 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux = static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, - 0, wm5102_sysclk_ev, SND_SOC_DAPM_POST_PMU), + 0, wm5102_sysclk_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, @@ -1367,7 +1412,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), -WM_ADSP2("DSP1", 0), +WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev), SND_SOC_DAPM_OUTPUT("HPOUT1L"), SND_SOC_DAPM_OUTPUT("HPOUT1R"), @@ -1827,19 +1872,25 @@ static struct snd_soc_dai_driver wm5102_dai[] = { static int wm5102_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(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); arizona_init_gpio(codec); - snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); - priv->core.arizona->dapm = &codec->dapm; + priv->core.arizona->dapm = dapm; return 0; } @@ -1848,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; @@ -1909,6 +1962,8 @@ static int wm5102_probe(struct platform_device *pdev) wm5102->core.arizona = arizona; wm5102->core.num_inputs = 6; + arizona_init_dvfs(&wm5102->core); + wm5102->core.adsp[0].part = "wm5102"; wm5102->core.adsp[0].num = 1; wm5102->core.adsp[0].type = WMFW_ADSP2; @@ -1918,7 +1973,7 @@ static int wm5102_probe(struct platform_device *pdev) wm5102->core.adsp[0].mem = wm5102_dsp1_regions; wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions); - ret = wm_adsp2_init(&wm5102->core.adsp[0], true); + ret = wm_adsp2_init(&wm5102->core.adsp[0]); if (ret != 0) return ret; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 3ee6cfd05..709fcc616 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -1598,22 +1598,29 @@ static struct snd_soc_dai_driver wm5110_dai[] = { 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 = &codec->dapm; + priv->core.arizona->dapm = dapm; arizona_init_spk(codec); arizona_init_gpio(codec); arizona_init_mono(codec); - ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 8); - if (ret != 0) - return ret; + for (i = 0; i < WM5110_NUM_ADSP; ++i) { + ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec); + if (ret) + return ret; + } - snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); + ret = snd_soc_add_codec_controls(codec, + arizona_adsp2_rate_controls, + WM5110_NUM_ADSP); + if (ret) + return ret; - priv->core.arizona->dapm = &codec->dapm; + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); return 0; } @@ -1621,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; @@ -1697,7 +1708,7 @@ static int wm5110_probe(struct platform_device *pdev) wm5110->core.adsp[i].num_mems = ARRAY_SIZE(wm5110_dsp1_regions); - ret = wm_adsp2_init(&wm5110->core.adsp[i], false); + ret = wm_adsp2_init(&wm5110->core.adsp[i]); if (ret != 0) return ret; } diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index c65e5a75f..41c62c1e6 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1102,7 +1102,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); if (ret != 0) @@ -1235,7 +1235,6 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec, priv->supplies); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index b0d84e552..d7555085e 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1145,7 +1145,7 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(power), &power[0]); if (ret != 0) { @@ -1232,7 +1232,6 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 8736ad094..dac5beb4d 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -519,7 +519,7 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN; - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8510->regmap); /* Initial cap charge at VMID 5k */ @@ -538,7 +538,6 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index b1cc94f5f..43ea8ae5f 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: @@ -308,7 +334,7 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); if (ret != 0) { @@ -344,7 +370,6 @@ static int wm8523_set_bias_level(struct snd_soc_codec *codec, wm8523->supplies); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 0a887c5ec..759a7928a 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -795,7 +795,7 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Power up and get individual control of the DACs */ snd_soc_update_bits(codec, WM8580_PWRDN1, WM8580_PWRDN1_PWDN | @@ -812,7 +812,6 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, WM8580_PWRDN1_PWDN, WM8580_PWRDN1_PWDN); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 121e46d53..cc8251f09 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -310,7 +310,7 @@ static int wm8711_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) regcache_sync(wm8711->regmap); snd_soc_write(codec, WM8711_PWR, reg | 0x0040); @@ -320,7 +320,6 @@ static int wm8711_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8711_PWR, 0xffff); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 55c7fb4fc..f1a173e6e 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -170,7 +170,7 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Power everything up... */ reg = snd_soc_read(codec, WM8728_DACCTL); snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4); @@ -185,7 +185,6 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8728_DACCTL, reg | 0x4); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 2245b6a32..915ea11ad 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -387,6 +387,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); switch (clk_id) { @@ -421,7 +422,7 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, wm8731->sysclk = freq; - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); return 0; } @@ -501,7 +502,7 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); if (ret != 0) @@ -523,7 +524,6 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, regcache_mark_dirty(wm8731->regmap); break; } - codec->dapm.bias_level = level; return 0; } @@ -599,7 +599,7 @@ static int wm8731_probe(struct snd_soc_codec *codec) goto err_regulator_enable; } - wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Latch the update bits */ snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0); diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index 51171e457..6ad606fd8 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -469,7 +469,7 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); if (ret != 0) { @@ -512,7 +512,6 @@ static int wm8737_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -562,7 +561,7 @@ static int wm8737_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8737_RIGHT_PGA_VOLUME, WM8737_RVU, WM8737_RVU); - wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Bias level configuration will have done an extra enable */ regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 9e71c7689..b34623786 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -41,6 +41,7 @@ static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = { /* codec private data */ struct wm8741_priv { + struct wm8741_platform_data pdata; struct regmap *regmap; struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; unsigned int sysclk; @@ -87,13 +88,27 @@ static int wm8741_reset(struct snd_soc_codec *codec) static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0); static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0); -static const struct snd_kcontrol_new wm8741_snd_controls[] = { +static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = { SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine), SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv), }; +static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = { +SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, + 1, 255, 1, dac_tlv_fine), +SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, + 0, 511, 1, dac_tlv), +}; + +static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = { +SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION, + 1, 255, 1, dac_tlv_fine), +SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION, + 0, 511, 1, dac_tlv), +}; + static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = { SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0), @@ -110,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, }; @@ -194,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; } @@ -226,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; } @@ -259,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; @@ -275,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; @@ -398,7 +404,7 @@ static struct snd_soc_dai_driver wm8741_dai = { .name = "wm8741", .playback = { .stream_name = "Playback", - .channels_min = 2, /* Mono modes not yet supported */ + .channels_min = 2, .channels_max = 2, .rates = WM8741_RATES, .formats = WM8741_FORMATS, @@ -416,6 +422,65 @@ static int wm8741_resume(struct snd_soc_codec *codec) #define wm8741_resume NULL #endif +static int wm8741_configure(struct snd_soc_codec *codec) +{ + struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); + + /* Configure differential mode */ + switch (wm8741->pdata.diff_mode) { + case WM8741_DIFF_MODE_STEREO: + case WM8741_DIFF_MODE_STEREO_REVERSED: + case WM8741_DIFF_MODE_MONO_LEFT: + case WM8741_DIFF_MODE_MONO_RIGHT: + snd_soc_update_bits(codec, WM8741_MODE_CONTROL_2, + WM8741_DIFF_MASK, + wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT); + break; + default: + return -EINVAL; + } + + /* Change some default settings - latch VU */ + snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, + WM8741_UPDATELL, WM8741_UPDATELL); + snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, + WM8741_UPDATELM, WM8741_UPDATELM); + snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, + WM8741_UPDATERL, WM8741_UPDATERL); + snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION, + WM8741_UPDATERM, WM8741_UPDATERM); + + return 0; +} + +static int wm8741_add_controls(struct snd_soc_codec *codec) +{ + struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); + + switch (wm8741->pdata.diff_mode) { + case WM8741_DIFF_MODE_STEREO: + case WM8741_DIFF_MODE_STEREO_REVERSED: + snd_soc_add_codec_controls(codec, + wm8741_snd_controls_stereo, + ARRAY_SIZE(wm8741_snd_controls_stereo)); + break; + case WM8741_DIFF_MODE_MONO_LEFT: + snd_soc_add_codec_controls(codec, + wm8741_snd_controls_mono_left, + ARRAY_SIZE(wm8741_snd_controls_mono_left)); + break; + case WM8741_DIFF_MODE_MONO_RIGHT: + snd_soc_add_codec_controls(codec, + wm8741_snd_controls_mono_right, + ARRAY_SIZE(wm8741_snd_controls_mono_right)); + break; + default: + return -EINVAL; + } + + return 0; +} + static int wm8741_probe(struct snd_soc_codec *codec) { struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); @@ -434,15 +499,17 @@ static int wm8741_probe(struct snd_soc_codec *codec) goto err_enable; } - /* Change some default settings - latch VU */ - snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, - WM8741_UPDATELL, WM8741_UPDATELL); - snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, - WM8741_UPDATELM, WM8741_UPDATELM); - snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, - WM8741_UPDATERL, WM8741_UPDATERL); - snd_soc_update_bits(codec, WM8741_DACRMSB_ATTENUATION, - WM8741_UPDATERM, WM8741_UPDATERM); + ret = wm8741_configure(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to change default settings\n"); + goto err_enable; + } + + ret = wm8741_add_controls(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to add controls\n"); + goto err_enable; + } dev_dbg(codec->dev, "Successful registration\n"); return ret; @@ -467,8 +534,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8741 = { .remove = wm8741_remove, .resume = wm8741_resume, - .controls = wm8741_snd_controls, - .num_controls = ARRAY_SIZE(wm8741_snd_controls), .dapm_widgets = wm8741_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets), .dapm_routes = wm8741_dapm_routes, @@ -493,6 +558,23 @@ static const struct regmap_config wm8741_regmap = { .readable_reg = wm8741_readable, }; +static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) +{ + const struct wm8741_platform_data *pdata = dev_get_platdata(dev); + u32 diff_mode; + + if (dev->of_node) { + if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode) + >= 0) + wm8741->pdata.diff_mode = diff_mode; + } else { + if (pdata != NULL) + memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata)); + } + + return 0; +} + #if IS_ENABLED(CONFIG_I2C) static int wm8741_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -522,6 +604,12 @@ static int wm8741_i2c_probe(struct i2c_client *i2c, return ret; } + ret = wm8741_set_pdata(&i2c->dev, wm8741); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret); + return ret; + } + i2c_set_clientdata(i2c, wm8741); ret = snd_soc_register_codec(&i2c->dev, @@ -582,6 +670,12 @@ static int wm8741_spi_probe(struct spi_device *spi) return ret; } + ret = wm8741_set_pdata(&spi->dev, wm8741); + if (ret != 0) { + dev_err(&spi->dev, "Failed to set pdata: %d\n", ret); + return ret; + } + spi_set_drvdata(spi, wm8741); ret = snd_soc_register_codec(&spi->dev, diff --git a/sound/soc/codecs/wm8741.h b/sound/soc/codecs/wm8741.h index 56c1b1d4a..c8835f65f 100644 --- a/sound/soc/codecs/wm8741.h +++ b/sound/soc/codecs/wm8741.h @@ -194,6 +194,12 @@ #define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */ #define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */ +/* DIFF field values */ +#define WM8741_DIFF_MODE_STEREO 0 /* stereo normal */ +#define WM8741_DIFF_MODE_STEREO_REVERSED 2 /* stereo reversed */ +#define WM8741_DIFF_MODE_MONO_LEFT 1 /* mono left */ +#define WM8741_DIFF_MODE_MONO_RIGHT 3 /* mono right */ + /* * R32 (0x20) - ADDITONAL_CONTROL_1 */ @@ -208,4 +214,8 @@ #define WM8741_SYSCLK 0 +struct wm8741_platform_data { + u32 diff_mode; /* Differential Output Mode */ +}; + #endif diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index eb0a1644b..56d89b086 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -634,7 +634,7 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_cache_sync(codec); /* Set VMID to 5k */ @@ -651,7 +651,6 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8750_PWR1, 0x0001); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index c50a59593..feb2997a3 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1352,7 +1352,7 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec, flush_delayed_work(&wm8753->charge_work); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* set vmid to 5k for quick power up */ snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1); schedule_delayed_work(&wm8753->charge_work, @@ -1367,7 +1367,6 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8753_PWR1, 0x0001); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c index 53e977da2..66c1f1510 100644 --- a/sound/soc/codecs/wm8770.c +++ b/sound/soc/codecs/wm8770.c @@ -510,7 +510,7 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); if (ret) { @@ -534,7 +534,6 @@ static int wm8770_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index c13050b77..ece9b4456 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -344,7 +344,7 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8776->regmap); /* Disable the global powerdown; DAPM does the rest */ @@ -357,7 +357,6 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 1e403f67c..c195c2e8a 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -162,7 +162,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l; unsigned int mask = 1 << e->shift_l; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 2eb986c19..f3759ec5a 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -998,8 +998,8 @@ static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute) SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) #define WM8900_PCM_FORMATS \ - (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ - SNDRV_PCM_FORMAT_S24_LE) + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) static const struct snd_soc_dai_ops wm8900_dai_ops = { .hw_params = wm8900_hw_params, @@ -1049,7 +1049,7 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: /* Charge capacitors if initial power up */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* STARTUP_BIAS_ENA on */ snd_soc_write(codec, WM8900_REG_POWER1, WM8900_REG_POWER1_STARTUP_BIAS_ENA); @@ -1117,7 +1117,6 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec, WM8900_REG_POWER2_SYSCLK_ENA); break; } - codec->dapm.bias_level = level; return 0; } @@ -1138,7 +1137,7 @@ static int wm8900_suspend(struct snd_soc_codec *codec) wm8900->fll_out = fll_out; wm8900->fll_in = fll_in; - wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -1156,7 +1155,7 @@ static int wm8900_resume(struct snd_soc_codec *codec) return ret; } - wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Restart the FLL? */ if (wm8900->fll_out) { @@ -1189,7 +1188,7 @@ static int wm8900_probe(struct snd_soc_codec *codec) wm8900_reset(codec); /* Turn the chip on */ - wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Latch the volume update bits */ snd_soc_update_bits(codec, WM8900_REG_LINVOL, 0x100, 0x100); diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 04b04f8e1..b5322c154 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1105,7 +1105,7 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, WM8903_POBCTRL | WM8903_ISEL_MASK | WM8903_STARTUP_BIAS_ENA | @@ -1200,8 +1200,6 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 215e93c1d..265a4a58a 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1168,7 +1168,7 @@ static const struct snd_soc_dapm_route wm8912_intercon[] = { static int wm8904_add_widgets(struct snd_soc_codec *codec) { struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); snd_soc_dapm_new_controls(dapm, wm8904_core_dapm_widgets, ARRAY_SIZE(wm8904_core_dapm_widgets)); @@ -1852,7 +1852,7 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); if (ret != 0) { @@ -1907,7 +1907,6 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec, clk_disable_unprepare(wm8904->mclk); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index e4142b430..98ef0ba5c 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -492,7 +492,7 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec, ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(wm8940->regmap); if (ret < 0) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); @@ -510,8 +510,6 @@ static int wm8940_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return ret; } @@ -707,7 +705,7 @@ static int wm8940_probe(struct snd_soc_codec *codec) return ret; } - wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); ret = snd_soc_write(codec, WM8940_POWER1, 0x180); if (ret < 0) diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 03e04bf6c..2d591c247 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -785,7 +785,7 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies); if (ret != 0) { @@ -838,7 +838,6 @@ static int wm8955_set_bias_level(struct snd_soc_codec *codec, wm8955->supplies); break; } - codec->dapm.bias_level = level; return 0; } @@ -929,7 +928,7 @@ static int wm8955_probe(struct snd_soc_codec *codec) WM8955_DMEN, WM8955_DMEN); } - wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Bias level configuration will have done an extra enable */ regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies); diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 8d7f63253..94c5c4681 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -127,6 +127,8 @@ struct wm8960_priv { struct snd_soc_dapm_widget *out3; bool deemph; int playback_fs; + int bclk; + int sysclk; struct wm8960_data pdata; }; @@ -445,7 +447,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); struct wm8960_data *pdata = &wm8960->pdata; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct snd_soc_dapm_widget *w; snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, @@ -476,7 +478,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) * and save the result. */ list_for_each_entry(w, &codec->component.card->widgets, list) { - if (w->dapm != &codec->dapm) + if (w->dapm != dapm) continue; if (strcmp(w->name, "LOUT1 PGA") == 0) wm8960->lout1 = w; @@ -563,6 +565,72 @@ static struct { { 8000, 5 }, }; +/* Multiply 256 for internal 256 div */ +static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; + +/* Multiply 10 to eliminate decimials */ +static const int bclk_divs[] = { + 10, 15, 20, 30, 40, 55, 60, 80, 110, + 120, 160, 220, 240, 320, 320, 320 +}; + +static void wm8960_configure_clocking(struct snd_soc_codec *codec, + bool tx, int lrclk) +{ + struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); + u16 iface2 = snd_soc_read(codec, WM8960_IFACE2); + u32 sysclk; + int i, j; + + if (!(iface1 & (1<<6))) { + dev_dbg(codec->dev, + "Codec is slave mode, no need to configure clock\n"); + return; + } + + if (!wm8960->sysclk) { + dev_dbg(codec->dev, "No SYSCLK configured\n"); + return; + } + + if (!wm8960->bclk || !lrclk) { + dev_dbg(codec->dev, "No audio clocks configured\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) { + if (wm8960->sysclk == lrclk * dac_divs[i]) { + for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) { + sysclk = wm8960->bclk * bclk_divs[j] / 10; + if (wm8960->sysclk == sysclk) + break; + } + if(j != ARRAY_SIZE(bclk_divs)) + break; + } + } + + if (i == ARRAY_SIZE(dac_divs)) { + dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk); + return; + } + + /* + * configure frame clock. If ADCLRC configure as GPIO pin, DACLRC + * pin is used as a frame clock for ADCs and DACs. + */ + if (iface2 & (1<<6)) + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); + else if (tx) + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); + else if (!tx) + snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6); + + /* configure bit clock */ + snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j); +} + static int wm8960_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -570,8 +638,13 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int i; + wm8960->bclk = snd_soc_params_to_bclk(params); + if (params_channels(params) == 1) + wm8960->bclk *= 2; + /* bit size */ switch (params_width(params)) { case 16: @@ -582,6 +655,12 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, case 24: iface |= 0x0008; break; + case 32: + /* right justify mode does not support 32 word length */ + if ((iface & 0x3) != 0) { + iface |= 0x000c; + break; + } default: dev_err(codec->dev, "unsupported width %d\n", params_width(params)); @@ -602,6 +681,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, /* set iface */ snd_soc_write(codec, WM8960_IFACE1, iface); + + wm8960_configure_clocking(codec, tx, params_rate(params)); + return 0; } @@ -627,7 +709,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_STANDBY: if (!IS_ERR(wm8960->mclk)) { ret = clk_prepare_enable(wm8960->mclk); @@ -655,7 +737,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8960->regmap); /* Enable anti-pop features */ @@ -691,8 +773,6 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } @@ -707,7 +787,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_STANDBY: /* Enable anti pop mode */ snd_soc_update_bits(codec, WM8960_APOP1, @@ -778,7 +858,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - switch (codec->dapm.bias_level) { + switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_PREPARE: /* Disable HP discharge */ snd_soc_update_bits(codec, WM8960_APOP2, @@ -802,8 +882,6 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } @@ -950,11 +1028,35 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec, return wm8960->set_bias_level(codec, level); } +static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case WM8960_SYSCLK_MCLK: + snd_soc_update_bits(codec, WM8960_CLOCK1, + 0x1, WM8960_SYSCLK_MCLK); + break; + case WM8960_SYSCLK_PLL: + snd_soc_update_bits(codec, WM8960_CLOCK1, + 0x1, WM8960_SYSCLK_PLL); + break; + default: + return -EINVAL; + } + + wm8960->sysclk = freq; + + return 0; +} + #define WM8960_RATES SNDRV_PCM_RATE_8000_48000 #define WM8960_FORMATS \ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE) + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops wm8960_dai_ops = { .hw_params = wm8960_hw_params, @@ -962,6 +1064,7 @@ static const struct snd_soc_dai_ops wm8960_dai_ops = { .set_fmt = wm8960_set_dai_fmt, .set_clkdiv = wm8960_set_dai_clkdiv, .set_pll = wm8960_set_dai_pll, + .set_sysclk = wm8960_set_dai_sysclk, }; static struct snd_soc_dai_driver wm8960_dai = { diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 95e2c1bfc..a05766263 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -758,7 +758,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { /* Enable bias generation */ reg = snd_soc_read(codec, WM8961_ANTI_POP); reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN; @@ -773,7 +773,7 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) { /* VREF off */ reg = snd_soc_read(codec, WM8961_PWR_MGMT_1); reg &= ~WM8961_VREF; @@ -795,8 +795,6 @@ static int wm8961_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 118b0034b..c5748fd4f 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2361,7 +2361,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec) { struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); struct wm8962_pdata *pdata = &wm8962->pdata; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); snd_soc_add_codec_controls(codec, wm8962_snd_controls, ARRAY_SIZE(wm8962_snd_controls)); @@ -2446,13 +2446,13 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec) * So we here provisionally enable it and then disable it afterward * if current bias_level hasn't reached SND_SOC_BIAS_ON. */ - if (codec->dapm.bias_level != SND_SOC_BIAS_ON) + if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON) snd_soc_update_bits(codec, WM8962_CLOCKING2, WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA); dspclk = snd_soc_read(codec, WM8962_CLOCKING1); - if (codec->dapm.bias_level != SND_SOC_BIAS_ON) + if (snd_soc_codec_get_bias_level(codec) != SND_SOC_BIAS_ON) snd_soc_update_bits(codec, WM8962_CLOCKING2, WM8962_SYSCLK_ENA_MASK, 0); @@ -2510,9 +2510,6 @@ static void wm8962_configure_bclk(struct snd_soc_codec *codec) static int wm8962_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - if (level == codec->dapm.bias_level) - return 0; - switch (level) { case SND_SOC_BIAS_ON: break; @@ -2530,7 +2527,7 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, WM8962_PWR_MGMT_1, WM8962_VMID_SEL_MASK, 0x100); - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) msleep(100); break; @@ -2538,7 +2535,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -2614,7 +2610,7 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream, dev_dbg(codec->dev, "hw_params set BCLK %dHz LRCLK %dHz\n", wm8962->bclk, wm8962->lrclk); - if (codec->dapm.bias_level == SND_SOC_BIAS_ON) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) wm8962_configure_bclk(codec); return 0; @@ -3118,7 +3114,7 @@ static irqreturn_t wm8962_irq(int irq, void *data) int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) { struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int irq_mask, enable; wm8962->jack = jack; @@ -3164,7 +3160,7 @@ static void wm8962_beep_work(struct work_struct *work) struct wm8962_priv *wm8962 = container_of(work, struct wm8962_priv, beep_work); struct snd_soc_codec *codec = wm8962->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int i; int reg = 0; int best = 0; @@ -3415,6 +3411,7 @@ static void wm8962_free_gpio(struct snd_soc_codec *codec) static int wm8962_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int ret; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); int i; @@ -3462,7 +3459,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) } if (!dmicclk || !dmicdat) { dev_dbg(codec->dev, "DMIC not in use, disabling\n"); - snd_soc_dapm_nc_pin(&codec->dapm, "DMICDAT"); + snd_soc_dapm_nc_pin(dapm, "DMICDAT"); } if (dmicclk != dmicdat) dev_warn(codec->dev, "DMIC GPIOs partially configured\n"); diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index f9cbabdc6..b51184c4e 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -577,7 +577,7 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec, flush_delayed_work(&wm8971->charge_work); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { snd_soc_cache_sync(codec); /* charge output caps - set vmid to 5k for quick power up */ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x01c0); @@ -594,7 +594,6 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8971_PWR1, 0x0001); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index ff0e4646b..33b16a7ba 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -514,7 +514,7 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN; - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(dev_get_regmap(codec->dev, NULL)); /* Initial cap charge at VMID 5k */ @@ -533,7 +533,6 @@ static int wm8974_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index cf7032911..cfc8cdf49 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -868,7 +868,7 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec, /* bit 3: enable bias, bit 2: enable I/O tie off buffer */ power1 |= 0xc; - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Initial cap charge at VMID 5k */ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1 | 0x3); @@ -888,7 +888,6 @@ static int wm8978_set_bias_level(struct snd_soc_codec *codec, dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1); - codec->dapm.bias_level = level; return 0; } @@ -928,7 +927,7 @@ static int wm8978_suspend(struct snd_soc_codec *codec) { struct wm8978_priv *wm8978 = snd_soc_codec_get_drvdata(codec); - wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); /* Also switch PLL off */ snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, 0); @@ -944,7 +943,7 @@ static int wm8978_resume(struct snd_soc_codec *codec) /* Sync reg_cache with the hardware */ regcache_sync(wm8978->regmap); - wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); if (wm8978->f_pllout) /* Switch PLL on */ diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 5d1cf08a7..2fdd2c6cc 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -915,7 +915,7 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec, 1 << WM8983_VMIDSEL_SHIFT); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(wm8983->regmap); if (ret < 0) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); @@ -963,7 +963,6 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 0b3b54c99..8a85f5004 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -897,7 +897,7 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec, 1 << WM8985_VMIDSEL_SHIFT); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies), wm8985->supplies); if (ret) { @@ -957,7 +957,6 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 24968aa86..f13a995af 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -738,7 +738,7 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8988->regmap); /* VREF, VMID=2x5k */ @@ -756,7 +756,6 @@ static int wm8988_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8988_PWR1, 0x0000); break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index c93bffcb3..1993fd2a6 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1124,7 +1124,7 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regcache_sync(wm8990->regmap); if (ret < 0) { dev_err(codec->dev, "Failed to sync cache: %d\n", ret); @@ -1227,7 +1227,6 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } @@ -1281,7 +1280,7 @@ static int wm8990_probe(struct snd_soc_codec *codec) wm8990_reset(codec); /* charge output caps */ - wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); snd_soc_update_bits(codec, WM8990_AUDIO_INTERFACE_4, WM8990_ALRCGPIO1, WM8990_ALRCGPIO1); diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 49df0dc60..44a677720 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -1131,7 +1131,7 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8991->regmap); /* Enable all output discharge bits */ snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE | @@ -1224,7 +1224,6 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 2e70a270e..8a8db8605 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -992,7 +992,7 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies); if (ret != 0) @@ -1065,8 +1065,6 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } @@ -1485,7 +1483,7 @@ static struct snd_soc_dai_driver wm8993_dai = { static int wm8993_probe(struct snd_soc_codec *codec) { struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); wm8993->hubs_data.hp_startup_mode = 1; wm8993->hubs_data.dcs_codes_l = -2; @@ -1539,7 +1537,7 @@ static int wm8993_probe(struct snd_soc_codec *codec) * VMID as an output and can disable it. */ if (wm8993->pdata.lineout1_diff && wm8993->pdata.lineout2_diff) - codec->dapm.idle_bias_off = 1; + dapm->idle_bias_off = 1; return 0; @@ -1563,7 +1561,7 @@ static int wm8993_suspend(struct snd_soc_codec *codec) wm8993->fll_fout = fll_fout; wm8993->fll_fref = fll_fref; - wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -1573,7 +1571,7 @@ static int wm8993_resume(struct snd_soc_codec *codec) struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec); int ret; - wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Restart the FLL? */ if (wm8993->fll_fout) { diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index a1c04dab6..962e1d31a 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -212,6 +212,7 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif) static int configure_clock(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int change, new; @@ -239,7 +240,7 @@ static int configure_clock(struct snd_soc_codec *codec) change = snd_soc_update_bits(codec, WM8994_CLOCKING_1, WM8994_SYSCLK_SRC, new); if (change) - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); wm8958_micd_set_rate(codec); @@ -2492,12 +2493,12 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, break; } - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) active_reference(codec); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { switch (control->type) { case WM8958: if (control->revision == 0) { @@ -2521,7 +2522,7 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, WM8994_LINEOUT2_DISCH); } - if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) active_dereference(codec); /* MICBIAS into bypass mode on newer devices */ @@ -2541,20 +2542,18 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_OFF: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) wm8994->cur_fw = NULL; break; } - codec->dapm.bias_level = level; - return 0; } int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); switch (mode) { case WM8994_VMID_NORMAL: @@ -3163,7 +3162,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec) i + 1, ret); } - wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -3356,6 +3355,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int micbias) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994_micdet *micdet; struct wm8994 *control = wm8994->wm8994; @@ -3370,20 +3370,16 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, case 1: micdet = &wm8994->micdet[0]; if (jack) - ret = snd_soc_dapm_force_enable_pin(&codec->dapm, - "MICBIAS1"); + ret = snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); else - ret = snd_soc_dapm_disable_pin(&codec->dapm, - "MICBIAS1"); + ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); break; case 2: micdet = &wm8994->micdet[1]; if (jack) - ret = snd_soc_dapm_force_enable_pin(&codec->dapm, - "MICBIAS1"); + ret = snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); else - ret = snd_soc_dapm_disable_pin(&codec->dapm, - "MICBIAS1"); + ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); break; default: dev_warn(codec->dev, "Invalid MICBIAS %d\n", micbias); @@ -3415,7 +3411,7 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, WM8994_MIC2_DET_DB_MASK | WM8994_MIC2_SHRT_DB_MASK, WM8994_MIC1_DET_DB | WM8994_MIC1_SHRT_DB); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); return 0; } @@ -3505,6 +3501,7 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data) /* Should be called with accdet_lock held */ static void wm1811_micd_stop(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); if (!wm8994->jackdet) @@ -3515,8 +3512,7 @@ static void wm1811_micd_stop(struct snd_soc_codec *codec) wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK); if (wm8994->wm8994->pdata.jd_ext_cap) - snd_soc_dapm_disable_pin(&codec->dapm, - "MICBIAS2"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS2"); } static void wm8958_button_det(struct snd_soc_codec *codec, u16 status) @@ -3625,14 +3621,14 @@ static void wm1811_mic_work(struct work_struct *work) mic_work.work); struct wm8994 *control = wm8994->wm8994; struct snd_soc_codec *codec = wm8994->hubs.codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); pm_runtime_get_sync(codec->dev); /* If required for an external cap force MICBIAS on */ if (control->pdata.jd_ext_cap) { - snd_soc_dapm_force_enable_pin(&codec->dapm, - "MICBIAS2"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS2"); + snd_soc_dapm_sync(dapm); } mutex_lock(&wm8994->accdet_lock); @@ -3664,6 +3660,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) struct wm8994_priv *wm8994 = data; struct wm8994 *control = wm8994->wm8994; struct snd_soc_codec *codec = wm8994->hubs.codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int reg, delay; bool present; @@ -3724,7 +3721,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) /* Turn off MICBIAS if it was on for an external cap */ if (control->pdata.jd_ext_cap && !present) - snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS2"); if (present) snd_soc_jack_report(wm8994->micdet[0].jack, @@ -3770,6 +3767,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, wm1811_micdet_cb det_cb, void *det_cb_data, wm1811_mic_id_cb id_cb, void *id_cb_data) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; u16 micd_lvl_sel; @@ -3783,8 +3781,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, } if (jack) { - snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "CLK_SYS"); + snd_soc_dapm_sync(dapm); wm8994->micdet[0].jack = jack; @@ -3819,7 +3817,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, snd_soc_update_bits(codec, WM8958_MIC_DETECT_2, WM8958_MICD_LVL_SEL_MASK, micd_lvl_sel); - WARN_ON(codec->dapm.bias_level > SND_SOC_BIAS_STANDBY); + WARN_ON(snd_soc_codec_get_bias_level(codec) > SND_SOC_BIAS_STANDBY); /* * If we can use jack detection start off with that, @@ -3846,8 +3844,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0); wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_NONE); - snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_disable_pin(dapm, "CLK_SYS"); + snd_soc_dapm_sync(dapm); } return 0; @@ -3985,9 +3983,9 @@ static irqreturn_t wm8994_temp_shut(int irq, void *data) static int wm8994_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8994 *control = dev_get_drvdata(codec->dev->parent); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; unsigned int reg; int ret, i; @@ -4018,7 +4016,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->micdet_irq = control->pdata.micdet_irq; /* By default use idle_bias_off, will override for WM8994 */ - codec->dapm.idle_bias_off = 1; + dapm->idle_bias_off = 1; /* Set revision-specific configuration */ switch (control->type) { @@ -4026,7 +4024,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) /* Single ended line outputs should have VMID on. */ if (!control->pdata.lineout1_diff || !control->pdata.lineout2_diff) - codec->dapm.idle_bias_off = 0; + dapm->idle_bias_off = 0; switch (control->revision) { case 2: @@ -4086,7 +4084,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) if (wm8994->micdet_irq) ret = request_threaded_irq(wm8994->micdet_irq, NULL, wm8994_mic_irq, - IRQF_TRIGGER_RISING, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "Mic1 detect", wm8994); else @@ -4134,7 +4133,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) if (wm8994->micdet_irq) { ret = request_threaded_irq(wm8994->micdet_irq, NULL, wm8958_mic_irq, - IRQF_TRIGGER_RISING, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "Mic detect", wm8994); if (ret != 0) diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 66103c2b0..505b65f57 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -721,6 +721,7 @@ static int configure_aif_clock(struct snd_soc_codec *codec, int aif) static int configure_clock(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8995_priv *wm8995; int change, new; @@ -751,7 +752,7 @@ static int configure_clock(struct snd_soc_codec *codec) if (!change) return 0; - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(dapm); return 0; } @@ -1929,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); @@ -1965,7 +1966,7 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); if (ret) @@ -1990,7 +1991,6 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; return 0; } diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 308748a02..3dd063f68 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -1590,7 +1590,7 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); if (ret != 0) { @@ -1628,8 +1628,6 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } @@ -2247,7 +2245,7 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, wm8996_polarity_fn polarity_cb) { struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); wm8996->jack = jack; wm8996->detecting = true; @@ -2292,6 +2290,7 @@ EXPORT_SYMBOL_GPL(wm8996_detect); static void wm8996_hpdet_irq(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); int val, reg, report; @@ -2345,12 +2344,14 @@ out: snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, WM8996_MICD_ENA); - snd_soc_dapm_disable_pin(&codec->dapm, "Bandgap"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_disable_pin(dapm, "Bandgap"); + snd_soc_dapm_sync(dapm); } static void wm8996_hpdet_start(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + /* Unclamp the output, we can't measure while we're shorting it */ snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1, WM8996_HPOUT1L_RMV_SHORT | @@ -2359,8 +2360,8 @@ static void wm8996_hpdet_start(struct snd_soc_codec *codec) WM8996_HPOUT1R_RMV_SHORT); /* We need bandgap for HPDET */ - snd_soc_dapm_force_enable_pin(&codec->dapm, "Bandgap"); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_force_enable_pin(dapm, "Bandgap"); + snd_soc_dapm_sync(dapm); /* Go into headphone detect left mode */ snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0); @@ -2646,10 +2647,12 @@ static int wm8996_probe(struct snd_soc_codec *codec) if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) ret = request_threaded_irq(i2c->irq, NULL, wm8996_edge_irq, - irq_flags, "wm8996", codec); + irq_flags | IRQF_ONESHOT, + "wm8996", codec); else ret = request_threaded_irq(i2c->irq, NULL, wm8996_irq, - irq_flags, "wm8996", codec); + irq_flags | IRQF_ONESHOT, + "wm8996", codec); if (ret == 0) { /* Unmask the interrupt */ diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index e7c81baef..4134dc7e1 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -106,11 +106,13 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w, regmap_write_async(regmap, patch[i].reg, patch[i].def); break; - default: + case SND_SOC_DAPM_PRE_PMD: break; + default: + return 0; } - return 0; + return arizona_dvfs_sysclk_ev(w, kcontrol, event); } static const char *wm8997_osr_text[] = { @@ -409,7 +411,8 @@ static const struct snd_kcontrol_new wm8997_aec_loopback_mux = static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, - 0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU), + 0, wm8997_sysclk_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, @@ -1055,13 +1058,14 @@ static struct snd_soc_dai_driver wm8997_dai[] = { static int wm8997_codec_probe(struct snd_soc_codec *codec) { + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec); arizona_init_spk(codec); - snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); + snd_soc_dapm_disable_pin(dapm, "HAPTICS"); - priv->core.arizona->dapm = &codec->dapm; + priv->core.arizona->dapm = dapm; return 0; } @@ -1126,6 +1130,8 @@ static int wm8997_probe(struct platform_device *pdev) wm8997->core.arizona = arizona; wm8997->core.num_inputs = 4; + arizona_init_dvfs(&wm8997->core); + for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++) wm8997->fll[i].vco_mult = 1; diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 13a3f335e..8a8b1c0f9 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -838,7 +838,7 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: /* Initial cold start */ - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_cache_only(wm9081->regmap, false); regcache_sync(wm9081->regmap); @@ -898,8 +898,6 @@ static int wm9081_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index 60d243c90..13d23fc79 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -425,7 +425,7 @@ static const struct snd_soc_dapm_route audio_map_in2_diff[] = { static int wm9090_add_controls(struct snd_soc_codec *codec) { struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int i; snd_soc_dapm_new_controls(dapm, wm9090_dapm_widgets, @@ -496,7 +496,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { /* Restore the register cache */ regcache_sync(wm9090->regmap); } @@ -515,8 +515,6 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec, break; } - codec->dapm.bias_level = level; - return 0; } diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 98c9525bd..1fda104df 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -610,7 +610,6 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec, ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->dapm.bias_level = level; return 0; } @@ -646,7 +645,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec) if (ret < 0) return ret; - wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret == 0) { /* Sync reg_cache with the hardware after cold reset */ diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 79552953e..89cd2d6f5 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1054,8 +1054,8 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream, SNDRV_PCM_RATE_48000) #define WM9713_PCM_FORMATS \ - (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ - SNDRV_PCM_FORMAT_S24_LE) + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) static const struct snd_soc_dai_ops wm9713_dai_ops_hifi = { .prepare = ac97_hifi_prepare, @@ -1171,7 +1171,6 @@ static int wm9713_set_bias_level(struct snd_soc_codec *codec, ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->dapm.bias_level = level; return 0; } @@ -1201,7 +1200,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) if (ret < 0) return ret; - wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); /* do we need to re-start the PLL ? */ if (wm9713->pll_in) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 28959f1ce..57775d35f 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> @@ -121,6 +122,11 @@ #define ADSP2_WDMA_CONFIG_2 0x31 #define ADSP2_RDMA_CONFIG_1 0x34 +#define ADSP2_SCRATCH0 0x40 +#define ADSP2_SCRATCH1 0x41 +#define ADSP2_SCRATCH2 0x42 +#define ADSP2_SCRATCH3 0x43 + /* * ADSP2 Control */ @@ -229,26 +235,197 @@ struct wm_coeff_ctl_ops { struct wm_coeff_ctl { const char *name; - struct wm_adsp_alg_region region; + const char *fw_name; + struct wm_adsp_alg_region alg_region; struct wm_coeff_ctl_ops ops; - struct wm_adsp *adsp; - void *private; + struct wm_adsp *dsp; unsigned int enabled:1; struct list_head list; void *cache; + unsigned int offset; size_t len; unsigned int set:1; struct snd_kcontrol *kcontrol; + 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) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); - ucontrol->value.integer.value[0] = adsp[e->shift_l].fw; + ucontrol->value.integer.value[0] = dsp[e->shift_l].fw; return 0; } @@ -258,18 +435,18 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); - if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw) + if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw) return 0; if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) return -EINVAL; - if (adsp[e->shift_l].running) + if (dsp[e->shift_l].running) return -EBUSY; - adsp[e->shift_l].fw = ucontrol->value.integer.value[0]; + dsp[e->shift_l].fw = ucontrol->value.integer.value[0]; return 0; } @@ -281,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[] = { - 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[] = { +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("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) @@ -340,28 +482,47 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, return NULL; } -static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, +static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, unsigned int offset) { - if (WARN_ON(!region)) + if (WARN_ON(!mem)) return offset; - switch (region->type) { + switch (mem->type) { case WMFW_ADSP1_PM: - return region->base + (offset * 3); + return mem->base + (offset * 3); case WMFW_ADSP1_DM: - return region->base + (offset * 2); + return mem->base + (offset * 2); case WMFW_ADSP2_XM: - return region->base + (offset * 2); + return mem->base + (offset * 2); case WMFW_ADSP2_YM: - return region->base + (offset * 2); + return mem->base + (offset * 2); case WMFW_ADSP1_ZM: - return region->base + (offset * 2); + return mem->base + (offset * 2); default: WARN(1, "Unknown memory region type"); return offset; } } +static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) +{ + u16 scratch[4]; + int ret; + + ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0, + scratch, sizeof(scratch)); + if (ret) { + adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); + return; + } + + adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", + be16_to_cpu(scratch[0]), + be16_to_cpu(scratch[1]), + be16_to_cpu(scratch[2]), + be16_to_cpu(scratch[3])); +} + static int wm_coeff_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -372,40 +533,39 @@ static int wm_coeff_info(struct snd_kcontrol *kcontrol, return 0; } -static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, +static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, const void *buf, size_t len) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; - struct wm_adsp_alg_region *region = &ctl->region; + struct wm_adsp_alg_region *alg_region = &ctl->alg_region; const struct wm_adsp_region *mem; - struct wm_adsp *adsp = ctl->adsp; + struct wm_adsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg; - mem = wm_adsp_find_region(adsp, region->type); + mem = wm_adsp_find_region(dsp, alg_region->type); if (!mem) { - adsp_err(adsp, "No base for region %x\n", - region->type); + adsp_err(dsp, "No base for region %x\n", + alg_region->type); return -EINVAL; } - reg = ctl->region.base; + reg = ctl->alg_region.base + ctl->offset; reg = wm_adsp_region_to_reg(mem, reg); scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; - ret = regmap_raw_write(adsp->regmap, reg, scratch, + ret = regmap_raw_write(dsp->regmap, reg, scratch, ctl->len); if (ret) { - adsp_err(adsp, "Failed to write %zu bytes to %x: %d\n", + adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", ctl->len, reg, ret); kfree(scratch); return ret; } - adsp_dbg(adsp, "Wrote %zu bytes to %x\n", ctl->len, reg); + adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg); kfree(scratch); @@ -424,42 +584,41 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol, if (!ctl->enabled) return 0; - return wm_coeff_write_control(kcontrol, p, ctl->len); + return wm_coeff_write_control(ctl, p, ctl->len); } -static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, +static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, void *buf, size_t len) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; - struct wm_adsp_alg_region *region = &ctl->region; + struct wm_adsp_alg_region *alg_region = &ctl->alg_region; const struct wm_adsp_region *mem; - struct wm_adsp *adsp = ctl->adsp; + struct wm_adsp *dsp = ctl->dsp; void *scratch; int ret; unsigned int reg; - mem = wm_adsp_find_region(adsp, region->type); + mem = wm_adsp_find_region(dsp, alg_region->type); if (!mem) { - adsp_err(adsp, "No base for region %x\n", - region->type); + adsp_err(dsp, "No base for region %x\n", + alg_region->type); return -EINVAL; } - reg = ctl->region.base; + reg = ctl->alg_region.base + ctl->offset; reg = wm_adsp_region_to_reg(mem, reg); scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; - ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len); + ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len); if (ret) { - adsp_err(adsp, "Failed to read %zu bytes from %x: %d\n", + adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", ctl->len, reg, ret); kfree(scratch); return ret; } - adsp_dbg(adsp, "Read %zu bytes from %x\n", ctl->len, reg); + adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg); memcpy(buf, scratch, ctl->len); kfree(scratch); @@ -473,17 +632,25 @@ static int wm_coeff_get(struct snd_kcontrol *kcontrol, struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; char *p = ucontrol->value.bytes.data; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { + if (ctl->enabled) + return wm_coeff_read_control(ctl, p, ctl->len); + else + return -EPERM; + } + memcpy(p, ctl->cache, ctl->len); + return 0; } struct wmfw_ctl_work { - struct wm_adsp *adsp; + struct wm_adsp *dsp; struct wm_coeff_ctl *ctl; struct work_struct work; }; -static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) +static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) { struct snd_kcontrol_new *kcontrol; int ret; @@ -502,17 +669,25 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) kcontrol->put = wm_coeff_put; kcontrol->private_value = (unsigned long)ctl; - ret = snd_soc_add_card_controls(adsp->card, + if (ctl->flags) { + if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE) + kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + if (ctl->flags & WMFW_CTL_FLAG_READABLE) + kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) + kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; + } + + ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); if (ret < 0) goto err_kcontrol; kfree(kcontrol); - ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card, + ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name); - list_add(&ctl->list, &adsp->ctl_list); return 0; err_kcontrol: @@ -520,6 +695,358 @@ err_kcontrol: return ret; } +static int wm_coeff_init_control_caches(struct wm_adsp *dsp) +{ + struct wm_coeff_ctl *ctl; + int ret; + + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (!ctl->enabled || ctl->set) + continue; + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) + continue; + + ret = wm_coeff_read_control(ctl, + ctl->cache, + ctl->len); + if (ret < 0) + return ret; + } + + return 0; +} + +static int wm_coeff_sync_controls(struct wm_adsp *dsp) +{ + struct wm_coeff_ctl *ctl; + int ret; + + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (!ctl->enabled) + continue; + if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { + ret = wm_coeff_write_control(ctl, + ctl->cache, + ctl->len); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static void wm_adsp_ctl_work(struct work_struct *work) +{ + struct wmfw_ctl_work *ctl_work = container_of(work, + struct wmfw_ctl_work, + work); + + wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); + kfree(ctl_work); +} + +static int wm_adsp_create_control(struct wm_adsp *dsp, + const struct wm_adsp_alg_region *alg_region, + unsigned int offset, unsigned int len, + const char *subname, unsigned int subname_len, + unsigned int flags) +{ + struct wm_coeff_ctl *ctl; + struct wmfw_ctl_work *ctl_work; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + char *region_name; + int ret; + + if (flags & WMFW_CTL_FLAG_SYS) + return 0; + + switch (alg_region->type) { + case WMFW_ADSP1_PM: + region_name = "PM"; + break; + case WMFW_ADSP1_DM: + region_name = "DM"; + break; + case WMFW_ADSP2_XM: + region_name = "XM"; + break; + case WMFW_ADSP2_YM: + region_name = "YM"; + break; + case WMFW_ADSP1_ZM: + region_name = "ZM"; + break; + default: + adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); + return -EINVAL; + } + + switch (dsp->fw_ver) { + case 0: + case 1: + snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x", + dsp->num, region_name, alg_region->alg); + break; + default: + ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "DSP%d%c %.12s %x", dsp->num, *region_name, + wm_adsp_fw_text[dsp->fw], alg_region->alg); + + /* Truncate the subname from the start if it is too long */ + if (subname) { + int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; + int skip = 0; + + if (subname_len > avail) + skip = subname_len - avail; + + snprintf(name + ret, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s", + subname_len - skip, subname + skip); + } + break; + } + + list_for_each_entry(ctl, &dsp->ctl_list, + list) { + if (!strcmp(ctl->name, name)) { + if (!ctl->enabled) + ctl->enabled = 1; + return 0; + } + } + + ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); + if (!ctl) + return -ENOMEM; + ctl->fw_name = wm_adsp_fw_text[dsp->fw]; + ctl->alg_region = *alg_region; + ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); + if (!ctl->name) { + ret = -ENOMEM; + goto err_ctl; + } + ctl->enabled = 1; + ctl->set = 0; + ctl->ops.xget = wm_coeff_get; + ctl->ops.xput = wm_coeff_put; + ctl->dsp = dsp; + + ctl->flags = flags; + ctl->offset = offset; + if (len > 512) { + adsp_warn(dsp, "Truncating control %s from %d\n", + ctl->name, len); + len = 512; + } + ctl->len = len; + ctl->cache = kzalloc(ctl->len, GFP_KERNEL); + if (!ctl->cache) { + ret = -ENOMEM; + goto err_ctl_name; + } + + list_add(&ctl->list, &dsp->ctl_list); + + ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); + if (!ctl_work) { + ret = -ENOMEM; + goto err_ctl_cache; + } + + ctl_work->dsp = dsp; + ctl_work->ctl = ctl; + INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); + schedule_work(&ctl_work->work); + + return 0; + +err_ctl_cache: + kfree(ctl->cache); +err_ctl_name: + kfree(ctl->name); +err_ctl: + kfree(ctl); + + return ret; +} + +struct wm_coeff_parsed_alg { + int id; + const u8 *name; + int name_len; + int ncoeff; +}; + +struct wm_coeff_parsed_coeff { + int offset; + int mem_type; + const u8 *name; + int name_len; + int ctl_type; + int flags; + int len; +}; + +static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) +{ + int length; + + switch (bytes) { + case 1: + length = **pos; + break; + case 2: + length = le16_to_cpu(*((__le16 *)*pos)); + break; + default: + return 0; + } + + if (str) + *str = *pos + bytes; + + *pos += ((length + bytes) + 3) & ~0x03; + + return length; +} + +static int wm_coeff_parse_int(int bytes, const u8 **pos) +{ + int val = 0; + + switch (bytes) { + case 2: + val = le16_to_cpu(*((__le16 *)*pos)); + break; + case 4: + val = le32_to_cpu(*((__le32 *)*pos)); + break; + default: + break; + } + + *pos += bytes; + + return val; +} + +static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, + struct wm_coeff_parsed_alg *blk) +{ + const struct wmfw_adsp_alg_data *raw; + + switch (dsp->fw_ver) { + case 0: + case 1: + raw = (const struct wmfw_adsp_alg_data *)*data; + *data = raw->data; + + blk->id = le32_to_cpu(raw->id); + blk->name = raw->name; + blk->name_len = strlen(raw->name); + blk->ncoeff = le32_to_cpu(raw->ncoeff); + break; + default: + blk->id = wm_coeff_parse_int(sizeof(raw->id), data); + blk->name_len = wm_coeff_parse_string(sizeof(u8), data, + &blk->name); + wm_coeff_parse_string(sizeof(u16), data, NULL); + blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); + break; + } + + adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); + adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); + adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); +} + +static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, + struct wm_coeff_parsed_coeff *blk) +{ + const struct wmfw_adsp_coeff_data *raw; + const u8 *tmp; + int length; + + switch (dsp->fw_ver) { + case 0: + case 1: + raw = (const struct wmfw_adsp_coeff_data *)*data; + *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); + + blk->offset = le16_to_cpu(raw->hdr.offset); + blk->mem_type = le16_to_cpu(raw->hdr.type); + blk->name = raw->name; + blk->name_len = strlen(raw->name); + blk->ctl_type = le16_to_cpu(raw->ctl_type); + blk->flags = le16_to_cpu(raw->flags); + blk->len = le32_to_cpu(raw->len); + break; + default: + tmp = *data; + blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); + blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); + length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); + blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, + &blk->name); + wm_coeff_parse_string(sizeof(u8), &tmp, NULL); + wm_coeff_parse_string(sizeof(u16), &tmp, NULL); + blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); + blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); + blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); + + *data = *data + sizeof(raw->hdr) + length; + break; + } + + adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); + adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); + adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); + adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); + adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); + adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); +} + +static int wm_adsp_parse_coeff(struct wm_adsp *dsp, + const struct wmfw_region *region) +{ + struct wm_adsp_alg_region alg_region = {}; + struct wm_coeff_parsed_alg alg_blk; + struct wm_coeff_parsed_coeff coeff_blk; + const u8 *data = region->data; + int i, ret; + + wm_coeff_parse_alg(dsp, &data, &alg_blk); + for (i = 0; i < alg_blk.ncoeff; i++) { + wm_coeff_parse_coeff(dsp, &data, &coeff_blk); + + switch (coeff_blk.ctl_type) { + case SNDRV_CTL_ELEM_TYPE_BYTES: + break; + default: + adsp_err(dsp, "Unknown control type: %d\n", + coeff_blk.ctl_type); + return -EINVAL; + } + + alg_region.type = coeff_blk.mem_type; + alg_region.alg = alg_blk.id; + + ret = wm_adsp_create_control(dsp, &alg_region, + coeff_blk.offset, + coeff_blk.len, + coeff_blk.name, + coeff_blk.name_len, + coeff_blk.flags); + if (ret < 0) + adsp_err(dsp, "Failed to create control: %.*s, %d\n", + coeff_blk.name_len, coeff_blk.name, ret); + } + + return 0; +} + static int wm_adsp_load(struct wm_adsp *dsp) { LIST_HEAD(buf_list); @@ -568,12 +1095,22 @@ static int wm_adsp_load(struct wm_adsp *dsp) goto out_fw; } - if (header->ver != 0) { + switch (header->ver) { + case 0: + adsp_warn(dsp, "%s: Depreciated file format %d\n", + file, header->ver); + break; + case 1: + case 2: + break; + default: adsp_err(dsp, "%s: unknown file format %d\n", file, header->ver); goto out_fw; } + adsp_info(dsp, "Firmware version: %d\n", header->ver); + dsp->fw_ver = header->ver; if (header->core != dsp->type) { adsp_err(dsp, "%s: invalid core %d != %d\n", @@ -638,6 +1175,12 @@ static int wm_adsp_load(struct wm_adsp *dsp) text = kzalloc(le32_to_cpu(region->len) + 1, GFP_KERNEL); break; + case WMFW_ALGORITHM_DATA: + region_name = "Algorithm"; + ret = wm_adsp_parse_coeff(dsp, region); + if (ret != 0) + goto out_fw; + break; case WMFW_INFO_TEXT: region_name = "Information"; text = kzalloc(le32_to_cpu(region->len) + 1, @@ -720,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); @@ -730,444 +1275,317 @@ out: return ret; } -static int wm_coeff_init_control_caches(struct wm_adsp *adsp) +static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, + const struct wm_adsp_alg_region *alg_region) { struct wm_coeff_ctl *ctl; - int ret; - list_for_each_entry(ctl, &adsp->ctl_list, list) { - if (!ctl->enabled || ctl->set) - continue; - ret = wm_coeff_read_control(ctl->kcontrol, - ctl->cache, - ctl->len); - if (ret < 0) - return ret; + list_for_each_entry(ctl, &dsp->ctl_list, list) { + if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && + alg_region->alg == ctl->alg_region.alg && + alg_region->type == ctl->alg_region.type) { + ctl->alg_region.base = alg_region->base; + } } - - return 0; } -static int wm_coeff_sync_controls(struct wm_adsp *adsp) +static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, + unsigned int pos, unsigned int len) { - struct wm_coeff_ctl *ctl; + void *alg; int ret; + __be32 val; - list_for_each_entry(ctl, &adsp->ctl_list, list) { - if (!ctl->enabled) - continue; - if (ctl->set) { - ret = wm_coeff_write_control(ctl->kcontrol, - ctl->cache, - ctl->len); - if (ret < 0) - return ret; - } + if (n_algs == 0) { + adsp_err(dsp, "No algorithms\n"); + return ERR_PTR(-EINVAL); } - return 0; -} - -static void wm_adsp_ctl_work(struct work_struct *work) -{ - struct wmfw_ctl_work *ctl_work = container_of(work, - struct wmfw_ctl_work, - work); - - wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl); - kfree(ctl_work); -} - -static int wm_adsp_create_control(struct wm_adsp *dsp, - const struct wm_adsp_alg_region *region) - -{ - struct wm_coeff_ctl *ctl; - struct wmfw_ctl_work *ctl_work; - char *name; - char *region_name; - int ret; - - name = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!name) - return -ENOMEM; + if (n_algs > 1024) { + adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); + return ERR_PTR(-EINVAL); + } - switch (region->type) { - case WMFW_ADSP1_PM: - region_name = "PM"; - break; - case WMFW_ADSP1_DM: - region_name = "DM"; - break; - case WMFW_ADSP2_XM: - region_name = "XM"; - break; - case WMFW_ADSP2_YM: - region_name = "YM"; - break; - case WMFW_ADSP1_ZM: - region_name = "ZM"; - break; - default: - ret = -EINVAL; - goto err_name; + /* Read the terminator first to validate the length */ + ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list end: %d\n", + ret); + return ERR_PTR(ret); } - snprintf(name, PAGE_SIZE, "DSP%d %s %x", - dsp->num, region_name, region->alg); + if (be32_to_cpu(val) != 0xbedead) + adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", + pos + len, be32_to_cpu(val)); - list_for_each_entry(ctl, &dsp->ctl_list, - list) { - if (!strcmp(ctl->name, name)) { - if (!ctl->enabled) - ctl->enabled = 1; - goto found; - } - } + alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); + if (!alg) + return ERR_PTR(-ENOMEM); - ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); - if (!ctl) { - ret = -ENOMEM; - goto err_name; - } - ctl->region = *region; - ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); - if (!ctl->name) { - ret = -ENOMEM; - goto err_ctl; + ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list: %d\n", + ret); + kfree(alg); + return ERR_PTR(ret); } - ctl->enabled = 1; - ctl->set = 0; - ctl->ops.xget = wm_coeff_get; - ctl->ops.xput = wm_coeff_put; - ctl->adsp = dsp; - ctl->len = region->len; - ctl->cache = kzalloc(ctl->len, GFP_KERNEL); - if (!ctl->cache) { - ret = -ENOMEM; - goto err_ctl_name; - } + return alg; +} - ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); - if (!ctl_work) { - ret = -ENOMEM; - goto err_ctl_cache; - } +static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, + int type, __be32 id, + __be32 base) +{ + struct wm_adsp_alg_region *alg_region; - ctl_work->adsp = dsp; - ctl_work->ctl = ctl; - INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); - schedule_work(&ctl_work->work); + alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); + if (!alg_region) + return ERR_PTR(-ENOMEM); -found: - kfree(name); + alg_region->type = type; + alg_region->alg = be32_to_cpu(id); + alg_region->base = be32_to_cpu(base); - return 0; + list_add_tail(&alg_region->list, &dsp->alg_regions); -err_ctl_cache: - kfree(ctl->cache); -err_ctl_name: - kfree(ctl->name); -err_ctl: - kfree(ctl); -err_name: - kfree(name); - return ret; + if (dsp->fw_ver > 0) + wm_adsp_ctl_fixup_base(dsp, alg_region); + + return alg_region; } -static int wm_adsp_setup_algs(struct wm_adsp *dsp) +static int wm_adsp1_setup_algs(struct wm_adsp *dsp) { - struct regmap *regmap = dsp->regmap; struct wmfw_adsp1_id_hdr adsp1_id; - struct wmfw_adsp2_id_hdr adsp2_id; struct wmfw_adsp1_alg_hdr *adsp1_alg; - struct wmfw_adsp2_alg_hdr *adsp2_alg; - void *alg, *buf; - struct wm_adsp_alg_region *region; + struct wm_adsp_alg_region *alg_region; const struct wm_adsp_region *mem; - unsigned int pos, term; - size_t algs, buf_size; - __be32 val; + unsigned int pos, len; + size_t n_algs; int i, ret; - switch (dsp->type) { - case WMFW_ADSP1: - mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); - break; - case WMFW_ADSP2: - mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); - break; - default: - mem = NULL; - break; - } - + mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); if (WARN_ON(!mem)) return -EINVAL; - switch (dsp->type) { - case WMFW_ADSP1: - ret = regmap_raw_read(regmap, mem->base, &adsp1_id, - sizeof(adsp1_id)); - if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm info: %d\n", - ret); - return ret; - } - - buf = &adsp1_id; - buf_size = sizeof(adsp1_id); - - algs = be32_to_cpu(adsp1_id.algs); - dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); - adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", - dsp->fw_id, - (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, - be32_to_cpu(adsp1_id.fw.ver) & 0xff, - algs); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP1_ZM; - region->alg = be32_to_cpu(adsp1_id.fw.id); - region->base = be32_to_cpu(adsp1_id.zm); - list_add_tail(®ion->list, &dsp->alg_regions); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP1_DM; - region->alg = be32_to_cpu(adsp1_id.fw.id); - region->base = be32_to_cpu(adsp1_id.dm); - list_add_tail(®ion->list, &dsp->alg_regions); - - pos = sizeof(adsp1_id) / 2; - term = pos + ((sizeof(*adsp1_alg) * algs) / 2); - break; - - case WMFW_ADSP2: - ret = regmap_raw_read(regmap, mem->base, &adsp2_id, - sizeof(adsp2_id)); - if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm info: %d\n", - ret); - return ret; - } - - buf = &adsp2_id; - buf_size = sizeof(adsp2_id); - - algs = be32_to_cpu(adsp2_id.algs); - dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); - 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, - algs); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_XM; - region->alg = be32_to_cpu(adsp2_id.fw.id); - region->base = be32_to_cpu(adsp2_id.xm); - list_add_tail(®ion->list, &dsp->alg_regions); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_YM; - region->alg = be32_to_cpu(adsp2_id.fw.id); - region->base = be32_to_cpu(adsp2_id.ym); - list_add_tail(®ion->list, &dsp->alg_regions); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - region->type = WMFW_ADSP2_ZM; - region->alg = be32_to_cpu(adsp2_id.fw.id); - region->base = be32_to_cpu(adsp2_id.zm); - list_add_tail(®ion->list, &dsp->alg_regions); - - pos = sizeof(adsp2_id) / 2; - term = pos + ((sizeof(*adsp2_alg) * algs) / 2); - break; - - default: - WARN(1, "Unknown DSP type"); - return -EINVAL; - } - - if (algs == 0) { - adsp_err(dsp, "No algorithms\n"); - return -EINVAL; - } - - if (algs > 1024) { - adsp_err(dsp, "Algorithm count %zx excessive\n", algs); - print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET, - buf, buf_size); - return -EINVAL; - } - - /* Read the terminator first to validate the length */ - ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val)); + ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, + sizeof(adsp1_id)); if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm list end: %d\n", - ret); + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); return ret; } - if (be32_to_cpu(val) != 0xbedead) - adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", - term, be32_to_cpu(val)); - - alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA); - if (!alg) - return -ENOMEM; - - ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2); - if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm list: %d\n", - ret); - goto out; - } - - adsp1_alg = alg; - adsp2_alg = alg; - - for (i = 0; i < algs; i++) { - switch (dsp->type) { - case WMFW_ADSP1: - adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", - i, be32_to_cpu(adsp1_alg[i].alg.id), - (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, - be32_to_cpu(adsp1_alg[i].dm), - be32_to_cpu(adsp1_alg[i].zm)); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) { - ret = -ENOMEM; - goto out; - } - region->type = WMFW_ADSP1_DM; - region->alg = be32_to_cpu(adsp1_alg[i].alg.id); - region->base = be32_to_cpu(adsp1_alg[i].dm); - region->len = 0; - list_add_tail(®ion->list, &dsp->alg_regions); - if (i + 1 < algs) { - region->len = be32_to_cpu(adsp1_alg[i + 1].dm); - region->len -= be32_to_cpu(adsp1_alg[i].dm); - region->len *= 4; - wm_adsp_create_control(dsp, region); + n_algs = be32_to_cpu(adsp1_id.n_algs); + dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + dsp->fw_id, + (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_id.fw.ver) & 0xff, + n_algs); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, + adsp1_id.fw.id, adsp1_id.zm); + if (IS_ERR(alg_region)) + return PTR_ERR(alg_region); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, + adsp1_id.fw.id, adsp1_id.dm); + if (IS_ERR(alg_region)) + return PTR_ERR(alg_region); + + pos = sizeof(adsp1_id) / 2; + len = (sizeof(*adsp1_alg) * n_algs) / 2; + + adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); + if (IS_ERR(adsp1_alg)) + return PTR_ERR(adsp1_alg); + + for (i = 0; i < n_algs; i++) { + adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", + i, be32_to_cpu(adsp1_alg[i].alg.id), + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp1_alg[i].dm), + be32_to_cpu(adsp1_alg[i].zm)); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, + adsp1_alg[i].alg.id, + adsp1_alg[i].dm); + if (IS_ERR(alg_region)) { + ret = PTR_ERR(alg_region); + goto out; + } + if (dsp->fw_ver == 0) { + if (i + 1 < n_algs) { + len = be32_to_cpu(adsp1_alg[i + 1].dm); + len -= be32_to_cpu(adsp1_alg[i].dm); + len *= 4; + wm_adsp_create_control(dsp, alg_region, 0, + len, NULL, 0, 0); } else { adsp_warn(dsp, "Missing length info for region DM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); } + } - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) { - ret = -ENOMEM; - goto out; - } - region->type = WMFW_ADSP1_ZM; - region->alg = be32_to_cpu(adsp1_alg[i].alg.id); - region->base = be32_to_cpu(adsp1_alg[i].zm); - region->len = 0; - list_add_tail(®ion->list, &dsp->alg_regions); - if (i + 1 < algs) { - region->len = be32_to_cpu(adsp1_alg[i + 1].zm); - region->len -= be32_to_cpu(adsp1_alg[i].zm); - region->len *= 4; - wm_adsp_create_control(dsp, region); + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, + adsp1_alg[i].alg.id, + adsp1_alg[i].zm); + if (IS_ERR(alg_region)) { + ret = PTR_ERR(alg_region); + goto out; + } + if (dsp->fw_ver == 0) { + if (i + 1 < n_algs) { + len = be32_to_cpu(adsp1_alg[i + 1].zm); + len -= be32_to_cpu(adsp1_alg[i].zm); + len *= 4; + wm_adsp_create_control(dsp, alg_region, 0, + len, NULL, 0, 0); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); } - break; + } + } - case WMFW_ADSP2: - adsp_info(dsp, - "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", - i, be32_to_cpu(adsp2_alg[i].alg.id), - (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, - be32_to_cpu(adsp2_alg[i].xm), - be32_to_cpu(adsp2_alg[i].ym), - be32_to_cpu(adsp2_alg[i].zm)); - - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) { - ret = -ENOMEM; - goto out; - } - region->type = WMFW_ADSP2_XM; - region->alg = be32_to_cpu(adsp2_alg[i].alg.id); - region->base = be32_to_cpu(adsp2_alg[i].xm); - region->len = 0; - list_add_tail(®ion->list, &dsp->alg_regions); - if (i + 1 < algs) { - region->len = be32_to_cpu(adsp2_alg[i + 1].xm); - region->len -= be32_to_cpu(adsp2_alg[i].xm); - region->len *= 4; - wm_adsp_create_control(dsp, region); +out: + kfree(adsp1_alg); + return ret; +} + +static int wm_adsp2_setup_algs(struct wm_adsp *dsp) +{ + struct wmfw_adsp2_id_hdr adsp2_id; + struct wmfw_adsp2_alg_hdr *adsp2_alg; + struct wm_adsp_alg_region *alg_region; + const struct wm_adsp_region *mem; + unsigned int pos, len; + size_t n_algs; + int i, ret; + + mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); + if (WARN_ON(!mem)) + return -EINVAL; + + ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, + sizeof(adsp2_id)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); + return ret; + } + + 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, + (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, + adsp2_id.fw.id, adsp2_id.xm); + if (IS_ERR(alg_region)) + return PTR_ERR(alg_region); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, + adsp2_id.fw.id, adsp2_id.ym); + if (IS_ERR(alg_region)) + return PTR_ERR(alg_region); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, + adsp2_id.fw.id, adsp2_id.zm); + if (IS_ERR(alg_region)) + return PTR_ERR(alg_region); + + pos = sizeof(adsp2_id) / 2; + len = (sizeof(*adsp2_alg) * n_algs) / 2; + + adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); + if (IS_ERR(adsp2_alg)) + return PTR_ERR(adsp2_alg); + + for (i = 0; i < n_algs; i++) { + adsp_info(dsp, + "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", + i, be32_to_cpu(adsp2_alg[i].alg.id), + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp2_alg[i].xm), + be32_to_cpu(adsp2_alg[i].ym), + be32_to_cpu(adsp2_alg[i].zm)); + + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, + adsp2_alg[i].alg.id, + adsp2_alg[i].xm); + if (IS_ERR(alg_region)) { + ret = PTR_ERR(alg_region); + goto out; + } + if (dsp->fw_ver == 0) { + if (i + 1 < n_algs) { + len = be32_to_cpu(adsp2_alg[i + 1].xm); + len -= be32_to_cpu(adsp2_alg[i].xm); + len *= 4; + wm_adsp_create_control(dsp, alg_region, 0, + len, NULL, 0, 0); } else { adsp_warn(dsp, "Missing length info for region XM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } + } - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) { - ret = -ENOMEM; - goto out; - } - region->type = WMFW_ADSP2_YM; - region->alg = be32_to_cpu(adsp2_alg[i].alg.id); - region->base = be32_to_cpu(adsp2_alg[i].ym); - region->len = 0; - list_add_tail(®ion->list, &dsp->alg_regions); - if (i + 1 < algs) { - region->len = be32_to_cpu(adsp2_alg[i + 1].ym); - region->len -= be32_to_cpu(adsp2_alg[i].ym); - region->len *= 4; - wm_adsp_create_control(dsp, region); + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, + adsp2_alg[i].alg.id, + adsp2_alg[i].ym); + if (IS_ERR(alg_region)) { + ret = PTR_ERR(alg_region); + goto out; + } + if (dsp->fw_ver == 0) { + if (i + 1 < n_algs) { + len = be32_to_cpu(adsp2_alg[i + 1].ym); + len -= be32_to_cpu(adsp2_alg[i].ym); + len *= 4; + wm_adsp_create_control(dsp, alg_region, 0, + len, NULL, 0, 0); } else { adsp_warn(dsp, "Missing length info for region YM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } + } - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) { - ret = -ENOMEM; - goto out; - } - region->type = WMFW_ADSP2_ZM; - region->alg = be32_to_cpu(adsp2_alg[i].alg.id); - region->base = be32_to_cpu(adsp2_alg[i].zm); - region->len = 0; - list_add_tail(®ion->list, &dsp->alg_regions); - if (i + 1 < algs) { - region->len = be32_to_cpu(adsp2_alg[i + 1].zm); - region->len -= be32_to_cpu(adsp2_alg[i].zm); - region->len *= 4; - wm_adsp_create_control(dsp, region); + alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, + adsp2_alg[i].alg.id, + adsp2_alg[i].zm); + if (IS_ERR(alg_region)) { + ret = PTR_ERR(alg_region); + goto out; + } + if (dsp->fw_ver == 0) { + if (i + 1 < n_algs) { + len = be32_to_cpu(adsp2_alg[i + 1].zm); + len -= be32_to_cpu(adsp2_alg[i].zm); + len *= 4; + wm_adsp_create_control(dsp, alg_region, 0, + len, NULL, 0, 0); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); } - break; } } out: - kfree(alg); + kfree(adsp2_alg); return ret; } @@ -1345,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); @@ -1354,10 +1774,13 @@ out: return ret; } -int wm_adsp1_init(struct wm_adsp *adsp) +int wm_adsp1_init(struct wm_adsp *dsp) { - INIT_LIST_HEAD(&adsp->alg_regions); + INIT_LIST_HEAD(&dsp->alg_regions); +#ifdef CONFIG_DEBUG_FS + mutex_init(&dsp->debugfs_lock); +#endif return 0; } EXPORT_SYMBOL_GPL(wm_adsp1_init); @@ -1410,7 +1833,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; - ret = wm_adsp_setup_algs(dsp); + ret = wm_adsp1_setup_algs(dsp); if (ret != 0) goto err; @@ -1531,35 +1954,6 @@ static void wm_adsp2_boot_work(struct work_struct *work) return; } - if (dsp->dvfs) { - ret = regmap_read(dsp->regmap, - dsp->base + ADSP2_CLOCKING, &val); - if (ret != 0) { - adsp_err(dsp, "Failed to read clocking: %d\n", ret); - return; - } - - if ((val & ADSP2_CLK_SEL_MASK) >= 3) { - ret = regulator_enable(dsp->dvfs); - if (ret != 0) { - adsp_err(dsp, - "Failed to enable supply: %d\n", - ret); - return; - } - - ret = regulator_set_voltage(dsp->dvfs, - 1800000, - 1800000); - if (ret != 0) { - adsp_err(dsp, - "Failed to raise supply: %d\n", - ret); - return; - } - } - } - ret = wm_adsp2_ena(dsp); if (ret != 0) return; @@ -1568,7 +1962,7 @@ static void wm_adsp2_boot_work(struct work_struct *work) if (ret != 0) goto err; - ret = wm_adsp_setup_algs(dsp); + ret = wm_adsp2_setup_algs(dsp); if (ret != 0) goto err; @@ -1642,6 +2036,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_PRE_PMD: + /* 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, @@ -1653,21 +2054,6 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); - if (dsp->dvfs) { - ret = regulator_set_voltage(dsp->dvfs, 1200000, - 1800000); - if (ret != 0) - adsp_warn(dsp, - "Failed to lower supply: %d\n", - ret); - - ret = regulator_disable(dsp->dvfs); - if (ret != 0) - adsp_err(dsp, - "Failed to enable supply: %d\n", - ret); - } - list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; @@ -1694,7 +2080,25 @@ err: } EXPORT_SYMBOL_GPL(wm_adsp2_event); -int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) +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; @@ -1702,44 +2106,20 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) * Disable the DSP memory by default when in reset for a small * power saving. */ - ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL, + ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_MEM_ENA, 0); if (ret != 0) { - adsp_err(adsp, "Failed to clear memory retention: %d\n", ret); + adsp_err(dsp, "Failed to clear memory retention: %d\n", ret); return ret; } - INIT_LIST_HEAD(&adsp->alg_regions); - INIT_LIST_HEAD(&adsp->ctl_list); - INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work); - - if (dvfs) { - adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); - if (IS_ERR(adsp->dvfs)) { - ret = PTR_ERR(adsp->dvfs); - adsp_err(adsp, "Failed to get DCVDD: %d\n", ret); - return ret; - } - - ret = regulator_enable(adsp->dvfs); - if (ret != 0) { - adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret); - return ret; - } - - ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); - if (ret != 0) { - adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret); - return ret; - } - - ret = regulator_disable(adsp->dvfs); - if (ret != 0) { - adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret); - return ret; - } - } + INIT_LIST_HEAD(&dsp->alg_regions); + 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 a4f6b64de..579a6350f 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -18,8 +18,6 @@ #include "wmfw.h" -struct regulator; - struct wm_adsp_region { int type; unsigned int base; @@ -30,7 +28,6 @@ struct wm_adsp_alg_region { unsigned int alg; int type; unsigned int base; - size_t len; }; struct wm_adsp { @@ -49,37 +46,49 @@ 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; - bool running; - - struct regulator *dvfs; + int fw_ver; + 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) \ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \ wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) -#define WM_ADSP2(wname, num) \ +#define WM_ADSP2_E(wname, num, event_fn) \ { .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \ - .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_early_event, \ - .event_flags = SND_SOC_DAPM_PRE_PMU }, \ + .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \ + .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \ { .id = snd_soc_dapm_out_drv, .name = wname, \ .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } -extern const struct snd_kcontrol_new wm_adsp1_fw_controls[]; -extern const struct snd_kcontrol_new wm_adsp2_fw_controls[]; +#define WM_ADSP2(wname, num) \ + WM_ADSP2_E(wname, num, wm_adsp2_early_event) + +extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; -int wm_adsp1_init(struct wm_adsp *adsp); -int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); +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/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 8366e1965..fd86bd105 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -1116,7 +1116,7 @@ static const struct snd_soc_dapm_route lineout2_se_routes[] = { int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec) { - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); /* Latch volume update bits & default ZC on */ snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME, @@ -1160,7 +1160,7 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec, int lineout1_diff, int lineout2_diff) { struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); hubs->codec = codec; diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index ef163360a..7613d60d6 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -15,6 +15,17 @@ #include <linux/types.h> +#define WMFW_MAX_ALG_NAME 256 +#define WMFW_MAX_ALG_DESCR_NAME 256 + +#define WMFW_MAX_COEFF_NAME 256 +#define WMFW_MAX_COEFF_DESCR_NAME 256 + +#define WMFW_CTL_FLAG_SYS 0x8000 +#define WMFW_CTL_FLAG_VOLATILE 0x0004 +#define WMFW_CTL_FLAG_WRITEABLE 0x0002 +#define WMFW_CTL_FLAG_READABLE 0x0001 + struct wmfw_header { char magic[4]; __le32 len; @@ -61,7 +72,7 @@ struct wmfw_adsp1_id_hdr { struct wmfw_id_hdr fw; __be32 zm; __be32 dm; - __be32 algs; + __be32 n_algs; } __packed; struct wmfw_adsp2_id_hdr { @@ -69,7 +80,7 @@ struct wmfw_adsp2_id_hdr { __be32 zm; __be32 xm; __be32 ym; - __be32 algs; + __be32 n_algs; } __packed; struct wmfw_alg_hdr { @@ -90,6 +101,28 @@ struct wmfw_adsp2_alg_hdr { __be32 ym; } __packed; +struct wmfw_adsp_alg_data { + __le32 id; + u8 name[WMFW_MAX_ALG_NAME]; + u8 descr[WMFW_MAX_ALG_DESCR_NAME]; + __le32 ncoeff; + u8 data[]; +} __packed; + +struct wmfw_adsp_coeff_data { + struct { + __le16 offset; + __le16 type; + __le32 size; + } hdr; + u8 name[WMFW_MAX_COEFF_NAME]; + u8 descr[WMFW_MAX_COEFF_DESCR_NAME]; + __le16 ctl_type; + __le16 flags; + __le32 len; + u8 data[]; +} __packed; + struct wmfw_coeff_hdr { u8 magic[4]; __le32 len; @@ -117,9 +150,10 @@ struct wmfw_coeff_item { #define WMFW_ADSP1 1 #define WMFW_ADSP2 2 -#define WMFW_ABSOLUTE 0xf0 -#define WMFW_NAME_TEXT 0xfe -#define WMFW_INFO_TEXT 0xff +#define WMFW_ABSOLUTE 0xf0 +#define WMFW_ALGORITHM_DATA 0xf2 +#define WMFW_NAME_TEXT 0xfe +#define WMFW_INFO_TEXT 0xff #define WMFW_ADSP1_PM 2 #define WMFW_ADSP1_DM 3 diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 23c91fa65..b960e626d 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -107,6 +107,7 @@ struct davinci_mcasp { #endif struct davinci_mcasp_ruledata ruledata[2]; + struct snd_pcm_hw_constraint_list chconstr[2]; }; static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset, @@ -685,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) { @@ -915,15 +918,12 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, * the machine driver, we need to calculate the ratio. */ if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { - int channels = params_channels(params); + int slots = mcasp->tdm_slots; int rate = params_rate(params); int sbits = params_width(params); int ppm, div; - if (channels > mcasp->tdm_slots) - channels = mcasp->tdm_slots; - - div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*channels, + div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots, &ppm); if (ppm) dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", @@ -1024,31 +1024,36 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_interval *ri = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); int sbits = params_width(params); - int channels = params_channels(params); - unsigned int list[ARRAY_SIZE(davinci_mcasp_dai_rates)]; - int i, count = 0; + int slots = rd->mcasp->tdm_slots; + struct snd_interval range; + int i; - if (channels > rd->mcasp->tdm_slots) - channels = rd->mcasp->tdm_slots; + snd_interval_any(&range); + range.empty = 1; for (i = 0; i < ARRAY_SIZE(davinci_mcasp_dai_rates); i++) { - if (ri->min <= davinci_mcasp_dai_rates[i] && - ri->max >= davinci_mcasp_dai_rates[i]) { - uint bclk_freq = sbits*channels* + if (snd_interval_test(ri, davinci_mcasp_dai_rates[i])) { + uint bclk_freq = sbits*slots* davinci_mcasp_dai_rates[i]; int ppm; davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); - if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) - list[count++] = davinci_mcasp_dai_rates[i]; + if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { + if (range.empty) { + range.min = davinci_mcasp_dai_rates[i]; + range.empty = 0; + } + range.max = davinci_mcasp_dai_rates[i]; + } } } + dev_dbg(rd->mcasp->dev, - "%d frequencies (%d-%d) for %d sbits and %d channels\n", - count, ri->min, ri->max, sbits, channels); + "Frequencies %d-%d -> %d-%d for %d sbits and %d tdm slots\n", + ri->min, ri->max, range.min, range.max, sbits, slots); - return snd_interval_list(hw_param_interval(params, rule->var), - count, list, 0); + return snd_interval_refine(hw_param_interval(params, rule->var), + &range); } static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, @@ -1058,17 +1063,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_mask nfmt; int rate = params_rate(params); - int channels = params_channels(params); + int slots = rd->mcasp->tdm_slots; int i, count = 0; snd_mask_none(&nfmt); - if (channels > rd->mcasp->tdm_slots) - channels = rd->mcasp->tdm_slots; - for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { if (snd_mask_test(fmt, i)) { - uint bclk_freq = snd_pcm_format_width(i)*channels*rate; + uint bclk_freq = snd_pcm_format_width(i)*slots*rate; int ppm; davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); @@ -1079,51 +1081,12 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, } } dev_dbg(rd->mcasp->dev, - "%d possible sample format for %d Hz and %d channels\n", - count, rate, channels); + "%d possible sample format for %d Hz and %d tdm slots\n", + count, rate, slots); return snd_mask_refine(fmt, &nfmt); } -static int davinci_mcasp_hw_rule_channels(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct davinci_mcasp_ruledata *rd = rule->private; - struct snd_interval *ci = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - int sbits = params_width(params); - int rate = params_rate(params); - int max_chan_per_wire = rd->mcasp->tdm_slots < ci->max ? - rd->mcasp->tdm_slots : ci->max; - unsigned int list[ci->max - ci->min + 1]; - int c1, c, count = 0; - - for (c1 = ci->min; c1 <= max_chan_per_wire; c1++) { - uint bclk_freq = c1*sbits*rate; - int ppm; - - davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); - if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { - /* If we can use all tdm_slots, we can put any - amount of channels to remaining wires as - long as they fit in. */ - if (c1 == rd->mcasp->tdm_slots) { - for (c = c1; c <= rd->serializers*c1 && - c <= ci->max; c++) - list[count++] = c; - } else { - list[count++] = c1; - } - } - } - dev_dbg(rd->mcasp->dev, - "%d possible channel counts (%d-%d) for %d Hz and %d sbits\n", - count, ci->min, ci->max, rate, sbits); - - return snd_interval_list(hw_param_interval(params, rule->var), - count, list, 0); -} - static int davinci_mcasp_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { @@ -1167,6 +1130,11 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_CHANNELS, 2, max_channels); + if (mcasp->chconstr[substream->stream].count) + snd_pcm_hw_constraint_list(substream->runtime, + 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &mcasp->chconstr[substream->stream]); + /* * If we rely on implicit BCLK divider setting we should * set constraints based on what we can provide. @@ -1180,24 +1148,14 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_RATE, davinci_mcasp_hw_rule_rate, ruledata, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + SNDRV_PCM_HW_PARAM_FORMAT, -1); if (ret) return ret; ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, davinci_mcasp_hw_rule_format, ruledata, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (ret) - return ret; - ret = snd_pcm_hw_rule_add(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - davinci_mcasp_hw_rule_channels, - ruledata, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_FORMAT, -1); + SNDRV_PCM_HW_PARAM_RATE, -1); if (ret) return ret; } @@ -1556,6 +1514,102 @@ nodata: return pdata; } +/* All serializers must have equal number of channels */ +static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, + struct snd_pcm_hw_constraint_list *cl, + int serializers) +{ + unsigned int *list; + int i, count = 0; + + if (serializers <= 1) + return 0; + + list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) * + (mcasp->tdm_slots + serializers - 2), + GFP_KERNEL); + if (!list) + return -ENOMEM; + + for (i = 2; i <= mcasp->tdm_slots; i++) + list[count++] = i; + + for (i = 2; i <= serializers; i++) + list[count++] = i*mcasp->tdm_slots; + + cl->count = count; + cl->list = list; + + return 0; +} + + +static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp) +{ + int rx_serializers = 0, tx_serializers = 0, ret, i; + + for (i = 0; i < mcasp->num_serializer; i++) + if (mcasp->serial_dir[i] == TX_MODE) + tx_serializers++; + else if (mcasp->serial_dir[i] == RX_MODE) + rx_serializers++; + + ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[ + SNDRV_PCM_STREAM_PLAYBACK], + tx_serializers); + if (ret) + return ret; + + ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[ + SNDRV_PCM_STREAM_CAPTURE], + rx_serializers); + + 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; @@ -1739,6 +1793,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE; } + ret = davinci_mcasp_init_ch_constraints(mcasp); + if (ret) + goto err; + dev_set_drvdata(&pdev->dev, mcasp); mcasp_reparent_fck(pdev); @@ -1750,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 79dc51118..a3be108a8 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_dma.c b/sound/soc/fsl/fsl_dma.c index 93d7e56c6..ccadefcee 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -445,7 +445,7 @@ static int fsl_dma_open(struct snd_pcm_substream *substream) return ret; } - dma->assigned = 1; + dma->assigned = true; snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware); @@ -814,7 +814,7 @@ static int fsl_dma_close(struct snd_pcm_substream *substream) substream->runtime->private_data = NULL; } - dma->assigned = 0; + dma->assigned = false; return 0; } diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index ec79c3d5e..5c73bea7b 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1,7 +1,7 @@ /* * Freescale ALSA SoC Digital Audio Interface (SAI) driver. * - * Copyright 2012-2013 Freescale Semiconductor, Inc. + * Copyright 2012-2015 Freescale Semiconductor, Inc. * * 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 @@ -27,6 +27,17 @@ #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\ FSL_SAI_CSR_FEIE) +static u32 fsl_sai_rates[] = { + 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000 +}; + +static struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = { + .count = ARRAY_SIZE(fsl_sai_rates), + .list = fsl_sai_rates, +}; + static irqreturn_t fsl_sai_isr(int irq, void *devid) { struct fsl_sai *sai = (struct fsl_sai *)devid; @@ -251,12 +262,14 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, val_cr4 |= FSL_SAI_CR4_FSD_MSTR; break; case SND_SOC_DAIFMT_CBM_CFM: + sai->is_slave_mode = true; break; case SND_SOC_DAIFMT_CBS_CFM: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; break; case SND_SOC_DAIFMT_CBM_CFS: val_cr4 |= FSL_SAI_CR4_FSD_MSTR; + sai->is_slave_mode = true; break; default: return -EINVAL; @@ -288,6 +301,79 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) return ret; } +static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(dai); + unsigned long clk_rate; + u32 savediv = 0, ratio, savesub = freq; + u32 id; + int ret = 0; + + /* Don't apply to slave mode */ + if (sai->is_slave_mode) + return 0; + + for (id = 0; id < FSL_SAI_MCLK_MAX; id++) { + clk_rate = clk_get_rate(sai->mclk_clk[id]); + if (!clk_rate) + continue; + + ratio = clk_rate / freq; + + ret = clk_rate - ratio * freq; + + /* + * Drop the source that can not be + * divided into the required rate. + */ + if (ret != 0 && clk_rate / ret < 1000) + continue; + + dev_dbg(dai->dev, + "ratio %d for freq %dHz based on clock %ldHz\n", + ratio, freq, clk_rate); + + if (ratio % 2 == 0 && ratio >= 2 && ratio <= 512) + ratio /= 2; + else + continue; + + if (ret < savesub) { + savediv = ratio; + sai->mclk_id[tx] = id; + savesub = ret; + } + + if (ret == 0) + break; + } + + if (savediv == 0) { + dev_err(dai->dev, "failed to derive required %cx rate: %d\n", + tx ? 'T' : 'R', freq); + return -EINVAL; + } + + if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) { + regmap_update_bits(sai->regmap, FSL_SAI_RCR2, + FSL_SAI_CR2_MSEL_MASK, + FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); + regmap_update_bits(sai->regmap, FSL_SAI_RCR2, + FSL_SAI_CR2_DIV_MASK, savediv - 1); + } else { + regmap_update_bits(sai->regmap, FSL_SAI_TCR2, + FSL_SAI_CR2_MSEL_MASK, + FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); + regmap_update_bits(sai->regmap, FSL_SAI_TCR2, + FSL_SAI_CR2_DIV_MASK, savediv - 1); + } + + dev_dbg(dai->dev, "best fit: clock id=%d, div=%d, deviation =%d\n", + sai->mclk_id[tx], savediv, savesub); + + return 0; +} + static int fsl_sai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) @@ -297,6 +383,24 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, unsigned int channels = params_channels(params); u32 word_width = snd_pcm_format_width(params_format(params)); u32 val_cr4 = 0, val_cr5 = 0; + int ret; + + if (!sai->is_slave_mode) { + ret = fsl_sai_set_bclk(cpu_dai, tx, + 2 * word_width * params_rate(params)); + if (ret) + return ret; + + /* Do not enable the clock if it is already enabled */ + if (!(sai->mclk_streams & BIT(substream->stream))) { + ret = clk_prepare_enable(sai->mclk_clk[sai->mclk_id[tx]]); + if (ret) + return ret; + + sai->mclk_streams |= BIT(substream->stream); + } + + } if (!sai->is_dsp_mode) val_cr4 |= FSL_SAI_CR4_SYWD(word_width); @@ -322,6 +426,22 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, return 0; } +static int fsl_sai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + if (!sai->is_slave_mode && + sai->mclk_streams & BIT(substream->stream)) { + clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); + sai->mclk_streams &= ~BIT(substream->stream); + } + + return 0; +} + + static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { @@ -410,7 +530,10 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx), FSL_SAI_CR3_TRCE, FSL_SAI_CR3_TRCE); - return 0; + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints); + + return ret; } static void fsl_sai_shutdown(struct snd_pcm_substream *substream, @@ -428,6 +551,7 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { .set_sysclk = fsl_sai_set_dai_sysclk, .set_fmt = fsl_sai_set_dai_fmt, .hw_params = fsl_sai_hw_params, + .hw_free = fsl_sai_hw_free, .trigger = fsl_sai_trigger, .startup = fsl_sai_startup, .shutdown = fsl_sai_shutdown, @@ -463,14 +587,18 @@ static struct snd_soc_dai_driver fsl_sai_dai = { .stream_name = "CPU-Playback", .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, + .rate_min = 8000, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_KNOT, .formats = FSL_SAI_FORMATS, }, .capture = { .stream_name = "CPU-Capture", .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, + .rate_min = 8000, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_KNOT, .formats = FSL_SAI_FORMATS, }, .ops = &fsl_sai_pcm_dai_ops, @@ -600,8 +728,9 @@ static int fsl_sai_probe(struct platform_device *pdev) sai->bus_clk = NULL; } - for (i = 0; i < FSL_SAI_MCLK_MAX; i++) { - sprintf(tmp, "mclk%d", i + 1); + sai->mclk_clk[0] = sai->bus_clk; + for (i = 1; i < FSL_SAI_MCLK_MAX; i++) { + sprintf(tmp, "mclk%d", i); sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp); if (IS_ERR(sai->mclk_clk[i])) { dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n", @@ -664,8 +793,7 @@ static int fsl_sai_probe(struct platform_device *pdev) if (sai->sai_on_imx) return imx_pcm_dma_init(pdev); else - return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, - SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); + return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); } static const struct of_device_id fsl_sai_ids[] = { diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 34667209b..066280953 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -72,13 +72,15 @@ /* SAI Transmit and Recieve Configuration 2 Register */ #define FSL_SAI_CR2_SYNC BIT(30) -#define FSL_SAI_CR2_MSEL_MASK (0xff << 26) +#define FSL_SAI_CR2_MSEL_MASK (0x3 << 26) #define FSL_SAI_CR2_MSEL_BUS 0 #define FSL_SAI_CR2_MSEL_MCLK1 BIT(26) #define FSL_SAI_CR2_MSEL_MCLK2 BIT(27) #define FSL_SAI_CR2_MSEL_MCLK3 (BIT(26) | BIT(27)) +#define FSL_SAI_CR2_MSEL(ID) ((ID) << 26) #define FSL_SAI_CR2_BCP BIT(25) #define FSL_SAI_CR2_BCD_MSTR BIT(24) +#define FSL_SAI_CR2_DIV_MASK 0xff /* SAI Transmit and Recieve Configuration 3 Register */ #define FSL_SAI_CR3_TRCE BIT(16) @@ -120,7 +122,7 @@ #define FSL_SAI_CLK_MAST2 2 #define FSL_SAI_CLK_MAST3 3 -#define FSL_SAI_MCLK_MAX 3 +#define FSL_SAI_MCLK_MAX 4 /* SAI data transfer numbers per DMA request */ #define FSL_SAI_MAXBURST_TX 6 @@ -132,11 +134,14 @@ struct fsl_sai { struct clk *bus_clk; struct clk *mclk_clk[FSL_SAI_MCLK_MAX]; + bool is_slave_mode; bool is_lsb_first; bool is_dsp_mode; bool sai_on_imx; bool synchronous[2]; + unsigned int mclk_id[2]; + unsigned int mclk_streams; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; }; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 91eb3aef7..8e932219c 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -417,11 +417,9 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, if (clk != STC_TXCLK_SPDIF_ROOT) goto clk_set_bypass; - /* - * The S/PDIF block needs a clock of 64 * fs * txclk_df. - * So request 64 * fs * (txclk_df + 1) to get rounded. - */ - ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1)); + /* The S/PDIF block needs a clock of 64 * fs * txclk_df */ + ret = clk_set_rate(spdif_priv->txclk[rate], + 64 * sample_rate * txclk_df); if (ret) { dev_err(&pdev->dev, "failed to set tx clock rate\n"); return ret; @@ -1060,7 +1058,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) { for (txclk_df = 1; txclk_df <= 128; txclk_df++) { - rate_ideal = rate[index] * (txclk_df + 1) * 64; + rate_ideal = rate[index] * txclk_df * 64; if (round) rate_actual = clk_round_rate(clk, rate_ideal); else diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 0d4880421..c0b940e20 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; @@ -1292,13 +1292,6 @@ static int fsl_ssi_probe(struct platform_device *pdev) void __iomem *iomem; char name[64]; - /* SSIs that are not connected on the board should have a - * status = "disabled" - * property in their device tree nodes. - */ - if (!of_device_is_available(np)) - return -ENODEV; - of_id = of_match_device(fsl_ssi_ids, &pdev->dev); if (!of_id || !of_id->data) return -EINVAL; diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index d9050d946..fc57da341 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -184,7 +184,7 @@ static enum imx_audmux_type { IMX31_AUDMUX, } audmux_type; -static struct platform_device_id imx_audmux_ids[] = { +static const struct platform_device_id imx_audmux_ids[] = { { .name = "imx21-audmux", .driver_data = IMX21_AUDMUX, diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c index 9e6493d4e..bb0459018 100644 --- a/sound/soc/fsl/imx-mc13783.c +++ b/sound/soc/fsl/imx-mc13783.c @@ -45,11 +45,7 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream, if (ret) return ret; - ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16); - if (ret) - return ret; - - return 0; + return snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16); } static struct snd_soc_ops imx_mc13783_hifi_ops = { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 33feee9ca..d55549391 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); } @@ -307,11 +315,13 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); struct device_node *cpu = NULL; + struct device_node *plat = NULL; struct device_node *codec = NULL; char *name; 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) @@ -320,6 +330,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, snprintf(prop, sizeof(prop), "%scpu", prefix); cpu = of_get_child_by_name(node, prop); + snprintf(prop, sizeof(prop), "%splat", prefix); + plat = of_get_child_by_name(node, prop); + snprintf(prop, sizeof(prop), "%scodec", prefix); codec = of_get_child_by_name(node, prop); @@ -334,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, @@ -352,8 +368,16 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, goto dai_link_of_err; } - /* Simple Card assumes platform == cpu */ - dai_link->platform_of_node = dai_link->cpu_of_node; + if (plat) { + struct of_phandle_args args; + + ret = of_parse_phandle_with_args(plat, "sound-dai", + "#sound-dai-cells", 0, &args); + dai_link->platform_of_node = args.np; + } else { + /* 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, diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index ee03dbdda..f3060a4ca 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -79,7 +79,6 @@ config SND_SOC_INTEL_BROADWELL_MACH depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \ I2C_DESIGNWARE_PLATFORM select SND_SOC_INTEL_HASWELL - select SND_COMPRESS_OFFLOAD select SND_SOC_RT286 help This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell @@ -112,12 +111,24 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH If unsure select "N". config SND_SOC_INTEL_CHT_BSW_RT5645_MACH - tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645 codec" - depends on X86_INTEL_LPSS + tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec" + depends on X86_INTEL_LPSS && I2C select SND_SOC_RT5645 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell - platforms with RT5645 audio codec. + platforms with RT5645/5650 audio codec. If unsure select "N". + +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 && I2C + select SND_SOC_MAX98090 + select SND_SOC_TS3A227E + select SND_SST_MFLD_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell + platforms with MAX98090 audio codec it also can support TI jack chip as aux device. + If unsure select "N". diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 3853ec2dd..6de5d5cd3 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 90aa5c047..31e9b9ecb 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -774,8 +774,120 @@ int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable) return ret; } +int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct sst_data *ctx = snd_soc_dai_get_drvdata(dai); + + ctx->ssp_cmd.nb_slots = slots; + ctx->ssp_cmd.active_tx_slot_map = tx_mask; + ctx->ssp_cmd.active_rx_slot_map = rx_mask; + ctx->ssp_cmd.nb_bits_per_slots = slot_width; + + return 0; +} + +static int sst_get_frame_sync_polarity(struct snd_soc_dai *dai, + unsigned int fmt) +{ + int format; + + format = fmt & SND_SOC_DAIFMT_INV_MASK; + dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format); + + switch (format) { + case SND_SOC_DAIFMT_NB_NF: + return SSP_FS_ACTIVE_LOW; + case SND_SOC_DAIFMT_NB_IF: + return SSP_FS_ACTIVE_HIGH; + case SND_SOC_DAIFMT_IB_IF: + return SSP_FS_ACTIVE_LOW; + case SND_SOC_DAIFMT_IB_NF: + return SSP_FS_ACTIVE_HIGH; + default: + dev_err(dai->dev, "Invalid frame sync polarity %d\n", format); + } + + return -EINVAL; +} + +static int sst_get_ssp_mode(struct snd_soc_dai *dai, unsigned int fmt) +{ + int format; + + format = (fmt & SND_SOC_DAIFMT_MASTER_MASK); + dev_dbg(dai->dev, "Enter:%s, format=%x\n", __func__, format); + + switch (format) { + case SND_SOC_DAIFMT_CBS_CFS: + return SSP_MODE_MASTER; + case SND_SOC_DAIFMT_CBM_CFM: + return SSP_MODE_SLAVE; + default: + dev_err(dai->dev, "Invalid ssp protocol: %d\n", format); + } + + return -EINVAL; +} + + +int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt) +{ + unsigned int mode; + int fs_polarity; + struct sst_data *ctx = snd_soc_dai_get_drvdata(dai); + + mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + switch (mode) { + case SND_SOC_DAIFMT_DSP_B: + ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM; + ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1); + ctx->ssp_cmd.start_delay = 0; + ctx->ssp_cmd.data_polarity = 1; + ctx->ssp_cmd.frame_sync_width = 1; + break; + + case SND_SOC_DAIFMT_DSP_A: + ctx->ssp_cmd.ssp_protocol = SSP_MODE_PCM; + ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NETWORK << 1); + ctx->ssp_cmd.start_delay = 1; + ctx->ssp_cmd.data_polarity = 1; + ctx->ssp_cmd.frame_sync_width = 1; + break; + + case SND_SOC_DAIFMT_I2S: + ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S; + ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1); + ctx->ssp_cmd.start_delay = 1; + ctx->ssp_cmd.data_polarity = 0; + ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots; + break; + + case SND_SOC_DAIFMT_LEFT_J: + ctx->ssp_cmd.ssp_protocol = SSP_MODE_I2S; + ctx->ssp_cmd.mode = sst_get_ssp_mode(dai, fmt) | (SSP_PCM_MODE_NORMAL << 1); + ctx->ssp_cmd.start_delay = 0; + ctx->ssp_cmd.data_polarity = 0; + ctx->ssp_cmd.frame_sync_width = ctx->ssp_cmd.nb_bits_per_slots; + break; + + default: + dev_dbg(dai->dev, "using default ssp configs\n"); + } + + fs_polarity = sst_get_frame_sync_polarity(dai, fmt); + if (fs_polarity < 0) + return fs_polarity; + + ctx->ssp_cmd.frame_sync_polarity = fs_polarity; + + return 0; +} + /** * sst_ssp_config - contains SSP configuration for media UC + * this can be overwritten by set_dai_xxx APIs */ static const struct sst_ssp_config sst_ssp_configs = { .ssp_id = SSP_CODEC, @@ -789,47 +901,56 @@ static const struct sst_ssp_config sst_ssp_configs = { .fs_frequency = SSP_FS_48_KHZ, .active_slot_map = 0xF, .start_delay = 0, + .frame_sync_polarity = SSP_FS_ACTIVE_HIGH, + .data_polarity = 1, }; +void sst_fill_ssp_defaults(struct snd_soc_dai *dai) +{ + const struct sst_ssp_config *config; + struct sst_data *ctx = snd_soc_dai_get_drvdata(dai); + + config = &sst_ssp_configs; + + ctx->ssp_cmd.selection = config->ssp_id; + ctx->ssp_cmd.nb_bits_per_slots = config->bits_per_slot; + ctx->ssp_cmd.nb_slots = config->slots; + ctx->ssp_cmd.mode = config->ssp_mode | (config->pcm_mode << 1); + ctx->ssp_cmd.duplex = config->duplex; + ctx->ssp_cmd.active_tx_slot_map = config->active_slot_map; + ctx->ssp_cmd.active_rx_slot_map = config->active_slot_map; + ctx->ssp_cmd.frame_sync_frequency = config->fs_frequency; + ctx->ssp_cmd.frame_sync_polarity = config->frame_sync_polarity; + ctx->ssp_cmd.data_polarity = config->data_polarity; + ctx->ssp_cmd.frame_sync_width = config->fs_width; + ctx->ssp_cmd.ssp_protocol = config->ssp_protocol; + ctx->ssp_cmd.start_delay = config->start_delay; + ctx->ssp_cmd.reserved1 = ctx->ssp_cmd.reserved2 = 0xFF; +} + int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable) { - struct sst_cmd_sba_hw_set_ssp cmd; struct sst_data *drv = snd_soc_dai_get_drvdata(dai); const struct sst_ssp_config *config; dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id); - SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); - cmd.header.command_id = SBA_HW_SET_SSP; - cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) + SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst); + drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP; + drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) - sizeof(struct sst_dsp_header); config = &sst_ssp_configs; dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id); if (enable) - cmd.switch_state = SST_SWITCH_ON; + drv->ssp_cmd.switch_state = SST_SWITCH_ON; else - cmd.switch_state = SST_SWITCH_OFF; - - cmd.selection = config->ssp_id; - cmd.nb_bits_per_slots = config->bits_per_slot; - cmd.nb_slots = config->slots; - cmd.mode = config->ssp_mode | (config->pcm_mode << 1); - cmd.duplex = config->duplex; - cmd.active_tx_slot_map = config->active_slot_map; - cmd.active_rx_slot_map = config->active_slot_map; - cmd.frame_sync_frequency = config->fs_frequency; - cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH; - cmd.data_polarity = 1; - cmd.frame_sync_width = config->fs_width; - cmd.ssp_protocol = config->ssp_protocol; - cmd.start_delay = config->start_delay; - cmd.reserved1 = cmd.reserved2 = 0xFF; + drv->ssp_cmd.switch_state = SST_SWITCH_OFF; return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, - SST_TASK_SBA, 0, &cmd, - sizeof(cmd.header) + cmd.header.length); + SST_TASK_SBA, 0, &drv->ssp_cmd, + sizeof(drv->ssp_cmd.header) + drv->ssp_cmd.header.length); } static int sst_set_be_modules(struct snd_soc_dapm_widget *w, @@ -1280,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-atom-controls.h b/sound/soc/intel/atom/sst-atom-controls.h index daecc58f2..93de8045d 100644 --- a/sound/soc/intel/atom/sst-atom-controls.h +++ b/sound/soc/intel/atom/sst-atom-controls.h @@ -562,6 +562,8 @@ struct sst_ssp_config { u8 active_slot_map; u8 start_delay; u16 fs_width; + u8 frame_sync_polarity; + u8 data_polarity; }; struct sst_ssp_cfg { @@ -695,7 +697,7 @@ struct sst_gain_mixer_control { u16 module_id; u16 pipe_id; u16 task_id; - char pname[44]; + char pname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; struct snd_soc_dapm_widget *w; }; @@ -867,4 +869,9 @@ struct sst_enum { SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \ SST_SSP_MUX_ENUM(xreg, xshift, xtexts)) +int sst_fill_ssp_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width); +int sst_fill_ssp_config(struct snd_soc_dai *dai, unsigned int fmt); +void sst_fill_ssp_defaults(struct snd_soc_dai *dai); + #endif diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 2fbaf2c75..641ebe61d 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -434,13 +434,51 @@ static int sst_enable_ssp(struct snd_pcm_substream *substream, if (!dai->active) { ret = sst_handle_vb_timer(dai, true); - if (ret) - return ret; - ret = send_ssp_cmd(dai, dai->name, 1); + sst_fill_ssp_defaults(dai); } return ret; } +static int sst_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret = 0; + + if (dai->active == 1) + ret = send_ssp_cmd(dai, dai->name, 1); + return ret; +} + +static int sst_set_format(struct snd_soc_dai *dai, unsigned int fmt) +{ + int ret = 0; + + if (!dai->active) + return 0; + + ret = sst_fill_ssp_config(dai, fmt); + if (ret < 0) + dev_err(dai->dev, "sst_set_format failed..\n"); + + return ret; +} + +static int sst_platform_set_ssp_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) { + int ret = 0; + + if (!dai->active) + return ret; + + ret = sst_fill_ssp_slot(dai, tx_mask, rx_mask, slots, slot_width); + if (ret < 0) + dev_err(dai->dev, "sst_fill_ssp_slot failed..%d\n", ret); + + return ret; +} + static void sst_disable_ssp(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -465,6 +503,9 @@ static struct snd_soc_dai_ops sst_compr_dai_ops = { static struct snd_soc_dai_ops sst_be_dai_ops = { .startup = sst_enable_ssp, + .hw_params = sst_be_hw_params, + .set_fmt = sst_set_format, + .set_tdm_slot = sst_platform_set_ssp_slot, .shutdown = sst_disable_ssp, }; diff --git a/sound/soc/intel/atom/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h index 9094314be..2409b23ee 100644 --- a/sound/soc/intel/atom/sst-mfld-platform.h +++ b/sound/soc/intel/atom/sst-mfld-platform.h @@ -22,6 +22,7 @@ #define __SST_PLATFORMDRV_H__ #include "sst-mfld-dsp.h" +#include "sst-atom-controls.h" extern struct sst_device *sst; @@ -175,6 +176,7 @@ struct sst_data { struct snd_sst_bytes_v2 *byte_stream; struct mutex lock; struct snd_soc_card *soc_card; + struct sst_cmd_sba_hw_set_ssp ssp_cmd; }; int sst_register_dsp(struct sst_device *sst); int sst_unregister_dsp(struct sst_device *sst); diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index 61d9aafe5..fc3e19371 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_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index ea6358a7b..58ab0b517 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -354,6 +354,10 @@ static struct sst_machines sst_acpi_chv[] = { &chv_platform_data }, {"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "/*(DEBLOBBED)*/", &chv_platform_data }, + {"10EC5650", "cht-bsw", "cht-bsw-rt5645", NULL, "/*(DEBLOBBED)*/", + &chv_platform_data }, + {"193C9890", "cht-bsw", "cht-bsw-max98090", NULL, + "/*(DEBLOBBED)*/", &chv_platform_data }, {}, }; diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index edc186908..0e0e4d9c0 100644 --- a/sound/soc/intel/atom/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -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/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c index a839dbfa5..5bbaa667b 100644 --- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c +++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c @@ -679,6 +679,14 @@ static u64 byt_reply_msg_match(u64 header, u64 *mask) return header; } +static bool byt_is_dsp_busy(struct sst_dsp *dsp) +{ + u64 ipcx; + + ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX); + return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)); +} + int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) { struct sst_byt *byt; @@ -693,12 +701,17 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) if (byt == NULL) return -ENOMEM; + byt->dev = dev; + ipc = &byt->ipc; ipc->dev = dev; ipc->ops.tx_msg = byt_tx_msg; ipc->ops.shim_dbg = byt_shim_dbg; ipc->ops.tx_data_copy = byt_tx_data_copy; ipc->ops.reply_msg_match = byt_reply_msg_match; + ipc->ops.is_dsp_busy = byt_is_dsp_busy; + ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES; + ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES; err = sst_ipc_init(ipc); if (err != 0) diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index f8237f004..cb94895c9 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -5,6 +5,7 @@ snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o +snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -13,3 +14,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o +obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c new file mode 100644 index 000000000..70f832114 --- /dev/null +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -0,0 +1,348 @@ +/* + * cht-bsw-max98090.c - ASoc Machine driver for Intel Cherryview-based + * platforms Cherrytrail and Braswell, with max98090 & TI codec. + * + * Copyright (C) 2015 Intel Corp + * Author: Fang, Yang A <yang.a.fang@intel.com> + * This file is modified from cht_bsw_rt5645.c + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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/platform_device.h> +#include <linux/slab.h> +#include <linux/acpi.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../../codecs/max98090.h" +#include "../atom/sst-atom-controls.h" +#include "../../codecs/ts3a227e.h" + +#define CHT_PLAT_CLK_3_HZ 19200000 +#define CHT_CODEC_DAI "HiFi" + +struct cht_mc_private { + struct snd_soc_jack jack; + bool ts3a227e_present; +}; + +static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) +{ + int i; + + for (i = 0; i < card->num_rtd; i++) { + struct snd_soc_pcm_runtime *rtd; + + rtd = card->rtd + i; + if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, + strlen(CHT_CODEC_DAI))) + return rtd->codec_dai; + } + return NULL; +} + +static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const struct snd_soc_dapm_route cht_audio_map[] = { + {"IN34", NULL, "Headset Mic"}, + {"Headset Mic", NULL, "MICBIAS"}, + {"DMICL", NULL, "Int Mic"}, + {"Headphone", NULL, "HPL"}, + {"Headphone", NULL, "HPR"}, + {"Ext Spk", NULL, "SPKL"}, + {"Ext Spk", NULL, "SPKR"}, + {"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, "HiFi Capture"}, +}; + +static const struct snd_kcontrol_new cht_mc_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static int cht_aif1_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; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, M98090_REG_SYSTEM_CLOCK, + CHT_PLAT_CLK_3_HZ, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + + 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; + int jack_type; + struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); + struct snd_soc_jack *jack = &ctx->jack; + + /** + * TI supports 4 butons headset detection + * KEY_MEDIA + * KEY_VOICECOMMAND + * KEY_VOLUMEUP + * KEY_VOLUMEDOWN + */ + if (ctx->ts3a227e_present) + jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3; + else + jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; + + ret = snd_soc_card_jack_new(runtime->card, "Headset Jack", + jack_type, jack, NULL, 0); + + if (ret) { + dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret); + return ret; + } + + if (ctx->ts3a227e_present) + snd_soc_jack_notifier_register(jack, &cht_jack_nb); + + return ret; +} + +static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + int ret = 0; + unsigned int fmt = 0; + + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16); + if (ret < 0) { + dev_err(rtd->dev, "can't set cpu_dai slot fmt: %d\n", ret); + return ret; + } + + fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS; + + ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt); + if (ret < 0) { + dev_err(rtd->dev, "can't set cpu_dai set fmt: %d\n", ret); + return ret; + } + + /* The DSP will covert the FE rate to 48k, stereo, 24bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP2 to 24-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + return 0; +} + +static unsigned int rates_48000[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int cht_aif1_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static int cht_max98090_headset_init(struct snd_soc_component *component) +{ + struct snd_soc_card *card = component->card; + struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); + + return ts3a227e_enable_jack_detect(component, &ctx->jack); +} + +static struct snd_soc_ops cht_aif1_ops = { + .startup = cht_aif1_startup, +}; + +static struct snd_soc_ops cht_be_ssp2_ops = { + .hw_params = cht_aif1_hw_params, +}; + +static struct snd_soc_aux_dev cht_max98090_headset_dev = { + .name = "Headset Chip", + .init = cht_max98090_headset_init, + .codec_name = "i2c-104C227E:00", +}; + +static struct snd_soc_dai_link cht_dailink[] = { + [MERR_DPCM_AUDIO] = { + .name = "Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "media-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + .nonatomic = true, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cht_aif1_ops, + }, + [MERR_DPCM_COMPR] = { + .name = "Compressed Port", + .stream_name = "Compress", + .cpu_dai_name = "compress-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + }, + /* back ends */ + { + .name = "SSP2-Codec", + .be_id = 1, + .cpu_dai_name = "ssp2-port", + .platform_name = "sst-mfld-platform", + .no_pcm = 1, + .codec_dai_name = "HiFi", + .codec_name = "i2c-193C9890:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .init = cht_codec_init, + .be_hw_params_fixup = cht_codec_fixup, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cht_be_ssp2_ops, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cht = { + .name = "chtmax98090", + .dai_link = cht_dailink, + .num_links = ARRAY_SIZE(cht_dailink), + .aux_dev = &cht_max98090_headset_dev, + .num_aux_devs = 1, + .dapm_widgets = cht_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), + .dapm_routes = cht_audio_map, + .num_dapm_routes = ARRAY_SIZE(cht_audio_map), + .controls = cht_mc_controls, + .num_controls = ARRAY_SIZE(cht_mc_controls), +}; + +static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level, + void *context, void **ret) +{ + *(bool *)context = true; + return AE_OK; +} + +static int snd_cht_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + bool found = false; + struct cht_mc_private *drv; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); + if (!drv) + return -ENOMEM; + + if (ACPI_SUCCESS(acpi_get_devices( + "104C227E", + snd_acpi_codec_match, + &found, NULL)) && found) { + drv->ts3a227e_present = true; + } else { + /* no need probe TI jack detection chip */ + snd_soc_card_cht.aux_dev = NULL; + snd_soc_card_cht.num_aux_devs = 0; + drv->ts3a227e_present = false; + } + + /* register the soc card */ + snd_soc_card_cht.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); + ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); + if (ret_val) { + dev_err(&pdev->dev, + "snd_soc_register_card failed %d\n", ret_val); + return ret_val; + } + platform_set_drvdata(pdev, &snd_soc_card_cht); + return ret_val; +} + +static struct platform_driver snd_cht_mc_driver = { + .driver = { + .name = "cht-bsw-max98090", + }, + .probe = snd_cht_mc_probe, +}; + +module_platform_driver(snd_cht_mc_driver) + +MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver"); +MODULE_AUTHOR("Fang, Yang A <yang.a.fang@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cht-bsw-max98090"); diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 20a28b22e..bdcaf4678 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -21,6 +21,7 @@ */ #include <linux/module.h> +#include <linux/acpi.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <sound/pcm.h> @@ -33,9 +34,15 @@ #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI "rt5645-aif1" +struct cht_acpi_card { + char *codec_id; + int codec_type; + struct snd_soc_card *soc_card; +}; + struct cht_mc_private { - struct snd_soc_jack hp_jack; - struct snd_soc_jack mic_jack; + struct snd_soc_jack jack; + struct cht_acpi_card *acpi_card; }; static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) @@ -94,7 +101,7 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { platform_clock_control, SND_SOC_DAPM_POST_PMD), }; -static const struct snd_soc_dapm_route cht_audio_map[] = { +static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = { {"IN1P", NULL, "Headset Mic"}, {"IN1N", NULL, "Headset Mic"}, {"DMIC L1", NULL, "Int Mic"}, @@ -115,6 +122,27 @@ static const struct snd_soc_dapm_route cht_audio_map[] = { {"Ext Spk", NULL, "Platform Clock"}, }; +static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = { + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, + {"DMIC L2", NULL, "Int Mic"}, + {"DMIC R2", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Ext Spk", NULL, "SPOL"}, + {"Ext Spk", NULL, "SPOR"}, + {"AIF1 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"}, + {"Headphone", NULL, "Platform Clock"}, + {"Headset Mic", NULL, "Platform Clock"}, + {"Int Mic", NULL, "Platform Clock"}, + {"Ext Spk", NULL, "Platform Clock"}, +}; + static const struct snd_kcontrol_new cht_mc_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -150,6 +178,7 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream, static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { int ret; + int jack_type; struct snd_soc_codec *codec = runtime->codec; struct snd_soc_dai *codec_dai = runtime->codec_dai; struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); @@ -169,23 +198,22 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) return ret; } - ret = snd_soc_card_jack_new(runtime->card, "Headphone Jack", - SND_JACK_HEADPHONE, &ctx->hp_jack, - NULL, 0); - if (ret) { - dev_err(runtime->dev, "HP jack creation failed %d\n", ret); - return ret; - } + if (ctx->acpi_card->codec_type == CODEC_TYPE_RT5650) + jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3; + else + jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; - ret = snd_soc_card_jack_new(runtime->card, "Mic Jack", - SND_JACK_MICROPHONE, &ctx->mic_jack, + ret = snd_soc_card_jack_new(runtime->card, "Headset Jack", + jack_type, &ctx->jack, NULL, 0); if (ret) { - dev_err(runtime->dev, "Mic jack creation failed %d\n", ret); + dev_err(runtime->dev, "Headset jack creation failed %d\n", ret); return ret; } - rt5645_set_jack_detect(codec, &ctx->hp_jack, &ctx->mic_jack); + rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack); return ret; } @@ -239,7 +267,7 @@ static struct snd_soc_dai_link cht_dailink[] = { .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", .platform_name = "sst-mfld-platform", - .ignore_suspend = 1, + .nonatomic = true, .dynamic = 1, .dpcm_playback = 1, .dpcm_capture = 1, @@ -267,7 +295,7 @@ static struct snd_soc_dai_link cht_dailink[] = { | SND_SOC_DAIFMT_CBS_CFS, .init = cht_codec_init, .be_hw_params_fixup = cht_codec_fixup, - .ignore_suspend = 1, + .nonatomic = true, .dpcm_playback = 1, .dpcm_capture = 1, .ops = &cht_be_ssp2_ops, @@ -275,43 +303,85 @@ static struct snd_soc_dai_link cht_dailink[] = { }; /* SoC card */ -static struct snd_soc_card snd_soc_card_cht = { +static struct snd_soc_card snd_soc_card_chtrt5645 = { .name = "chtrt5645", .dai_link = cht_dailink, .num_links = ARRAY_SIZE(cht_dailink), .dapm_widgets = cht_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), - .dapm_routes = cht_audio_map, - .num_dapm_routes = ARRAY_SIZE(cht_audio_map), + .dapm_routes = cht_rt5645_audio_map, + .num_dapm_routes = ARRAY_SIZE(cht_rt5645_audio_map), .controls = cht_mc_controls, .num_controls = ARRAY_SIZE(cht_mc_controls), }; +static struct snd_soc_card snd_soc_card_chtrt5650 = { + .name = "chtrt5650", + .dai_link = cht_dailink, + .num_links = ARRAY_SIZE(cht_dailink), + .dapm_widgets = cht_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), + .dapm_routes = cht_rt5650_audio_map, + .num_dapm_routes = ARRAY_SIZE(cht_rt5650_audio_map), + .controls = cht_mc_controls, + .num_controls = ARRAY_SIZE(cht_mc_controls), +}; + +static struct cht_acpi_card snd_soc_cards[] = { + {"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645}, + {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650}, +}; + +static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level, + void *context, void **ret) +{ + *(bool *)context = true; + return AE_OK; +} + static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; + int i; struct cht_mc_private *drv; + struct snd_soc_card *card = snd_soc_cards[0].soc_card; + bool found = false; + char codec_name[16]; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); if (!drv) return -ENOMEM; - snd_soc_card_cht.dev = &pdev->dev; - snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); - ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); + for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) { + if (ACPI_SUCCESS(acpi_get_devices( + snd_soc_cards[i].codec_id, + snd_acpi_codec_match, + &found, NULL)) && found) { + dev_dbg(&pdev->dev, + "found codec %s\n", snd_soc_cards[i].codec_id); + card = snd_soc_cards[i].soc_card; + drv->acpi_card = &snd_soc_cards[i]; + break; + } + } + card->dev = &pdev->dev; + sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id); + /* set correct codec name */ + strcpy((char *)card->dai_link[2].codec_name, codec_name); + snd_soc_card_set_drvdata(card, drv); + ret_val = devm_snd_soc_register_card(&pdev->dev, card); if (ret_val) { dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret_val); return ret_val; } - platform_set_drvdata(pdev, &snd_soc_card_cht); + platform_set_drvdata(pdev, card); return ret_val; } static struct platform_driver snd_cht_mc_driver = { .driver = { .name = "cht-bsw-rt5645", - .pm = &snd_soc_pm_ops, }, .probe = snd_cht_mc_probe, }; diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 23cd229e5..fb5e0aa07 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/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index 4b62a5538..a12c7bb08 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -129,11 +129,31 @@ static int msg_empty_list_init(struct sst_generic_ipc *ipc) return -ENOMEM; for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + ipc->msg[i].tx_data = kzalloc(ipc->tx_data_max_size, GFP_KERNEL); + if (ipc->msg[i].tx_data == NULL) + goto free_mem; + + ipc->msg[i].rx_data = kzalloc(ipc->rx_data_max_size, GFP_KERNEL); + if (ipc->msg[i].rx_data == NULL) { + kfree(ipc->msg[i].tx_data); + goto free_mem; + } + init_waitqueue_head(&ipc->msg[i].waitq); list_add(&ipc->msg[i].list, &ipc->empty_list); } return 0; + +free_mem: + while (i > 0) { + kfree(ipc->msg[i-1].tx_data); + kfree(ipc->msg[i-1].rx_data); + --i; + } + kfree(ipc->msg); + + return -ENOMEM; } static void ipc_tx_msgs(struct kthread_work *work) @@ -142,7 +162,6 @@ static void ipc_tx_msgs(struct kthread_work *work) container_of(work, struct sst_generic_ipc, kwork); struct ipc_message *msg; unsigned long flags; - u64 ipcx; spin_lock_irqsave(&ipc->dsp->spinlock, flags); @@ -153,8 +172,8 @@ static void ipc_tx_msgs(struct kthread_work *work) /* if the DSP is busy, we will TX messages after IRQ. * also postpone if we are in the middle of procesing completion irq*/ - ipcx = sst_dsp_shim_read_unlocked(ipc->dsp, SST_IPCX); - if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) { + if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) { + dev_dbg(ipc->dev, "ipc_tx_msgs dsp busy\n"); spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); return; } @@ -280,11 +299,18 @@ EXPORT_SYMBOL_GPL(sst_ipc_init); void sst_ipc_fini(struct sst_generic_ipc *ipc) { + int i; + if (ipc->tx_thread) kthread_stop(ipc->tx_thread); - if (ipc->msg) + if (ipc->msg) { + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + kfree(ipc->msg[i].tx_data); + kfree(ipc->msg[i].rx_data); + } kfree(ipc->msg); + } } EXPORT_SYMBOL_GPL(sst_ipc_fini); diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index 125ea451a..ceb7e468a 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -32,9 +32,9 @@ struct ipc_message { u64 header; /* direction wrt host CPU */ - char tx_data[IPC_MAX_MAILBOX_BYTES]; + char *tx_data; size_t tx_size; - char rx_data[IPC_MAX_MAILBOX_BYTES]; + char *rx_data; size_t rx_size; wait_queue_head_t waitq; @@ -51,6 +51,7 @@ struct sst_plat_ipc_ops { void (*shim_dbg)(struct sst_generic_ipc *, const char *); void (*tx_data_copy)(struct ipc_message *, char *, size_t); u64 (*reply_msg_match)(u64 header, u64 *mask); + bool (*is_dsp_busy)(struct sst_dsp *dsp); }; /* SST generic IPC data */ @@ -68,6 +69,8 @@ struct sst_generic_ipc { struct kthread_work kwork; bool pending; struct ipc_message *msg; + int tx_data_max_size; + int rx_data_max_size; struct sst_plat_ipc_ops ops; }; diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index 27957a215..670afbe77 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -2098,6 +2098,14 @@ static u64 hsw_reply_msg_match(u64 header, u64 *mask) return header; } +static bool hsw_is_dsp_busy(struct sst_dsp *dsp) +{ + u64 ipcx; + + ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX); + return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)); +} + int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) { struct sst_hsw_ipc_fw_version version; @@ -2111,12 +2119,18 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) if (hsw == NULL) return -ENOMEM; + hsw->dev = dev; + ipc = &hsw->ipc; ipc->dev = dev; ipc->ops.tx_msg = hsw_tx_msg; ipc->ops.shim_dbg = hsw_shim_dbg; ipc->ops.tx_data_copy = hsw_tx_data_copy; ipc->ops.reply_msg_match = hsw_reply_msg_match; + ipc->ops.is_dsp_busy = hsw_is_dsp_busy; + + ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES; + ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES; ret = sst_ipc_init(ipc); if (ret != 0) diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index 23ae0400d..1aa819c7e 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -928,10 +928,15 @@ static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) for (i = 0; i < ARRAY_SIZE(mod_map); i++) { pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; - sst_hsw_runtime_module_free(pcm_data->runtime); + if (pcm_data->runtime){ + sst_hsw_runtime_module_free(pcm_data->runtime); + pcm_data->runtime = NULL; + } } - if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) { + if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES) && + pdata->runtime_waves) { sst_hsw_runtime_module_free(pdata->runtime_waves); + pdata->runtime_waves = NULL; } } @@ -1204,6 +1209,20 @@ static int hsw_pcm_runtime_idle(struct device *dev) return 0; } +static int hsw_pcm_suspend(struct device *dev) +{ + struct hsw_priv_data *pdata = dev_get_drvdata(dev); + struct sst_hsw *hsw = pdata->hsw; + + /* enter D3 state and stall */ + sst_hsw_dsp_runtime_suspend(hsw); + /* free all runtime modules */ + hsw_pcm_free_modules(pdata); + /* put the DSP to sleep, fw unloaded after runtime modules freed */ + sst_hsw_dsp_runtime_sleep(hsw); + return 0; +} + static int hsw_pcm_runtime_suspend(struct device *dev) { struct hsw_priv_data *pdata = dev_get_drvdata(dev); @@ -1220,8 +1239,7 @@ static int hsw_pcm_runtime_suspend(struct device *dev) return ret; sst_hsw_set_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES); } - sst_hsw_dsp_runtime_suspend(hsw); - sst_hsw_dsp_runtime_sleep(hsw); + hsw_pcm_suspend(dev); pdata->pm_state = HSW_PM_STATE_RTD3; return 0; @@ -1328,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; @@ -1361,10 +1378,7 @@ static int hsw_pcm_prepare(struct device *dev) if (err < 0) dev_err(dev, "failed to save context for PCM %d\n", i); } - /* enter D3 state and stall */ - sst_hsw_dsp_runtime_suspend(hsw); - /* put the DSP to sleep */ - sst_hsw_dsp_runtime_sleep(hsw); + hsw_pcm_suspend(dev); } snd_soc_suspend(pdata->soc_card->dev); diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig new file mode 100644 index 000000000..15c04e2ea --- /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 000000000..75effbec4 --- /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 000000000..2d2536af1 --- /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 000000000..6f52eca05 --- /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 000000000..a88b17511 --- /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 000000000..9863da73d --- /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 4775da4c4..aeef25c0c 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/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c index 3673ada43..743131473 100644 --- a/sound/soc/omap/omap-twl4030.c +++ b/sound/soc/omap/omap-twl4030.c @@ -159,9 +159,8 @@ static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = rtd->card; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = &card->dapm; struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); int ret = 0; diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index c2ddf0fbf..3bebfb1d3 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), @@ -455,50 +451,36 @@ static int rx51_soc_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, pdata); pdata->tvout_selection_gpio = devm_gpiod_get(card->dev, - "tvout-selection"); + "tvout-selection", + GPIOD_OUT_LOW); if (IS_ERR(pdata->tvout_selection_gpio)) { dev_err(card->dev, "could not get tvout selection gpio\n"); return PTR_ERR(pdata->tvout_selection_gpio); } - err = gpiod_direction_output(pdata->tvout_selection_gpio, 0); - if (err) { - dev_err(card->dev, "could not setup tvout selection gpio\n"); - return err; - } - pdata->jack_detection_gpio = devm_gpiod_get(card->dev, - "jack-detection"); + "jack-detection", + GPIOD_ASIS); if (IS_ERR(pdata->jack_detection_gpio)) { dev_err(card->dev, "could not get jack detection gpio\n"); return PTR_ERR(pdata->jack_detection_gpio); } - pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch"); + pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch", + GPIOD_OUT_HIGH); if (IS_ERR(pdata->eci_sw_gpio)) { dev_err(card->dev, "could not get eci switch gpio\n"); return PTR_ERR(pdata->eci_sw_gpio); } - err = gpiod_direction_output(pdata->eci_sw_gpio, 1); - if (err) { - dev_err(card->dev, "could not setup eci switch gpio\n"); - return err; - } - pdata->speaker_amp_gpio = devm_gpiod_get(card->dev, - "speaker-amplifier"); + "speaker-amplifier", + GPIOD_OUT_LOW); if (IS_ERR(pdata->speaker_amp_gpio)) { dev_err(card->dev, "could not get speaker enable gpio\n"); return PTR_ERR(pdata->speaker_amp_gpio); } - err = gpiod_direction_output(pdata->speaker_amp_gpio, 0); - if (err) { - dev_err(card->dev, "could not setup speaker enable gpio\n"); - return err; - } - err = devm_snd_soc_register_card(card->dev, card); if (err) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", err); diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 79936e3e8..2b26318bc 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -45,29 +45,6 @@ static const struct snd_soc_dapm_route brownstone_audio_map[] = { {"MICBIAS1", NULL, "Main Mic"}, }; -static int brownstone_wm8994_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - /* set endpoints to not connected */ - snd_soc_dapm_nc_pin(dapm, "HPOUT2P"); - snd_soc_dapm_nc_pin(dapm, "HPOUT2N"); - snd_soc_dapm_nc_pin(dapm, "LINEOUT1N"); - snd_soc_dapm_nc_pin(dapm, "LINEOUT1P"); - snd_soc_dapm_nc_pin(dapm, "LINEOUT2N"); - snd_soc_dapm_nc_pin(dapm, "LINEOUT2P"); - snd_soc_dapm_nc_pin(dapm, "IN1LN"); - snd_soc_dapm_nc_pin(dapm, "IN1LP"); - snd_soc_dapm_nc_pin(dapm, "IN1RP"); - snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN"); - snd_soc_dapm_nc_pin(dapm, "IN2RN"); - snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP"); - snd_soc_dapm_nc_pin(dapm, "IN2LN"); - - return 0; -} - static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -115,7 +92,6 @@ static struct snd_soc_dai_link brownstone_wm8994_dai[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ops = &brownstone_ops, - .init = brownstone_wm8994_init, }, }; @@ -132,6 +108,7 @@ static struct snd_soc_card brownstone = { .num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets), .dapm_routes = brownstone_audio_map, .num_dapm_routes = ARRAY_SIZE(brownstone_audio_map), + .fully_routed = true, }; static int brownstone_probe(struct platform_device *pdev) diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 0fce8c420..80b457ac5 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -192,6 +192,7 @@ static int poodle_amp_event(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event), +SND_SOC_DAPM_MIC("Microphone", NULL), }; /* Corgi machine connections to the codec pins */ @@ -204,6 +205,8 @@ static const struct snd_soc_dapm_route poodle_audio_map[] = { /* speaker connected to LOUT, ROUT */ {"Ext Spk", NULL, "ROUT"}, {"Ext Spk", NULL, "LOUT"}, + + {"MICIN", NULL, "Microphone"}, }; static const char *jack_function[] = {"Off", "Headphone"}; @@ -220,20 +223,6 @@ static const struct snd_kcontrol_new wm8731_poodle_controls[] = { poodle_set_spk), }; -/* - * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device - */ -static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_nc_pin(dapm, "LLINEIN"); - snd_soc_dapm_nc_pin(dapm, "RLINEIN"); - - return 0; -} - /* poodle digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link poodle_dai = { .name = "WM8731", @@ -242,7 +231,6 @@ static struct snd_soc_dai_link poodle_dai = { .codec_dai_name = "wm8731-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm8731.0-001b", - .init = poodle_wm8731_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ops = &poodle_ops, @@ -261,6 +249,7 @@ static struct snd_soc_card poodle = { .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), .dapm_routes = poodle_audio_map, .num_dapm_routes = ARRAY_SIZE(poodle_audio_map), + .fully_routed = true, }; static int poodle_probe(struct platform_device *pdev) diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index cb49284e8..f59f56655 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -185,17 +185,6 @@ static const struct snd_kcontrol_new tosa_controls[] = { tosa_set_spk), }; -static int tosa_ac97_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_nc_pin(dapm, "OUT3"); - snd_soc_dapm_nc_pin(dapm, "MONOOUT"); - - return 0; -} - static struct snd_soc_dai_link tosa_dai[] = { { .name = "AC97", @@ -204,7 +193,6 @@ static struct snd_soc_dai_link tosa_dai[] = { .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", - .init = tosa_ac97_init, .ops = &tosa_ops, }, { @@ -230,6 +218,7 @@ static struct snd_soc_card tosa = { .num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets), .dapm_routes = audio_map, .num_dapm_routes = ARRAY_SIZE(audio_map), + .fully_routed = true, }; static int tosa_probe(struct platform_device *pdev) diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index bcbfbe830..990b1aa6d 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -132,16 +132,8 @@ static const struct snd_soc_dapm_route z2_audio_map[] = { */ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - /* NC codec pins */ - snd_soc_dapm_disable_pin(dapm, "LINPUT3"); - snd_soc_dapm_disable_pin(dapm, "RINPUT3"); - snd_soc_dapm_disable_pin(dapm, "OUT3"); - snd_soc_dapm_disable_pin(dapm, "MONO1"); - /* Jack detection API stuff */ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET, &hs_jack, hs_jack_pins, @@ -189,6 +181,7 @@ static struct snd_soc_card snd_soc_z2 = { .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets), .dapm_routes = z2_audio_map, .num_dapm_routes = ARRAY_SIZE(z2_audio_map), + .fully_routed = true, }; static struct platform_device *z2_snd_device; diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index b07f183fc..807fedfa1 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -12,12 +12,32 @@ config SND_SOC_LPASS_PLATFORM tristate select REGMAP_MMIO -config SND_SOC_STORM - tristate "ASoC I2S support for Storm boards" - depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST +config SND_SOC_LPASS_IPQ806X + tristate + depends on SND_SOC_QCOM + select SND_SOC_LPASS_CPU + select SND_SOC_LPASS_PLATFORM + +config SND_SOC_LPASS_APQ8016 + tristate + depends on SND_SOC_QCOM select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM + +config SND_SOC_STORM + tristate "ASoC I2S support for Storm boards" + depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST) + select SND_SOC_LPASS_IPQ806X select SND_SOC_MAX98357A 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 c5ce96c76..79e5c50a8 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -1,11 +1,17 @@ # Platform snd-soc-lpass-cpu-objs := lpass-cpu.o snd-soc-lpass-platform-objs := lpass-platform.o +snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o +snd-soc-lpass-apq8016-objs := lpass-apq8016.o obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o +obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o +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 000000000..1efdf0088 --- /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/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c new file mode 100644 index 000000000..94efc0102 --- /dev/null +++ b/sound/soc/qcom/lpass-apq8016.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2010-2011,2013-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. + * + * lpass-apq8016.c -- ALSA SoC CPU DAI driver for APQ8016 LPASS + * + */ + + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include <dt-bindings/sound/apq8016-lpass.h> +#include "lpass-lpaif-reg.h" +#include "lpass.h" + +static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { + [MI2S_PRIMARY] = { + .id = MI2S_PRIMARY, + .name = "Primary MI2S", + .playback = { + .stream_name = "Primary Playback", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, + [MI2S_SECONDARY] = { + .id = MI2S_SECONDARY, + .name = "Secondary MI2S", + .playback = { + .stream_name = "Secondary Playback", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, + [MI2S_TERTIARY] = { + .id = MI2S_TERTIARY, + .name = "Tertiary MI2S", + .capture = { + .stream_name = "Tertiary Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, + [MI2S_QUATERNARY] = { + .id = MI2S_QUATERNARY, + .name = "Quatenary MI2S", + .playback = { + .stream_name = "Quatenary Playback", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + }, + .capture = { + .stream_name = "Quatenary Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, +}; + +static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata) +{ + struct lpass_variant *v = drvdata->variant; + int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map, + v->rdma_channels); + + if (chan >= v->rdma_channels) + return -EBUSY; + + set_bit(chan, &drvdata->rdma_ch_bit_map); + + return chan; +} + +static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) +{ + clear_bit(chan, &drvdata->rdma_ch_bit_map); + + return 0; +} + +static int apq8016_lpass_init(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + drvdata->pcnoc_mport_clk = devm_clk_get(dev, "pcnoc-mport-clk"); + if (IS_ERR(drvdata->pcnoc_mport_clk)) { + dev_err(&pdev->dev, "%s() error getting pcnoc-mport-clk: %ld\n", + __func__, PTR_ERR(drvdata->pcnoc_mport_clk)); + return PTR_ERR(drvdata->pcnoc_mport_clk); + } + + ret = clk_prepare_enable(drvdata->pcnoc_mport_clk); + if (ret) { + dev_err(&pdev->dev, "%s() Error enabling pcnoc-mport-clk: %d\n", + __func__, ret); + return ret; + } + + drvdata->pcnoc_sway_clk = devm_clk_get(dev, "pcnoc-sway-clk"); + if (IS_ERR(drvdata->pcnoc_sway_clk)) { + dev_err(&pdev->dev, "%s() error getting pcnoc-sway-clk: %ld\n", + __func__, PTR_ERR(drvdata->pcnoc_sway_clk)); + return PTR_ERR(drvdata->pcnoc_sway_clk); + } + + ret = clk_prepare_enable(drvdata->pcnoc_sway_clk); + if (ret) { + dev_err(&pdev->dev, "%s() Error enabling pcnoc_sway_clk: %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int apq8016_lpass_exit(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + + clk_disable_unprepare(drvdata->pcnoc_mport_clk); + clk_disable_unprepare(drvdata->pcnoc_sway_clk); + + return 0; +} + + +static struct lpass_variant apq8016_data = { + .i2sctrl_reg_base = 0x1000, + .i2sctrl_reg_stride = 0x1000, + .i2s_ports = 4, + .irq_reg_base = 0x6000, + .irq_reg_stride = 0x1000, + .irq_ports = 3, + .rdma_reg_base = 0x8400, + .rdma_reg_stride = 0x1000, + .rdma_channels = 2, + .rdmactl_audif_start = 1, + .dai_driver = apq8016_lpass_cpu_dai_driver, + .num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver), + .init = apq8016_lpass_init, + .exit = apq8016_lpass_exit, + .alloc_dma_channel = apq8016_lpass_alloc_dma_channel, + .free_dma_channel = apq8016_lpass_free_dma_channel, +}; + +static const struct of_device_id apq8016_lpass_cpu_device_id[] = { + { .compatible = "qcom,lpass-cpu-apq8016", .data = &apq8016_data }, + {} +}; +MODULE_DEVICE_TABLE(of, apq8016_lpass_cpu_device_id); + +static struct platform_driver apq8016_lpass_cpu_platform_driver = { + .driver = { + .name = "apq8016-lpass-cpu", + .of_match_table = of_match_ptr(apq8016_lpass_cpu_device_id), + }, + .probe = asoc_qcom_lpass_cpu_platform_probe, + .remove = asoc_qcom_lpass_cpu_platform_remove, +}; +module_platform_driver(apq8016_lpass_cpu_platform_driver); + +MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index dc790abaa..23f3d59e6 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -14,21 +14,17 @@ */ #include <linux/clk.h> -#include <linux/compiler.h> -#include <linux/device.h> -#include <linux/err.h> -#include <linux/ioport.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <linux/regmap.h> #include <sound/soc.h> #include <sound/soc-dai.h> -#include "lpass-lpaif-ipq806x.h" +#include "lpass-lpaif-reg.h" #include "lpass.h" static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, @@ -37,7 +33,10 @@ static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; - ret = clk_set_rate(drvdata->mi2s_osr_clk, freq); + if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) + return 0; + + ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq); if (ret) dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n", __func__, freq, ret); @@ -51,18 +50,23 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; - ret = clk_prepare_enable(drvdata->mi2s_osr_clk); - if (ret) { - dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n", - __func__, ret); - return ret; + if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) { + ret = clk_prepare_enable( + drvdata->mi2s_osr_clk[dai->driver->id]); + if (ret) { + dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n", + __func__, ret); + return ret; + } } - ret = clk_prepare_enable(drvdata->mi2s_bit_clk); + ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]); if (ret) { dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n", __func__, ret); - clk_disable_unprepare(drvdata->mi2s_osr_clk); + if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) + clk_disable_unprepare( + drvdata->mi2s_osr_clk[dai->driver->id]); return ret; } @@ -74,8 +78,10 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); - clk_disable_unprepare(drvdata->mi2s_bit_clk); - clk_disable_unprepare(drvdata->mi2s_osr_clk); + clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); + + if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) + clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); } static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, @@ -142,14 +148,16 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, } ret = regmap_write(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval); + LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), + regval); if (ret) { dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); return ret; } - ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2); + ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id], + rate * bitwidth * 2); if (ret) { dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n", __func__, rate * bitwidth * 2, ret); @@ -166,7 +174,8 @@ static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream, int ret; ret = regmap_write(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); + LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), + 0); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); @@ -181,7 +190,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, int ret; ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), + LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", @@ -201,7 +210,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), + LPAIF_I2SCTL_REG(drvdata->variant, + dai->driver->id), LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); if (ret) @@ -212,7 +222,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), + LPAIF_I2SCTL_REG(drvdata->variant, + dai->driver->id), LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_DISABLE); if (ret) @@ -224,7 +235,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_dai_ops lpass_cpu_dai_ops = { +struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { .set_sysclk = lpass_cpu_daiops_set_sysclk, .startup = lpass_cpu_daiops_startup, .shutdown = lpass_cpu_daiops_shutdown, @@ -233,41 +244,23 @@ static struct snd_soc_dai_ops lpass_cpu_dai_ops = { .prepare = lpass_cpu_daiops_prepare, .trigger = lpass_cpu_daiops_trigger, }; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); -static int lpass_cpu_dai_probe(struct snd_soc_dai *dai) +int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai) { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; /* ensure audio hardware is disabled */ ret = regmap_write(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); + LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); return ret; } - -static struct snd_soc_dai_driver lpass_cpu_dai_driver = { - .playback = { - .stream_name = "lpass-cpu-playback", - .formats = SNDRV_PCM_FMTBIT_S16 | - SNDRV_PCM_FMTBIT_S24 | - SNDRV_PCM_FMTBIT_S32, - .rates = SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000, - .rate_min = 8000, - .rate_max = 96000, - .channels_min = 1, - .channels_max = 8, - }, - .probe = &lpass_cpu_dai_probe, - .ops = &lpass_cpu_dai_ops, -}; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe); static const struct snd_soc_component_driver lpass_cpu_comp_driver = { .name = "lpass-cpu", @@ -275,27 +268,29 @@ static const struct snd_soc_component_driver lpass_cpu_comp_driver = { static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) { + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; int i; - for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) - if (reg == LPAIF_I2SCTL_REG(i)) + for (i = 0; i < v->i2s_ports; ++i) + if (reg == LPAIF_I2SCTL_REG(v, i)) return true; - for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { - if (reg == LPAIF_IRQEN_REG(i)) + for (i = 0; i < v->irq_ports; ++i) { + if (reg == LPAIF_IRQEN_REG(v, i)) return true; - if (reg == LPAIF_IRQCLEAR_REG(i)) + if (reg == LPAIF_IRQCLEAR_REG(v, i)) return true; } - for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { - if (reg == LPAIF_RDMACTL_REG(i)) + for (i = 0; i < v->rdma_channels; ++i) { + if (reg == LPAIF_RDMACTL_REG(v, i)) return true; - if (reg == LPAIF_RDMABASE_REG(i)) + if (reg == LPAIF_RDMABASE_REG(v, i)) return true; - if (reg == LPAIF_RDMABUFF_REG(i)) + if (reg == LPAIF_RDMABUFF_REG(v, i)) return true; - if (reg == LPAIF_RDMAPER_REG(i)) + if (reg == LPAIF_RDMAPER_REG(v, i)) return true; } @@ -304,29 +299,31 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) { + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; int i; - for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) - if (reg == LPAIF_I2SCTL_REG(i)) + for (i = 0; i < v->i2s_ports; ++i) + if (reg == LPAIF_I2SCTL_REG(v, i)) return true; - for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { - if (reg == LPAIF_IRQEN_REG(i)) + for (i = 0; i < v->irq_ports; ++i) { + if (reg == LPAIF_IRQEN_REG(v, i)) return true; - if (reg == LPAIF_IRQSTAT_REG(i)) + if (reg == LPAIF_IRQSTAT_REG(v, i)) return true; } - for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { - if (reg == LPAIF_RDMACTL_REG(i)) + for (i = 0; i < v->rdma_channels; ++i) { + if (reg == LPAIF_RDMACTL_REG(v, i)) return true; - if (reg == LPAIF_RDMABASE_REG(i)) + if (reg == LPAIF_RDMABASE_REG(v, i)) return true; - if (reg == LPAIF_RDMABUFF_REG(i)) + if (reg == LPAIF_RDMABUFF_REG(v, i)) return true; - if (reg == LPAIF_RDMACURR_REG(i)) + if (reg == LPAIF_RDMACURR_REG(v, i)) return true; - if (reg == LPAIF_RDMAPER_REG(i)) + if (reg == LPAIF_RDMAPER_REG(v, i)) return true; } @@ -335,36 +332,41 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) { + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; int i; - for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) - if (reg == LPAIF_IRQSTAT_REG(i)) + for (i = 0; i < v->irq_ports; ++i) + if (reg == LPAIF_IRQSTAT_REG(v, i)) return true; - for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) - if (reg == LPAIF_RDMACURR_REG(i)) + for (i = 0; i < v->rdma_channels; ++i) + if (reg == LPAIF_RDMACURR_REG(v, i)) return true; return false; } -static const struct regmap_config lpass_cpu_regmap_config = { +static struct regmap_config lpass_cpu_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX), .writeable_reg = lpass_cpu_regmap_writeable, .readable_reg = lpass_cpu_regmap_readable, .volatile_reg = lpass_cpu_regmap_volatile, .cache_type = REGCACHE_FLAT, }; -static int lpass_cpu_platform_probe(struct platform_device *pdev) +int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) { struct lpass_data *drvdata; struct device_node *dsp_of_node; struct resource *res; - int ret; + struct lpass_variant *variant; + struct device *dev = &pdev->dev; + const struct of_device_id *match; + char clk_name[16]; + int ret, i, dai_id; dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0); if (dsp_of_node) { @@ -379,11 +381,14 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, drvdata); + match = of_match_device(dev->driver->of_match_table, dev); + if (!match || !match->data) + return -EINVAL; + + drvdata->variant = (struct lpass_variant *)match->data; + variant = drvdata->variant; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); - if (!res) { - dev_err(&pdev->dev, "%s() error getting resource\n", __func__); - return -ENODEV; - } drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR((void const __force *)drvdata->lpaif)) { @@ -393,6 +398,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev) return PTR_ERR((void const __force *)drvdata->lpaif); } + lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant, + variant->rdma_channels); + drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, &lpass_cpu_regmap_config); if (IS_ERR(drvdata->lpaif_map)) { @@ -401,18 +409,38 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev) return PTR_ERR(drvdata->lpaif_map); } - drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk"); - if (IS_ERR(drvdata->mi2s_osr_clk)) { - dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n", - __func__, PTR_ERR(drvdata->mi2s_osr_clk)); - return PTR_ERR(drvdata->mi2s_osr_clk); - } - - drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk"); - if (IS_ERR(drvdata->mi2s_bit_clk)) { - dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n", - __func__, PTR_ERR(drvdata->mi2s_bit_clk)); - return PTR_ERR(drvdata->mi2s_bit_clk); + if (variant->init) + variant->init(pdev); + + for (i = 0; i < variant->num_dai; i++) { + dai_id = variant->dai_driver[i].id; + if (variant->num_dai > 1) + sprintf(clk_name, "mi2s-osr-clk%d", i); + else + sprintf(clk_name, "mi2s-osr-clk"); + + drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev, + clk_name); + if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) { + dev_warn(&pdev->dev, + "%s() error getting mi2s-osr-clk: %ld\n", + __func__, + PTR_ERR(drvdata->mi2s_osr_clk[dai_id])); + } + + if (variant->num_dai > 1) + sprintf(clk_name, "mi2s-bit-clk%d", i); + else + sprintf(clk_name, "mi2s-bit-clk"); + + drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev, + clk_name); + if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) { + dev_err(&pdev->dev, + "%s() error getting mi2s-bit-clk: %ld\n", + __func__, PTR_ERR(drvdata->mi2s_bit_clk[i])); + return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]); + } } drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk"); @@ -439,7 +467,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_component(&pdev->dev, - &lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1); + &lpass_cpu_comp_driver, + variant->dai_driver, + variant->num_dai); if (ret) { dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n", __func__, ret); @@ -459,33 +489,17 @@ err_clk: clk_disable_unprepare(drvdata->ahbix_clk); return ret; } +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe); -static int lpass_cpu_platform_remove(struct platform_device *pdev) +int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev) { struct lpass_data *drvdata = platform_get_drvdata(pdev); + if (drvdata->variant->exit) + drvdata->variant->exit(pdev); + clk_disable_unprepare(drvdata->ahbix_clk); return 0; } - -#ifdef CONFIG_OF -static const struct of_device_id lpass_cpu_device_id[] = { - { .compatible = "qcom,lpass-cpu" }, - {} -}; -MODULE_DEVICE_TABLE(of, lpass_cpu_device_id); -#endif - -static struct platform_driver lpass_cpu_platform_driver = { - .driver = { - .name = "lpass-cpu", - .of_match_table = of_match_ptr(lpass_cpu_device_id), - }, - .probe = lpass_cpu_platform_probe, - .remove = lpass_cpu_platform_remove, -}; -module_platform_driver(lpass_cpu_platform_driver); - -MODULE_DESCRIPTION("QTi LPASS CPU Driver"); -MODULE_LICENSE("GPL v2"); +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove); diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c new file mode 100644 index 000000000..7356d3a76 --- /dev/null +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2010-2011,2013-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. + * + * lpass-ipq806x.c -- ALSA SoC CPU DAI driver for QTi LPASS + * Splited out the IPQ8064 soc specific from lpass-cpu.c + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "lpass-lpaif-reg.h" +#include "lpass.h" + +enum lpaif_i2s_ports { + IPQ806X_LPAIF_I2S_PORT_CODEC_SPK, + IPQ806X_LPAIF_I2S_PORT_CODEC_MIC, + IPQ806X_LPAIF_I2S_PORT_SEC_SPK, + IPQ806X_LPAIF_I2S_PORT_SEC_MIC, + IPQ806X_LPAIF_I2S_PORT_MI2S, +}; + +enum lpaif_dma_channels { + IPQ806X_LPAIF_RDMA_CHAN_MI2S, + IPQ806X_LPAIF_RDMA_CHAN_PCM0, + IPQ806X_LPAIF_RDMA_CHAN_PCM1, +}; + +static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { + .id = IPQ806X_LPAIF_I2S_PORT_MI2S, + .playback = { + .stream_name = "lpass-cpu-playback", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, +}; + +static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata) +{ + return IPQ806X_LPAIF_RDMA_CHAN_MI2S; +} + +static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) +{ + return 0; +} + +struct lpass_variant ipq806x_data = { + .i2sctrl_reg_base = 0x0010, + .i2sctrl_reg_stride = 0x04, + .i2s_ports = 5, + .irq_reg_base = 0x3000, + .irq_reg_stride = 0x1000, + .irq_ports = 3, + .rdma_reg_base = 0x6000, + .rdma_reg_stride = 0x1000, + .rdma_channels = 4, + .dai_driver = &ipq806x_lpass_cpu_dai_driver, + .num_dai = 1, + .alloc_dma_channel = ipq806x_lpass_alloc_dma_channel, + .free_dma_channel = ipq806x_lpass_free_dma_channel, +}; + +static const struct of_device_id ipq806x_lpass_cpu_device_id[] = { + { .compatible = "qcom,lpass-cpu", .data = &ipq806x_data }, + {} +}; +MODULE_DEVICE_TABLE(of, ipq806x_lpass_cpu_device_id); + +static struct platform_driver ipq806x_lpass_cpu_platform_driver = { + .driver = { + .name = "lpass-cpu", + .of_match_table = of_match_ptr(ipq806x_lpass_cpu_device_id), + }, + .probe = asoc_qcom_lpass_cpu_platform_probe, + .remove = asoc_qcom_lpass_cpu_platform_remove, +}; +module_platform_driver(ipq806x_lpass_cpu_platform_driver); + +MODULE_DESCRIPTION("QTi LPASS CPU Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/lpass-lpaif-ipq806x.h b/sound/soc/qcom/lpass-lpaif-reg.h index dc423b888..95e22f131 100644 --- a/sound/soc/qcom/lpass-lpaif-ipq806x.h +++ b/sound/soc/qcom/lpass-lpaif-reg.h @@ -9,37 +9,17 @@ * 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. - * - * lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS */ -#ifndef __LPASS_LPAIF_H__ -#define __LPASS_LPAIF_H__ - -#define LPAIF_BANK_OFFSET 0x1000 +#ifndef __LPASS_LPAIF_REG_H__ +#define __LPASS_LPAIF_REG_H__ /* LPAIF I2S */ -#define LPAIF_I2SCTL_REG_BASE 0x0010 -#define LPAIF_I2SCTL_REG_STRIDE 0x4 -#define LPAIF_I2SCTL_REG_ADDR(addr, port) \ - (LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port))) - -enum lpaif_i2s_ports { - LPAIF_I2S_PORT_MIN = 0, - - LPAIF_I2S_PORT_CODEC_SPK = 0, - LPAIF_I2S_PORT_CODEC_MIC = 1, - LPAIF_I2S_PORT_SEC_SPK = 2, - LPAIF_I2S_PORT_SEC_MIC = 3, - LPAIF_I2S_PORT_MI2S = 4, - - LPAIF_I2S_PORT_MAX = 4, - LPAIF_I2S_PORT_NUM = 5, -}; - -#define LPAIF_I2SCTL_REG(port) LPAIF_I2SCTL_REG_ADDR(0x0, (port)) +#define LPAIF_I2SCTL_REG_ADDR(v, addr, port) \ + (v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port)) +#define LPAIF_I2SCTL_REG(v, port) LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port)) #define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000 #define LPAIF_I2SCTL_LOOPBACK_SHIFT 15 #define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT) @@ -79,55 +59,36 @@ enum lpaif_i2s_ports { #define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT) /* LPAIF IRQ */ +#define LPAIF_IRQ_REG_ADDR(v, addr, port) \ + (v->irq_reg_base + (addr) + v->irq_reg_stride * (port)) -#define LPAIF_IRQ_REG_BASE 0x3000 -#define LPAIF_IRQ_REG_STRIDE 0x1000 -#define LPAIF_IRQ_REG_ADDR(addr, port) \ - (LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port))) - -enum lpaif_irq_ports { - LPAIF_IRQ_PORT_MIN = 0, +#define LPAIF_IRQ_PORT_HOST 0 - LPAIF_IRQ_PORT_HOST = 0, - LPAIF_IRQ_PORT_ADSP = 1, - - LPAIF_IRQ_PORT_MAX = 2, - LPAIF_IRQ_PORT_NUM = 3, -}; - -#define LPAIF_IRQEN_REG(port) LPAIF_IRQ_REG_ADDR(0x0, (port)) -#define LPAIF_IRQSTAT_REG(port) LPAIF_IRQ_REG_ADDR(0x4, (port)) -#define LPAIF_IRQCLEAR_REG(port) LPAIF_IRQ_REG_ADDR(0xC, (port)) +#define LPAIF_IRQEN_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x0, (port)) +#define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port)) +#define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port)) #define LPAIF_IRQ_BITSTRIDE 3 + #define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan))) #define LPAIF_IRQ_XRUN(chan) (2 << (LPAIF_IRQ_BITSTRIDE * (chan))) #define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan))) + #define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan))) /* LPAIF DMA */ -#define LPAIF_RDMA_REG_BASE 0x6000 -#define LPAIF_RDMA_REG_STRIDE 0x1000 -#define LPAIF_RDMA_REG_ADDR(addr, chan) \ - (LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan))) - -enum lpaif_dma_channels { - LPAIF_RDMA_CHAN_MIN = 0, - - LPAIF_RDMA_CHAN_MI2S = 0, - LPAIF_RDMA_CHAN_PCM0 = 1, - LPAIF_RDMA_CHAN_PCM1 = 2, +#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \ + (v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan)) - LPAIF_RDMA_CHAN_MAX = 4, - LPAIF_RDMA_CHAN_NUM = 5, -}; +#define LPAIF_RDMACTL_AUDINTF(id) (id << LPAIF_RDMACTL_AUDINTF_SHIFT) -#define LPAIF_RDMACTL_REG(chan) LPAIF_RDMA_REG_ADDR(0x00, (chan)) -#define LPAIF_RDMABASE_REG(chan) LPAIF_RDMA_REG_ADDR(0x04, (chan)) -#define LPAIF_RDMABUFF_REG(chan) LPAIF_RDMA_REG_ADDR(0x08, (chan)) -#define LPAIF_RDMACURR_REG(chan) LPAIF_RDMA_REG_ADDR(0x0C, (chan)) -#define LPAIF_RDMAPER_REG(chan) LPAIF_RDMA_REG_ADDR(0x10, (chan)) +#define LPAIF_RDMACTL_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x00, (chan)) +#define LPAIF_RDMABASE_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x04, (chan)) +#define LPAIF_RDMABUFF_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x08, (chan)) +#define LPAIF_RDMACURR_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x0C, (chan)) +#define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan)) +#define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan)) #define LPAIF_RDMACTL_BURSTEN_MASK 0x800 #define LPAIF_RDMACTL_BURSTEN_SHIFT 11 @@ -145,13 +106,6 @@ enum lpaif_dma_channels { #define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0 #define LPAIF_RDMACTL_AUDINTF_SHIFT 4 -#define LPAIF_RDMACTL_AUDINTF_NONE (0 << LPAIF_RDMACTL_AUDINTF_SHIFT) -#define LPAIF_RDMACTL_AUDINTF_CODEC (1 << LPAIF_RDMACTL_AUDINTF_SHIFT) -#define LPAIF_RDMACTL_AUDINTF_PCM (2 << LPAIF_RDMACTL_AUDINTF_SHIFT) -#define LPAIF_RDMACTL_AUDINTF_SEC_I2S (3 << LPAIF_RDMACTL_AUDINTF_SHIFT) -#define LPAIF_RDMACTL_AUDINTF_MI2S (4 << LPAIF_RDMACTL_AUDINTF_SHIFT) -#define LPAIF_RDMACTL_AUDINTF_HDMI (5 << LPAIF_RDMACTL_AUDINTF_SHIFT) -#define LPAIF_RDMACTL_AUDINTF_SEC_PCM (7 << LPAIF_RDMACTL_AUDINTF_SHIFT) #define LPAIF_RDMACTL_FIFOWM_MASK 0x00E #define LPAIF_RDMACTL_FIFOWM_SHIFT 1 @@ -169,4 +123,4 @@ enum lpaif_dma_channels { #define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT) #define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT) -#endif /* __LPASS_LPAIF_H__ */ +#endif /* __LPASS_LPAIF_REG_H__ */ diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 2fa6280df..79688aa19 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -13,23 +13,22 @@ * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS */ -#include <linux/compiler.h> -#include <linux/device.h> #include <linux/dma-mapping.h> -#include <linux/err.h> #include <linux/export.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/io.h> #include <linux/platform_device.h> -#include <sound/memalloc.h> -#include <sound/pcm.h> #include <sound/pcm_params.h> #include <linux/regmap.h> #include <sound/soc.h> -#include "lpass-lpaif-ipq806x.h" +#include "lpass-lpaif-reg.h" #include "lpass.h" +struct lpass_pcm_data { + int rdma_ch; + int i2s_port; +}; + #define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024) #define LPASS_PLATFORM_PERIODS 2 @@ -84,13 +83,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime); struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); + struct lpass_variant *v = drvdata->variant; snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int regval; int bitwidth; - int ret; + int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start; bitwidth = snd_pcm_format_width(format); if (bitwidth < 0) { @@ -100,7 +101,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, } regval = LPAIF_RDMACTL_BURSTEN_INCR4 | - LPAIF_RDMACTL_AUDINTF_MI2S | + LPAIF_RDMACTL_AUDINTF(rdma_port) | LPAIF_RDMACTL_FIFOWM_8; switch (bitwidth) { @@ -156,7 +157,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval); + LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -169,12 +170,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime); struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); + struct lpass_variant *v = drvdata->variant; int ret; ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0); + LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0); if (ret) dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -186,12 +189,14 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime); struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); - int ret; + struct lpass_variant *v = drvdata->variant; + int ret, ch = pcm_data->rdma_ch; ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMABASE_REG(v, ch), runtime->dma_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", @@ -200,7 +205,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMABUFF_REG(v, ch), (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", @@ -209,7 +214,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMAPER_REG(v, ch), (snd_pcm_lib_period_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", @@ -218,7 +223,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMACTL_REG(v, ch), LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", @@ -233,9 +238,11 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime); struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); - int ret; + struct lpass_variant *v = drvdata->variant; + int ret, ch = pcm_data->rdma_ch; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -243,8 +250,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* clear status before enabling interrupts */ ret = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S)); + LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_ALL(ch)); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", __func__, ret); @@ -252,9 +259,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), - LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S)); + LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_ALL(ch), + LPAIF_IRQ_ALL(ch)); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", __func__, ret); @@ -262,7 +269,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMACTL_REG(v, ch), LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); if (ret) { @@ -275,7 +282,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMACTL_REG(v, ch), LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_OFF); if (ret) { @@ -285,8 +292,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0); + LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_ALL(ch), 0); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", __func__, ret); @@ -302,13 +309,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime); struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); + struct lpass_variant *v = drvdata->variant; unsigned int base_addr, curr_addr; - int ret; + int ret, ch = pcm_data->rdma_ch; ret = regmap_read(drvdata->lpaif_map, - LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr); + LPAIF_RDMABASE_REG(v, ch), &base_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", __func__, ret); @@ -316,7 +325,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( } ret = regmap_read(drvdata->lpaif_map, - LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr); + LPAIF_RDMACURR_REG(v, ch), &curr_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", __func__, ret); @@ -347,29 +356,20 @@ static struct snd_pcm_ops lpass_platform_pcm_ops = { .mmap = lpass_platform_pcmops_mmap, }; -static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) +static irqreturn_t lpass_dma_interrupt_handler( + struct snd_pcm_substream *substream, + struct lpass_data *drvdata, + int chan, u32 interrupts) { - struct snd_pcm_substream *substream = data; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct lpass_data *drvdata = - snd_soc_platform_get_drvdata(soc_runtime->platform); - unsigned int interrupts; + struct lpass_variant *v = drvdata->variant; irqreturn_t ret = IRQ_NONE; int rv; - rv = regmap_read(drvdata->lpaif_map, - LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts); - if (rv) { - dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n", - __func__, rv); - return IRQ_NONE; - } - interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S); - - if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) { + if (interrupts & LPAIF_IRQ_PER(chan)) { rv = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)); + LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_PER(chan)); if (rv) { dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", __func__, rv); @@ -379,10 +379,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) ret = IRQ_HANDLED; } - if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) { + if (interrupts & LPAIF_IRQ_XRUN(chan)) { rv = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)); + LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_XRUN(chan)); if (rv) { dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", __func__, rv); @@ -393,10 +393,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) ret = IRQ_HANDLED; } - if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) { + if (interrupts & LPAIF_IRQ_ERR(chan)) { rv = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)); + LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_ERR(chan)); if (rv) { dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", __func__, rv); @@ -410,6 +410,35 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) return ret; } +static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) +{ + struct lpass_data *drvdata = data; + struct lpass_variant *v = drvdata->variant; + unsigned int irqs; + int rv, chan; + + rv = regmap_read(drvdata->lpaif_map, + LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs); + if (rv) { + pr_err("%s() error reading from irqstat reg: %d\n", + __func__, rv); + return IRQ_NONE; + } + + /* Handle per channel interrupts */ + for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) { + if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) { + rv = lpass_dma_interrupt_handler( + drvdata->substream[chan], + drvdata, chan, irqs); + if (rv != IRQ_HANDLED) + return rv; + } + } + + return IRQ_HANDLED; +} + static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *soc_runtime) { @@ -448,9 +477,27 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) struct snd_pcm *pcm = soc_runtime->pcm; struct snd_pcm_substream *substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); + struct lpass_variant *v = drvdata->variant; int ret; + struct lpass_pcm_data *data; + + data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (v->alloc_dma_channel) + data->rdma_ch = v->alloc_dma_channel(drvdata); + + if (IS_ERR_VALUE(data->rdma_ch)) + return data->rdma_ch; + + drvdata->substream[data->rdma_ch] = substream; + data->i2s_port = cpu_dai->driver->id; + + snd_soc_pcm_set_drvdata(soc_runtime, data); soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32); soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask; @@ -459,29 +506,12 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) if (ret) return ret; - ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq, - lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING, - "lpass-irq-lpaif", substream); - if (ret) { - dev_err(soc_runtime->dev, "%s() irq request failed: %d\n", - __func__, ret); - goto err_buf; - } - - /* ensure audio hardware is disabled */ ret = regmap_write(drvdata->lpaif_map, - LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0); - if (ret) { - dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", - __func__, ret); - return ret; - } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0); + LPAIF_RDMACTL_REG(v, data->rdma_ch), 0); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); - return ret; + goto err_buf; } return 0; @@ -496,6 +526,15 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm) struct snd_pcm_substream *substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime); + struct lpass_variant *v = drvdata->variant; + + drvdata->substream[data->rdma_ch] = NULL; + + if (v->free_dma_channel) + v->free_dma_channel(drvdata, data->rdma_ch); lpass_platform_free_buffer(substream, soc_runtime); } @@ -509,6 +548,8 @@ static struct snd_soc_platform_driver lpass_platform_driver = { int asoc_qcom_lpass_platform_register(struct platform_device *pdev) { struct lpass_data *drvdata = platform_get_drvdata(pdev); + struct lpass_variant *v = drvdata->variant; + int ret; drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif"); if (drvdata->lpaif_irq < 0) { @@ -517,6 +558,25 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev) return -ENODEV; } + /* ensure audio hardware is disabled */ + ret = regmap_write(drvdata->lpaif_map, + LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0); + if (ret) { + dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n", + __func__, ret); + return ret; + } + + ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq, + lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING, + "lpass-irq-lpaif", drvdata); + if (ret) { + dev_err(&pdev->dev, "%s() irq request failed: %d\n", + __func__, ret); + return ret; + } + + return devm_snd_soc_register_platform(&pdev->dev, &lpass_platform_driver); } diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 5c99b3dac..d6e86c119 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -22,6 +22,8 @@ #include <linux/regmap.h> #define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 +#define LPASS_MAX_MI2S_PORTS (8) +#define LPASS_MAX_DMA_CHANNELS (8) /* Both the CPU DAI and platform drivers will access this data */ struct lpass_data { @@ -30,10 +32,10 @@ struct lpass_data { struct clk *ahbix_clk; /* MI2S system clock */ - struct clk *mi2s_osr_clk; + struct clk *mi2s_osr_clk[LPASS_MAX_MI2S_PORTS]; /* MI2S bit clock (derived from system clock by a divider */ - struct clk *mi2s_bit_clk; + struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS]; /* low-power audio interface (LPAIF) registers */ void __iomem *lpaif; @@ -43,9 +45,54 @@ struct lpass_data { /* interrupts from the low-power audio interface (LPAIF) */ int lpaif_irq; + + /* SOC specific variations in the LPASS IP integration */ + struct lpass_variant *variant; + + /* bit map to keep track of static channel allocations */ + unsigned long rdma_ch_bit_map; + + /* used it for handling interrupt per dma channel */ + struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS]; + + /* 8016 specific */ + struct clk *pcnoc_mport_clk; + struct clk *pcnoc_sway_clk; +}; + +/* Vairant data per each SOC */ +struct lpass_variant { + u32 i2sctrl_reg_base; + u32 i2sctrl_reg_stride; + u32 i2s_ports; + u32 irq_reg_base; + u32 irq_reg_stride; + u32 irq_ports; + u32 rdma_reg_base; + u32 rdma_reg_stride; + u32 rdma_channels; + + /** + * on SOCs like APQ8016 the channel control bits start + * at different offset to ipq806x + **/ + u32 rdmactl_audif_start; + /* SOC specific intialization like clocks */ + int (*init)(struct platform_device *pdev); + int (*exit)(struct platform_device *pdev); + int (*alloc_dma_channel)(struct lpass_data *data); + int (*free_dma_channel)(struct lpass_data *data, int ch); + + /* SOC specific dais */ + struct snd_soc_dai_driver *dai_driver; + int num_dai; }; /* register the platform driver from the CPU DAI driver */ int asoc_qcom_lpass_platform_register(struct platform_device *); +int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev); +int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev); +int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai); +extern struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops; #endif /* __LPASS_H__ */ diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c index b8bd29619..2d833bffd 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/samsung/Kconfig b/sound/soc/samsung/Kconfig index 0632a3685..3744c9ed5 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -174,7 +174,8 @@ config SND_SOC_SMDK_WM8994_PCM config SND_SOC_SPEYSIDE tristate "Audio support for Wolfson Speyside" - depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C && SPI_MASTER + depends on SND_SOC_SAMSUNG && I2C && SPI_MASTER + depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select SND_SOC_WM8996 select SND_SOC_WM9081 @@ -183,13 +184,15 @@ config SND_SOC_SPEYSIDE config SND_SOC_TOBERMORY tristate "Audio support for Wolfson Tobermory" - depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && INPUT && I2C + depends on SND_SOC_SAMSUNG && INPUT && I2C + depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select SND_SOC_WM8962 config SND_SOC_BELLS tristate "Audio support for Wolfson Bells" - depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && MFD_ARIZONA && I2C && SPI_MASTER + depends on SND_SOC_SAMSUNG && MFD_ARIZONA && I2C && SPI_MASTER + depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select SND_SOC_WM5102 select SND_SOC_WM5110 @@ -199,14 +202,16 @@ config SND_SOC_BELLS config SND_SOC_LOWLAND tristate "Audio support for Wolfson Lowland" - depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C + depends on SND_SOC_SAMSUNG && I2C + depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select SND_SOC_WM5100 select SND_SOC_WM9081 config SND_SOC_LITTLEMILL tristate "Audio support for Wolfson Littlemill" - depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C + depends on SND_SOC_SAMSUNG && I2C + depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST select SND_SAMSUNG_I2S select MFD_WM8994 select SND_SOC_WM8994 diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index b92ab40d2..ea4ab374a 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1493,7 +1493,7 @@ static const struct samsung_i2s_dai_data samsung_dai_type_sec = { .dai_type = TYPE_SEC, }; -static struct platform_device_id samsung_i2s_driver_ids[] = { +static const struct platform_device_id samsung_i2s_driver_ids[] = { { .name = "samsung-i2s", .driver_data = (kernel_ulong_t)&i2sv3_dai_type, diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 5f1560931..0d0f58208 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -72,7 +72,7 @@ static int lowland_wm9081_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - snd_soc_dapm_nc_pin(&codec->dapm, "LINEOUT"); + snd_soc_dapm_nc_pin(&rtd->card->dapm, "LINEOUT"); /* At any time the WM9081 is active it will have this clock */ return snd_soc_codec_set_sysclk(codec, WM9081_SYSCLK_MCLK, 0, diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index dfbe2db1c..a0fe37fbe 100644 --- a/sound/soc/samsung/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -137,8 +137,7 @@ static const struct snd_soc_dapm_route audio_map[] = { static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = &rtd->card->dapm; int err = 0; /* set endpoints to not connected */ @@ -147,9 +146,6 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_nc_pin(dapm, "OUT3"); snd_soc_dapm_nc_pin(dapm, "ROUT1"); - /* set endpoints to default off mode */ - snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); - /* Headphone jack detection */ err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, &smartq_jack, diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index d38595fbd..ff57b192d 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -86,8 +86,7 @@ static struct snd_soc_ops smdk_ops = { static int smdk_wm8994_init_paiftx(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_dapm_context *dapm = &rtd->card->dapm; /* Other pins NC */ snd_soc_dapm_nc_pin(dapm, "HPOUT2P"); diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 2dcb988bd..d1ae21c5e 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -123,7 +123,7 @@ static void speyside_set_polarity(struct snd_soc_codec *codec, gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity); /* Re-run DAPM to make sure we're using the correct mic bias */ - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); } static int speyside_wm0010_init(struct snd_soc_pcm_runtime *rtd) diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c index 82f582344..672bcd4c2 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 9f48d75fa..f1e592065 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,13 +173,41 @@ void rsnd_mod_quit(struct rsnd_mod *mod) clk_unprepare(mod->clk); } +void rsnd_mod_interrupt(struct rsnd_mod *mod, + void (*callback)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io)) +{ + 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; +} + /* * 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; @@ -199,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...) \ ({ \ @@ -228,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; \ } \ @@ -252,7 +288,6 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, } io->mod[mod->type] = mod; - mod->io = io; return 0; } @@ -260,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; } @@ -272,9 +306,10 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id) return priv->rdai + id; } +#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai) static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) { - struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + struct rsnd_priv *priv = rsnd_dai_to_priv(dai); return rsnd_rdai_get(priv, dai->id); } @@ -293,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; @@ -310,11 +345,27 @@ 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; } -static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, +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, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -326,8 +377,11 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, runtime->channels * samples_to_bytes(runtime, 1); io->next_period_byte = io->byte_per_period; +} - return 0; +static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io) +{ + io->substream = NULL; } static @@ -351,20 +405,18 @@ struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai, static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + struct rsnd_priv *priv = rsnd_dai_to_priv(dai); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io)); int ret; unsigned long flags; - rsnd_lock(priv, flags); + spin_lock_irqsave(&priv->lock, flags); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - ret = rsnd_dai_stream_init(io, substream); - if (ret < 0) - goto dai_trigger_end; + rsnd_dai_stream_init(io, substream); ret = rsnd_platform_call(priv, dai, start, ssi_id); if (ret < 0) @@ -390,13 +442,15 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, ret = rsnd_platform_call(priv, dai, stop, ssi_id); if (ret < 0) goto dai_trigger_end; + + rsnd_dai_stream_quit(io); break; default: ret = -EINVAL; } dai_trigger_end: - rsnd_unlock(priv, flags); + spin_unlock_irqrestore(&priv->lock, flags); return ret; } @@ -822,23 +876,27 @@ 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; struct snd_kcontrol *kctrl; struct snd_kcontrol_new knew = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = name, .info = rsnd_kctrl_info, + .index = rtd - soc_card->rtd, .get = rsnd_kctrl_get, .put = rsnd_kctrl_put, .private_value = (unsigned long)cfg, @@ -858,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; } @@ -868,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) { @@ -905,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 144308f15..d306e298c 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 e5fcb062a..36fc020cb 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 4e6de6804..09fcc54a8 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,21 +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); -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 @@ -328,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]; @@ -354,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); /* @@ -449,8 +475,6 @@ struct rsnd_priv { #define rsnd_priv_to_pdev(priv) ((priv)->pdev) #define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev)) #define rsnd_priv_to_info(priv) ((priv)->info) -#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) -#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) /* * rsnd_kctrl @@ -460,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; }; @@ -480,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); @@ -512,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); @@ -530,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 a68517afe..84e935711 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,30 +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; - } 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) @@ -246,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); @@ -420,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) @@ -451,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; @@ -461,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 3beb32eb4..c61c17180 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,13 +676,16 @@ 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_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - if (!io) - return IRQ_NONE; + spin_lock(&priv->lock); + + /* ignore all cases if not working */ + if (!rsnd_io_is_working(io)) + goto rsnd_src_interrupt_gen2_out; if (rsnd_src_error_record_gen2(mod)) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); @@ -688,22 +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; @@ -721,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; @@ -757,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) @@ -776,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); @@ -797,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); @@ -805,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; @@ -821,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; @@ -833,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) @@ -873,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; @@ -907,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", @@ -916,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", @@ -1041,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 7bb9c087f..2fbe59f7f 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -66,6 +66,7 @@ struct rsnd_ssi { u32 cr_own; u32 cr_clk; + int chan; int err; unsigned int usrcnt; }; @@ -80,16 +81,15 @@ struct rsnd_ssi { #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) #define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) -#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) +#define rsnd_ssi_parent(ssi) ((ssi)->parent) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) #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)) @@ -189,22 +189,26 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, rsnd_mod_hw_start(&ssi->mod); if (rsnd_rdai_is_clk_master(rdai)) { - if (rsnd_ssi_clk_from_parent(ssi)) - rsnd_ssi_hw_start(ssi->parent, io); + struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); + + if (ssi_parent) + rsnd_ssi_hw_start(ssi_parent, io); else rsnd_ssi_master_clk_start(ssi, io); } } - 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); @@ -221,16 +225,17 @@ 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; - if (0 == ssi->usrcnt) /* stop might be called without start */ + if (0 == ssi->usrcnt) { + dev_err(dev, "%s called without starting\n", __func__); return; + } ssi->usrcnt--; @@ -253,13 +258,17 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi) rsnd_ssi_status_check(&ssi->mod, IIRQ); if (rsnd_rdai_is_clk_master(rdai)) { - if (rsnd_ssi_clk_from_parent(ssi)) - rsnd_ssi_hw_stop(ssi->parent); + struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); + + if (ssi_parent) + rsnd_ssi_hw_stop(io, ssi_parent); else rsnd_ssi_master_clk_stop(ssi); } rsnd_mod_hw_stop(&ssi->mod); + + ssi->chan = 0; } dev_dbg(dev, "%s[%d] hw stopped\n", @@ -270,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; @@ -321,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); @@ -336,6 +346,37 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, return 0; } +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) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi); + int chan = params_channels(params); + + /* + * Already working. + * It will happen if SSI has parent/child connection. + */ + if (ssi->usrcnt) { + /* + * it is error if child <-> parent SSI uses + * different channels. + */ + if (ssi->chan != chan) + return -EIO; + } + + /* It will be removed on rsnd_ssi_hw_stop */ + ssi->chan = chan; + if (ssi_parent) + return rsnd_ssi_hw_params(&ssi_parent->mod, io, + substream, params); + + return 0; +} + static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) { /* under/over flow error */ @@ -348,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); @@ -363,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); @@ -371,24 +413,29 @@ 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 = rsnd_mod_read(mod, SSISR); + u32 status; + bool elapsed = false; + + spin_lock(&priv->lock); + + /* ignore all cases if not working */ + if (!rsnd_io_is_working(io)) + goto rsnd_ssi_interrupt_out; - if (!io) - return IRQ_NONE; + status = rsnd_mod_read(mod, SSISR); /* PIO only */ if (!is_dma && (status & DIRQ)) { @@ -406,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); /* @@ -419,15 +466,28 @@ 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"); } rsnd_ssi_record_error(ssi, status); +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; } @@ -435,6 +495,7 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) * 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); @@ -444,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; } @@ -456,9 +517,11 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .hw_params = rsnd_ssi_hw_params, }; 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); @@ -469,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); @@ -496,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); @@ -516,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"; @@ -565,6 +632,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .start = rsnd_ssi_dma_start, .stop = rsnd_ssi_dma_stop, .fallback = rsnd_ssi_fallback, + .hw_params = rsnd_ssi_hw_params, }; int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) @@ -598,7 +666,7 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); } -static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) +static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) { if (!rsnd_ssi_is_pin_sharing(&ssi->mod)) return; @@ -728,11 +796,11 @@ 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; - rsnd_ssi_parent_clk_setup(priv, ssi); + rsnd_ssi_parent_setup(priv, ssi); } return 0; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 23732523f..0e1e69c7a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -40,6 +40,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dpcm.h> +#include <sound/soc-topology.h> #include <sound/initval.h> #define CREATE_TRACE_POINTS @@ -92,30 +93,21 @@ static int format_register_str(struct snd_soc_codec *codec, int wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; int regsize = codec->driver->reg_word_size * 2; int ret; - char tmpbuf[len + 1]; - char regbuf[regsize + 1]; - - /* since tmpbuf is allocated on the stack, warn the callers if they - * try to abuse this function */ - WARN_ON(len > 63); /* +2 for ': ' and + 1 for '\n' */ if (wordsize + regsize + 2 + 1 != len) return -EINVAL; - ret = snd_soc_read(codec, reg); - if (ret < 0) { - memset(regbuf, 'X', regsize); - regbuf[regsize] = '\0'; - } else { - snprintf(regbuf, regsize + 1, "%.*x", regsize, ret); - } - - /* prepare the buffer */ - snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf); - /* copy it back to the caller without the '\0' */ - memcpy(buf, tmpbuf, len); + sprintf(buf, "%.*x: ", wordsize, reg); + buf += wordsize + 2; + ret = snd_soc_read(codec, reg); + if (ret < 0) + memset(buf, 'X', regsize); + else + sprintf(buf, "%.*x", regsize, ret); + buf[regsize] = '\n'; + /* no NUL-termination needed */ return 0; } @@ -750,23 +742,10 @@ static void soc_resume_deferred(struct work_struct *work) } list_for_each_entry(codec, &card->codec_dev_list, card_list) { - /* If the CODEC was idle over suspend then it will have been - * left with bias OFF or STANDBY and suspended so we must now - * resume. Otherwise the suspend was suppressed. - */ if (codec->suspended) { - switch (codec->dapm.bias_level) { - case SND_SOC_BIAS_STANDBY: - case SND_SOC_BIAS_OFF: - if (codec->driver->resume) - codec->driver->resume(codec); - codec->suspended = 0; - break; - default: - dev_dbg(codec->dev, - "ASoC: CODEC was on over suspend\n"); - break; - } + if (codec->driver->resume) + codec->driver->resume(codec); + codec->suspended = 0; } } @@ -904,12 +883,17 @@ static struct snd_soc_dai *snd_soc_find_dai( { struct snd_soc_component *component; struct snd_soc_dai *dai; + struct device_node *component_of_node; lockdep_assert_held(&client_mutex); /* Find CPU DAI from registered DAIs*/ list_for_each_entry(component, &component_list, list) { - if (dlc->of_node && component->dev->of_node != dlc->of_node) + component_of_node = component->dev->of_node; + if (!component_of_node && component->dev->parent) + component_of_node = component->dev->parent->of_node; + + if (dlc->of_node && component_of_node != dlc->of_node) continue; if (dlc->name && strcmp(component->name, dlc->name)) continue; @@ -1732,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); @@ -2435,6 +2420,7 @@ int snd_soc_register_card(struct snd_soc_card *card) card->rtd_aux[i].card = card; INIT_LIST_HEAD(&card->dapm_dirty); + INIT_LIST_HEAD(&card->dobj_list); card->instantiated = 0; mutex_init(&card->mutex); mutex_init(&card->dapm_mutex); @@ -2599,7 +2585,8 @@ static int snd_soc_register_dais(struct snd_soc_component *component, * the same naming style even though those DAIs are not * component-less anymore. */ - if (count == 1 && legacy_dai_naming) { + if (count == 1 && legacy_dai_naming && + (dai_drv[i].id == 0 || dai_drv[i].name == NULL)) { dai->name = fmt_single_name(dev, &dai->id); } else { dai->name = fmt_multiple_name(dev, &dai_drv[i]); @@ -2749,6 +2736,7 @@ static void snd_soc_component_add_unlocked(struct snd_soc_component *component) } list_add(&component->list, &component_list); + INIT_LIST_HEAD(&component->dobj_list); } static void snd_soc_component_add(struct snd_soc_component *component) @@ -2825,6 +2813,7 @@ void snd_soc_unregister_component(struct device *dev) return; found: + snd_soc_tplg_component_remove(cmpnt, SND_SOC_TPLG_INDEX_ALL); snd_soc_component_del_unlocked(cmpnt); mutex_unlock(&client_mutex); snd_soc_component_cleanup(cmpnt); @@ -3488,11 +3477,16 @@ static int snd_soc_get_dai_name(struct of_phandle_args *args, const char **dai_name) { struct snd_soc_component *pos; + struct device_node *component_of_node; int ret = -EPROBE_DEFER; mutex_lock(&client_mutex); list_for_each_entry(pos, &component_list, list) { - if (pos->dev->of_node != args->np) + component_of_node = pos->dev->of_node; + if (!component_of_node && pos->dev->parent) + component_of_node = pos->dev->parent->of_node; + + if (component_of_node != args->np) continue; if (pos->driver->of_xlate_dai_name) { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b6c12dccb..e0de8072c 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -52,10 +52,15 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, const char *control, int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink)); -static struct snd_soc_dapm_widget * + +struct snd_soc_dapm_widget * snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget); +struct snd_soc_dapm_widget * +snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget); + /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, @@ -70,6 +75,7 @@ static int dapm_up_seq[] = { [snd_soc_dapm_aif_out] = 4, [snd_soc_dapm_mic] = 5, [snd_soc_dapm_mux] = 6, + [snd_soc_dapm_demux] = 6, [snd_soc_dapm_dac] = 7, [snd_soc_dapm_switch] = 8, [snd_soc_dapm_mixer] = 8, @@ -100,6 +106,7 @@ static int dapm_down_seq[] = { [snd_soc_dapm_mic] = 7, [snd_soc_dapm_micbias] = 8, [snd_soc_dapm_mux] = 9, + [snd_soc_dapm_demux] = 9, [snd_soc_dapm_aif_in] = 10, [snd_soc_dapm_aif_out] = 10, [snd_soc_dapm_dai_in] = 10, @@ -308,14 +315,13 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, { struct dapm_kcontrol_data *data; struct soc_mixer_control *mc; + struct soc_enum *e; + const char *name; + int ret; data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - dev_err(widget->dapm->dev, - "ASoC: can't allocate kcontrol data for %s\n", - widget->name); + if (!data) return -ENOMEM; - } INIT_LIST_HEAD(&data->paths); @@ -328,6 +334,13 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, if (mc->autodisable) { struct snd_soc_dapm_widget template; + name = kasprintf(GFP_KERNEL, "%s %s", kcontrol->id.name, + "Autodisable"); + if (!name) { + ret = -ENOMEM; + goto err_data; + } + memset(&template, 0, sizeof(template)); template.reg = mc->reg; template.mask = (1 << fls(mc->max)) - 1; @@ -338,18 +351,57 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, template.off_val = 0; template.on_val = template.off_val; template.id = snd_soc_dapm_kcontrol; - template.name = kcontrol->id.name; + template.name = name; data->value = template.on_val; - data->widget = snd_soc_dapm_new_control(widget->dapm, + data->widget = + snd_soc_dapm_new_control_unlocked(widget->dapm, &template); + kfree(name); if (!data->widget) { - kfree(data); - return -ENOMEM; + ret = -ENOMEM; + goto err_data; } } break; + case snd_soc_dapm_demux: + case snd_soc_dapm_mux: + e = (struct soc_enum *)kcontrol->private_value; + + if (e->autodisable) { + struct snd_soc_dapm_widget template; + + name = kasprintf(GFP_KERNEL, "%s %s", kcontrol->id.name, + "Autodisable"); + if (!name) { + ret = -ENOMEM; + goto err_data; + } + + memset(&template, 0, sizeof(template)); + template.reg = e->reg; + template.mask = e->mask << e->shift_l; + template.shift = e->shift_l; + template.off_val = snd_soc_enum_item_to_val(e, 0); + template.on_val = template.off_val; + template.id = snd_soc_dapm_kcontrol; + template.name = name; + + data->value = template.on_val; + + data->widget = snd_soc_dapm_new_control_unlocked( + widget->dapm, &template); + kfree(name); + if (!data->widget) { + ret = -ENOMEM; + goto err_data; + } + + snd_soc_dapm_add_path(widget->dapm, data->widget, + widget, NULL, NULL); + } + break; default: break; } @@ -357,6 +409,10 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, kcontrol->private_data = data; return 0; + +err_data: + kfree(data); + return ret; } static void dapm_kcontrol_free(struct snd_kcontrol *kctl) @@ -405,11 +461,6 @@ static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); list_add_tail(&path->list_kcontrol, &data->paths); - - if (data->widget) { - snd_soc_dapm_add_path(data->widget->dapm, data->widget, - path->source, NULL, NULL); - } } static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol) @@ -525,6 +576,67 @@ static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm) snd_soc_component_async_complete(dapm->component); } +static struct snd_soc_dapm_widget * +dapm_wcache_lookup(struct snd_soc_dapm_wcache *wcache, const char *name) +{ + struct snd_soc_dapm_widget *w = wcache->widget; + struct list_head *wlist; + const int depth = 2; + int i = 0; + + if (w) { + wlist = &w->dapm->card->widgets; + + list_for_each_entry_from(w, wlist, list) { + if (!strcmp(name, w->name)) + return w; + + if (++i == depth) + break; + } + } + + return NULL; +} + +static inline void dapm_wcache_update(struct snd_soc_dapm_wcache *wcache, + struct snd_soc_dapm_widget *w) +{ + wcache->widget = w; +} + +/** + * snd_soc_dapm_force_bias_level() - Sets the DAPM bias level + * @dapm: The DAPM context for which to set the level + * @level: The level to set + * + * Forces the DAPM bias level to a specific state. It will call the bias level + * callback of DAPM context with the specified level. This will even happen if + * the context is already at the same level. Furthermore it will not go through + * the normal bias level sequencing, meaning any intermediate states between the + * current and the target state will not be entered. + * + * Note that the change in bias level is only temporary and the next time + * snd_soc_dapm_sync() is called the state will be set to the level as + * determined by the DAPM core. The function is mainly intended to be used to + * used during probe or resume from suspend to power up the device so + * initialization can be done, before the DAPM core takes over. + */ +int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + int ret = 0; + + if (dapm->set_bias_level) + ret = dapm->set_bias_level(dapm, level); + + if (ret == 0) + dapm->bias_level = level; + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_force_bias_level); + /** * snd_soc_dapm_set_bias_level - set the bias level for the system * @dapm: DAPM context @@ -547,10 +659,8 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, if (ret != 0) goto out; - if (dapm->set_bias_level) - ret = dapm->set_bias_level(dapm, level); - else if (!card || dapm != &card->dapm) - dapm->bias_level = level; + if (!card || dapm != &card->dapm) + ret = snd_soc_dapm_force_bias_level(dapm, level); if (ret != 0) goto out; @@ -565,9 +675,10 @@ out: /* connect mux widget to its interconnecting audio paths */ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_path *path, const char *control_name) + struct snd_soc_dapm_path *path, const char *control_name, + struct snd_soc_dapm_widget *w) { - const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0]; + const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0]; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, item; int i; @@ -707,6 +818,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, wname_in_long_name = false; kcname_in_long_name = true; break; + case snd_soc_dapm_demux: case snd_soc_dapm_mux: wname_in_long_name = true; kcname_in_long_name = false; @@ -777,6 +889,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) { int i, ret; struct snd_soc_dapm_path *path; + struct dapm_kcontrol_data *data; /* add kcontrol */ for (i = 0; i < w->num_kcontrols; i++) { @@ -786,16 +899,20 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) if (path->name != (char *)w->kcontrol_news[i].name) continue; - if (w->kcontrols[i]) { - dapm_kcontrol_add_path(w->kcontrols[i], path); - continue; + if (!w->kcontrols[i]) { + ret = dapm_create_or_share_mixmux_kcontrol(w, i); + if (ret < 0) + return ret; } - ret = dapm_create_or_share_mixmux_kcontrol(w, i); - if (ret < 0) - return ret; - dapm_kcontrol_add_path(w->kcontrols[i], path); + + data = snd_kcontrol_chip(w->kcontrols[i]); + if (data->widget) + snd_soc_dapm_add_path(data->widget->dapm, + data->widget, + path->source, + NULL, NULL); } } @@ -807,17 +924,32 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) { struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_dapm_path *path; + struct list_head *paths; + const char *type; int ret; + switch (w->id) { + case snd_soc_dapm_mux: + paths = &w->sources; + type = "mux"; + break; + case snd_soc_dapm_demux: + paths = &w->sinks; + type = "demux"; + break; + default: + return -EINVAL; + } + if (w->num_kcontrols != 1) { dev_err(dapm->dev, - "ASoC: mux %s has incorrect number of controls\n", + "ASoC: %s %s has incorrect number of controls\n", type, w->name); return -EINVAL; } - if (list_empty(&w->sources)) { - dev_err(dapm->dev, "ASoC: mux %s has no paths\n", w->name); + if (list_empty(paths)) { + dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name); return -EINVAL; } @@ -825,9 +957,16 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) if (ret < 0) return ret; - list_for_each_entry(path, &w->sources, list_sink) { - if (path->name) - dapm_kcontrol_add_path(w->kcontrols[0], path); + if (w->id == snd_soc_dapm_mux) { + list_for_each_entry(path, &w->sources, list_sink) { + if (path->name) + dapm_kcontrol_add_path(w->kcontrols[0], path); + } + } else { + list_for_each_entry(path, &w->sinks, list_source) { + if (path->name) + dapm_kcontrol_add_path(w->kcontrols[0], path); + } } return 0; @@ -2344,6 +2483,50 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) } } +static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink, + const char *control) +{ + bool dynamic_source = false; + bool dynamic_sink = false; + + if (!control) + return 0; + + switch (source->id) { + case snd_soc_dapm_demux: + dynamic_source = true; + break; + default: + break; + } + + switch (sink->id) { + case snd_soc_dapm_mux: + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + dynamic_sink = true; + break; + default: + break; + } + + if (dynamic_source && dynamic_sink) { + dev_err(dapm->dev, + "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n", + source->name, control, sink->name); + return -EINVAL; + } else if (!dynamic_source && !dynamic_sink) { + dev_err(dapm->dev, + "Control not supported for path %s -> [%s] -> %s\n", + source->name, control, sink->name); + return -EINVAL; + } + + return 0; +} + static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, const char *control, @@ -2374,6 +2557,10 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, return -EINVAL; } + ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control); + if (ret) + return ret; + path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); if (!path) return -ENOMEM; @@ -2393,10 +2580,19 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, if (control == NULL) { path->connect = 1; } else { - /* connect dynamic paths */ + switch (wsource->id) { + case snd_soc_dapm_demux: + ret = dapm_connect_mux(dapm, path, control, wsource); + if (ret) + goto err; + break; + default: + break; + } + switch (wsink->id) { case snd_soc_dapm_mux: - ret = dapm_connect_mux(dapm, path, control); + ret = dapm_connect_mux(dapm, path, control, wsink); if (ret != 0) goto err; break; @@ -2408,11 +2604,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, goto err; break; default: - dev_err(dapm->dev, - "Control not supported for path %s -> [%s] -> %s\n", - wsource->name, control, wsink->name); - ret = -EINVAL; - goto err; + break; } } @@ -2460,6 +2652,12 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, source = route->source; } + wsource = dapm_wcache_lookup(&dapm->path_source_cache, source); + wsink = dapm_wcache_lookup(&dapm->path_sink_cache, sink); + + if (wsink && wsource) + goto skip_search; + /* * find src and dest widgets over all widgets but favor a widget from * current DAPM context @@ -2467,14 +2665,20 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, list_for_each_entry(w, &dapm->card->widgets, list) { if (!wsink && !(strcmp(w->name, sink))) { wtsink = w; - if (w->dapm == dapm) + if (w->dapm == dapm) { wsink = w; + if (wsource) + break; + } continue; } if (!wsource && !(strcmp(w->name, source))) { wtsource = w; - if (w->dapm == dapm) + if (w->dapm == dapm) { wsource = w; + if (wsink) + break; + } } } /* use widget from another DAPM context if not found from this */ @@ -2494,6 +2698,10 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, return -ENODEV; } +skip_search: + dapm_wcache_update(&dapm->path_sink_cache, wsink); + dapm_wcache_update(&dapm->path_source_cache, wsource); + ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control, route->connected); if (ret) @@ -2745,6 +2953,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) dapm_new_mixer(w); break; case snd_soc_dapm_mux: + case snd_soc_dapm_demux: dapm_new_mux(w); break; case snd_soc_dapm_pga: @@ -2911,16 +3120,21 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_card *card = dapm->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val; - if (e->reg != SND_SOC_NOPM) { + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + if (e->reg != SND_SOC_NOPM && dapm_kcontrol_is_powered(kcontrol)) { int ret = soc_dapm_read(dapm, e->reg, ®_val); - if (ret) + if (ret) { + mutex_unlock(&card->dapm_mutex); return ret; + } } else { reg_val = dapm_kcontrol_get_value(kcontrol); } + mutex_unlock(&card->dapm_mutex); val = (reg_val >> e->shift_l) & e->mask; ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); @@ -2950,7 +3164,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = dapm->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int *item = ucontrol->value.enumerated.item; - unsigned int val, change; + unsigned int val, change, reg_change = 0; unsigned int mask; struct snd_soc_dapm_update update; int ret = 0; @@ -2969,19 +3183,20 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + change = dapm_kcontrol_set_value(kcontrol, val); + if (e->reg != SND_SOC_NOPM) - change = soc_dapm_test_bits(dapm, e->reg, mask, val); - else - change = dapm_kcontrol_set_value(kcontrol, val); + reg_change = soc_dapm_test_bits(dapm, e->reg, mask, val); - if (change) { - if (e->reg != SND_SOC_NOPM) { + if (change || reg_change) { + if (reg_change) { update.kcontrol = kcontrol; update.reg = e->reg; update.mask = mask; update.val = val; card->update = &update; } + change |= reg_change; ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); @@ -3062,8 +3277,25 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); -static struct snd_soc_dapm_widget * +struct snd_soc_dapm_widget * snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget) +{ + struct snd_soc_dapm_widget *w; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + w = snd_soc_dapm_new_control_unlocked(dapm, widget); + if (!w) + dev_err(dapm->dev, + "ASoC: Failed to create DAPM control %s\n", + widget->name); + + mutex_unlock(&dapm->card->dapm_mutex); + return w; +} + +struct snd_soc_dapm_widget * +snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget) { struct snd_soc_dapm_widget *w; @@ -3144,6 +3376,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, w->power_check = dapm_always_on_check_power; break; case snd_soc_dapm_mux: + case snd_soc_dapm_demux: case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: @@ -3177,7 +3410,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); INIT_LIST_HEAD(&w->dirty); - list_add(&w->list, &dapm->card->widgets); + list_add_tail(&w->list, &dapm->card->widgets); w->inputs = -1; w->outputs = -1; @@ -3207,7 +3440,7 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { - w = snd_soc_dapm_new_control(dapm, widget); + w = snd_soc_dapm_new_control_unlocked(dapm, widget); if (!w) { dev_err(dapm->dev, "ASoC: Failed to create DAPM control %s\n", @@ -3445,7 +3678,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); - w = snd_soc_dapm_new_control(&card->dapm, &template); + w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template); if (!w) { dev_err(card->dev, "ASoC: Failed to create %s widget\n", link_name); @@ -3496,7 +3729,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, dev_dbg(dai->dev, "ASoC: adding %s widget\n", template.name); - w = snd_soc_dapm_new_control(dapm, &template); + w = snd_soc_dapm_new_control_unlocked(dapm, &template); if (!w) { dev_err(dapm->dev, "ASoC: Failed to create %s widget\n", dai->driver->playback.stream_name); @@ -3515,7 +3748,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, dev_dbg(dai->dev, "ASoC: adding %s widget\n", template.name); - w = snd_soc_dapm_new_control(dapm, &template); + w = snd_soc_dapm_new_control_unlocked(dapm, &template); if (!w) { dev_err(dapm->dev, "ASoC: Failed to create %s widget\n", dai->driver->capture.stream_name); diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index c9917ca5d..6fd1906af 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -24,6 +24,12 @@ #include <sound/dmaengine_pcm.h> +/* + * The platforms dmaengine driver does not support reporting the amount of + * bytes that are still left to transfer. + */ +#define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(31) + struct dmaengine_pcm { struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1]; const struct snd_dmaengine_pcm_config *config; @@ -222,14 +228,18 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel( return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data); } -static bool dmaengine_pcm_can_report_residue(struct dma_chan *chan) +static bool dmaengine_pcm_can_report_residue(struct device *dev, + struct dma_chan *chan) { struct dma_slave_caps dma_caps; int ret; ret = dma_get_slave_caps(chan, &dma_caps); - if (ret != 0) - return true; + if (ret != 0) { + dev_warn(dev, "Failed to get DMA channel capabilities, falling back to period counting: %d\n", + ret); + return false; + } if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR) return false; @@ -289,14 +299,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd) if (ret) return ret; - /* - * This will only return false if we know for sure that at least - * one channel does not support residue reporting. If the DMA - * driver does not implement the slave_caps API we rely having - * the NO_RESIDUE flag set manually in case residue reporting is - * not supported. - */ - if (!dmaengine_pcm_can_report_residue(pcm->chan[i])) + if (!dmaengine_pcm_can_report_residue(dev, pcm->chan[i])) pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE; } diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 9f60c25c4..fbaa1bb41 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 @@ -315,8 +316,11 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, goto undo; } - if (gpios[i].gpiod_dev) { - /* GPIO descriptor */ + if (gpios[i].desc) { + /* Already have a GPIO descriptor. */ + goto got_gpio; + } else if (gpios[i].gpiod_dev) { + /* Get a GPIO descriptor */ gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev, gpios[i].name, gpios[i].idx, GPIOD_IN); @@ -344,7 +348,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, gpios[i].desc = gpio_to_desc(gpios[i].gpio); } - +got_gpio: INIT_DELAYED_WORK(&gpios[i].work, gpio_work); gpios[i].jack = jack; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 35fe58f4f..256b9c91a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1485,30 +1485,67 @@ unwind: } static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime, - struct snd_soc_pcm_stream *stream) + struct snd_soc_pcm_stream *stream, + u64 formats) { runtime->hw.rate_min = stream->rate_min; runtime->hw.rate_max = stream->rate_max; runtime->hw.channels_min = stream->channels_min; runtime->hw.channels_max = stream->channels_max; if (runtime->hw.formats) - runtime->hw.formats &= stream->formats; + runtime->hw.formats &= formats & stream->formats; else - runtime->hw.formats = stream->formats; + runtime->hw.formats = formats & stream->formats; runtime->hw.rates = stream->rates; } +static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct snd_soc_dpcm *dpcm; + u64 formats = ULLONG_MAX; + int stream = substream->stream; + + if (!fe->dai_link->dpcm_merged_format) + return formats; + + /* + * It returns merged BE codec format + * if FE want to use it (= dpcm_merged_format) + */ + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai_driver *codec_dai_drv; + struct snd_soc_pcm_stream *codec_stream; + int i; + + for (i = 0; i < be->num_codecs; i++) { + codec_dai_drv = be->codec_dais[i]->driver; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + codec_stream = &codec_dai_drv->playback; + else + codec_stream = &codec_dai_drv->capture; + + formats &= codec_stream->formats; + } + } + + return formats; +} + static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; + u64 format = dpcm_runtime_base_format(substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback, format); else - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture, format); } static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd); diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c new file mode 100644 index 000000000..31068b8f3 --- /dev/null +++ b/sound/soc/soc-topology.c @@ -0,0 +1,1850 @@ +/* + * soc-topology.c -- ALSA SoC Topology + * + * Copyright (C) 2012 Texas Instruments Inc. + * Copyright (C) 2015 Intel Corporation. + * + * Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> + * K, Mythri P <mythri.p.k@intel.com> + * Prusty, Subhransu S <subhransu.s.prusty@intel.com> + * B, Jayachandran <jayachandran.b@intel.com> + * Abdullah, Omair M <omair.m.abdullah@intel.com> + * Jin, Yao <yao.jin@intel.com> + * Lin, Mengdong <mengdong.lin@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; either version 2 of the License, or (at your + * option) any later version. + * + * Add support to read audio firmware topology alongside firmware text. The + * topology data can contain kcontrols, DAPM graphs, widgets, DAIs, DAI links, + * equalizers, firmware, coefficients etc. + * + * This file only manages the core ALSA and ASoC components, all other bespoke + * firmware topology data is passed to component drivers for bespoke handling. + */ + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/list.h> +#include <linux/firmware.h> +#include <linux/slab.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/soc-topology.h> +#include <sound/tlv.h> + +/* + * We make several passes over the data (since it wont necessarily be ordered) + * and process objects in the following order. This guarantees the component + * drivers will be ready with any vendor data before the mixers and DAPM objects + * are loaded (that may make use of the vendor data). + */ +#define SOC_TPLG_PASS_MANIFEST 0 +#define SOC_TPLG_PASS_VENDOR 1 +#define SOC_TPLG_PASS_MIXER 2 +#define SOC_TPLG_PASS_WIDGET 3 +#define SOC_TPLG_PASS_GRAPH 4 +#define SOC_TPLG_PASS_PINS 5 +#define SOC_TPLG_PASS_PCM_DAI 6 + +#define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST +#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PCM_DAI + +struct soc_tplg { + const struct firmware *fw; + + /* runtime FW parsing */ + const u8 *pos; /* read postion */ + const u8 *hdr_pos; /* header position */ + unsigned int pass; /* pass number */ + + /* component caller */ + struct device *dev; + struct snd_soc_component *comp; + u32 index; /* current block index */ + u32 req_index; /* required index, only loaded/free matching blocks */ + + /* kcontrol operations */ + const struct snd_soc_tplg_kcontrol_ops *io_ops; + int io_ops_count; + + /* optional fw loading callbacks to component drivers */ + struct snd_soc_tplg_ops *ops; +}; + +static int soc_tplg_process_headers(struct soc_tplg *tplg); +static void soc_tplg_complete(struct soc_tplg *tplg); +struct snd_soc_dapm_widget * +snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget); +struct snd_soc_dapm_widget * +snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget); + +/* check we dont overflow the data for this control chunk */ +static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size, + unsigned int count, size_t bytes, const char *elem_type) +{ + const u8 *end = tplg->pos + elem_size * count; + + if (end > tplg->fw->data + tplg->fw->size) { + dev_err(tplg->dev, "ASoC: %s overflow end of data\n", + elem_type); + return -EINVAL; + } + + /* check there is enough room in chunk for control. + extra bytes at the end of control are for vendor data here */ + if (elem_size * count > bytes) { + dev_err(tplg->dev, + "ASoC: %s count %d of size %zu is bigger than chunk %zu\n", + elem_type, count, elem_size, bytes); + return -EINVAL; + } + + return 0; +} + +static inline int soc_tplg_is_eof(struct soc_tplg *tplg) +{ + const u8 *end = tplg->hdr_pos; + + if (end >= tplg->fw->data + tplg->fw->size) + return 1; + return 0; +} + +static inline unsigned long soc_tplg_get_hdr_offset(struct soc_tplg *tplg) +{ + return (unsigned long)(tplg->hdr_pos - tplg->fw->data); +} + +static inline unsigned long soc_tplg_get_offset(struct soc_tplg *tplg) +{ + return (unsigned long)(tplg->pos - tplg->fw->data); +} + +/* mapping of Kcontrol types and associated operations. */ +static const struct snd_soc_tplg_kcontrol_ops io_ops[] = { + {SND_SOC_TPLG_CTL_VOLSW, snd_soc_get_volsw, + snd_soc_put_volsw, snd_soc_info_volsw}, + {SND_SOC_TPLG_CTL_VOLSW_SX, snd_soc_get_volsw_sx, + snd_soc_put_volsw_sx, NULL}, + {SND_SOC_TPLG_CTL_ENUM, snd_soc_get_enum_double, + snd_soc_put_enum_double, snd_soc_info_enum_double}, + {SND_SOC_TPLG_CTL_ENUM_VALUE, snd_soc_get_enum_double, + snd_soc_put_enum_double, NULL}, + {SND_SOC_TPLG_CTL_BYTES, snd_soc_bytes_get, + snd_soc_bytes_put, snd_soc_bytes_info}, + {SND_SOC_TPLG_CTL_RANGE, snd_soc_get_volsw_range, + snd_soc_put_volsw_range, snd_soc_info_volsw_range}, + {SND_SOC_TPLG_CTL_VOLSW_XR_SX, snd_soc_get_xr_sx, + snd_soc_put_xr_sx, snd_soc_info_xr_sx}, + {SND_SOC_TPLG_CTL_STROBE, snd_soc_get_strobe, + snd_soc_put_strobe, NULL}, + {SND_SOC_TPLG_DAPM_CTL_VOLSW, snd_soc_dapm_get_volsw, + snd_soc_dapm_put_volsw, snd_soc_info_volsw}, + {SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE, snd_soc_dapm_get_enum_double, + snd_soc_dapm_put_enum_double, snd_soc_info_enum_double}, + {SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT, snd_soc_dapm_get_enum_double, + snd_soc_dapm_put_enum_double, NULL}, + {SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE, snd_soc_dapm_get_enum_double, + snd_soc_dapm_put_enum_double, NULL}, + {SND_SOC_TPLG_DAPM_CTL_PIN, snd_soc_dapm_get_pin_switch, + snd_soc_dapm_put_pin_switch, snd_soc_dapm_info_pin_switch}, +}; + +struct soc_tplg_map { + int uid; + int kid; +}; + +/* mapping of widget types from UAPI IDs to kernel IDs */ +static const struct soc_tplg_map dapm_map[] = { + {SND_SOC_TPLG_DAPM_INPUT, snd_soc_dapm_input}, + {SND_SOC_TPLG_DAPM_OUTPUT, snd_soc_dapm_output}, + {SND_SOC_TPLG_DAPM_MUX, snd_soc_dapm_mux}, + {SND_SOC_TPLG_DAPM_MIXER, snd_soc_dapm_mixer}, + {SND_SOC_TPLG_DAPM_PGA, snd_soc_dapm_pga}, + {SND_SOC_TPLG_DAPM_OUT_DRV, snd_soc_dapm_out_drv}, + {SND_SOC_TPLG_DAPM_ADC, snd_soc_dapm_adc}, + {SND_SOC_TPLG_DAPM_DAC, snd_soc_dapm_dac}, + {SND_SOC_TPLG_DAPM_SWITCH, snd_soc_dapm_switch}, + {SND_SOC_TPLG_DAPM_PRE, snd_soc_dapm_pre}, + {SND_SOC_TPLG_DAPM_POST, snd_soc_dapm_post}, + {SND_SOC_TPLG_DAPM_AIF_IN, snd_soc_dapm_aif_in}, + {SND_SOC_TPLG_DAPM_AIF_OUT, snd_soc_dapm_aif_out}, + {SND_SOC_TPLG_DAPM_DAI_IN, snd_soc_dapm_dai_in}, + {SND_SOC_TPLG_DAPM_DAI_OUT, snd_soc_dapm_dai_out}, + {SND_SOC_TPLG_DAPM_DAI_LINK, snd_soc_dapm_dai_link}, +}; + +static int tplc_chan_get_reg(struct soc_tplg *tplg, + struct snd_soc_tplg_channel *chan, int map) +{ + int i; + + for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) { + if (chan[i].id == map) + return chan[i].reg; + } + + return -EINVAL; +} + +static int tplc_chan_get_shift(struct soc_tplg *tplg, + struct snd_soc_tplg_channel *chan, int map) +{ + int i; + + for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) { + if (chan[i].id == map) + return chan[i].shift; + } + + return -EINVAL; +} + +static int get_widget_id(int tplg_type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dapm_map); i++) { + if (tplg_type == dapm_map[i].uid) + return dapm_map[i].kid; + } + + return -EINVAL; +} + +static enum snd_soc_dobj_type get_dobj_mixer_type( + struct snd_soc_tplg_ctl_hdr *control_hdr) +{ + if (control_hdr == NULL) + return SND_SOC_DOBJ_NONE; + + switch (control_hdr->ops.info) { + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + case SND_SOC_TPLG_CTL_RANGE: + case SND_SOC_TPLG_CTL_STROBE: + return SND_SOC_DOBJ_MIXER; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + return SND_SOC_DOBJ_ENUM; + case SND_SOC_TPLG_CTL_BYTES: + return SND_SOC_DOBJ_BYTES; + default: + return SND_SOC_DOBJ_NONE; + } +} + +static enum snd_soc_dobj_type get_dobj_type(struct snd_soc_tplg_hdr *hdr, + struct snd_soc_tplg_ctl_hdr *control_hdr) +{ + switch (hdr->type) { + case SND_SOC_TPLG_TYPE_MIXER: + return get_dobj_mixer_type(control_hdr); + case SND_SOC_TPLG_TYPE_DAPM_GRAPH: + case SND_SOC_TPLG_TYPE_MANIFEST: + return SND_SOC_DOBJ_NONE; + case SND_SOC_TPLG_TYPE_DAPM_WIDGET: + return SND_SOC_DOBJ_WIDGET; + case SND_SOC_TPLG_TYPE_DAI_LINK: + return SND_SOC_DOBJ_DAI_LINK; + case SND_SOC_TPLG_TYPE_PCM: + return SND_SOC_DOBJ_PCM; + case SND_SOC_TPLG_TYPE_CODEC_LINK: + return SND_SOC_DOBJ_CODEC_LINK; + default: + return SND_SOC_DOBJ_NONE; + } +} + +static inline void soc_bind_err(struct soc_tplg *tplg, + struct snd_soc_tplg_ctl_hdr *hdr, int index) +{ + dev_err(tplg->dev, + "ASoC: invalid control type (g,p,i) %d:%d:%d index %d at 0x%lx\n", + hdr->ops.get, hdr->ops.put, hdr->ops.info, index, + soc_tplg_get_offset(tplg)); +} + +static inline void soc_control_err(struct soc_tplg *tplg, + struct snd_soc_tplg_ctl_hdr *hdr, const char *name) +{ + dev_err(tplg->dev, + "ASoC: no complete mixer IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n", + name, hdr->ops.get, hdr->ops.put, hdr->ops.info, + soc_tplg_get_offset(tplg)); +} + +/* pass vendor data to component driver for processing */ +static int soc_tplg_vendor_load_(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + int ret = 0; + + if (tplg->comp && tplg->ops && tplg->ops->vendor_load) + ret = tplg->ops->vendor_load(tplg->comp, hdr); + else { + dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", + hdr->vendor_type); + return -EINVAL; + } + + if (ret < 0) + dev_err(tplg->dev, + "ASoC: vendor load failed at hdr offset %ld/0x%lx for type %d:%d\n", + soc_tplg_get_hdr_offset(tplg), + soc_tplg_get_hdr_offset(tplg), + hdr->type, hdr->vendor_type); + return ret; +} + +/* pass vendor data to component driver for processing */ +static int soc_tplg_vendor_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + if (tplg->pass != SOC_TPLG_PASS_VENDOR) + return 0; + + return soc_tplg_vendor_load_(tplg, hdr); +} + +/* optionally pass new dynamic widget to component driver. This is mainly for + * external widgets where we can assign private data/ops */ +static int soc_tplg_widget_load(struct soc_tplg *tplg, + struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) +{ + if (tplg->comp && tplg->ops && tplg->ops->widget_load) + return tplg->ops->widget_load(tplg->comp, w, tplg_w); + + return 0; +} + +/* pass dynamic FEs configurations to component driver */ +static int soc_tplg_pcm_dai_load(struct soc_tplg *tplg, + struct snd_soc_tplg_pcm_dai *pcm_dai, int num_pcm_dai) +{ + if (tplg->comp && tplg->ops && tplg->ops->pcm_dai_load) + return tplg->ops->pcm_dai_load(tplg->comp, pcm_dai, num_pcm_dai); + + return 0; +} + +/* tell the component driver that all firmware has been loaded in this request */ +static void soc_tplg_complete(struct soc_tplg *tplg) +{ + if (tplg->comp && tplg->ops && tplg->ops->complete) + tplg->ops->complete(tplg->comp); +} + +/* add a dynamic kcontrol */ +static int soc_tplg_add_dcontrol(struct snd_card *card, struct device *dev, + const struct snd_kcontrol_new *control_new, const char *prefix, + void *data, struct snd_kcontrol **kcontrol) +{ + int err; + + *kcontrol = snd_soc_cnew(control_new, data, control_new->name, prefix); + if (*kcontrol == NULL) { + dev_err(dev, "ASoC: Failed to create new kcontrol %s\n", + control_new->name); + return -ENOMEM; + } + + err = snd_ctl_add(card, *kcontrol); + if (err < 0) { + dev_err(dev, "ASoC: Failed to add %s: %d\n", + control_new->name, err); + return err; + } + + return 0; +} + +/* add a dynamic kcontrol for component driver */ +static int soc_tplg_add_kcontrol(struct soc_tplg *tplg, + struct snd_kcontrol_new *k, struct snd_kcontrol **kcontrol) +{ + struct snd_soc_component *comp = tplg->comp; + + return soc_tplg_add_dcontrol(comp->card->snd_card, + comp->dev, k, NULL, comp, kcontrol); +} + +/* remove a mixer kcontrol */ +static void remove_mixer(struct snd_soc_component *comp, + struct snd_soc_dobj *dobj, int pass) +{ + struct snd_card *card = comp->card->snd_card; + struct soc_mixer_control *sm = + container_of(dobj, struct soc_mixer_control, dobj); + const unsigned int *p = NULL; + + if (pass != SOC_TPLG_PASS_MIXER) + return; + + if (dobj->ops && dobj->ops->control_unload) + dobj->ops->control_unload(comp, dobj); + + if (sm->dobj.control.kcontrol->tlv.p) + p = sm->dobj.control.kcontrol->tlv.p; + snd_ctl_remove(card, sm->dobj.control.kcontrol); + list_del(&sm->dobj.list); + kfree(sm); + kfree(p); +} + +/* remove an enum kcontrol */ +static void remove_enum(struct snd_soc_component *comp, + struct snd_soc_dobj *dobj, int pass) +{ + struct snd_card *card = comp->card->snd_card; + struct soc_enum *se = container_of(dobj, struct soc_enum, dobj); + int i; + + if (pass != SOC_TPLG_PASS_MIXER) + return; + + if (dobj->ops && dobj->ops->control_unload) + dobj->ops->control_unload(comp, dobj); + + snd_ctl_remove(card, se->dobj.control.kcontrol); + list_del(&se->dobj.list); + + kfree(se->dobj.control.dvalues); + for (i = 0; i < se->items; i++) + kfree(se->dobj.control.dtexts[i]); + kfree(se); +} + +/* remove a byte kcontrol */ +static void remove_bytes(struct snd_soc_component *comp, + struct snd_soc_dobj *dobj, int pass) +{ + struct snd_card *card = comp->card->snd_card; + struct soc_bytes_ext *sb = + container_of(dobj, struct soc_bytes_ext, dobj); + + if (pass != SOC_TPLG_PASS_MIXER) + return; + + if (dobj->ops && dobj->ops->control_unload) + dobj->ops->control_unload(comp, dobj); + + snd_ctl_remove(card, sb->dobj.control.kcontrol); + list_del(&sb->dobj.list); + kfree(sb); +} + +/* remove a widget and it's kcontrols - routes must be removed first */ +static void remove_widget(struct snd_soc_component *comp, + struct snd_soc_dobj *dobj, int pass) +{ + struct snd_card *card = comp->card->snd_card; + struct snd_soc_dapm_widget *w = + container_of(dobj, struct snd_soc_dapm_widget, dobj); + int i; + + if (pass != SOC_TPLG_PASS_WIDGET) + return; + + if (dobj->ops && dobj->ops->widget_unload) + dobj->ops->widget_unload(comp, dobj); + + /* + * Dynamic Widgets either have 1 enum kcontrol or 1..N mixers. + * The enum may either have an array of values or strings. + */ + if (dobj->widget.kcontrol_enum) { + /* enumerated widget mixer */ + struct soc_enum *se = + (struct soc_enum *)w->kcontrols[0]->private_value; + + snd_ctl_remove(card, w->kcontrols[0]); + + kfree(se->dobj.control.dvalues); + for (i = 0; i < se->items; i++) + kfree(se->dobj.control.dtexts[i]); + + kfree(se); + kfree(w->kcontrol_news); + } else { + /* non enumerated widget mixer */ + for (i = 0; i < w->num_kcontrols; i++) { + struct snd_kcontrol *kcontrol = w->kcontrols[i]; + struct soc_mixer_control *sm = + (struct soc_mixer_control *) kcontrol->private_value; + + kfree(w->kcontrols[i]->tlv.p); + + snd_ctl_remove(card, w->kcontrols[i]); + kfree(sm); + } + kfree(w->kcontrol_news); + } + /* widget w is freed by soc-dapm.c */ +} + +/* remove PCM DAI configurations */ +static void remove_pcm_dai(struct snd_soc_component *comp, + struct snd_soc_dobj *dobj, int pass) +{ + if (pass != SOC_TPLG_PASS_PCM_DAI) + return; + + if (dobj->ops && dobj->ops->pcm_dai_unload) + dobj->ops->pcm_dai_unload(comp, dobj); + + list_del(&dobj->list); + kfree(dobj); +} + +/* bind a kcontrol to it's IO handlers */ +static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, + struct snd_kcontrol_new *k, + const struct snd_soc_tplg_kcontrol_ops *ops, int num_ops, + const struct snd_soc_tplg_kcontrol_ops *bops, int num_bops) +{ + int i; + + /* try and map standard kcontrols handler first */ + for (i = 0; i < num_ops; i++) { + + if (ops[i].id == hdr->ops.put) + k->put = ops[i].put; + if (ops[i].id == hdr->ops.get) + k->get = ops[i].get; + if (ops[i].id == hdr->ops.info) + k->info = ops[i].info; + } + + /* standard handlers found ? */ + if (k->put && k->get && k->info) + return 0; + + /* none found so try bespoke handlers */ + for (i = 0; i < num_bops; i++) { + + if (k->put == NULL && bops[i].id == hdr->ops.put) + k->put = bops[i].put; + if (k->get == NULL && bops[i].id == hdr->ops.get) + k->get = bops[i].get; + if (k->info == NULL && bops[i].id == hdr->ops.info) + k->info = bops[i].info; + } + + /* bespoke handlers found ? */ + if (k->put && k->get && k->info) + return 0; + + /* nothing to bind */ + return -EINVAL; +} + +/* bind a widgets to it's evnt handlers */ +int snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w, + const struct snd_soc_tplg_widget_events *events, + int num_events, u16 event_type) +{ + int i; + + w->event = NULL; + + for (i = 0; i < num_events; i++) { + if (event_type == events[i].type) { + + /* found - so assign event */ + w->event = events[i].event_handler; + return 0; + } + } + + /* not found */ + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event); + +/* optionally pass new dynamic kcontrol to component driver. */ +static int soc_tplg_init_kcontrol(struct soc_tplg *tplg, + struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) +{ + if (tplg->comp && tplg->ops && tplg->ops->control_load) + return tplg->ops->control_load(tplg->comp, k, hdr); + + return 0; +} + + +static int soc_tplg_create_tlv_db_scale(struct soc_tplg *tplg, + struct snd_kcontrol_new *kc, struct snd_soc_tplg_tlv_dbscale *scale) +{ + unsigned int item_len = 2 * sizeof(unsigned int); + unsigned int *p; + + p = kzalloc(item_len + 2 * sizeof(unsigned int), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p[0] = SNDRV_CTL_TLVT_DB_SCALE; + p[1] = item_len; + p[2] = scale->min; + p[3] = (scale->step & TLV_DB_SCALE_MASK) + | (scale->mute ? TLV_DB_SCALE_MUTE : 0); + + kc->tlv.p = (void *)p; + return 0; +} + +static int soc_tplg_create_tlv(struct soc_tplg *tplg, + struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *tc) +{ + struct snd_soc_tplg_ctl_tlv *tplg_tlv; + + if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)) + return 0; + + if (tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + kc->tlv.c = snd_soc_bytes_tlv_callback; + } else { + tplg_tlv = &tc->tlv; + switch (tplg_tlv->type) { + case SNDRV_CTL_TLVT_DB_SCALE: + return soc_tplg_create_tlv_db_scale(tplg, kc, + &tplg_tlv->scale); + + /* TODO: add support for other TLV types */ + default: + dev_dbg(tplg->dev, "Unsupported TLV type %d\n", + tplg_tlv->type); + return -EINVAL; + } + } + + return 0; +} + +static inline void soc_tplg_free_tlv(struct soc_tplg *tplg, + struct snd_kcontrol_new *kc) +{ + kfree(kc->tlv.p); +} + +static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count, + size_t size) +{ + struct snd_soc_tplg_bytes_control *be; + struct soc_bytes_ext *sbe; + struct snd_kcontrol_new kc; + int i, err; + + if (soc_tplg_check_elem_count(tplg, + sizeof(struct snd_soc_tplg_bytes_control), count, + size, "mixer bytes")) { + dev_err(tplg->dev, "ASoC: Invalid count %d for byte control\n", + count); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + be = (struct snd_soc_tplg_bytes_control *)tplg->pos; + + /* validate kcontrol */ + if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + sbe = kzalloc(sizeof(*sbe), GFP_KERNEL); + if (sbe == NULL) + return -ENOMEM; + + tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + + be->priv.size); + + dev_dbg(tplg->dev, + "ASoC: adding bytes kcontrol %s with access 0x%x\n", + be->hdr.name, be->hdr.access); + + memset(&kc, 0, sizeof(kc)); + kc.name = be->hdr.name; + kc.private_value = (long)sbe; + kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc.access = be->hdr.access; + + sbe->max = be->max; + sbe->dobj.type = SND_SOC_DOBJ_BYTES; + sbe->dobj.ops = tplg->ops; + INIT_LIST_HEAD(&sbe->dobj.list); + + /* map io handlers */ + err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, io_ops, + ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + if (err) { + soc_control_err(tplg, &be->hdr, be->hdr.name); + kfree(sbe); + continue; + } + + /* pass control to driver for optional further init */ + err = soc_tplg_init_kcontrol(tplg, &kc, + (struct snd_soc_tplg_ctl_hdr *)be); + if (err < 0) { + dev_err(tplg->dev, "ASoC: failed to init %s\n", + be->hdr.name); + kfree(sbe); + continue; + } + + /* register control here */ + err = soc_tplg_add_kcontrol(tplg, &kc, + &sbe->dobj.control.kcontrol); + if (err < 0) { + dev_err(tplg->dev, "ASoC: failed to add %s\n", + be->hdr.name); + kfree(sbe); + continue; + } + + list_add(&sbe->dobj.list, &tplg->comp->dobj_list); + } + return 0; + +} + +static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count, + size_t size) +{ + struct snd_soc_tplg_mixer_control *mc; + struct soc_mixer_control *sm; + struct snd_kcontrol_new kc; + int i, err; + + if (soc_tplg_check_elem_count(tplg, + sizeof(struct snd_soc_tplg_mixer_control), + count, size, "mixers")) { + + dev_err(tplg->dev, "ASoC: invalid count %d for controls\n", + count); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; + + /* validate kcontrol */ + if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + sm = kzalloc(sizeof(*sm), GFP_KERNEL); + if (sm == NULL) + return -ENOMEM; + tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + + mc->priv.size); + + dev_dbg(tplg->dev, + "ASoC: adding mixer kcontrol %s with access 0x%x\n", + mc->hdr.name, mc->hdr.access); + + memset(&kc, 0, sizeof(kc)); + kc.name = mc->hdr.name; + kc.private_value = (long)sm; + kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc.access = mc->hdr.access; + + /* we only support FL/FR channel mapping atm */ + sm->reg = tplc_chan_get_reg(tplg, mc->channel, + SNDRV_CHMAP_FL); + sm->rreg = tplc_chan_get_reg(tplg, mc->channel, + SNDRV_CHMAP_FR); + sm->shift = tplc_chan_get_shift(tplg, mc->channel, + SNDRV_CHMAP_FL); + sm->rshift = tplc_chan_get_shift(tplg, mc->channel, + SNDRV_CHMAP_FR); + + sm->max = mc->max; + sm->min = mc->min; + sm->invert = mc->invert; + sm->platform_max = mc->platform_max; + sm->dobj.index = tplg->index; + sm->dobj.ops = tplg->ops; + sm->dobj.type = SND_SOC_DOBJ_MIXER; + INIT_LIST_HEAD(&sm->dobj.list); + + /* map io handlers */ + err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, io_ops, + ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + if (err) { + soc_control_err(tplg, &mc->hdr, mc->hdr.name); + kfree(sm); + continue; + } + + /* pass control to driver for optional further init */ + err = soc_tplg_init_kcontrol(tplg, &kc, + (struct snd_soc_tplg_ctl_hdr *) mc); + if (err < 0) { + dev_err(tplg->dev, "ASoC: failed to init %s\n", + mc->hdr.name); + kfree(sm); + continue; + } + + /* create any TLV data */ + soc_tplg_create_tlv(tplg, &kc, &mc->hdr); + + /* register control here */ + err = soc_tplg_add_kcontrol(tplg, &kc, + &sm->dobj.control.kcontrol); + if (err < 0) { + dev_err(tplg->dev, "ASoC: failed to add %s\n", + mc->hdr.name); + soc_tplg_free_tlv(tplg, &kc); + kfree(sm); + continue; + } + + list_add(&sm->dobj.list, &tplg->comp->dobj_list); + } + + return 0; +} + +static int soc_tplg_denum_create_texts(struct soc_enum *se, + struct snd_soc_tplg_enum_control *ec) +{ + int i, ret; + + se->dobj.control.dtexts = + kzalloc(sizeof(char *) * ec->items, GFP_KERNEL); + if (se->dobj.control.dtexts == NULL) + return -ENOMEM; + + for (i = 0; i < ec->items; i++) { + + if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { + ret = -EINVAL; + goto err; + } + + se->dobj.control.dtexts[i] = kstrdup(ec->texts[i], GFP_KERNEL); + if (!se->dobj.control.dtexts[i]) { + ret = -ENOMEM; + goto err; + } + } + + return 0; + +err: + for (--i; i >= 0; i--) + kfree(se->dobj.control.dtexts[i]); + kfree(se->dobj.control.dtexts); + return ret; +} + +static int soc_tplg_denum_create_values(struct soc_enum *se, + struct snd_soc_tplg_enum_control *ec) +{ + if (ec->items > sizeof(*ec->values)) + return -EINVAL; + + se->dobj.control.dvalues = + kmalloc(ec->items * sizeof(u32), GFP_KERNEL); + if (!se->dobj.control.dvalues) + return -ENOMEM; + + memcpy(se->dobj.control.dvalues, ec->values, ec->items * sizeof(u32)); + return 0; +} + +static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count, + size_t size) +{ + struct snd_soc_tplg_enum_control *ec; + struct soc_enum *se; + struct snd_kcontrol_new kc; + int i, ret, err; + + if (soc_tplg_check_elem_count(tplg, + sizeof(struct snd_soc_tplg_enum_control), + count, size, "enums")) { + + dev_err(tplg->dev, "ASoC: invalid count %d for enum controls\n", + count); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + ec = (struct snd_soc_tplg_enum_control *)tplg->pos; + tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + + ec->priv.size); + + /* validate kcontrol */ + if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + se = kzalloc((sizeof(*se)), GFP_KERNEL); + if (se == NULL) + return -ENOMEM; + + dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", + ec->hdr.name, ec->items); + + memset(&kc, 0, sizeof(kc)); + kc.name = ec->hdr.name; + kc.private_value = (long)se; + kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc.access = ec->hdr.access; + + se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_l = tplc_chan_get_shift(tplg, ec->channel, + SNDRV_CHMAP_FL); + se->shift_r = tplc_chan_get_shift(tplg, ec->channel, + SNDRV_CHMAP_FL); + + se->items = ec->items; + se->mask = ec->mask; + se->dobj.index = tplg->index; + se->dobj.type = SND_SOC_DOBJ_ENUM; + se->dobj.ops = tplg->ops; + INIT_LIST_HEAD(&se->dobj.list); + + switch (ec->hdr.ops.info) { + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + err = soc_tplg_denum_create_values(se, ec); + if (err < 0) { + dev_err(tplg->dev, + "ASoC: could not create values for %s\n", + ec->hdr.name); + kfree(se); + continue; + } + /* fall through and create texts */ + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + err = soc_tplg_denum_create_texts(se, ec); + if (err < 0) { + dev_err(tplg->dev, + "ASoC: could not create texts for %s\n", + ec->hdr.name); + kfree(se); + continue; + } + break; + default: + dev_err(tplg->dev, + "ASoC: invalid enum control type %d for %s\n", + ec->hdr.ops.info, ec->hdr.name); + kfree(se); + continue; + } + + /* map io handlers */ + err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, io_ops, + ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + if (err) { + soc_control_err(tplg, &ec->hdr, ec->hdr.name); + kfree(se); + continue; + } + + /* pass control to driver for optional further init */ + err = soc_tplg_init_kcontrol(tplg, &kc, + (struct snd_soc_tplg_ctl_hdr *) ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: failed to init %s\n", + ec->hdr.name); + kfree(se); + continue; + } + + /* register control here */ + ret = soc_tplg_add_kcontrol(tplg, + &kc, &se->dobj.control.kcontrol); + if (ret < 0) { + dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n", + ec->hdr.name); + kfree(se); + continue; + } + + list_add(&se->dobj.list, &tplg->comp->dobj_list); + } + + return 0; +} + +static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + struct snd_soc_tplg_ctl_hdr *control_hdr; + int i; + + if (tplg->pass != SOC_TPLG_PASS_MIXER) { + tplg->pos += hdr->size + hdr->payload_size; + return 0; + } + + dev_dbg(tplg->dev, "ASoC: adding %d kcontrols at 0x%lx\n", hdr->count, + soc_tplg_get_offset(tplg)); + + for (i = 0; i < hdr->count; i++) { + + control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; + + switch (control_hdr->ops.info) { + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_STROBE: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + case SND_SOC_TPLG_CTL_RANGE: + case SND_SOC_TPLG_DAPM_CTL_VOLSW: + case SND_SOC_TPLG_DAPM_CTL_PIN: + soc_tplg_dmixer_create(tplg, 1, hdr->payload_size); + break; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + soc_tplg_denum_create(tplg, 1, hdr->payload_size); + break; + case SND_SOC_TPLG_CTL_BYTES: + soc_tplg_dbytes_create(tplg, 1, hdr->payload_size); + break; + default: + soc_bind_err(tplg, control_hdr, i); + return -EINVAL; + } + } + + return 0; +} + +static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; + struct snd_soc_dapm_route route; + struct snd_soc_tplg_dapm_graph_elem *elem; + int count = hdr->count, i; + + if (tplg->pass != SOC_TPLG_PASS_GRAPH) { + tplg->pos += hdr->size + hdr->payload_size; + return 0; + } + + if (soc_tplg_check_elem_count(tplg, + sizeof(struct snd_soc_tplg_dapm_graph_elem), + count, hdr->payload_size, "graph")) { + + dev_err(tplg->dev, "ASoC: invalid count %d for DAPM routes\n", + count); + return -EINVAL; + } + + dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes\n", count); + + for (i = 0; i < count; i++) { + elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos; + tplg->pos += sizeof(struct snd_soc_tplg_dapm_graph_elem); + + /* validate routes */ + if (strnlen(elem->source, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + if (strnlen(elem->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + route.source = elem->source; + route.sink = elem->sink; + route.connected = NULL; /* set to NULL atm for tplg users */ + if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0) + route.control = NULL; + else + route.control = elem->control; + + /* add route, but keep going if some fail */ + snd_soc_dapm_add_routes(dapm, &route, 1); + } + + return 0; +} + +static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( + struct soc_tplg *tplg, int num_kcontrols) +{ + struct snd_kcontrol_new *kc; + struct soc_mixer_control *sm; + struct snd_soc_tplg_mixer_control *mc; + int i, err; + + kc = kzalloc(sizeof(*kc) * num_kcontrols, GFP_KERNEL); + if (kc == NULL) + return NULL; + + for (i = 0; i < num_kcontrols; i++) { + mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; + sm = kzalloc(sizeof(*sm), GFP_KERNEL); + if (sm == NULL) + goto err; + + tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + + mc->priv.size); + + /* validate kcontrol */ + if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + goto err_str; + + dev_dbg(tplg->dev, " adding DAPM widget mixer control %s at %d\n", + mc->hdr.name, i); + + kc[i].name = mc->hdr.name; + kc[i].private_value = (long)sm; + kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc[i].access = mc->hdr.access; + + /* we only support FL/FR channel mapping atm */ + sm->reg = tplc_chan_get_reg(tplg, mc->channel, + SNDRV_CHMAP_FL); + sm->rreg = tplc_chan_get_reg(tplg, mc->channel, + SNDRV_CHMAP_FR); + sm->shift = tplc_chan_get_shift(tplg, mc->channel, + SNDRV_CHMAP_FL); + sm->rshift = tplc_chan_get_shift(tplg, mc->channel, + SNDRV_CHMAP_FR); + + sm->max = mc->max; + sm->min = mc->min; + sm->invert = mc->invert; + sm->platform_max = mc->platform_max; + sm->dobj.index = tplg->index; + INIT_LIST_HEAD(&sm->dobj.list); + + /* map io handlers */ + err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc[i], io_ops, + ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + if (err) { + soc_control_err(tplg, &mc->hdr, mc->hdr.name); + kfree(sm); + continue; + } + + /* pass control to driver for optional further init */ + err = soc_tplg_init_kcontrol(tplg, &kc[i], + (struct snd_soc_tplg_ctl_hdr *)mc); + if (err < 0) { + dev_err(tplg->dev, "ASoC: failed to init %s\n", + mc->hdr.name); + kfree(sm); + continue; + } + } + return kc; + +err_str: + kfree(sm); +err: + for (--i; i >= 0; i--) + kfree((void *)kc[i].private_value); + kfree(kc); + return NULL; +} + +static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( + struct soc_tplg *tplg) +{ + struct snd_kcontrol_new *kc; + struct snd_soc_tplg_enum_control *ec; + struct soc_enum *se; + int i, err; + + ec = (struct snd_soc_tplg_enum_control *)tplg->pos; + tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + + ec->priv.size); + + /* validate kcontrol */ + if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return NULL; + + kc = kzalloc(sizeof(*kc), GFP_KERNEL); + if (kc == NULL) + return NULL; + + se = kzalloc(sizeof(*se), GFP_KERNEL); + if (se == NULL) + goto err; + + dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", + ec->hdr.name); + + kc->name = ec->hdr.name; + kc->private_value = (long)se; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = ec->hdr.access; + + /* we only support FL/FR channel mapping atm */ + se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_l = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL); + se->shift_r = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR); + + se->items = ec->items; + se->mask = ec->mask; + se->dobj.index = tplg->index; + + switch (ec->hdr.ops.info) { + case SND_SOC_TPLG_CTL_ENUM_VALUE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + err = soc_tplg_denum_create_values(se, ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: could not create values for %s\n", + ec->hdr.name); + goto err_se; + } + /* fall through to create texts */ + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + err = soc_tplg_denum_create_texts(se, ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: could not create texts for %s\n", + ec->hdr.name); + goto err_se; + } + break; + default: + dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", + ec->hdr.ops.info, ec->hdr.name); + goto err_se; + } + + /* map io handlers */ + err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, io_ops, + ARRAY_SIZE(io_ops), tplg->io_ops, tplg->io_ops_count); + if (err) { + soc_control_err(tplg, &ec->hdr, ec->hdr.name); + goto err_se; + } + + /* pass control to driver for optional further init */ + err = soc_tplg_init_kcontrol(tplg, kc, + (struct snd_soc_tplg_ctl_hdr *)ec); + if (err < 0) { + dev_err(tplg->dev, "ASoC: failed to init %s\n", + ec->hdr.name); + goto err_se; + } + + return kc; + +err_se: + /* free values and texts */ + kfree(se->dobj.control.dvalues); + for (i = 0; i < ec->items; i++) + kfree(se->dobj.control.dtexts[i]); + + kfree(se); +err: + kfree(kc); + + return NULL; +} + +static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create( + struct soc_tplg *tplg, int count) +{ + struct snd_soc_tplg_bytes_control *be; + struct soc_bytes_ext *sbe; + struct snd_kcontrol_new *kc; + int i, err; + + kc = kzalloc(sizeof(*kc) * count, GFP_KERNEL); + if (!kc) + return NULL; + + for (i = 0; i < count; i++) { + be = (struct snd_soc_tplg_bytes_control *)tplg->pos; + + /* validate kcontrol */ + if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + goto err; + + sbe = kzalloc(sizeof(*sbe), GFP_KERNEL); + if (sbe == NULL) + goto err; + + tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + + be->priv.size); + + dev_dbg(tplg->dev, + "ASoC: adding bytes kcontrol %s with access 0x%x\n", + be->hdr.name, be->hdr.access); + + memset(kc, 0, sizeof(*kc)); + kc[i].name = be->hdr.name; + kc[i].private_value = (long)sbe; + kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc[i].access = be->hdr.access; + + sbe->max = be->max; + INIT_LIST_HEAD(&sbe->dobj.list); + + /* map standard io handlers and check for external handlers */ + err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc[i], io_ops, + ARRAY_SIZE(io_ops), tplg->io_ops, + tplg->io_ops_count); + if (err) { + soc_control_err(tplg, &be->hdr, be->hdr.name); + kfree(sbe); + continue; + } + + /* pass control to driver for optional further init */ + err = soc_tplg_init_kcontrol(tplg, &kc[i], + (struct snd_soc_tplg_ctl_hdr *)be); + if (err < 0) { + dev_err(tplg->dev, "ASoC: failed to init %s\n", + be->hdr.name); + kfree(sbe); + continue; + } + } + + return kc; + +err: + for (--i; i >= 0; i--) + kfree((void *)kc[i].private_value); + + kfree(kc); + return NULL; +} + +static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, + struct snd_soc_tplg_dapm_widget *w) +{ + struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; + struct snd_soc_dapm_widget template, *widget; + struct snd_soc_tplg_ctl_hdr *control_hdr; + struct snd_soc_card *card = tplg->comp->card; + int ret = 0; + + if (strnlen(w->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + if (strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == + SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + return -EINVAL; + + dev_dbg(tplg->dev, "ASoC: creating DAPM widget %s id %d\n", + w->name, w->id); + + memset(&template, 0, sizeof(template)); + + /* map user to kernel widget ID */ + template.id = get_widget_id(w->id); + if (template.id < 0) + return template.id; + + template.name = kstrdup(w->name, GFP_KERNEL); + if (!template.name) + return -ENOMEM; + template.sname = kstrdup(w->sname, GFP_KERNEL); + if (!template.sname) { + ret = -ENOMEM; + goto err; + } + template.reg = w->reg; + template.shift = w->shift; + template.mask = w->mask; + template.subseq = w->subseq; + template.on_val = w->invert ? 0 : 1; + template.off_val = w->invert ? 1 : 0; + template.ignore_suspend = w->ignore_suspend; + template.event_flags = w->event_flags; + template.dobj.index = tplg->index; + + tplg->pos += + (sizeof(struct snd_soc_tplg_dapm_widget) + w->priv.size); + if (w->num_kcontrols == 0) { + template.num_kcontrols = 0; + goto widget; + } + + control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; + dev_dbg(tplg->dev, "ASoC: template %s has %d controls of type %x\n", + w->name, w->num_kcontrols, control_hdr->type); + + switch (control_hdr->ops.info) { + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_STROBE: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + case SND_SOC_TPLG_CTL_RANGE: + case SND_SOC_TPLG_DAPM_CTL_VOLSW: + template.num_kcontrols = w->num_kcontrols; + template.kcontrol_news = + soc_tplg_dapm_widget_dmixer_create(tplg, + template.num_kcontrols); + if (!template.kcontrol_news) { + ret = -ENOMEM; + goto hdr_err; + } + break; + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: + case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + template.dobj.widget.kcontrol_enum = 1; + template.num_kcontrols = 1; + template.kcontrol_news = + soc_tplg_dapm_widget_denum_create(tplg); + if (!template.kcontrol_news) { + ret = -ENOMEM; + goto hdr_err; + } + break; + case SND_SOC_TPLG_CTL_BYTES: + template.num_kcontrols = w->num_kcontrols; + template.kcontrol_news = + soc_tplg_dapm_widget_dbytes_create(tplg, + template.num_kcontrols); + if (!template.kcontrol_news) { + ret = -ENOMEM; + goto hdr_err; + } + break; + default: + dev_err(tplg->dev, "ASoC: invalid widget control type %d:%d:%d\n", + control_hdr->ops.get, control_hdr->ops.put, + control_hdr->ops.info); + ret = -EINVAL; + goto hdr_err; + } + +widget: + ret = soc_tplg_widget_load(tplg, &template, w); + if (ret < 0) + goto hdr_err; + + /* card dapm mutex is held by the core if we are loading topology + * data during sound card init. */ + if (card->instantiated) + widget = snd_soc_dapm_new_control(dapm, &template); + else + widget = snd_soc_dapm_new_control_unlocked(dapm, &template); + if (widget == NULL) { + dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n", + w->name); + goto hdr_err; + } + + widget->dobj.type = SND_SOC_DOBJ_WIDGET; + widget->dobj.ops = tplg->ops; + widget->dobj.index = tplg->index; + list_add(&widget->dobj.list, &tplg->comp->dobj_list); + return 0; + +hdr_err: + kfree(template.sname); +err: + kfree(template.name); + return ret; +} + +static int soc_tplg_dapm_widget_elems_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + struct snd_soc_tplg_dapm_widget *widget; + int ret, count = hdr->count, i; + + if (tplg->pass != SOC_TPLG_PASS_WIDGET) + return 0; + + dev_dbg(tplg->dev, "ASoC: adding %d DAPM widgets\n", count); + + for (i = 0; i < count; i++) { + widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos; + ret = soc_tplg_dapm_widget_create(tplg, widget); + if (ret < 0) + dev_err(tplg->dev, "ASoC: failed to load widget %s\n", + widget->name); + } + + return 0; +} + +static int soc_tplg_dapm_complete(struct soc_tplg *tplg) +{ + struct snd_soc_card *card = tplg->comp->card; + int ret; + + /* Card might not have been registered at this point. + * If so, just return success. + */ + if (!card || !card->instantiated) { + dev_warn(tplg->dev, "ASoC: Parent card not yet available," + "Do not add new widgets now\n"); + return 0; + } + + ret = snd_soc_dapm_new_widgets(card); + if (ret < 0) + dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n", + ret); + + return 0; +} + +static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + struct snd_soc_tplg_pcm_dai *pcm_dai; + struct snd_soc_dobj *dobj; + int count = hdr->count; + int ret; + + if (tplg->pass != SOC_TPLG_PASS_PCM_DAI) + return 0; + + pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos; + + if (soc_tplg_check_elem_count(tplg, + sizeof(struct snd_soc_tplg_pcm_dai), count, + hdr->payload_size, "PCM DAI")) { + dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n", + count); + return -EINVAL; + } + + dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); + tplg->pos += sizeof(struct snd_soc_tplg_pcm_dai) * count; + + dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL); + if (dobj == NULL) + return -ENOMEM; + + /* Call the platform driver call back to register the dais */ + ret = soc_tplg_pcm_dai_load(tplg, pcm_dai, count); + if (ret < 0) { + dev_err(tplg->comp->dev, "ASoC: PCM DAI loading failed\n"); + goto err; + } + + dobj->type = get_dobj_type(hdr, NULL); + dobj->pcm_dai.count = count; + dobj->pcm_dai.pd = pcm_dai; + dobj->ops = tplg->ops; + dobj->index = tplg->index; + list_add(&dobj->list, &tplg->comp->dobj_list); + return 0; + +err: + kfree(dobj); + return ret; +} + +static int soc_tplg_manifest_load(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + struct snd_soc_tplg_manifest *manifest; + + if (tplg->pass != SOC_TPLG_PASS_MANIFEST) + return 0; + + manifest = (struct snd_soc_tplg_manifest *)tplg->pos; + tplg->pos += sizeof(struct snd_soc_tplg_manifest); + + if (tplg->comp && tplg->ops && tplg->ops->manifest) + return tplg->ops->manifest(tplg->comp, manifest); + + dev_err(tplg->dev, "ASoC: Firmware manifest not supported\n"); + return 0; +} + +/* validate header magic, size and type */ +static int soc_valid_header(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size) + return 0; + + /* big endian firmware objects not supported atm */ + if (hdr->magic == cpu_to_be32(SND_SOC_TPLG_MAGIC)) { + dev_err(tplg->dev, + "ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n", + tplg->pass, hdr->magic, + soc_tplg_get_hdr_offset(tplg), tplg->fw->size); + return -EINVAL; + } + + if (hdr->magic != SND_SOC_TPLG_MAGIC) { + dev_err(tplg->dev, + "ASoC: pass %d does not have a valid header got %x at offset 0x%lx size 0x%zx.\n", + tplg->pass, hdr->magic, + soc_tplg_get_hdr_offset(tplg), tplg->fw->size); + return -EINVAL; + } + + if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) { + dev_err(tplg->dev, + "ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n", + tplg->pass, hdr->abi, + SND_SOC_TPLG_ABI_VERSION, soc_tplg_get_hdr_offset(tplg), + tplg->fw->size); + return -EINVAL; + } + + if (hdr->payload_size == 0) { + dev_err(tplg->dev, "ASoC: header has 0 size at offset 0x%lx.\n", + soc_tplg_get_hdr_offset(tplg)); + return -EINVAL; + } + + if (tplg->pass == hdr->type) + dev_dbg(tplg->dev, + "ASoC: Got 0x%x bytes of type %d version %d vendor %d at pass %d\n", + hdr->payload_size, hdr->type, hdr->version, + hdr->vendor_type, tplg->pass); + + return 1; +} + +/* check header type and call appropriate handler */ +static int soc_tplg_load_header(struct soc_tplg *tplg, + struct snd_soc_tplg_hdr *hdr) +{ + tplg->pos = tplg->hdr_pos + sizeof(struct snd_soc_tplg_hdr); + + /* check for matching ID */ + if (hdr->index != tplg->req_index && + hdr->index != SND_SOC_TPLG_INDEX_ALL) + return 0; + + tplg->index = hdr->index; + + switch (hdr->type) { + case SND_SOC_TPLG_TYPE_MIXER: + case SND_SOC_TPLG_TYPE_ENUM: + case SND_SOC_TPLG_TYPE_BYTES: + return soc_tplg_kcontrol_elems_load(tplg, hdr); + case SND_SOC_TPLG_TYPE_DAPM_GRAPH: + return soc_tplg_dapm_graph_elems_load(tplg, hdr); + case SND_SOC_TPLG_TYPE_DAPM_WIDGET: + return soc_tplg_dapm_widget_elems_load(tplg, hdr); + case SND_SOC_TPLG_TYPE_PCM: + case SND_SOC_TPLG_TYPE_DAI_LINK: + case SND_SOC_TPLG_TYPE_CODEC_LINK: + return soc_tplg_pcm_dai_elems_load(tplg, hdr); + case SND_SOC_TPLG_TYPE_MANIFEST: + return soc_tplg_manifest_load(tplg, hdr); + default: + /* bespoke vendor data object */ + return soc_tplg_vendor_load(tplg, hdr); + } + + return 0; +} + +/* process the topology file headers */ +static int soc_tplg_process_headers(struct soc_tplg *tplg) +{ + struct snd_soc_tplg_hdr *hdr; + int ret; + + tplg->pass = SOC_TPLG_PASS_START; + + /* process the header types from start to end */ + while (tplg->pass <= SOC_TPLG_PASS_END) { + + tplg->hdr_pos = tplg->fw->data; + hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos; + + while (!soc_tplg_is_eof(tplg)) { + + /* make sure header is valid before loading */ + ret = soc_valid_header(tplg, hdr); + if (ret < 0) + return ret; + else if (ret == 0) + break; + + /* load the header object */ + ret = soc_tplg_load_header(tplg, hdr); + if (ret < 0) + return ret; + + /* goto next header */ + tplg->hdr_pos += hdr->payload_size + + sizeof(struct snd_soc_tplg_hdr); + hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos; + } + + /* next data type pass */ + tplg->pass++; + } + + /* signal DAPM we are complete */ + ret = soc_tplg_dapm_complete(tplg); + if (ret < 0) + dev_err(tplg->dev, + "ASoC: failed to initialise DAPM from Firmware\n"); + + return ret; +} + +static int soc_tplg_load(struct soc_tplg *tplg) +{ + int ret; + + ret = soc_tplg_process_headers(tplg); + if (ret == 0) + soc_tplg_complete(tplg); + + return ret; +} + +/* load audio component topology from "firmware" file */ +int snd_soc_tplg_component_load(struct snd_soc_component *comp, + struct snd_soc_tplg_ops *ops, const struct firmware *fw, u32 id) +{ + struct soc_tplg tplg; + + /* setup parsing context */ + memset(&tplg, 0, sizeof(tplg)); + tplg.fw = fw; + tplg.dev = comp->dev; + tplg.comp = comp; + tplg.ops = ops; + tplg.req_index = id; + tplg.io_ops = ops->io_ops; + tplg.io_ops_count = ops->io_ops_count; + + return soc_tplg_load(&tplg); +} +EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load); + +/* remove this dynamic widget */ +void snd_soc_tplg_widget_remove(struct snd_soc_dapm_widget *w) +{ + /* make sure we are a widget */ + if (w->dobj.type != SND_SOC_DOBJ_WIDGET) + return; + + remove_widget(w->dapm->component, &w->dobj, SOC_TPLG_PASS_WIDGET); +} +EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove); + +/* remove all dynamic widgets from this DAPM context */ +void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, + u32 index) +{ + struct snd_soc_dapm_widget *w, *next_w; + struct snd_soc_dapm_path *p, *next_p; + + list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { + + /* make sure we are a widget with correct context */ + if (w->dobj.type != SND_SOC_DOBJ_WIDGET || w->dapm != dapm) + continue; + + /* match ID */ + if (w->dobj.index != index && + w->dobj.index != SND_SOC_TPLG_INDEX_ALL) + continue; + + list_del(&w->list); + + /* + * remove source and sink paths associated to this widget. + * While removing the path, remove reference to it from both + * source and sink widgets so that path is removed only once. + */ + list_for_each_entry_safe(p, next_p, &w->sources, list_sink) { + list_del(&p->list_sink); + list_del(&p->list_source); + list_del(&p->list); + kfree(p); + } + list_for_each_entry_safe(p, next_p, &w->sinks, list_source) { + list_del(&p->list_sink); + list_del(&p->list_source); + list_del(&p->list); + kfree(p); + } + /* check and free and dynamic widget kcontrols */ + snd_soc_tplg_widget_remove(w); + kfree(w->kcontrols); + kfree(w->name); + kfree(w); + } +} +EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all); + +/* remove dynamic controls from the component driver */ +int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) +{ + struct snd_soc_dobj *dobj, *next_dobj; + int pass = SOC_TPLG_PASS_END; + + /* process the header types from end to start */ + while (pass >= SOC_TPLG_PASS_START) { + + /* remove mixer controls */ + list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list, + list) { + + /* match index */ + if (dobj->index != index && + dobj->index != SND_SOC_TPLG_INDEX_ALL) + continue; + + switch (dobj->type) { + case SND_SOC_DOBJ_MIXER: + remove_mixer(comp, dobj, pass); + break; + case SND_SOC_DOBJ_ENUM: + remove_enum(comp, dobj, pass); + break; + case SND_SOC_DOBJ_BYTES: + remove_bytes(comp, dobj, pass); + break; + case SND_SOC_DOBJ_WIDGET: + remove_widget(comp, dobj, pass); + break; + case SND_SOC_DOBJ_PCM: + case SND_SOC_DOBJ_DAI_LINK: + case SND_SOC_DOBJ_CODEC_LINK: + remove_pcm_dai(comp, dobj, pass); + break; + default: + dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", + dobj->type); + break; + } + } + pass--; + } + + /* let caller know if FW can be freed when no objects are left */ + return !list_empty(&comp->dobj_list); +} +EXPORT_SYMBOL_GPL(snd_soc_tplg_component_remove); diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index b81a7a4c9..85d810d76 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -372,6 +372,10 @@ int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd) /* Create driver private-data struct */ drvdata = devm_kzalloc(dev, sizeof(struct mop500_ab8500_drvdata), GFP_KERNEL); + + if (!drvdata) + return -ENOMEM; + snd_soc_card_set_drvdata(rtd->card, drvdata); /* Setup clocks */ diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index 51a66a873..f12c01ddd 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -147,7 +147,6 @@ int ux500_pcm_register_platform(struct platform_device *pdev) pcm_config = &ux500_dmaengine_pcm_config; ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, - SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | SND_DMAENGINE_PCM_FLAG_COMPAT); if (ret < 0) { dev_err(&pdev->dev, diff --git a/sound/soc/zte/Kconfig b/sound/soc/zte/Kconfig new file mode 100644 index 000000000..c47eb25e4 --- /dev/null +++ b/sound/soc/zte/Kconfig @@ -0,0 +1,17 @@ +config ZX296702_SPDIF + tristate "ZX296702 spdif" + depends on SOC_ZX296702 || COMPILE_TEST + depends on COMMON_CLK + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for codecs attached to the + zx296702 spdif interface + +config ZX296702_I2S + tristate "ZX296702 i2s" + depends on SOC_ZX296702 || COMPILE_TEST + depends on COMMON_CLK + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for codecs attached to the + zx296702 i2s interface diff --git a/sound/soc/zte/Makefile b/sound/soc/zte/Makefile new file mode 100644 index 000000000..254ed2c8c --- /dev/null +++ b/sound/soc/zte/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ZX296702_SPDIF) += zx296702-spdif.o +obj-$(CONFIG_ZX296702_I2S) += zx296702-i2s.o diff --git a/sound/soc/zte/zx296702-i2s.c b/sound/soc/zte/zx296702-i2s.c new file mode 100644 index 000000000..1930c42e1 --- /dev/null +++ b/sound/soc/zte/zx296702-i2s.c @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2015 Linaro + * + * Author: Jun Nie <jun.nie@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define ZX_I2S_PROCESS_CTRL 0x04 +#define ZX_I2S_TIMING_CTRL 0x08 +#define ZX_I2S_FIFO_CTRL 0x0C +#define ZX_I2S_FIFO_STATUS 0x10 +#define ZX_I2S_INT_EN 0x14 +#define ZX_I2S_INT_STATUS 0x18 +#define ZX_I2S_DATA 0x1C +#define ZX_I2S_FRAME_CNTR 0x20 + +#define I2S_DEAGULT_FIFO_THRES (0x10) +#define I2S_MAX_FIFO_THRES (0x20) + +#define ZX_I2S_PROCESS_TX_EN (1 << 0) +#define ZX_I2S_PROCESS_TX_DIS (0 << 0) +#define ZX_I2S_PROCESS_RX_EN (1 << 1) +#define ZX_I2S_PROCESS_RX_DIS (0 << 1) +#define ZX_I2S_PROCESS_I2S_EN (1 << 2) +#define ZX_I2S_PROCESS_I2S_DIS (0 << 2) + +#define ZX_I2S_TIMING_MAST (1 << 0) +#define ZX_I2S_TIMING_SLAVE (0 << 0) +#define ZX_I2S_TIMING_MS_MASK (1 << 0) +#define ZX_I2S_TIMING_LOOP (1 << 1) +#define ZX_I2S_TIMING_NOR (0 << 1) +#define ZX_I2S_TIMING_LOOP_MASK (1 << 1) +#define ZX_I2S_TIMING_PTNR (1 << 2) +#define ZX_I2S_TIMING_NTPR (0 << 2) +#define ZX_I2S_TIMING_PHASE_MASK (1 << 2) +#define ZX_I2S_TIMING_TDM (1 << 3) +#define ZX_I2S_TIMING_I2S (0 << 3) +#define ZX_I2S_TIMING_TIMING_MASK (1 << 3) +#define ZX_I2S_TIMING_LONG_SYNC (1 << 4) +#define ZX_I2S_TIMING_SHORT_SYNC (0 << 4) +#define ZX_I2S_TIMING_SYNC_MASK (1 << 4) +#define ZX_I2S_TIMING_TEAK_EN (1 << 5) +#define ZX_I2S_TIMING_TEAK_DIS (0 << 5) +#define ZX_I2S_TIMING_TEAK_MASK (1 << 5) +#define ZX_I2S_TIMING_STD_I2S (0 << 6) +#define ZX_I2S_TIMING_MSB_JUSTIF (1 << 6) +#define ZX_I2S_TIMING_LSB_JUSTIF (2 << 6) +#define ZX_I2S_TIMING_ALIGN_MASK (3 << 6) +#define ZX_I2S_TIMING_CHN_MASK (7 << 8) +#define ZX_I2S_TIMING_CHN(x) ((x - 1) << 8) +#define ZX_I2S_TIMING_LANE_MASK (3 << 11) +#define ZX_I2S_TIMING_LANE(x) ((x - 1) << 11) +#define ZX_I2S_TIMING_TSCFG_MASK (7 << 13) +#define ZX_I2S_TIMING_TSCFG(x) (x << 13) +#define ZX_I2S_TIMING_TS_WIDTH_MASK (0x1f << 16) +#define ZX_I2S_TIMING_TS_WIDTH(x) ((x - 1) << 16) +#define ZX_I2S_TIMING_DATA_SIZE_MASK (0x1f << 21) +#define ZX_I2S_TIMING_DATA_SIZE(x) ((x - 1) << 21) +#define ZX_I2S_TIMING_CFG_ERR_MASK (1 << 31) + +#define ZX_I2S_FIFO_CTRL_TX_RST (1 << 0) +#define ZX_I2S_FIFO_CTRL_TX_RST_MASK (1 << 0) +#define ZX_I2S_FIFO_CTRL_RX_RST (1 << 1) +#define ZX_I2S_FIFO_CTRL_RX_RST_MASK (1 << 1) +#define ZX_I2S_FIFO_CTRL_TX_DMA_EN (1 << 4) +#define ZX_I2S_FIFO_CTRL_TX_DMA_DIS (0 << 4) +#define ZX_I2S_FIFO_CTRL_TX_DMA_MASK (1 << 4) +#define ZX_I2S_FIFO_CTRL_RX_DMA_EN (1 << 5) +#define ZX_I2S_FIFO_CTRL_RX_DMA_DIS (0 << 5) +#define ZX_I2S_FIFO_CTRL_RX_DMA_MASK (1 << 5) +#define ZX_I2S_FIFO_CTRL_TX_THRES_MASK (0x1F << 8) +#define ZX_I2S_FIFO_CTRL_RX_THRES_MASK (0x1F << 16) + +#define CLK_RAT (32 * 4) + +struct zx_i2s_info { + struct snd_dmaengine_dai_dma_data dma_playback; + struct snd_dmaengine_dai_dma_data dma_capture; + struct clk *dai_clk; + void __iomem *reg_base; + int master; + resource_size_t mapbase; +}; + +static void zx_i2s_tx_en(void __iomem *base, bool on) +{ + unsigned long val; + + val = readl_relaxed(base + ZX_I2S_PROCESS_CTRL); + if (on) + val |= ZX_I2S_PROCESS_TX_EN | ZX_I2S_PROCESS_I2S_EN; + else + val &= ~(ZX_I2S_PROCESS_TX_EN | ZX_I2S_PROCESS_I2S_EN); + writel_relaxed(val, base + ZX_I2S_PROCESS_CTRL); +} + +static void zx_i2s_rx_en(void __iomem *base, bool on) +{ + unsigned long val; + + val = readl_relaxed(base + ZX_I2S_PROCESS_CTRL); + if (on) + val |= ZX_I2S_PROCESS_RX_EN | ZX_I2S_PROCESS_I2S_EN; + else + val &= ~(ZX_I2S_PROCESS_RX_EN | ZX_I2S_PROCESS_I2S_EN); + writel_relaxed(val, base + ZX_I2S_PROCESS_CTRL); +} + +static void zx_i2s_tx_dma_en(void __iomem *base, bool on) +{ + unsigned long val; + + val = readl_relaxed(base + ZX_I2S_FIFO_CTRL); + val |= ZX_I2S_FIFO_CTRL_TX_RST | (I2S_DEAGULT_FIFO_THRES << 8); + if (on) + val |= ZX_I2S_FIFO_CTRL_TX_DMA_EN; + else + val &= ~ZX_I2S_FIFO_CTRL_TX_DMA_EN; + writel_relaxed(val, base + ZX_I2S_FIFO_CTRL); +} + +static void zx_i2s_rx_dma_en(void __iomem *base, bool on) +{ + unsigned long val; + + val = readl_relaxed(base + ZX_I2S_FIFO_CTRL); + val |= ZX_I2S_FIFO_CTRL_RX_RST | (I2S_DEAGULT_FIFO_THRES << 16); + if (on) + val |= ZX_I2S_FIFO_CTRL_RX_DMA_EN; + else + val &= ~ZX_I2S_FIFO_CTRL_RX_DMA_EN; + writel_relaxed(val, base + ZX_I2S_FIFO_CTRL); +} + +#define ZX_I2S_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | 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) + +#define ZX_I2S_FMTBIT \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static int zx_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev); + + snd_soc_dai_set_drvdata(dai, zx_i2s); + zx_i2s->dma_playback.addr = zx_i2s->mapbase + ZX_I2S_DATA; + zx_i2s->dma_playback.maxburst = 16; + zx_i2s->dma_capture.addr = zx_i2s->mapbase + ZX_I2S_DATA; + zx_i2s->dma_capture.maxburst = 16; + snd_soc_dai_init_dma_data(dai, &zx_i2s->dma_playback, + &zx_i2s->dma_capture); + return 0; +} + +static int zx_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct zx_i2s_info *i2s = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long val; + + val = readl_relaxed(i2s->reg_base + ZX_I2S_TIMING_CTRL); + val &= ~(ZX_I2S_TIMING_TIMING_MASK | ZX_I2S_TIMING_ALIGN_MASK | + ZX_I2S_TIMING_TEAK_MASK | ZX_I2S_TIMING_SYNC_MASK | + ZX_I2S_TIMING_MS_MASK); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val |= (ZX_I2S_TIMING_I2S | ZX_I2S_TIMING_STD_I2S); + break; + case SND_SOC_DAIFMT_LEFT_J: + val |= (ZX_I2S_TIMING_I2S | ZX_I2S_TIMING_MSB_JUSTIF); + break; + case SND_SOC_DAIFMT_RIGHT_J: + val |= (ZX_I2S_TIMING_I2S | ZX_I2S_TIMING_LSB_JUSTIF); + break; + default: + dev_err(cpu_dai->dev, "Unknown i2s timeing\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + i2s->master = 1; + val |= ZX_I2S_TIMING_MAST; + break; + case SND_SOC_DAIFMT_CBS_CFS: + i2s->master = 0; + val |= ZX_I2S_TIMING_SLAVE; + break; + default: + dev_err(cpu_dai->dev, "Unknown master/slave format\n"); + return -EINVAL; + } + + writel_relaxed(val, i2s->reg_base + ZX_I2S_TIMING_CTRL); + return 0; +} + +static int zx_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *socdai) +{ + struct zx_i2s_info *i2s = snd_soc_dai_get_drvdata(socdai); + struct snd_dmaengine_dai_dma_data *dma_data; + unsigned int lane, ch_num, len, ret = 0; + unsigned long val, format; + unsigned long chn_cfg; + + dma_data = snd_soc_dai_get_dma_data(socdai, substream); + dma_data->addr_width = params_width(params) >> 3; + + val = readl_relaxed(i2s->reg_base + ZX_I2S_TIMING_CTRL); + val &= ~(ZX_I2S_TIMING_TS_WIDTH_MASK | ZX_I2S_TIMING_DATA_SIZE_MASK | + ZX_I2S_TIMING_LANE_MASK | ZX_I2S_TIMING_CHN_MASK | + ZX_I2S_TIMING_TSCFG_MASK); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + format = 0; + len = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + format = 1; + len = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + format = 2; + len = 32; + break; + default: + dev_err(socdai->dev, "Unknown data format\n"); + return -EINVAL; + } + val |= ZX_I2S_TIMING_TS_WIDTH(len) | ZX_I2S_TIMING_DATA_SIZE(len); + + ch_num = params_channels(params); + switch (ch_num) { + case 1: + lane = 1; + chn_cfg = 2; + break; + case 2: + case 4: + case 6: + case 8: + lane = ch_num / 2; + chn_cfg = 3; + break; + default: + dev_err(socdai->dev, "Not support channel num %d\n", ch_num); + return -EINVAL; + } + val |= ZX_I2S_TIMING_LANE(lane); + val |= ZX_I2S_TIMING_TSCFG(chn_cfg); + val |= ZX_I2S_TIMING_CHN(ch_num); + writel_relaxed(val, i2s->reg_base + ZX_I2S_TIMING_CTRL); + + if (i2s->master) + ret = clk_set_rate(i2s->dai_clk, + params_rate(params) * ch_num * CLK_RAT); + return ret; +} + +static int zx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev); + int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (capture) + zx_i2s_rx_dma_en(zx_i2s->reg_base, true); + else + zx_i2s_tx_dma_en(zx_i2s->reg_base, true); + /* fall thru */ + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (capture) + zx_i2s_rx_en(zx_i2s->reg_base, true); + else + zx_i2s_tx_en(zx_i2s->reg_base, true); + break; + + case SNDRV_PCM_TRIGGER_STOP: + if (capture) + zx_i2s_rx_dma_en(zx_i2s->reg_base, false); + else + zx_i2s_tx_dma_en(zx_i2s->reg_base, false); + /* fall thru */ + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (capture) + zx_i2s_rx_en(zx_i2s->reg_base, false); + else + zx_i2s_tx_en(zx_i2s->reg_base, false); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int zx_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev); + + return clk_prepare_enable(zx_i2s->dai_clk); +} + +static void zx_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct zx_i2s_info *zx_i2s = dev_get_drvdata(dai->dev); + + clk_disable_unprepare(zx_i2s->dai_clk); +} + +static struct snd_soc_dai_ops zx_i2s_dai_ops = { + .trigger = zx_i2s_trigger, + .hw_params = zx_i2s_hw_params, + .set_fmt = zx_i2s_set_fmt, + .startup = zx_i2s_startup, + .shutdown = zx_i2s_shutdown, +}; + +static const struct snd_soc_component_driver zx_i2s_component = { + .name = "zx-i2s", +}; + +static struct snd_soc_dai_driver zx_i2s_dai = { + .name = "zx-i2s-dai", + .id = 0, + .probe = zx_i2s_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 8, + .rates = ZX_I2S_RATES, + .formats = ZX_I2S_FMTBIT, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = ZX_I2S_RATES, + .formats = ZX_I2S_FMTBIT, + }, + .ops = &zx_i2s_dai_ops, +}; + +static int zx_i2s_probe(struct platform_device *pdev) +{ + struct resource *res; + struct zx_i2s_info *zx_i2s; + int ret; + + zx_i2s = kzalloc(sizeof(*zx_i2s), GFP_KERNEL); + if (!zx_i2s) + return -ENOMEM; + + zx_i2s->dai_clk = devm_clk_get(&pdev->dev, "tx"); + if (IS_ERR(zx_i2s->dai_clk)) { + dev_err(&pdev->dev, "Fail to get clk\n"); + return PTR_ERR(zx_i2s->dai_clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + zx_i2s->mapbase = res->start; + zx_i2s->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(zx_i2s->reg_base)) { + dev_err(&pdev->dev, "ioremap failed!\n"); + return PTR_ERR(zx_i2s->reg_base); + } + + writel_relaxed(0, zx_i2s->reg_base + ZX_I2S_FIFO_CTRL); + platform_set_drvdata(pdev, zx_i2s); + + ret = snd_soc_register_component(&pdev->dev, &zx_i2s_component, + &zx_i2s_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Register DAI failed: %d\n", ret); + return ret; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret); + + return ret; +} + +static const struct of_device_id zx_i2s_dt_ids[] = { + { .compatible = "zte,zx296702-i2s", }, + {} +}; +MODULE_DEVICE_TABLE(of, zx_i2s_dt_ids); + +static struct platform_driver i2s_driver = { + .probe = zx_i2s_probe, + .driver = { + .name = "zx-i2s", + .of_match_table = zx_i2s_dt_ids, + }, +}; + +module_platform_driver(i2s_driver); + +MODULE_AUTHOR("Jun Nie <jun.nie@linaro.org>"); +MODULE_DESCRIPTION("ZTE I2S SoC DAI"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/zte/zx296702-spdif.c b/sound/soc/zte/zx296702-spdif.c new file mode 100644 index 000000000..26265ce4c --- /dev/null +++ b/sound/soc/zte/zx296702-spdif.c @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2015 Linaro + * + * Author: Jun Nie <jun.nie@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <sound/asoundef.h> +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#define ZX_CTRL 0x04 +#define ZX_FIFOCTRL 0x08 +#define ZX_INT_STATUS 0x10 +#define ZX_INT_MASK 0x14 +#define ZX_DATA 0x18 +#define ZX_VALID_BIT 0x1c +#define ZX_CH_STA_1 0x20 +#define ZX_CH_STA_2 0x24 +#define ZX_CH_STA_3 0x28 +#define ZX_CH_STA_4 0x2c +#define ZX_CH_STA_5 0x30 +#define ZX_CH_STA_6 0x34 + +#define ZX_CTRL_MODA_16 (0 << 6) +#define ZX_CTRL_MODA_18 BIT(6) +#define ZX_CTRL_MODA_20 (2 << 6) +#define ZX_CTRL_MODA_24 (3 << 6) +#define ZX_CTRL_MODA_MASK (3 << 6) + +#define ZX_CTRL_ENB BIT(4) +#define ZX_CTRL_DNB (0 << 4) +#define ZX_CTRL_ENB_MASK BIT(4) + +#define ZX_CTRL_TX_OPEN BIT(0) +#define ZX_CTRL_TX_CLOSE (0 << 0) +#define ZX_CTRL_TX_MASK BIT(0) + +#define ZX_CTRL_OPEN (ZX_CTRL_TX_OPEN | ZX_CTRL_ENB) +#define ZX_CTRL_CLOSE (ZX_CTRL_TX_CLOSE | ZX_CTRL_DNB) + +#define ZX_CTRL_DOUBLE_TRACK (0 << 8) +#define ZX_CTRL_LEFT_TRACK BIT(8) +#define ZX_CTRL_RIGHT_TRACK (2 << 8) +#define ZX_CTRL_TRACK_MASK (3 << 8) + +#define ZX_FIFOCTRL_TXTH_MASK (0x1f << 8) +#define ZX_FIFOCTRL_TXTH(x) (x << 8) +#define ZX_FIFOCTRL_TX_DMA_EN BIT(2) +#define ZX_FIFOCTRL_TX_DMA_DIS (0 << 2) +#define ZX_FIFOCTRL_TX_DMA_EN_MASK BIT(2) +#define ZX_FIFOCTRL_TX_FIFO_RST BIT(0) +#define ZX_FIFOCTRL_TX_FIFO_RST_MASK BIT(0) + +#define ZX_VALID_DOUBLE_TRACK (0 << 0) +#define ZX_VALID_LEFT_TRACK BIT(1) +#define ZX_VALID_RIGHT_TRACK (2 << 0) +#define ZX_VALID_TRACK_MASK (3 << 0) + +#define ZX_SPDIF_CLK_RAT (4 * 32) + +struct zx_spdif_info { + struct snd_dmaengine_dai_dma_data dma_data; + struct clk *dai_clk; + void __iomem *reg_base; + resource_size_t mapbase; +}; + +static int zx_spdif_dai_probe(struct snd_soc_dai *dai) +{ + struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev); + + snd_soc_dai_set_drvdata(dai, zx_spdif); + zx_spdif->dma_data.addr = zx_spdif->mapbase + ZX_DATA; + zx_spdif->dma_data.maxburst = 8; + snd_soc_dai_init_dma_data(dai, &zx_spdif->dma_data, NULL); + return 0; +} + +static int zx_spdif_chanstats(void __iomem *base, unsigned int rate) +{ + u32 cstas1; + + switch (rate) { + case 22050: + cstas1 = IEC958_AES3_CON_FS_22050; + break; + case 24000: + cstas1 = IEC958_AES3_CON_FS_24000; + break; + case 32000: + cstas1 = IEC958_AES3_CON_FS_32000; + break; + case 44100: + cstas1 = IEC958_AES3_CON_FS_44100; + break; + case 48000: + cstas1 = IEC958_AES3_CON_FS_48000; + break; + case 88200: + cstas1 = IEC958_AES3_CON_FS_88200; + break; + case 96000: + cstas1 = IEC958_AES3_CON_FS_96000; + break; + case 176400: + cstas1 = IEC958_AES3_CON_FS_176400; + break; + case 192000: + cstas1 = IEC958_AES3_CON_FS_192000; + break; + default: + return -EINVAL; + } + cstas1 = cstas1 << 24; + cstas1 |= IEC958_AES0_CON_NOT_COPYRIGHT; + + writel_relaxed(cstas1, base + ZX_CH_STA_1); + return 0; +} + +static int zx_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *socdai) +{ + struct zx_spdif_info *zx_spdif = dev_get_drvdata(socdai->dev); + struct zx_spdif_info *spdif = snd_soc_dai_get_drvdata(socdai); + struct snd_dmaengine_dai_dma_data *dma_data = &zx_spdif->dma_data; + u32 val, ch_num, rate; + int ret; + + dma_data = snd_soc_dai_get_dma_data(socdai, substream); + dma_data->addr_width = params_width(params) >> 3; + + val = readl_relaxed(zx_spdif->reg_base + ZX_CTRL); + val &= ~ZX_CTRL_MODA_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + val |= ZX_CTRL_MODA_16; + break; + + case SNDRV_PCM_FORMAT_S18_3LE: + val |= ZX_CTRL_MODA_18; + break; + + case SNDRV_PCM_FORMAT_S20_3LE: + val |= ZX_CTRL_MODA_20; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + val |= ZX_CTRL_MODA_24; + break; + default: + dev_err(socdai->dev, "Format not support!\n"); + return -EINVAL; + } + + ch_num = params_channels(params); + if (ch_num == 2) + val |= ZX_CTRL_DOUBLE_TRACK; + else + val |= ZX_CTRL_LEFT_TRACK; + writel_relaxed(val, zx_spdif->reg_base + ZX_CTRL); + + val = readl_relaxed(zx_spdif->reg_base + ZX_VALID_BIT); + val &= ~ZX_VALID_TRACK_MASK; + if (ch_num == 2) + val |= ZX_VALID_DOUBLE_TRACK; + else + val |= ZX_VALID_RIGHT_TRACK; + writel_relaxed(val, zx_spdif->reg_base + ZX_VALID_BIT); + + rate = params_rate(params); + ret = zx_spdif_chanstats(zx_spdif->reg_base, rate); + if (ret) + return ret; + return clk_set_rate(spdif->dai_clk, rate * ch_num * ZX_SPDIF_CLK_RAT); +} + +static void zx_spdif_cfg_tx(void __iomem *base, int on) +{ + u32 val; + + val = readl_relaxed(base + ZX_CTRL); + val &= ~(ZX_CTRL_ENB_MASK | ZX_CTRL_TX_MASK); + val |= on ? ZX_CTRL_OPEN : ZX_CTRL_CLOSE; + writel_relaxed(val, base + ZX_CTRL); + + val = readl_relaxed(base + ZX_FIFOCTRL); + val &= ~ZX_FIFOCTRL_TX_DMA_EN_MASK; + if (on) + val |= ZX_FIFOCTRL_TX_DMA_EN; + writel_relaxed(val, base + ZX_FIFOCTRL); +} + +static int zx_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + u32 val; + struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val = readl_relaxed(zx_spdif->reg_base + ZX_FIFOCTRL); + val |= ZX_FIFOCTRL_TX_FIFO_RST; + writel_relaxed(val, zx_spdif->reg_base + ZX_FIFOCTRL); + /* fall thru */ + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + zx_spdif_cfg_tx(zx_spdif->reg_base, true); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + zx_spdif_cfg_tx(zx_spdif->reg_base, false); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int zx_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev); + + return clk_prepare_enable(zx_spdif->dai_clk); +} + +static void zx_spdif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct zx_spdif_info *zx_spdif = dev_get_drvdata(dai->dev); + + clk_disable_unprepare(zx_spdif->dai_clk); +} + +#define ZX_RATES \ + (SNDRV_PCM_RATE_22050 | 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) + +#define ZX_FORMAT \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE \ + | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops zx_spdif_dai_ops = { + .trigger = zx_spdif_trigger, + .startup = zx_spdif_startup, + .shutdown = zx_spdif_shutdown, + .hw_params = zx_spdif_hw_params, +}; + +static struct snd_soc_dai_driver zx_spdif_dai = { + .name = "spdif", + .id = 0, + .probe = zx_spdif_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = ZX_RATES, + .formats = ZX_FORMAT, + }, + .ops = &zx_spdif_dai_ops, +}; + +static const struct snd_soc_component_driver zx_spdif_component = { + .name = "spdif", +}; + +static void zx_spdif_dev_init(void __iomem *base) +{ + u32 val; + + writel_relaxed(0, base + ZX_CTRL); + writel_relaxed(0, base + ZX_INT_MASK); + writel_relaxed(0xf, base + ZX_INT_STATUS); + writel_relaxed(0x1, base + ZX_FIFOCTRL); + + val = readl_relaxed(base + ZX_FIFOCTRL); + val &= ~(ZX_FIFOCTRL_TXTH_MASK | ZX_FIFOCTRL_TX_FIFO_RST_MASK); + val |= ZX_FIFOCTRL_TXTH(8); + writel_relaxed(val, base + ZX_FIFOCTRL); +} + +static int zx_spdif_probe(struct platform_device *pdev) +{ + struct resource *res; + struct zx_spdif_info *zx_spdif; + int ret; + + zx_spdif = devm_kzalloc(&pdev->dev, sizeof(*zx_spdif), GFP_KERNEL); + if (!zx_spdif) + return -ENOMEM; + + zx_spdif->dai_clk = devm_clk_get(&pdev->dev, "tx"); + if (IS_ERR(zx_spdif->dai_clk)) { + dev_err(&pdev->dev, "Fail to get clk\n"); + return PTR_ERR(zx_spdif->dai_clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + zx_spdif->mapbase = res->start; + zx_spdif->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(zx_spdif->reg_base)) { + dev_err(&pdev->dev, "ioremap failed!\n"); + return PTR_ERR(zx_spdif->reg_base); + } + + zx_spdif_dev_init(zx_spdif->reg_base); + platform_set_drvdata(pdev, zx_spdif); + + ret = devm_snd_soc_register_component(&pdev->dev, &zx_spdif_component, + &zx_spdif_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Register DAI failed: %d\n", ret); + return ret; + } + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (ret) + dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret); + + return ret; +} + +static const struct of_device_id zx_spdif_dt_ids[] = { + { .compatible = "zte,zx296702-spdif", }, + {} +}; +MODULE_DEVICE_TABLE(of, zx_spdif_dt_ids); + +static struct platform_driver spdif_driver = { + .probe = zx_spdif_probe, + .driver = { + .name = "zx-spdif", + .of_match_table = zx_spdif_dt_ids, + }, +}; + +module_platform_driver(spdif_driver); + +MODULE_AUTHOR("Jun Nie <jun.nie@linaro.org>"); +MODULE_DESCRIPTION("ZTE SPDIF SoC DAI"); +MODULE_LICENSE("GPL"); diff --git a/sound/sound_firmware.c b/sound/sound_firmware.c index b155137ee..026347643 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/sparc/amd7930.c b/sound/sparc/amd7930.c index 1b1a89e80..784ceb85b 100644 --- a/sound/sparc/amd7930.c +++ b/sound/sparc/amd7930.c @@ -956,6 +956,7 @@ static int snd_amd7930_create(struct snd_card *card, if (!amd->regs) { snd_printk(KERN_ERR "amd7930-%d: Unable to map chip registers.\n", dev); + kfree(amd); return -EIO; } diff --git a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile index 328594e61..fb761c2c2 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 49195325f..9312cd8a6 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 58a32a10d..a82b4053b 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 09711f84e..a7073c371 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 820d6ca8c..d060dddcc 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/card.c b/sound/usb/card.c index 1fab97788..045059398 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -638,7 +638,7 @@ int snd_usb_autoresume(struct snd_usb_audio *chip) int err = -ENODEV; down_read(&chip->shutdown_rwsem); - if (chip->probing && chip->in_pm) + if (chip->probing || chip->in_pm) err = 0; else if (!chip->shutdown) err = usb_autopm_get_interface(chip->pm_intf); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 8b7e391dd..6b3acba5d 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 }, }; diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 754e68959..00ebc0ca0 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1268,6 +1268,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; + case USB_ID(0x20b1, 0x000a): /* Gustard DAC-X20U */ case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */ case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */ if (fp->altsetting == 3) |