diff options
author | Dave Airlie <airlied@redhat.com> | 2015-04-20 03:32:26 +0200 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2015-04-20 05:05:20 +0200 |
commit | 2c33ce009ca2389dbf0535d0672214d09738e35e (patch) | |
tree | 6186a6458c3c160385d794a23eaf07c786a9e61b /sound | |
parent | media-bus: Fixup RGB444_1X12, RGB565_1X16, and YUV8_1X24 media bus format (diff) | |
parent | Merge branch 'turbostat' of git://git.kernel.org/pub/scm/linux/kernel/git/len... (diff) | |
download | linux-2c33ce009ca2389dbf0535d0672214d09738e35e.tar.xz linux-2c33ce009ca2389dbf0535d0672214d09738e35e.zip |
Merge Linus master into drm-next
The merge is clean, but the arm build fails afterwards,
due to API changes in the regulator tree.
I've included the patch into the merge to fix the build.
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'sound')
284 files changed, 14122 insertions, 9028 deletions
diff --git a/sound/Kconfig b/sound/Kconfig index c710ce2c5c37..5a240e050ae6 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -76,6 +76,8 @@ source "sound/isa/Kconfig" source "sound/pci/Kconfig" +source "sound/hda/Kconfig" + source "sound/ppc/Kconfig" source "sound/aoa/Kconfig" diff --git a/sound/Makefile b/sound/Makefile index ce9132b1c395..77320709fd26 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index b9737fae656a..1cbf210080a1 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -31,7 +31,7 @@ module_param(force, int, 0444); MODULE_PARM_DESC(force, "Force loading i2sbus even when" " no layout-id property is present"); -static struct of_device_id i2sbus_match[] = { +static const struct of_device_id i2sbus_match[] = { { .name = "i2s" }, { } }; diff --git a/sound/core/control.c b/sound/core/control.c index eeb691d1911f..196a6fe100ca 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -192,36 +192,41 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, EXPORT_SYMBOL(snd_ctl_notify); /** - * snd_ctl_new - create a control instance from the template - * @control: the control template - * @access: the default control access + * snd_ctl_new - create a new control instance with some elements + * @kctl: the pointer to store new control instance + * @count: the number of elements in this control + * @access: the default access flags for elements in this control + * @file: given when locking these elements * - * Allocates a new struct snd_kcontrol instance and copies the given template - * to the new instance. It does not copy volatile data (access). + * Allocates a memory object for a new control instance. The instance has + * elements as many as the given number (@count). Each element has given + * access permissions (@access). Each element is locked when @file is given. * - * Return: The pointer of the new instance, or %NULL on failure. + * Return: 0 on success, error code on failure */ -static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, - unsigned int access) +static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count, + unsigned int access, struct snd_ctl_file *file) { - struct snd_kcontrol *kctl; + unsigned int size; unsigned int idx; - if (snd_BUG_ON(!control || !control->count)) - return NULL; + if (count == 0 || count > MAX_CONTROL_COUNT) + return -EINVAL; - if (control->count > MAX_CONTROL_COUNT) - return NULL; + size = sizeof(struct snd_kcontrol); + size += sizeof(struct snd_kcontrol_volatile) * count; - kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL); - if (kctl == NULL) { - pr_err("ALSA: Cannot allocate control instance\n"); - return NULL; + *kctl = kzalloc(size, GFP_KERNEL); + if (!*kctl) + return -ENOMEM; + + for (idx = 0; idx < count; idx++) { + (*kctl)->vd[idx].access = access; + (*kctl)->vd[idx].owner = file; } - *kctl = *control; - for (idx = 0; idx < kctl->count; idx++) - kctl->vd[idx].access = access; - return kctl; + (*kctl)->count = count; + + return 0; } /** @@ -238,37 +243,53 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, void *private_data) { - struct snd_kcontrol kctl; + struct snd_kcontrol *kctl; + unsigned int count; unsigned int access; + int err; if (snd_BUG_ON(!ncontrol || !ncontrol->info)) return NULL; - memset(&kctl, 0, sizeof(kctl)); - kctl.id.iface = ncontrol->iface; - kctl.id.device = ncontrol->device; - kctl.id.subdevice = ncontrol->subdevice; + + count = ncontrol->count; + if (count == 0) + count = 1; + + access = ncontrol->access; + if (access == 0) + access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); + + err = snd_ctl_new(&kctl, count, access, NULL); + if (err < 0) + return NULL; + + /* The 'numid' member is decided when calling snd_ctl_add(). */ + kctl->id.iface = ncontrol->iface; + kctl->id.device = ncontrol->device; + kctl->id.subdevice = ncontrol->subdevice; if (ncontrol->name) { - strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); - if (strcmp(ncontrol->name, kctl.id.name) != 0) + strlcpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name)); + if (strcmp(ncontrol->name, kctl->id.name) != 0) pr_warn("ALSA: Control name '%s' truncated to '%s'\n", - ncontrol->name, kctl.id.name); + ncontrol->name, kctl->id.name); } - kctl.id.index = ncontrol->index; - kctl.count = ncontrol->count ? ncontrol->count : 1; - access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : - (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| - SNDRV_CTL_ELEM_ACCESS_VOLATILE| - SNDRV_CTL_ELEM_ACCESS_INACTIVE| - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| - SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND| - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); - kctl.info = ncontrol->info; - kctl.get = ncontrol->get; - kctl.put = ncontrol->put; - kctl.tlv.p = ncontrol->tlv.p; - kctl.private_value = ncontrol->private_value; - kctl.private_data = private_data; - return snd_ctl_new(&kctl, access); + kctl->id.index = ncontrol->index; + + kctl->info = ncontrol->info; + kctl->get = ncontrol->get; + kctl->put = ncontrol->put; + kctl->tlv.p = ncontrol->tlv.p; + + kctl->private_value = ncontrol->private_value; + kctl->private_data = private_data; + + return kctl; } EXPORT_SYMBOL(snd_ctl_new1); @@ -557,6 +578,7 @@ error: * * Finds the control instance with the given id, and activate or * inactivate the control together with notification, if changed. + * The given ID data is filled with full information. * * Return: 0 if unchanged, 1 if changed, or a negative error code on failure. */ @@ -586,6 +608,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, goto unlock; vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; } + snd_ctl_build_ioff(id, kctl, index_offset); ret = 1; unlock: up_write(&card->controls_rwsem); @@ -1006,7 +1029,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, struct user_element { struct snd_ctl_elem_info info; struct snd_card *card; - void *elem_data; /* element data */ + char *elem_data; /* element data */ unsigned long elem_data_size; /* size of element data in bytes */ void *tlv_data; /* TLV data */ unsigned long tlv_data_size; /* TLV data size */ @@ -1017,8 +1040,12 @@ static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct user_element *ue = kcontrol->private_data; + unsigned int offset; + offset = snd_ctl_get_ioff(kcontrol, &uinfo->id); *uinfo = ue->info; + snd_ctl_build_ioff(&uinfo->id, kcontrol, offset); + return 0; } @@ -1028,10 +1055,13 @@ static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol, struct user_element *ue = kcontrol->private_data; const char *names; unsigned int item; + unsigned int offset; item = uinfo->value.enumerated.item; + offset = snd_ctl_get_ioff(kcontrol, &uinfo->id); *uinfo = ue->info; + snd_ctl_build_ioff(&uinfo->id, kcontrol, offset); item = min(item, uinfo->value.enumerated.items - 1); uinfo->value.enumerated.item = item; @@ -1048,9 +1078,12 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct user_element *ue = kcontrol->private_data; + unsigned int size = ue->elem_data_size; + char *src = ue->elem_data + + snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size; mutex_lock(&ue->card->user_ctl_lock); - memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size); + memcpy(&ucontrol->value, src, size); mutex_unlock(&ue->card->user_ctl_lock); return 0; } @@ -1060,11 +1093,14 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol, { int change; struct user_element *ue = kcontrol->private_data; + unsigned int size = ue->elem_data_size; + char *dst = ue->elem_data + + snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size; mutex_lock(&ue->card->user_ctl_lock); - change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0; + change = memcmp(&ucontrol->value, dst, size) != 0; if (change) - memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); + memcpy(dst, &ucontrol->value, size); mutex_unlock(&ue->card->user_ctl_lock); return change; } @@ -1078,7 +1114,7 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol, int change = 0; void *new_data; - if (op_flag > 0) { + if (op_flag == SNDRV_CTL_TLV_OP_WRITE) { if (size > 1024 * 128) /* sane value */ return -EINVAL; @@ -1161,84 +1197,103 @@ static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) static int snd_ctl_elem_add(struct snd_ctl_file *file, struct snd_ctl_elem_info *info, int replace) { + /* The capacity of struct snd_ctl_elem_value.value.*/ + static const unsigned int value_sizes[] = { + [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long), + [SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long), + [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int), + [SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char), + [SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958), + [SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long), + }; + static const unsigned int max_value_counts[] = { + [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128, + [SNDRV_CTL_ELEM_TYPE_INTEGER] = 128, + [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128, + [SNDRV_CTL_ELEM_TYPE_BYTES] = 512, + [SNDRV_CTL_ELEM_TYPE_IEC958] = 1, + [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64, + }; struct snd_card *card = file->card; - struct snd_kcontrol kctl, *_kctl; + struct snd_kcontrol *kctl; + unsigned int count; unsigned int access; long private_size; struct user_element *ue; - int idx, err; + unsigned int offset; + int err; - if (info->count < 1) - return -EINVAL; if (!*info->id.name) return -EINVAL; if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name)) return -EINVAL; - access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : - (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| - SNDRV_CTL_ELEM_ACCESS_INACTIVE| - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)); - info->id.numid = 0; - memset(&kctl, 0, sizeof(kctl)); + /* Delete a control to replace them if needed. */ if (replace) { + info->id.numid = 0; err = snd_ctl_remove_user_ctl(file, &info->id); if (err) return err; } - if (card->user_ctl_count >= MAX_USER_CONTROLS) + /* + * The number of userspace controls are counted control by control, + * not element by element. + */ + if (card->user_ctl_count + 1 > MAX_USER_CONTROLS) return -ENOMEM; - memcpy(&kctl.id, &info->id, sizeof(info->id)); - kctl.count = info->owner ? info->owner : 1; - access |= SNDRV_CTL_ELEM_ACCESS_USER; - if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) - kctl.info = snd_ctl_elem_user_enum_info; - else - kctl.info = snd_ctl_elem_user_info; - if (access & SNDRV_CTL_ELEM_ACCESS_READ) - kctl.get = snd_ctl_elem_user_get; - if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) - kctl.put = snd_ctl_elem_user_put; - if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { - kctl.tlv.c = snd_ctl_elem_user_tlv; + /* Check the number of elements for this userspace control. */ + count = info->owner; + if (count == 0) + count = 1; + + /* Arrange access permissions if needed. */ + access = info->access; + if (access == 0) + access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE); + if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; - } - switch (info->type) { - case SNDRV_CTL_ELEM_TYPE_BOOLEAN: - case SNDRV_CTL_ELEM_TYPE_INTEGER: - private_size = sizeof(long); - if (info->count > 128) - return -EINVAL; - break; - case SNDRV_CTL_ELEM_TYPE_INTEGER64: - private_size = sizeof(long long); - if (info->count > 64) - return -EINVAL; - break; - case SNDRV_CTL_ELEM_TYPE_ENUMERATED: - private_size = sizeof(unsigned int); - if (info->count > 128 || info->value.enumerated.items == 0) - return -EINVAL; - break; - case SNDRV_CTL_ELEM_TYPE_BYTES: - private_size = sizeof(unsigned char); - if (info->count > 512) - return -EINVAL; - break; - case SNDRV_CTL_ELEM_TYPE_IEC958: - private_size = sizeof(struct snd_aes_iec958); - if (info->count != 1) - return -EINVAL; - break; - default: + access |= SNDRV_CTL_ELEM_ACCESS_USER; + + /* + * Check information and calculate the size of data specific to + * this userspace control. + */ + if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN || + info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) return -EINVAL; - } - private_size *= info->count; - ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); - if (ue == NULL) + if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED && + info->value.enumerated.items == 0) + return -EINVAL; + if (info->count < 1 || + info->count > max_value_counts[info->type]) + return -EINVAL; + private_size = value_sizes[info->type] * info->count; + + /* + * Keep memory object for this userspace control. After passing this + * code block, the instance should be freed by snd_ctl_free_one(). + * + * Note that these elements in this control are locked. + */ + err = snd_ctl_new(&kctl, count, access, file); + if (err < 0) + return err; + memcpy(&kctl->id, &info->id, sizeof(kctl->id)); + kctl->private_data = kzalloc(sizeof(struct user_element) + private_size * count, + GFP_KERNEL); + if (kctl->private_data == NULL) { + kfree(kctl); return -ENOMEM; + } + kctl->private_free = snd_ctl_elem_user_free; + + /* Set private data for this userspace control. */ + ue = (struct user_element *)kctl->private_data; ue->card = card; ue->info = *info; ue->info.access = 0; @@ -1247,23 +1302,36 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { err = snd_ctl_elem_init_enum_names(ue); if (err < 0) { - kfree(ue); + snd_ctl_free_one(kctl); return err; } } - kctl.private_free = snd_ctl_elem_user_free; - _kctl = snd_ctl_new(&kctl, access); - if (_kctl == NULL) { - kfree(ue->priv_data); - kfree(ue); - return -ENOMEM; - } - _kctl->private_data = ue; - for (idx = 0; idx < _kctl->count; idx++) - _kctl->vd[idx].owner = file; - err = snd_ctl_add(card, _kctl); + + /* Set callback functions. */ + if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) + kctl->info = snd_ctl_elem_user_enum_info; + else + kctl->info = snd_ctl_elem_user_info; + if (access & SNDRV_CTL_ELEM_ACCESS_READ) + kctl->get = snd_ctl_elem_user_get; + if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) + kctl->put = snd_ctl_elem_user_put; + if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) + kctl->tlv.c = snd_ctl_elem_user_tlv; + + /* This function manage to free the instance on failure. */ + err = snd_ctl_add(card, kctl); if (err < 0) return err; + offset = snd_ctl_get_ioff(kctl, &info->id); + snd_ctl_build_ioff(&info->id, kctl, offset); + /* + * Here we cannot fill any field for the number of elements added by + * this operation because there're no specific fields. The usage of + * 'owner' field for this purpose may cause any bugs to userspace + * applications because the field originally means PID of a process + * which locks the element. + */ down_write(&card->controls_rwsem); card->user_ctl_count++; @@ -1276,9 +1344,19 @@ static int snd_ctl_elem_add_user(struct snd_ctl_file *file, struct snd_ctl_elem_info __user *_info, int replace) { struct snd_ctl_elem_info info; + int err; + if (copy_from_user(&info, _info, sizeof(info))) return -EFAULT; - return snd_ctl_elem_add(file, &info, replace); + err = snd_ctl_elem_add(file, &info, replace); + if (err < 0) + return err; + if (copy_to_user(_info, &info, sizeof(info))) { + snd_ctl_remove_user_ctl(file, &info.id); + return -EFAULT; + } + + return 0; } static int snd_ctl_elem_remove(struct snd_ctl_file *file, @@ -1338,9 +1416,12 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, goto __kctl_end; } vd = &kctl->vd[tlv.numid - kctl->id.numid]; - if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) || - (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) || - (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) { + if ((op_flag == SNDRV_CTL_TLV_OP_READ && + (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) || + (op_flag == SNDRV_CTL_TLV_OP_WRITE && + (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) || + (op_flag == SNDRV_CTL_TLV_OP_CMD && + (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) { err = -ENXIO; goto __kctl_end; } @@ -1357,7 +1438,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, return 0; } } else { - if (op_flag) { + if (op_flag != SNDRV_CTL_TLV_OP_READ) { err = -ENXIO; goto __kctl_end; } diff --git a/sound/core/device.c b/sound/core/device.c index 41bec3075ae5..8918838b1999 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -50,10 +50,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, if (snd_BUG_ON(!card || !device_data || !ops)) return -ENXIO; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - dev_err(card->dev, "Cannot allocate device, type=%d\n", type); + if (!dev) return -ENOMEM; - } INIT_LIST_HEAD(&dev->list); dev->card = card; dev->type = type; @@ -73,7 +71,7 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, } EXPORT_SYMBOL(snd_device_new); -static int __snd_device_disconnect(struct snd_device *dev) +static void __snd_device_disconnect(struct snd_device *dev) { if (dev->state == SNDRV_DEV_REGISTERED) { if (dev->ops->dev_disconnect && @@ -81,7 +79,6 @@ static int __snd_device_disconnect(struct snd_device *dev) dev_err(dev->card->dev, "device disconnect failure\n"); dev->state = SNDRV_DEV_DISCONNECTED; } - return 0; } static void __snd_device_free(struct snd_device *dev) @@ -109,6 +106,34 @@ static struct snd_device *look_for_dev(struct snd_card *card, void *device_data) } /** + * snd_device_disconnect - disconnect the device + * @card: the card instance + * @device_data: the data pointer to disconnect + * + * Turns the device into the disconnection state, invoking + * dev_disconnect callback, if the device was already registered. + * + * Usually called from snd_card_disconnect(). + * + * Return: Zero if successful, or a negative error code on failure or if the + * device not found. + */ +void snd_device_disconnect(struct snd_card *card, void *device_data) +{ + struct snd_device *dev; + + if (snd_BUG_ON(!card || !device_data)) + return; + dev = look_for_dev(card, device_data); + if (dev) + __snd_device_disconnect(dev); + else + dev_dbg(card->dev, "device disconnect %p (from %pF), not found\n", + device_data, __builtin_return_address(0)); +} +EXPORT_SYMBOL_GPL(snd_device_disconnect); + +/** * snd_device_free - release the device from the card * @card: the card instance * @device_data: the data pointer to release @@ -195,18 +220,14 @@ int snd_device_register_all(struct snd_card *card) * disconnect all the devices on the card. * called from init.c */ -int snd_device_disconnect_all(struct snd_card *card) +void snd_device_disconnect_all(struct snd_card *card) { struct snd_device *dev; - int err = 0; if (snd_BUG_ON(!card)) - return -ENXIO; - list_for_each_entry_reverse(dev, &card->devices, list) { - if (__snd_device_disconnect(dev) < 0) - err = -ENXIO; - } - return err; + return; + list_for_each_entry_reverse(dev, &card->devices, list) + __snd_device_disconnect(dev); } /* diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 84244a5143cf..51692c8a39ea 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -378,10 +378,8 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device, if (rhwdep) *rhwdep = NULL; hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL); - if (hwdep == NULL) { - dev_err(card->dev, "hwdep: cannot allocate\n"); + if (!hwdep) return -ENOMEM; - } init_waitqueue_head(&hwdep->open_wait); mutex_init(&hwdep->open_mutex); diff --git a/sound/core/init.c b/sound/core/init.c index 35419054821c..04734e047bfe 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -400,7 +400,6 @@ static const struct file_operations snd_shutdown_f_ops = int snd_card_disconnect(struct snd_card *card) { struct snd_monitor_file *mfile; - int err; if (!card) return -EINVAL; @@ -445,9 +444,7 @@ int snd_card_disconnect(struct snd_card *card) #endif /* notify all devices that we are disconnected */ - err = snd_device_disconnect_all(card); - if (err < 0) - dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number); + snd_device_disconnect_all(card); snd_info_card_disconnect(card); if (card->registered) { diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 5e6349f00ecd..056f8e274851 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1212,10 +1212,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, /* not changed */ goto __unlock; tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); - if (! tbl) { - pr_err("ALSA: mixer_oss: no memory\n"); + if (!tbl) goto __unlock; - } tbl->oss_id = ch; tbl->name = kstrdup(str, GFP_KERNEL); if (! tbl->name) { diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 80423a4ccab6..58550cc93f28 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -854,7 +854,6 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) params = kmalloc(sizeof(*params), GFP_KERNEL); sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); if (!sw_params || !params || !sparams) { - pcm_dbg(substream->pcm, "No memory\n"); err = -ENOMEM; goto failure; } diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 0345e53a340c..b25bcf5b8644 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -49,8 +49,6 @@ static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device) struct snd_pcm *pcm; list_for_each_entry(pcm, &snd_pcm_devices, list) { - if (pcm->internal) - continue; if (pcm->card == card && pcm->device == device) return pcm; } @@ -62,8 +60,6 @@ static int snd_pcm_next(struct snd_card *card, int device) struct snd_pcm *pcm; list_for_each_entry(pcm, &snd_pcm_devices, list) { - if (pcm->internal) - continue; if (pcm->card == card && pcm->device > device) return pcm->device; else if (pcm->card->number > card->number) @@ -76,6 +72,9 @@ static int snd_pcm_add(struct snd_pcm *newpcm) { struct snd_pcm *pcm; + if (newpcm->internal) + return 0; + list_for_each_entry(pcm, &snd_pcm_devices, list) { if (pcm->card == newpcm->card && pcm->device == newpcm->device) return -EBUSY; @@ -344,11 +343,8 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, return; info = kmalloc(sizeof(*info), GFP_KERNEL); - if (! info) { - pcm_dbg(substream->pcm, - "snd_pcm_proc_info_read: cannot malloc\n"); + if (!info) return; - } err = snd_pcm_info(substream, info); if (err < 0) { @@ -718,10 +714,8 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) prev = NULL; for (idx = 0, prev = NULL; idx < substream_count; idx++) { substream = kzalloc(sizeof(*substream), GFP_KERNEL); - if (substream == NULL) { - pcm_err(pcm, "Cannot allocate PCM substream\n"); + if (!substream) return -ENOMEM; - } substream->pcm = pcm; substream->pstr = pstr; substream->number = idx; @@ -775,13 +769,14 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device, if (rpcm) *rpcm = NULL; pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); - if (pcm == NULL) { - dev_err(card->dev, "Cannot allocate PCM\n"); + if (!pcm) return -ENOMEM; - } pcm->card = card; pcm->device = device; pcm->internal = internal; + mutex_init(&pcm->open_mutex); + init_waitqueue_head(&pcm->open_wait); + INIT_LIST_HEAD(&pcm->list); if (id) strlcpy(pcm->id, id, sizeof(pcm->id)); if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { @@ -792,8 +787,6 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device, snd_pcm_free(pcm); return err; } - mutex_init(&pcm->open_mutex); - init_waitqueue_head(&pcm->open_wait); if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { snd_pcm_free(pcm); return err; @@ -888,8 +881,9 @@ static int snd_pcm_free(struct snd_pcm *pcm) if (!pcm) return 0; - list_for_each_entry(notify, &snd_pcm_notify_list, list) { - notify->n_unregister(pcm); + if (!pcm->internal) { + list_for_each_entry(notify, &snd_pcm_notify_list, list) + notify->n_unregister(pcm); } if (pcm->private_free) pcm->private_free(pcm); @@ -919,6 +913,9 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, if (snd_BUG_ON(!pcm || !rsubstream)) return -ENXIO; + if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK && + stream != SNDRV_PCM_STREAM_CAPTURE)) + return -EINVAL; *rsubstream = NULL; pstr = &pcm->streams[stream]; if (pstr->substream == NULL || pstr->substream_count == 0) @@ -927,25 +924,14 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, card = pcm->card; prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM); - switch (stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { - for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { - if (SUBSTREAM_BUSY(substream)) - return -EAGAIN; - } - } - break; - case SNDRV_PCM_STREAM_CAPTURE: - if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { - for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { - if (SUBSTREAM_BUSY(substream)) - return -EAGAIN; - } + if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { + int opposite = !stream; + + for (substream = pcm->streams[opposite].substream; substream; + substream = substream->next) { + if (SUBSTREAM_BUSY(substream)) + return -EAGAIN; } - break; - default: - return -EINVAL; } if (file->f_flags & O_APPEND) { @@ -968,15 +954,12 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, return 0; } - if (prefer_subdevice >= 0) { - for (substream = pstr->substream; substream; substream = substream->next) - if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) - goto __ok; - } - for (substream = pstr->substream; substream; substream = substream->next) - if (!SUBSTREAM_BUSY(substream)) + for (substream = pstr->substream; substream; substream = substream->next) { + if (!SUBSTREAM_BUSY(substream) && + (prefer_subdevice == -1 || + substream->number == prefer_subdevice)) break; - __ok: + } if (substream == NULL) return -EAGAIN; @@ -1086,15 +1069,16 @@ static int snd_pcm_dev_register(struct snd_device *device) if (snd_BUG_ON(!device || !device->device_data)) return -ENXIO; pcm = device->device_data; + if (pcm->internal) + return 0; + mutex_lock(®ister_mutex); err = snd_pcm_add(pcm); - if (err) { - mutex_unlock(®ister_mutex); - return err; - } + if (err) + goto unlock; for (cidx = 0; cidx < 2; cidx++) { int devtype = -1; - if (pcm->streams[cidx].substream == NULL || pcm->internal) + if (pcm->streams[cidx].substream == NULL) continue; switch (cidx) { case SNDRV_PCM_STREAM_PLAYBACK: @@ -1109,9 +1093,8 @@ static int snd_pcm_dev_register(struct snd_device *device) &snd_pcm_f_ops[cidx], pcm, &pcm->streams[cidx].dev); if (err < 0) { - list_del(&pcm->list); - mutex_unlock(®ister_mutex); - return err; + list_del_init(&pcm->list); + goto unlock; } for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) @@ -1121,8 +1104,9 @@ static int snd_pcm_dev_register(struct snd_device *device) list_for_each_entry(notify, &snd_pcm_notify_list, list) notify->n_register(pcm); + unlock: mutex_unlock(®ister_mutex); - return 0; + return err; } static int snd_pcm_dev_disconnect(struct snd_device *device) @@ -1133,13 +1117,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) int cidx; mutex_lock(®ister_mutex); - if (list_empty(&pcm->list)) - goto unlock; - mutex_lock(&pcm->open_mutex); wake_up(&pcm->open_wait); list_del_init(&pcm->list); - for (cidx = 0; cidx < 2; cidx++) + for (cidx = 0; cidx < 2; cidx++) { for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { snd_pcm_stream_lock_irq(substream); if (substream->runtime) { @@ -1149,18 +1130,20 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) } snd_pcm_stream_unlock_irq(substream); } - list_for_each_entry(notify, &snd_pcm_notify_list, list) { - notify->n_disconnect(pcm); + } + if (!pcm->internal) { + list_for_each_entry(notify, &snd_pcm_notify_list, list) + notify->n_disconnect(pcm); } for (cidx = 0; cidx < 2; cidx++) { - snd_unregister_device(&pcm->streams[cidx].dev); + if (!pcm->internal) + snd_unregister_device(&pcm->streams[cidx].dev); if (pcm->streams[cidx].chmap_kctl) { snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl); pcm->streams[cidx].chmap_kctl = NULL; } } mutex_unlock(&pcm->open_mutex); - unlock: mutex_unlock(®ister_mutex); return 0; } diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 2d957ba63557..b48b434444ed 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -194,18 +194,30 @@ struct snd_pcm_status32 { u32 avail_max; u32 overrange; s32 suspended_state; - u32 reserved_alignment; + u32 audio_tstamp_data; struct compat_timespec audio_tstamp; - unsigned char reserved[56-sizeof(struct compat_timespec)]; + struct compat_timespec driver_tstamp; + u32 audio_tstamp_accuracy; + unsigned char reserved[52-2*sizeof(struct compat_timespec)]; } __attribute__((packed)); static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, - struct snd_pcm_status32 __user *src) + struct snd_pcm_status32 __user *src, + bool ext) { struct snd_pcm_status status; int err; + memset(&status, 0, sizeof(status)); + /* + * with extension, parameters are read/write, + * get audio_tstamp_data from user, + * ignore rest of status structure + */ + if (ext && get_user(status.audio_tstamp_data, + (u32 __user *)(&src->audio_tstamp_data))) + return -EFAULT; err = snd_pcm_status(substream, &status); if (err < 0) return err; @@ -222,7 +234,10 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, put_user(status.avail_max, &src->avail_max) || put_user(status.overrange, &src->overrange) || put_user(status.suspended_state, &src->suspended_state) || - compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp)) + put_user(status.audio_tstamp_data, &src->audio_tstamp_data) || + compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) || + compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) || + put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy)) return -EFAULT; return err; @@ -457,6 +472,7 @@ enum { SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32), SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32), SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32), + SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32), SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32), SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32), SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), @@ -517,7 +533,9 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l case SNDRV_PCM_IOCTL_SW_PARAMS32: return snd_pcm_ioctl_sw_params_compat(substream, argp); case SNDRV_PCM_IOCTL_STATUS32: - return snd_pcm_status_user_compat(substream, argp); + return snd_pcm_status_user_compat(substream, argp, false); + case SNDRV_PCM_IOCTL_STATUS_EXT32: + return snd_pcm_status_user_compat(substream, argp, true); case SNDRV_PCM_IOCTL_SYNC_PTR32: return snd_pcm_ioctl_sync_ptr_compat(substream, argp); case SNDRV_PCM_IOCTL_CHANNEL_INFO32: diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 6542c4083594..fba365a78390 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -289,7 +289,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel); * * The function should usually be called from the pcm open callback. Note that * this function will use private_data field of the substream's runtime. So it - * is not availabe to your pcm driver implementation. + * is not available to your pcm driver implementation. */ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, struct dma_chan *chan) @@ -328,7 +328,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); * This function will request a DMA channel using the passed filter function and * data. The function should usually be called from the pcm open callback. Note * that this function will use private_data field of the substream's runtime. So - * it is not availabe to your pcm driver implementation. + * it is not available to your pcm driver implementation. */ int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ffd656012ab8..ac6b33f3779c 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -232,6 +232,49 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, return 0; } +static void update_audio_tstamp(struct snd_pcm_substream *substream, + struct timespec *curr_tstamp, + struct timespec *audio_tstamp) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + u64 audio_frames, audio_nsecs; + struct timespec driver_tstamp; + + if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE) + return; + + if (!(substream->ops->get_time_info) || + (runtime->audio_tstamp_report.actual_type == + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { + + /* + * provide audio timestamp derived from pointer position + * add delay only if requested + */ + + audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr; + + if (runtime->audio_tstamp_config.report_delay) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + audio_frames -= runtime->delay; + else + audio_frames += runtime->delay; + } + audio_nsecs = div_u64(audio_frames * 1000000000LL, + runtime->rate); + *audio_tstamp = ns_to_timespec(audio_nsecs); + } + runtime->status->audio_tstamp = *audio_tstamp; + runtime->status->tstamp = *curr_tstamp; + + /* + * re-take a driver timestamp to let apps detect if the reference tstamp + * read by low-level hardware was provided with a delay + */ + snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp); + runtime->driver_tstamp = driver_tstamp; +} + static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, unsigned int in_interrupt) { @@ -256,11 +299,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, pos = substream->ops->pointer(substream); curr_jiffies = jiffies; if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { - snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); - - if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) && - (substream->ops->wall_clock)) - substream->ops->wall_clock(substream, &audio_tstamp); + if ((substream->ops->get_time_info) && + (runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { + substream->ops->get_time_info(substream, &curr_tstamp, + &audio_tstamp, + &runtime->audio_tstamp_config, + &runtime->audio_tstamp_report); + + /* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */ + if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT) + snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); + } else + snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); } if (pos == SNDRV_PCM_POS_XRUN) { @@ -403,8 +453,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, } no_delta_check: - if (runtime->status->hw_ptr == new_hw_ptr) + if (runtime->status->hw_ptr == new_hw_ptr) { + update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return 0; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) @@ -426,30 +478,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, snd_BUG_ON(crossed_boundary != 1); runtime->hw_ptr_wrap += runtime->boundary; } - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { - runtime->status->tstamp = curr_tstamp; - if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) { - /* - * no wall clock available, provide audio timestamp - * derived from pointer position+delay - */ - u64 audio_frames, audio_nsecs; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - audio_frames = runtime->hw_ptr_wrap - + runtime->status->hw_ptr - - runtime->delay; - else - audio_frames = runtime->hw_ptr_wrap - + runtime->status->hw_ptr - + runtime->delay; - audio_nsecs = div_u64(audio_frames * 1000000000LL, - runtime->rate); - audio_tstamp = ns_to_timespec(audio_nsecs); - } - runtime->status->audio_tstamp = audio_tstamp; - } + update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return snd_pcm_update_state(substream, runtime); } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 279e24f61305..d126c03361ae 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -25,7 +25,6 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/pm_qos.h> -#include <linux/aio.h> #include <linux/io.h> #include <linux/dma-mapping.h> #include <sound/core.h> @@ -35,6 +34,7 @@ #include <sound/pcm_params.h> #include <sound/timer.h> #include <sound/minors.h> +#include <linux/uio.h> /* * Compatibility @@ -707,6 +707,23 @@ int snd_pcm_status(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_stream_lock_irq(substream); + + snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data, + &runtime->audio_tstamp_config); + + /* backwards compatible behavior */ + if (runtime->audio_tstamp_config.type_requested == + SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT) { + if (runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) + runtime->audio_tstamp_config.type_requested = + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; + else + runtime->audio_tstamp_config.type_requested = + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; + runtime->audio_tstamp_report.valid = 0; + } else + runtime->audio_tstamp_report.valid = 1; + status->state = runtime->status->state; status->suspended_state = runtime->status->suspended_state; if (status->state == SNDRV_PCM_STATE_OPEN) @@ -716,8 +733,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream, snd_pcm_update_hw_ptr(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { status->tstamp = runtime->status->tstamp; + status->driver_tstamp = runtime->driver_tstamp; status->audio_tstamp = runtime->status->audio_tstamp; + if (runtime->audio_tstamp_report.valid == 1) + /* backwards compatibility, no report provided in COMPAT mode */ + snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data, + &status->audio_tstamp_accuracy, + &runtime->audio_tstamp_report); + goto _tstamp_end; } } else { @@ -753,12 +777,21 @@ int snd_pcm_status(struct snd_pcm_substream *substream, } static int snd_pcm_status_user(struct snd_pcm_substream *substream, - struct snd_pcm_status __user * _status) + struct snd_pcm_status __user * _status, + bool ext) { struct snd_pcm_status status; int res; - + memset(&status, 0, sizeof(status)); + /* + * with extension, parameters are read/write, + * get audio_tstamp_data from user, + * ignore rest of status structure + */ + if (ext && get_user(status.audio_tstamp_data, + (u32 __user *)(&_status->audio_tstamp_data))) + return -EFAULT; res = snd_pcm_status(substream, &status); if (res < 0) return res; @@ -2725,7 +2758,9 @@ static int snd_pcm_common_ioctl1(struct file *file, case SNDRV_PCM_IOCTL_SW_PARAMS: return snd_pcm_sw_params_user(substream, arg); case SNDRV_PCM_IOCTL_STATUS: - return snd_pcm_status_user(substream, arg); + return snd_pcm_status_user(substream, arg, false); + case SNDRV_PCM_IOCTL_STATUS_EXT: + return snd_pcm_status_user(substream, arg, true); case SNDRV_PCM_IOCTL_CHANNEL_INFO: return snd_pcm_channel_info_user(substream, arg); case SNDRV_PCM_IOCTL_PREPARE: @@ -3033,9 +3068,7 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf, return result; } -static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) - +static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to) { struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream; @@ -3052,16 +3085,18 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov, runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; - if (nr_segs > 1024 || nr_segs != runtime->channels) + if (!iter_is_iovec(to)) + return -EINVAL; + if (to->nr_segs > 1024 || to->nr_segs != runtime->channels) return -EINVAL; - if (!frame_aligned(runtime, iov->iov_len)) + if (!frame_aligned(runtime, to->iov->iov_len)) return -EINVAL; - frames = bytes_to_samples(runtime, iov->iov_len); - bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL); + frames = bytes_to_samples(runtime, to->iov->iov_len); + bufs = kmalloc(sizeof(void *) * to->nr_segs, GFP_KERNEL); if (bufs == NULL) return -ENOMEM; - for (i = 0; i < nr_segs; ++i) - bufs[i] = iov[i].iov_base; + for (i = 0; i < to->nr_segs; ++i) + bufs[i] = to->iov[i].iov_base; result = snd_pcm_lib_readv(substream, bufs, frames); if (result > 0) result = frames_to_bytes(runtime, result); @@ -3069,8 +3104,7 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov, return result; } -static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from) { struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream; @@ -3087,15 +3121,17 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov, runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; - if (nr_segs > 128 || nr_segs != runtime->channels || - !frame_aligned(runtime, iov->iov_len)) + if (!iter_is_iovec(from)) + return -EINVAL; + if (from->nr_segs > 128 || from->nr_segs != runtime->channels || + !frame_aligned(runtime, from->iov->iov_len)) return -EINVAL; - frames = bytes_to_samples(runtime, iov->iov_len); - bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL); + frames = bytes_to_samples(runtime, from->iov->iov_len); + bufs = kmalloc(sizeof(void *) * from->nr_segs, GFP_KERNEL); if (bufs == NULL) return -ENOMEM; - for (i = 0; i < nr_segs; ++i) - bufs[i] = iov[i].iov_base; + for (i = 0; i < from->nr_segs; ++i) + bufs[i] = from->iov[i].iov_base; result = snd_pcm_lib_writev(substream, bufs, frames); if (result > 0) result = frames_to_bytes(runtime, result); @@ -3633,7 +3669,7 @@ const struct file_operations snd_pcm_f_ops[2] = { { .owner = THIS_MODULE, .write = snd_pcm_write, - .aio_write = snd_pcm_aio_write, + .write_iter = snd_pcm_writev, .open = snd_pcm_playback_open, .release = snd_pcm_release, .llseek = no_llseek, @@ -3647,7 +3683,7 @@ const struct file_operations snd_pcm_f_ops[2] = { { .owner = THIS_MODULE, .read = snd_pcm_read, - .aio_read = snd_pcm_aio_read, + .read_iter = snd_pcm_readv, .open = snd_pcm_capture_open, .release = snd_pcm_release, .llseek = no_llseek, diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index b5a748596fc4..a7759846fbaa 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1429,10 +1429,8 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi, for (idx = 0; idx < count; idx++) { substream = kzalloc(sizeof(*substream), GFP_KERNEL); - if (substream == NULL) { - rmidi_err(rmidi, "rawmidi: cannot allocate substream\n"); + if (!substream) return -ENOMEM; - } substream->stream = direction; substream->number = idx; substream->rmidi = rmidi; @@ -1479,10 +1477,8 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device, if (rrawmidi) *rrawmidi = NULL; rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL); - if (rmidi == NULL) { - dev_err(card->dev, "rawmidi: cannot allocate\n"); + if (!rmidi) return -ENOMEM; - } rmidi->card = card; rmidi->device = device; mutex_init(&rmidi->open_mutex); diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 16d42679e43f..72873a46afeb 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -65,15 +65,20 @@ static unsigned int odev_poll(struct file *file, poll_table * wait); * module interface */ +static struct snd_seq_driver seq_oss_synth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_seq_oss_synth_probe, + .remove = snd_seq_oss_synth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OSS, + .argsize = sizeof(struct snd_seq_oss_reg), +}; + static int __init alsa_seq_oss_init(void) { int rc; - static struct snd_seq_dev_ops ops = { - snd_seq_oss_synth_register, - snd_seq_oss_synth_unregister, - }; - snd_seq_autoload_lock(); if ((rc = register_device()) < 0) goto error; if ((rc = register_proc()) < 0) { @@ -86,8 +91,8 @@ static int __init alsa_seq_oss_init(void) goto error; } - if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops, - sizeof(struct snd_seq_oss_reg))) < 0) { + rc = snd_seq_driver_register(&seq_oss_synth_driver); + if (rc < 0) { snd_seq_oss_delete_client(); unregister_proc(); unregister_device(); @@ -98,13 +103,12 @@ static int __init alsa_seq_oss_init(void) snd_seq_oss_synth_init(); error: - snd_seq_autoload_unlock(); return rc; } static void __exit alsa_seq_oss_exit(void) { - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS); + snd_seq_driver_unregister(&seq_oss_synth_driver); snd_seq_oss_delete_client(); unregister_proc(); unregister_device(); diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index b0e32e161dd1..2de3feff70d0 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -188,10 +188,8 @@ snd_seq_oss_open(struct file *file, int level) struct seq_oss_devinfo *dp; dp = kzalloc(sizeof(*dp), GFP_KERNEL); - if (!dp) { - pr_err("ALSA: seq_oss: can't malloc device info\n"); + if (!dp) return -ENOMEM; - } dp->cseq = system_client; dp->port = -1; diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index e79cc44b1394..96e8395ae586 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -173,10 +173,9 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo) /* * allocate midi info record */ - if ((mdev = kzalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) { - pr_err("ALSA: seq_oss: can't malloc midi info\n"); + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) return -ENOMEM; - } /* copy the port information */ mdev->client = pinfo->addr.client; diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c index 654d17a5023c..c080c73cea04 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -47,13 +47,12 @@ snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen) { struct seq_oss_readq *q; - if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL) { - pr_err("ALSA: seq_oss: can't malloc read queue\n"); + q = kzalloc(sizeof(*q), GFP_KERNEL); + if (!q) return NULL; - } - if ((q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL)) == NULL) { - pr_err("ALSA: seq_oss: can't malloc read queue buffer\n"); + q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL); + if (!q->q) { kfree(q); return NULL; } diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 701feb71b700..48e4fe1b68ab 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -98,17 +98,17 @@ snd_seq_oss_synth_init(void) * registration of the synth device */ int -snd_seq_oss_synth_register(struct snd_seq_device *dev) +snd_seq_oss_synth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); int i; struct seq_oss_synth *rec; struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); unsigned long flags; - if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL) { - pr_err("ALSA: seq_oss: can't malloc synth info\n"); + rec = kzalloc(sizeof(*rec), GFP_KERNEL); + if (!rec) return -ENOMEM; - } rec->seq_device = -1; rec->synth_type = reg->type; rec->synth_subtype = reg->subtype; @@ -149,8 +149,9 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev) int -snd_seq_oss_synth_unregister(struct snd_seq_device *dev) +snd_seq_oss_synth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); int index; struct seq_oss_synth *rec = dev->driver_data; unsigned long flags; @@ -247,7 +248,6 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp) if (info->nr_voices > 0) { info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL); if (!info->ch) { - pr_err("ALSA: seq_oss: Cannot malloc voices\n"); rec->oper.close(&info->arg); module_put(rec->oper.owner); snd_use_lock_free(&rec->use_lock); diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h index dbdfcbb80eaa..74ac55f166b6 100644 --- a/sound/core/seq/oss/seq_oss_synth.h +++ b/sound/core/seq/oss/seq_oss_synth.h @@ -28,8 +28,8 @@ #include <sound/seq_device.h> void snd_seq_oss_synth_init(void); -int snd_seq_oss_synth_register(struct snd_seq_device *dev); -int snd_seq_oss_synth_unregister(struct snd_seq_device *dev); +int snd_seq_oss_synth_probe(struct device *dev); +int snd_seq_oss_synth_remove(struct device *dev); void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp); void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp); void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp); diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 48287651ac77..edbdab85fc02 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1879,6 +1879,7 @@ static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client, if (cptr == NULL) return -ENOENT; memset(&info, 0, sizeof(info)); + info.client = cptr->number; info.output_pool = cptr->pool->size; info.output_room = cptr->pool->room; info.output_free = info.output_pool; diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 0631bdadd12b..d99f99d61983 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -36,6 +36,7 @@ * */ +#include <linux/device.h> #include <linux/init.h> #include <linux/module.h> #include <sound/core.h> @@ -51,140 +52,78 @@ MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); MODULE_DESCRIPTION("ALSA sequencer device management"); MODULE_LICENSE("GPL"); -/* driver state */ -#define DRIVER_EMPTY 0 -#define DRIVER_LOADED (1<<0) -#define DRIVER_REQUESTED (1<<1) -#define DRIVER_LOCKED (1<<2) -#define DRIVER_REQUESTING (1<<3) - -struct ops_list { - char id[ID_LEN]; /* driver id */ - int driver; /* driver state */ - int used; /* reference counter */ - int argsize; /* argument size */ - - /* operators */ - struct snd_seq_dev_ops ops; - - /* registered devices */ - struct list_head dev_list; /* list of devices */ - int num_devices; /* number of associated devices */ - int num_init_devices; /* number of initialized devices */ - struct mutex reg_mutex; - - struct list_head list; /* next driver */ -}; +/* + * bus definition + */ +static int snd_seq_bus_match(struct device *dev, struct device_driver *drv) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + struct snd_seq_driver *sdrv = to_seq_drv(drv); + return strcmp(sdrv->id, sdev->id) == 0 && + sdrv->argsize == sdev->argsize; +} -static LIST_HEAD(opslist); -static int num_ops; -static DEFINE_MUTEX(ops_mutex); -#ifdef CONFIG_PROC_FS -static struct snd_info_entry *info_entry; -#endif +static struct bus_type snd_seq_bus_type = { + .name = "snd_seq", + .match = snd_seq_bus_match, +}; /* - * prototypes + * proc interface -- just for compatibility */ -static int snd_seq_device_free(struct snd_seq_device *dev); -static int snd_seq_device_dev_free(struct snd_device *device); -static int snd_seq_device_dev_register(struct snd_device *device); -static int snd_seq_device_dev_disconnect(struct snd_device *device); - -static int init_device(struct snd_seq_device *dev, struct ops_list *ops); -static int free_device(struct snd_seq_device *dev, struct ops_list *ops); -static struct ops_list *find_driver(char *id, int create_if_empty); -static struct ops_list *create_driver(char *id); -static void unlock_driver(struct ops_list *ops); -static void remove_drivers(void); +#ifdef CONFIG_PROC_FS +static struct snd_info_entry *info_entry; -/* - * show all drivers and their status - */ +static int print_dev_info(struct device *dev, void *data) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + struct snd_info_buffer *buffer = data; + + snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id, + dev->driver ? "loaded" : "empty", + dev->driver ? 1 : 0); + return 0; +} -#ifdef CONFIG_PROC_FS static void snd_seq_device_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", - ops->id, - ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), - ops->driver & DRIVER_REQUESTED ? ",requested" : "", - ops->driver & DRIVER_LOCKED ? ",locked" : "", - ops->num_devices); - } - mutex_unlock(&ops_mutex); + bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info); } #endif - + /* * load all registered drivers (called from seq_clientmgr.c) */ #ifdef CONFIG_MODULES -/* avoid auto-loading during module_init() */ +/* flag to block auto-loading */ static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */ -void snd_seq_autoload_lock(void) -{ - atomic_inc(&snd_seq_in_init); -} -void snd_seq_autoload_unlock(void) +static int request_seq_drv(struct device *dev, void *data) { - atomic_dec(&snd_seq_in_init); + struct snd_seq_device *sdev = to_seq_dev(dev); + + if (!dev->driver) + request_module("snd-%s", sdev->id); + return 0; } -static void autoload_drivers(void) +static void autoload_drivers(struct work_struct *work) { /* avoid reentrance */ - if (atomic_inc_return(&snd_seq_in_init) == 1) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - if ((ops->driver & DRIVER_REQUESTING) && - !(ops->driver & DRIVER_REQUESTED)) { - ops->used++; - mutex_unlock(&ops_mutex); - ops->driver |= DRIVER_REQUESTED; - request_module("snd-%s", ops->id); - mutex_lock(&ops_mutex); - ops->used--; - } - } - mutex_unlock(&ops_mutex); - } + if (atomic_inc_return(&snd_seq_in_init) == 1) + bus_for_each_dev(&snd_seq_bus_type, NULL, NULL, + request_seq_drv); atomic_dec(&snd_seq_in_init); } -static void call_autoload(struct work_struct *work) -{ - autoload_drivers(); -} - -static DECLARE_WORK(autoload_work, call_autoload); - -static void try_autoload(struct ops_list *ops) -{ - if (!ops->driver) { - ops->driver |= DRIVER_REQUESTING; - schedule_work(&autoload_work); - } -} +static DECLARE_WORK(autoload_work, autoload_drivers); static void queue_autoload_drivers(void) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) - try_autoload(ops); - mutex_unlock(&ops_mutex); + schedule_work(&autoload_work); } void snd_seq_autoload_init(void) @@ -195,384 +134,143 @@ void snd_seq_autoload_init(void) queue_autoload_drivers(); #endif } -#else -#define try_autoload(ops) /* NOP */ -#endif +EXPORT_SYMBOL(snd_seq_autoload_init); -void snd_seq_device_load_drivers(void) +void snd_seq_autoload_exit(void) { -#ifdef CONFIG_MODULES - queue_autoload_drivers(); - flush_work(&autoload_work); -#endif + atomic_inc(&snd_seq_in_init); } +EXPORT_SYMBOL(snd_seq_autoload_exit); -/* - * register a sequencer device - * card = card info - * device = device number (if any) - * id = id of driver - * result = return pointer (NULL allowed if unnecessary) - */ -int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, - struct snd_seq_device **result) +void snd_seq_device_load_drivers(void) { - struct snd_seq_device *dev; - struct ops_list *ops; - int err; - static struct snd_device_ops dops = { - .dev_free = snd_seq_device_dev_free, - .dev_register = snd_seq_device_dev_register, - .dev_disconnect = snd_seq_device_dev_disconnect, - }; - - if (result) - *result = NULL; - - if (snd_BUG_ON(!id)) - return -EINVAL; - - ops = find_driver(id, 1); - if (ops == NULL) - return -ENOMEM; - - dev = kzalloc(sizeof(*dev)*2 + argsize, GFP_KERNEL); - if (dev == NULL) { - unlock_driver(ops); - return -ENOMEM; - } - - /* set up device info */ - dev->card = card; - dev->device = device; - strlcpy(dev->id, id, sizeof(dev->id)); - dev->argsize = argsize; - dev->status = SNDRV_SEQ_DEVICE_FREE; - - /* add this device to the list */ - mutex_lock(&ops->reg_mutex); - list_add_tail(&dev->list, &ops->dev_list); - ops->num_devices++; - mutex_unlock(&ops->reg_mutex); - - if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { - snd_seq_device_free(dev); - return err; - } - - try_autoload(ops); - unlock_driver(ops); - - if (result) - *result = dev; - - return 0; + queue_autoload_drivers(); + flush_work(&autoload_work); } +EXPORT_SYMBOL(snd_seq_device_load_drivers); +#else +#define queue_autoload_drivers() /* NOP */ +#endif /* - * free the existing device + * device management */ -static int snd_seq_device_free(struct snd_seq_device *dev) -{ - struct ops_list *ops; - - if (snd_BUG_ON(!dev)) - return -EINVAL; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENXIO; - - /* remove the device from the list */ - mutex_lock(&ops->reg_mutex); - list_del(&dev->list); - ops->num_devices--; - mutex_unlock(&ops->reg_mutex); - - free_device(dev, ops); - if (dev->private_free) - dev->private_free(dev); - kfree(dev); - - unlock_driver(ops); - - return 0; -} - static int snd_seq_device_dev_free(struct snd_device *device) { struct snd_seq_device *dev = device->device_data; - return snd_seq_device_free(dev); + + put_device(&dev->dev); + return 0; } -/* - * register the device - */ static int snd_seq_device_dev_register(struct snd_device *device) { struct snd_seq_device *dev = device->device_data; - struct ops_list *ops; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENOENT; - - /* initialize this device if the corresponding driver was - * already loaded - */ - if (ops->driver & DRIVER_LOADED) - init_device(dev, ops); + int err; - unlock_driver(ops); + err = device_add(&dev->dev); + if (err < 0) + return err; + if (!dev->dev.driver) + queue_autoload_drivers(); return 0; } -/* - * disconnect the device - */ static int snd_seq_device_dev_disconnect(struct snd_device *device) { struct snd_seq_device *dev = device->device_data; - struct ops_list *ops; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENOENT; - - free_device(dev, ops); - unlock_driver(ops); + device_del(&dev->dev); return 0; } -/* - * register device driver - * id = driver id - * entry = driver operators - duplicated to each instance - */ -int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, - int argsize) +static void snd_seq_dev_release(struct device *dev) { - struct ops_list *ops; - struct snd_seq_device *dev; + struct snd_seq_device *sdev = to_seq_dev(dev); - if (id == NULL || entry == NULL || - entry->init_device == NULL || entry->free_device == NULL) - return -EINVAL; - - ops = find_driver(id, 1); - if (ops == NULL) - return -ENOMEM; - if (ops->driver & DRIVER_LOADED) { - pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id); - unlock_driver(ops); - return -EBUSY; - } - - mutex_lock(&ops->reg_mutex); - /* copy driver operators */ - ops->ops = *entry; - ops->driver |= DRIVER_LOADED; - ops->argsize = argsize; - - /* initialize existing devices if necessary */ - list_for_each_entry(dev, &ops->dev_list, list) { - init_device(dev, ops); - } - mutex_unlock(&ops->reg_mutex); - - unlock_driver(ops); - - return 0; + if (sdev->private_free) + sdev->private_free(sdev); + kfree(sdev); } - -/* - * create driver record - */ -static struct ops_list * create_driver(char *id) -{ - struct ops_list *ops; - - ops = kzalloc(sizeof(*ops), GFP_KERNEL); - if (ops == NULL) - return ops; - - /* set up driver entry */ - strlcpy(ops->id, id, sizeof(ops->id)); - mutex_init(&ops->reg_mutex); - /* - * The ->reg_mutex locking rules are per-driver, so we create - * separate per-driver lock classes: - */ - lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id); - - ops->driver = DRIVER_EMPTY; - INIT_LIST_HEAD(&ops->dev_list); - /* lock this instance */ - ops->used = 1; - - /* register driver entry */ - mutex_lock(&ops_mutex); - list_add_tail(&ops->list, &opslist); - num_ops++; - mutex_unlock(&ops_mutex); - - return ops; -} - - /* - * unregister the specified driver + * register a sequencer device + * card = card info + * device = device number (if any) + * id = id of driver + * result = return pointer (NULL allowed if unnecessary) */ -int snd_seq_device_unregister_driver(char *id) +int snd_seq_device_new(struct snd_card *card, int device, const char *id, + int argsize, struct snd_seq_device **result) { - struct ops_list *ops; struct snd_seq_device *dev; + int err; + static struct snd_device_ops dops = { + .dev_free = snd_seq_device_dev_free, + .dev_register = snd_seq_device_dev_register, + .dev_disconnect = snd_seq_device_dev_disconnect, + }; - ops = find_driver(id, 0); - if (ops == NULL) - return -ENXIO; - if (! (ops->driver & DRIVER_LOADED) || - (ops->driver & DRIVER_LOCKED)) { - pr_err("ALSA: seq: driver_unregister: cannot unload driver '%s': status=%x\n", - id, ops->driver); - unlock_driver(ops); - return -EBUSY; - } - - /* close and release all devices associated with this driver */ - mutex_lock(&ops->reg_mutex); - ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ - list_for_each_entry(dev, &ops->dev_list, list) { - free_device(dev, ops); - } - - ops->driver = 0; - if (ops->num_init_devices > 0) - pr_err("ALSA: seq: free_driver: init_devices > 0!! (%d)\n", - ops->num_init_devices); - mutex_unlock(&ops->reg_mutex); - - unlock_driver(ops); + if (result) + *result = NULL; - /* remove empty driver entries */ - remove_drivers(); + if (snd_BUG_ON(!id)) + return -EINVAL; - return 0; -} + dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL); + if (!dev) + return -ENOMEM; + /* set up device info */ + dev->card = card; + dev->device = device; + dev->id = id; + dev->argsize = argsize; -/* - * remove empty driver entries - */ -static void remove_drivers(void) -{ - struct list_head *head; - - mutex_lock(&ops_mutex); - head = opslist.next; - while (head != &opslist) { - struct ops_list *ops = list_entry(head, struct ops_list, list); - if (! (ops->driver & DRIVER_LOADED) && - ops->used == 0 && ops->num_devices == 0) { - head = head->next; - list_del(&ops->list); - kfree(ops); - num_ops--; - } else - head = head->next; - } - mutex_unlock(&ops_mutex); -} + device_initialize(&dev->dev); + dev->dev.parent = &card->card_dev; + dev->dev.bus = &snd_seq_bus_type; + dev->dev.release = snd_seq_dev_release; + dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device); -/* - * initialize the device - call init_device operator - */ -static int init_device(struct snd_seq_device *dev, struct ops_list *ops) -{ - if (! (ops->driver & DRIVER_LOADED)) - return 0; /* driver is not loaded yet */ - if (dev->status != SNDRV_SEQ_DEVICE_FREE) - return 0; /* already initialized */ - if (ops->argsize != dev->argsize) { - pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", - dev->name, ops->id, ops->argsize, dev->argsize); - return -EINVAL; - } - if (ops->ops.init_device(dev) >= 0) { - dev->status = SNDRV_SEQ_DEVICE_REGISTERED; - ops->num_init_devices++; - } else { - pr_err("ALSA: seq: init_device failed: %s: %s\n", - dev->name, dev->id); + /* add this device to the list */ + err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops); + if (err < 0) { + put_device(&dev->dev); + return err; } + + if (result) + *result = dev; return 0; } +EXPORT_SYMBOL(snd_seq_device_new); /* - * release the device - call free_device operator + * driver registration */ -static int free_device(struct snd_seq_device *dev, struct ops_list *ops) +int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod) { - int result; - - if (! (ops->driver & DRIVER_LOADED)) - return 0; /* driver is not loaded yet */ - if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) - return 0; /* not registered */ - if (ops->argsize != dev->argsize) { - pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", - dev->name, ops->id, ops->argsize, dev->argsize); + if (WARN_ON(!drv->driver.name || !drv->id)) return -EINVAL; - } - if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { - dev->status = SNDRV_SEQ_DEVICE_FREE; - dev->driver_data = NULL; - ops->num_init_devices--; - } else { - pr_err("ALSA: seq: free_device failed: %s: %s\n", - dev->name, dev->id); - } - - return 0; + drv->driver.bus = &snd_seq_bus_type; + drv->driver.owner = mod; + return driver_register(&drv->driver); } +EXPORT_SYMBOL_GPL(__snd_seq_driver_register); -/* - * find the matching driver with given id - */ -static struct ops_list * find_driver(char *id, int create_if_empty) +void snd_seq_driver_unregister(struct snd_seq_driver *drv) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - if (strcmp(ops->id, id) == 0) { - ops->used++; - mutex_unlock(&ops_mutex); - return ops; - } - } - mutex_unlock(&ops_mutex); - if (create_if_empty) - return create_driver(id); - return NULL; + driver_unregister(&drv->driver); } - -static void unlock_driver(struct ops_list *ops) -{ - mutex_lock(&ops_mutex); - ops->used--; - mutex_unlock(&ops_mutex); -} - +EXPORT_SYMBOL_GPL(snd_seq_driver_unregister); /* * module part */ -static int __init alsa_seq_device_init(void) +static int __init seq_dev_proc_init(void) { #ifdef CONFIG_PROC_FS info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", @@ -589,28 +287,29 @@ static int __init alsa_seq_device_init(void) return 0; } +static int __init alsa_seq_device_init(void) +{ + int err; + + err = bus_register(&snd_seq_bus_type); + if (err < 0) + return err; + err = seq_dev_proc_init(); + if (err < 0) + bus_unregister(&snd_seq_bus_type); + return err; +} + static void __exit alsa_seq_device_exit(void) { #ifdef CONFIG_MODULES cancel_work_sync(&autoload_work); #endif - remove_drivers(); #ifdef CONFIG_PROC_FS snd_info_free_entry(info_entry); #endif - if (num_ops) - pr_err("ALSA: seq: drivers not released (%d)\n", num_ops); + bus_unregister(&snd_seq_bus_type); } -module_init(alsa_seq_device_init) +subsys_initcall(alsa_seq_device_init) module_exit(alsa_seq_device_exit) - -EXPORT_SYMBOL(snd_seq_device_load_drivers); -EXPORT_SYMBOL(snd_seq_device_new); -EXPORT_SYMBOL(snd_seq_device_register_driver); -EXPORT_SYMBOL(snd_seq_device_unregister_driver); -#ifdef CONFIG_MODULES -EXPORT_SYMBOL(snd_seq_autoload_init); -EXPORT_SYMBOL(snd_seq_autoload_lock); -EXPORT_SYMBOL(snd_seq_autoload_unlock); -#endif diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index 5d905d90d504..d3a2ec4f0561 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -214,11 +214,7 @@ delete_client(void) static int __init alsa_seq_dummy_init(void) { - int err; - snd_seq_autoload_lock(); - err = register_client(); - snd_seq_autoload_unlock(); - return err; + return register_client(); } static void __exit alsa_seq_dummy_exit(void) diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 53a403e17c5b..1d5acbe0c08b 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -33,10 +33,8 @@ struct snd_seq_fifo *snd_seq_fifo_new(int poolsize) struct snd_seq_fifo *f; f = kzalloc(sizeof(*f), GFP_KERNEL); - if (f == NULL) { - pr_debug("ALSA: seq: malloc failed for snd_seq_fifo_new() \n"); + if (!f) return NULL; - } f->pool = snd_seq_pool_new(poolsize); if (f->pool == NULL) { diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index ba8e4a64e13e..801076687bb1 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -387,10 +387,8 @@ int snd_seq_pool_init(struct snd_seq_pool *pool) return 0; pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size); - if (pool->ptr == NULL) { - pr_debug("ALSA: seq: malloc for sequencer events failed\n"); + if (!pool->ptr) return -ENOMEM; - } /* add new cells to the free cell list */ spin_lock_irqsave(&pool->lock, flags); @@ -463,10 +461,8 @@ struct snd_seq_pool *snd_seq_pool_new(int poolsize) /* create pool block */ pool = kzalloc(sizeof(*pool), GFP_KERNEL); - if (pool == NULL) { - pr_debug("ALSA: seq: malloc failed for pool\n"); + if (!pool) return NULL; - } spin_lock_init(&pool->lock); pool->ptr = NULL; pool->free = NULL; diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 68fec776da26..5dd0ee258359 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -273,8 +273,9 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth) /* register new midi synth port */ static int -snd_seq_midisynth_register_port(struct snd_seq_device *dev) +snd_seq_midisynth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth, *ms; struct snd_seq_port_info *port; @@ -427,8 +428,9 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev) /* release midi synth port */ static int -snd_seq_midisynth_unregister_port(struct snd_seq_device *dev) +snd_seq_midisynth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth; struct snd_card *card = dev->card; @@ -457,24 +459,14 @@ snd_seq_midisynth_unregister_port(struct snd_seq_device *dev) return 0; } +static struct snd_seq_driver seq_midisynth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_seq_midisynth_probe, + .remove = snd_seq_midisynth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_MIDISYNTH, + .argsize = 0, +}; -static int __init alsa_seq_midi_init(void) -{ - static struct snd_seq_dev_ops ops = { - snd_seq_midisynth_register_port, - snd_seq_midisynth_unregister_port, - }; - memset(&synths, 0, sizeof(synths)); - snd_seq_autoload_lock(); - snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0); - snd_seq_autoload_unlock(); - return 0; -} - -static void __exit alsa_seq_midi_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH); -} - -module_init(alsa_seq_midi_init) -module_exit(alsa_seq_midi_exit) +module_snd_seq_driver(seq_midisynth_driver); diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 46ff593f618d..55170a20ae72 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -141,10 +141,8 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, /* create a new port */ new_port = kzalloc(sizeof(*new_port), GFP_KERNEL); - if (! new_port) { - pr_debug("ALSA: seq: malloc failed for registering client port\n"); + if (!new_port) return NULL; /* failure, out of memory */ - } /* init port data */ new_port->addr.client = client->number; new_port->addr.port = -1; diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c index 021b02bc9330..bc1c8488fc2a 100644 --- a/sound/core/seq/seq_prioq.c +++ b/sound/core/seq/seq_prioq.c @@ -59,10 +59,8 @@ struct snd_seq_prioq *snd_seq_prioq_new(void) struct snd_seq_prioq *f; f = kzalloc(sizeof(*f), GFP_KERNEL); - if (f == NULL) { - pr_debug("ALSA: seq: malloc failed for snd_seq_prioq_new()\n"); + if (!f) return NULL; - } spin_lock_init(&f->lock); f->head = NULL; diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index aad4878cee55..a0cda38205b9 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -111,10 +111,8 @@ static struct snd_seq_queue *queue_new(int owner, int locked) struct snd_seq_queue *q; q = kzalloc(sizeof(*q), GFP_KERNEL); - if (q == NULL) { - pr_debug("ALSA: seq: malloc failed for snd_seq_queue_new()\n"); + if (!q) return NULL; - } spin_lock_init(&q->owner_lock); spin_lock_init(&q->check_lock); diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index e73605393eee..186f1611103c 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -56,10 +56,8 @@ struct snd_seq_timer *snd_seq_timer_new(void) struct snd_seq_timer *tmr; tmr = kzalloc(sizeof(*tmr), GFP_KERNEL); - if (tmr == NULL) { - pr_debug("ALSA: seq: malloc failed for snd_seq_timer_new() \n"); + if (!tmr) return NULL; - } spin_lock_init(&tmr->lock); /* reset setup to defaults */ diff --git a/sound/core/sound.c b/sound/core/sound.c index 185cec01ee25..5fc93d00572a 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -186,7 +186,7 @@ static const struct file_operations snd_fops = }; #ifdef CONFIG_SND_DYNAMIC_MINORS -static int snd_find_free_minor(int type) +static int snd_find_free_minor(int type, struct snd_card *card, int dev) { int minor; @@ -209,7 +209,7 @@ static int snd_find_free_minor(int type) return -EBUSY; } #else -static int snd_kernel_minor(int type, struct snd_card *card, int dev) +static int snd_find_free_minor(int type, struct snd_card *card, int dev) { int minor; @@ -237,6 +237,8 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev) } if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS)) return -EINVAL; + if (snd_minors[minor]) + return -EBUSY; return minor; } #endif @@ -276,13 +278,7 @@ int snd_register_device(int type, struct snd_card *card, int dev, preg->private_data = private_data; preg->card_ptr = card; mutex_lock(&sound_mutex); -#ifdef CONFIG_SND_DYNAMIC_MINORS - minor = snd_find_free_minor(type); -#else - minor = snd_kernel_minor(type, card, dev); - if (minor >= 0 && snd_minors[minor]) - minor = -EBUSY; -#endif + minor = snd_find_free_minor(type, card, dev); if (minor < 0) { err = minor; goto error; diff --git a/sound/core/timer.c b/sound/core/timer.c index 490b489d713d..a9a1a047c521 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -774,10 +774,8 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, if (rtimer) *rtimer = NULL; timer = kzalloc(sizeof(*timer), GFP_KERNEL); - if (timer == NULL) { - pr_err("ALSA: timer: cannot allocate\n"); + if (!timer) return -ENOMEM; - } timer->tmr_class = tid->dev_class; timer->card = card; timer->tmr_device = tid->device; diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index a9f618e06a22..fdae5d7f421f 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -216,8 +216,9 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3) /* ------------------------------ */ -static int snd_opl3_seq_new_device(struct snd_seq_device *dev) +static int snd_opl3_seq_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl3 *opl3; int client, err; char name[32]; @@ -257,8 +258,9 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev) return 0; } -static int snd_opl3_seq_delete_device(struct snd_seq_device *dev) +static int snd_opl3_seq_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl3 *opl3; opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -275,22 +277,14 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev) return 0; } -static int __init alsa_opl3_seq_init(void) -{ - static struct snd_seq_dev_ops ops = - { - snd_opl3_seq_new_device, - snd_opl3_seq_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops, - sizeof(struct snd_opl3 *)); -} - -static void __exit alsa_opl3_seq_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3); -} +static struct snd_seq_driver opl3_seq_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_opl3_seq_probe, + .remove = snd_opl3_seq_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OPL3, + .argsize = sizeof(struct snd_opl3 *), +}; -module_init(alsa_opl3_seq_init) -module_exit(alsa_opl3_seq_exit) +module_snd_seq_driver(opl3_seq_driver); diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c index 99197699c55a..03d6202f4829 100644 --- a/sound/drivers/opl4/opl4_seq.c +++ b/sound/drivers/opl4/opl4_seq.c @@ -124,8 +124,9 @@ static void snd_opl4_seq_free_port(void *private_data) snd_midi_channel_free_set(opl4->chset); } -static int snd_opl4_seq_new_device(struct snd_seq_device *dev) +static int snd_opl4_seq_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl4 *opl4; int client; struct snd_seq_port_callback pcallbacks; @@ -180,8 +181,9 @@ static int snd_opl4_seq_new_device(struct snd_seq_device *dev) return 0; } -static int snd_opl4_seq_delete_device(struct snd_seq_device *dev) +static int snd_opl4_seq_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl4 *opl4; opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -195,21 +197,14 @@ static int snd_opl4_seq_delete_device(struct snd_seq_device *dev) return 0; } -static int __init alsa_opl4_synth_init(void) -{ - static struct snd_seq_dev_ops ops = { - snd_opl4_seq_new_device, - snd_opl4_seq_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL4, &ops, - sizeof(struct snd_opl4 *)); -} - -static void __exit alsa_opl4_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL4); -} +static struct snd_seq_driver opl4_seq_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_opl4_seq_probe, + .remove = snd_opl4_seq_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OPL4, + .argsize = sizeof(struct snd_opl4 *), +}; -module_init(alsa_opl4_synth_init) -module_exit(alsa_opl4_synth_exit) +module_snd_seq_driver(opl4_seq_driver); diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 5cc356db5351..e061355f535f 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -166,10 +166,10 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, * One AMDTP packet can include some frames. In blocking mode, the * number equals to SYT_INTERVAL. So the number is 8, 16 or 32, * depending on its sampling rate. For accurate period interrupt, it's - * preferrable to aligh period/buffer sizes to current SYT_INTERVAL. + * preferrable to align period/buffer sizes to current SYT_INTERVAL. * - * TODO: These constraints can be improved with propper rules. - * Currently apply LCM of SYT_INTEVALs. + * TODO: These constraints can be improved with proper rules. + * Currently apply LCM of SYT_INTERVALs. */ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32); @@ -270,7 +270,7 @@ static void amdtp_read_s32(struct amdtp_stream *s, * @s: the AMDTP stream to configure * @format: the format of the ALSA PCM device * - * The sample format must be set after the other paramters (rate/PCM channels/ + * The sample format must be set after the other parameters (rate/PCM channels/ * MIDI) and before the stream is started, and must not be changed while the * stream is running. */ diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c index a422aaa3bb0c..9ee25a63f684 100644 --- a/sound/firewire/bebob/bebob_maudio.c +++ b/sound/firewire/bebob/bebob_maudio.c @@ -96,10 +96,10 @@ int snd_bebob_maudio_load_firmware(struct fw_unit *unit) struct fw_device *device = fw_parent_device(unit); int err, rcode; u64 date; - __be32 cues[3] = { - MAUDIO_BOOTLOADER_CUE1, - MAUDIO_BOOTLOADER_CUE2, - MAUDIO_BOOTLOADER_CUE3 + __le32 cues[3] = { + cpu_to_le32(MAUDIO_BOOTLOADER_CUE1), + cpu_to_le32(MAUDIO_BOOTLOADER_CUE2), + cpu_to_le32(MAUDIO_BOOTLOADER_CUE3) }; /* check date of software used to build */ diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c index 2a85e4209f0b..f550808d1784 100644 --- a/sound/firewire/fireworks/fireworks_transaction.c +++ b/sound/firewire/fireworks/fireworks_transaction.c @@ -13,7 +13,7 @@ * * Transaction substance: * At first, 6 data exist. Following to the data, parameters for each command - * exist. All of the parameters are 32 bit alighed to big endian. + * exist. All of the parameters are 32 bit aligned to big endian. * data[0]: Length of transaction substance * data[1]: Transaction version * data[2]: Sequence number. This is incremented by the device diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig new file mode 100644 index 000000000000..001c6588a5ff --- /dev/null +++ b/sound/hda/Kconfig @@ -0,0 +1,3 @@ +config SND_HDA_CORE + tristate + select REGMAP diff --git a/sound/hda/Makefile b/sound/hda/Makefile new file mode 100644 index 000000000000..7a359f5b7e25 --- /dev/null +++ b/sound/hda/Makefile @@ -0,0 +1,7 @@ +snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ + hdac_regmap.o array.o + +snd-hda-core-objs += trace.o +CFLAGS_trace.o := -I$(src) + +obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o diff --git a/sound/hda/array.c b/sound/hda/array.c new file mode 100644 index 000000000000..516795baa7db --- /dev/null +++ b/sound/hda/array.c @@ -0,0 +1,49 @@ +/* + * generic arrays + */ + +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/hdaudio.h> + +/** + * snd_array_new - get a new element from the given array + * @array: the array object + * + * Get a new element from the given array. If it exceeds the + * pre-allocated array size, re-allocate the array. + * + * Returns NULL if allocation failed. + */ +void *snd_array_new(struct snd_array *array) +{ + if (snd_BUG_ON(!array->elem_size)) + return NULL; + if (array->used >= array->alloced) { + int num = array->alloced + array->alloc_align; + int size = (num + 1) * array->elem_size; + void *nlist; + if (snd_BUG_ON(num >= 4096)) + return NULL; + nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO); + if (!nlist) + return NULL; + array->list = nlist; + array->alloced = num; + } + return snd_array_elem(array, array->used++); +} +EXPORT_SYMBOL_GPL(snd_array_new); + +/** + * snd_array_free - free the given array elements + * @array: the array object + */ +void snd_array_free(struct snd_array *array) +{ + kfree(array->list); + array->used = 0; + array->alloced = 0; + array->list = NULL; +} +EXPORT_SYMBOL_GPL(snd_array_free); diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c new file mode 100644 index 000000000000..519914a12e8a --- /dev/null +++ b/sound/hda/hda_bus_type.c @@ -0,0 +1,42 @@ +/* + * HD-audio bus + */ +#include <linux/init.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/export.h> +#include <sound/hdaudio.h> + +MODULE_DESCRIPTION("HD-audio bus"); +MODULE_LICENSE("GPL"); + +static int hda_bus_match(struct device *dev, struct device_driver *drv) +{ + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_driver *hdrv = drv_to_hdac_driver(drv); + + if (hdev->type != hdrv->type) + return 0; + if (hdrv->match) + return hdrv->match(hdev, hdrv); + return 1; +} + +struct bus_type snd_hda_bus_type = { + .name = "hdaudio", + .match = hda_bus_match, +}; +EXPORT_SYMBOL_GPL(snd_hda_bus_type); + +static int __init hda_bus_init(void) +{ + return bus_register(&snd_hda_bus_type); +} + +static void __exit hda_bus_exit(void) +{ + bus_unregister(&snd_hda_bus_type); +} + +subsys_initcall(hda_bus_init); +module_exit(hda_bus_exit); diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c new file mode 100644 index 000000000000..8e262da74f6a --- /dev/null +++ b/sound/hda/hdac_bus.c @@ -0,0 +1,186 @@ +/* + * HD-audio core bus driver + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/export.h> +#include <sound/hdaudio.h> +#include "trace.h" + +static void process_unsol_events(struct work_struct *work); + +/** + * snd_hdac_bus_init - initialize a HD-audio bas bus + * @bus: the pointer to bus object + * + * 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) +{ + memset(bus, 0, sizeof(*bus)); + bus->dev = dev; + bus->ops = ops; + INIT_LIST_HEAD(&bus->codec_list); + INIT_WORK(&bus->unsol_work, process_unsol_events); + mutex_init(&bus->cmd_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_init); + +/** + * snd_hdac_bus_exit - clean up a HD-audio bas bus + * @bus: the pointer to bus object + */ +void snd_hdac_bus_exit(struct hdac_bus *bus) +{ + WARN_ON(!list_empty(&bus->codec_list)); + cancel_work_sync(&bus->unsol_work); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exit); + +/** + * snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus + * @bus: bus object + * @cmd: HD-audio encoded verb + * @res: pointer to store the response, NULL if performing asynchronously + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res) +{ + int err; + + mutex_lock(&bus->cmd_mutex); + err = snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res); + mutex_unlock(&bus->cmd_mutex); + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb); + +/** + * snd_hdac_bus_exec_verb_unlocked - unlocked version + * @bus: bus object + * @cmd: HD-audio encoded verb + * @res: pointer to store the response, NULL if performing asynchronously + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res) +{ + unsigned int tmp; + int err; + + if (cmd == ~0) + return -EINVAL; + + if (res) + *res = -1; + else if (bus->sync_write) + res = &tmp; + for (;;) { + trace_hda_send_cmd(bus, cmd); + err = bus->ops->command(bus, cmd); + if (err != -EAGAIN) + break; + /* process pending verbs */ + err = bus->ops->get_response(bus, addr, &tmp); + if (err) + break; + } + if (!err && res) { + err = bus->ops->get_response(bus, addr, res); + trace_hda_get_response(bus, addr, *res); + } + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked); + +/** + * snd_hdac_bus_queue_event - add an unsolicited event to queue + * @bus: the BUS + * @res: unsolicited event (lower 32bit of RIRB entry) + * @res_ex: codec addr and flags (upper 32bit or RIRB entry) + * + * Adds the given event to the queue. The events are processed in + * the workqueue asynchronously. Call this function in the interrupt + * hanlder when RIRB receives an unsolicited event. + */ +void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex) +{ + unsigned int wp; + + if (!bus) + return; + + trace_hda_unsol_event(bus, res, res_ex); + wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE; + bus->unsol_wp = wp; + + wp <<= 1; + bus->unsol_queue[wp] = res; + bus->unsol_queue[wp + 1] = res_ex; + + schedule_work(&bus->unsol_work); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event); + +/* + * process queued unsolicited events + */ +static void process_unsol_events(struct work_struct *work) +{ + struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work); + struct hdac_device *codec; + struct hdac_driver *drv; + unsigned int rp, caddr, res; + + while (bus->unsol_rp != bus->unsol_wp) { + rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE; + bus->unsol_rp = rp; + rp <<= 1; + res = bus->unsol_queue[rp]; + caddr = bus->unsol_queue[rp + 1]; + if (!(caddr & (1 << 4))) /* no unsolicited event? */ + continue; + codec = bus->caddr_tbl[caddr & 0x0f]; + if (!codec || !codec->dev.driver) + continue; + drv = drv_to_hdac_driver(codec->dev.driver); + if (drv->unsol_event) + drv->unsol_event(codec, res); + } +} + +int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec) +{ + if (bus->caddr_tbl[codec->addr]) { + dev_err(bus->dev, "address 0x%x is already occupied\n", + codec->addr); + return -EBUSY; + } + + list_add_tail(&codec->list, &bus->codec_list); + bus->caddr_tbl[codec->addr] = codec; + set_bit(codec->addr, &bus->codec_powered); + bus->num_codecs++; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device); + +void snd_hdac_bus_remove_device(struct hdac_bus *bus, + struct hdac_device *codec) +{ + WARN_ON(bus != codec->bus); + if (list_empty(&codec->list)) + return; + list_del_init(&codec->list); + bus->caddr_tbl[codec->addr] = NULL; + clear_bit(codec->addr, &bus->codec_powered); + bus->num_codecs--; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c new file mode 100644 index 000000000000..f75bf5622687 --- /dev/null +++ b/sound/hda/hdac_device.c @@ -0,0 +1,599 @@ +/* + * HD-audio codec core device + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/pm_runtime.h> +#include <sound/hdaudio.h> +#include <sound/hda_regmap.h> +#include "local.h" + +static void setup_fg_nodes(struct hdac_device *codec); +static int get_codec_vendor_name(struct hdac_device *codec); + +static void default_release(struct device *dev) +{ + snd_hdac_device_exit(container_of(dev, struct hdac_device, dev)); +} + +/** + * snd_hdac_device_init - initialize the HD-audio codec base device + * @codec: device to initialize + * @bus: but to attach + * @name: device name string + * @addr: codec address + * + * Returns zero for success or a negative error code. + * + * This function increments the runtime PM counter and marks it active. + * The caller needs to turn it off appropriately later. + * + * The caller needs to set the device's release op properly by itself. + */ +int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus, + const char *name, unsigned int addr) +{ + struct device *dev; + hda_nid_t fg; + int err; + + dev = &codec->dev; + device_initialize(dev); + dev->parent = bus->dev; + dev->bus = &snd_hda_bus_type; + dev->release = default_release; + dev->groups = hdac_dev_attr_groups; + dev_set_name(dev, "%s", name); + device_enable_async_suspend(dev); + + codec->bus = bus; + codec->addr = addr; + codec->type = HDA_DEV_CORE; + pm_runtime_set_active(&codec->dev); + pm_runtime_get_noresume(&codec->dev); + atomic_set(&codec->in_pm, 0); + + err = snd_hdac_bus_add_device(bus, codec); + if (err < 0) + goto error; + + /* fill parameters */ + codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_VENDOR_ID); + if (codec->vendor_id == -1) { + /* read again, hopefully the access method was corrected + * in the last read... + */ + codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_VENDOR_ID); + } + + codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_SUBSYSTEM_ID); + codec->revision_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_REV_ID); + + setup_fg_nodes(codec); + if (!codec->afg && !codec->mfg) { + dev_err(dev, "no AFG or MFG node found\n"); + err = -ENODEV; + goto error; + } + + fg = codec->afg ? codec->afg : codec->mfg; + + err = snd_hdac_refresh_widgets(codec); + if (err < 0) + goto error; + + codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE); + /* reread ssid if not set by parameter */ + if (codec->subsystem_id == -1 || codec->subsystem_id == 0) + snd_hdac_read(codec, fg, AC_VERB_GET_SUBSYSTEM_ID, 0, + &codec->subsystem_id); + + err = get_codec_vendor_name(codec); + if (err < 0) + goto error; + + codec->chip_name = kasprintf(GFP_KERNEL, "ID %x", + codec->vendor_id & 0xffff); + if (!codec->chip_name) { + err = -ENOMEM; + goto error; + } + + return 0; + + error: + put_device(&codec->dev); + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_device_init); + +/** + * snd_hdac_device_exit - clean up the HD-audio codec base device + * @codec: device to clean up + */ +void snd_hdac_device_exit(struct hdac_device *codec) +{ + pm_runtime_put_noidle(&codec->dev); + snd_hdac_bus_remove_device(codec->bus, codec); + kfree(codec->vendor_name); + kfree(codec->chip_name); +} +EXPORT_SYMBOL_GPL(snd_hdac_device_exit); + +/** + * snd_hdac_device_register - register the hd-audio codec base device + * codec: the device to register + */ +int snd_hdac_device_register(struct hdac_device *codec) +{ + int err; + + err = device_add(&codec->dev); + if (err < 0) + return err; + err = hda_widget_sysfs_init(codec); + if (err < 0) { + device_del(&codec->dev); + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_device_register); + +/** + * snd_hdac_device_unregister - unregister the hd-audio codec base device + * codec: the device to unregister + */ +void snd_hdac_device_unregister(struct hdac_device *codec) +{ + if (device_is_registered(&codec->dev)) { + hda_widget_sysfs_exit(codec); + device_del(&codec->dev); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_device_unregister); + +/** + * snd_hdac_make_cmd - compose a 32bit command word to be sent to the + * HD-audio controller + * @codec: the codec object + * @nid: NID to encode + * @verb: verb to encode + * @parm: parameter to encode + * + * Return an encoded command verb or -1 for error. + */ +unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm) +{ + u32 val, addr; + + addr = codec->addr; + if ((addr & ~0xf) || (nid & ~0x7f) || + (verb & ~0xfff) || (parm & ~0xffff)) { + dev_err(&codec->dev, "out of range cmd %x:%x:%x:%x\n", + addr, nid, verb, parm); + return -1; + } + + val = addr << 28; + val |= (u32)nid << 20; + val |= verb << 8; + val |= parm; + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_make_cmd); + +/** + * snd_hdac_exec_verb - execute an encoded verb + * @codec: the codec object + * @cmd: encoded verb to execute + * @flags: optional flags, pass zero for default + * @res: the pointer to store the result, NULL if running async + * + * Returns zero if successful, or a negative error code. + * + * This calls the exec_verb op when set in hdac_codec. If not, + * call the default snd_hdac_bus_exec_verb(). + */ +int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd, + unsigned int flags, unsigned int *res) +{ + if (codec->exec_verb) + return codec->exec_verb(codec, cmd, flags, res); + return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res); +} +EXPORT_SYMBOL_GPL(snd_hdac_exec_verb); + + +/** + * snd_hdac_read - execute a verb + * @codec: the codec object + * @nid: NID to execute a verb + * @verb: verb to execute + * @parm: parameter for a verb + * @res: the pointer to store the result, NULL if running async + * + * Returns zero if successful, or a negative error code. + */ +int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm, unsigned int *res) +{ + unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm); + + return snd_hdac_exec_verb(codec, cmd, 0, res); +} +EXPORT_SYMBOL_GPL(snd_hdac_read); + +/** + * _snd_hdac_read_parm - read a parmeter + * + * This function returns zero or an error unlike snd_hdac_read_parm(). + */ +int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm, + unsigned int *res) +{ + unsigned int cmd; + + cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm; + return snd_hdac_regmap_read_raw(codec, cmd, res); +} +EXPORT_SYMBOL_GPL(_snd_hdac_read_parm); + +/** + * snd_hdac_read_parm_uncached - read a codec parameter without caching + * @codec: the codec object + * @nid: NID to read a parameter + * @parm: parameter to read + * + * Returns -1 for error. If you need to distinguish the error more + * strictly, use snd_hdac_read() directly. + */ +int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid, + int parm) +{ + int val; + + if (codec->regmap) + regcache_cache_bypass(codec->regmap, true); + val = snd_hdac_read_parm(codec, nid, parm); + if (codec->regmap) + regcache_cache_bypass(codec->regmap, false); + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached); + +/** + * snd_hdac_override_parm - override read-only parameters + * @codec: the codec object + * @nid: NID for the parameter + * @parm: the parameter to change + * @val: the parameter value to overwrite + */ +int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid, + unsigned int parm, unsigned int val) +{ + unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm; + int err; + + if (!codec->regmap) + return -EINVAL; + + codec->caps_overwriting = true; + err = snd_hdac_regmap_write_raw(codec, verb, val); + codec->caps_overwriting = false; + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_override_parm); + +/** + * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes + * @codec: the codec object + * @nid: NID to inspect + * @start_id: the pointer to store the starting NID + * + * Returns the number of subtree nodes or zero if not found. + * This function reads parameters always without caching. + */ +int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, + hda_nid_t *start_id) +{ + unsigned int parm; + + parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT); + if (parm == -1) { + *start_id = 0; + return 0; + } + *start_id = (parm >> 16) & 0x7fff; + return (int)(parm & 0x7fff); +} +EXPORT_SYMBOL_GPL(snd_hdac_get_sub_nodes); + +/* + * look for an AFG and MFG nodes + */ +static void setup_fg_nodes(struct hdac_device *codec) +{ + int i, total_nodes, function_id; + hda_nid_t nid; + + total_nodes = snd_hdac_get_sub_nodes(codec, AC_NODE_ROOT, &nid); + for (i = 0; i < total_nodes; i++, nid++) { + function_id = snd_hdac_read_parm(codec, nid, + AC_PAR_FUNCTION_TYPE); + switch (function_id & 0xff) { + case AC_GRP_AUDIO_FUNCTION: + codec->afg = nid; + codec->afg_function_id = function_id & 0xff; + codec->afg_unsol = (function_id >> 8) & 1; + break; + case AC_GRP_MODEM_FUNCTION: + codec->mfg = nid; + codec->mfg_function_id = function_id & 0xff; + codec->mfg_unsol = (function_id >> 8) & 1; + break; + default: + break; + } + } +} + +/** + * snd_hdac_refresh_widgets - Reset the widget start/end nodes + * @codec: the codec object + */ +int snd_hdac_refresh_widgets(struct hdac_device *codec) +{ + hda_nid_t start_nid; + int nums; + + nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid); + if (!start_nid || nums <= 0 || nums >= 0xff) { + dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n", + codec->afg); + return -EINVAL; + } + + codec->num_nodes = nums; + codec->start_nid = start_nid; + codec->end_nid = start_nid + nums; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets); + +/* return CONNLIST_LEN parameter of the given widget */ +static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int parm; + + if (!(wcaps & AC_WCAP_CONN_LIST) && + get_wcaps_type(wcaps) != AC_WID_VOL_KNB) + return 0; + + parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN); + if (parm == -1) + parm = 0; + return parm; +} + +/** + * snd_hdac_get_connections - get a widget connection list + * @codec: the codec object + * @nid: NID + * @conn_list: the array to store the results, can be NULL + * @max_conns: the max size of the given array + * + * Returns the number of connected widgets, zero for no connection, or a + * negative error code. When the number of elements don't fit with the + * given array size, it returns -ENOSPC. + * + * When @conn_list is NULL, it just checks the number of connections. + */ +int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns) +{ + unsigned int parm; + int i, conn_len, conns, err; + unsigned int shift, num_elems, mask; + hda_nid_t prev_nid; + int null_count = 0; + + parm = get_num_conns(codec, nid); + if (!parm) + return 0; + + if (parm & AC_CLIST_LONG) { + /* long form */ + shift = 16; + num_elems = 2; + } else { + /* short form */ + shift = 8; + num_elems = 4; + } + conn_len = parm & AC_CLIST_LENGTH; + mask = (1 << (shift-1)) - 1; + + if (!conn_len) + return 0; /* no connection */ + + if (conn_len == 1) { + /* single connection */ + err = snd_hdac_read(codec, nid, AC_VERB_GET_CONNECT_LIST, 0, + &parm); + if (err < 0) + return err; + if (conn_list) + conn_list[0] = parm & mask; + return 1; + } + + /* multi connection */ + conns = 0; + prev_nid = 0; + for (i = 0; i < conn_len; i++) { + int range_val; + hda_nid_t val, n; + + if (i % num_elems == 0) { + err = snd_hdac_read(codec, nid, + AC_VERB_GET_CONNECT_LIST, i, + &parm); + if (err < 0) + return -EIO; + } + range_val = !!(parm & (1 << (shift-1))); /* ranges */ + val = parm & mask; + if (val == 0 && null_count++) { /* no second chance */ + dev_dbg(&codec->dev, + "invalid CONNECT_LIST verb %x[%i]:%x\n", + nid, i, parm); + return 0; + } + parm >>= shift; + if (range_val) { + /* ranges between the previous and this one */ + if (!prev_nid || prev_nid >= val) { + dev_warn(&codec->dev, + "invalid dep_range_val %x:%x\n", + prev_nid, val); + continue; + } + for (n = prev_nid + 1; n <= val; n++) { + if (conn_list) { + if (conns >= max_conns) + return -ENOSPC; + conn_list[conns] = n; + } + conns++; + } + } else { + if (conn_list) { + if (conns >= max_conns) + return -ENOSPC; + conn_list[conns] = val; + } + conns++; + } + prev_nid = val; + } + return conns; +} +EXPORT_SYMBOL_GPL(snd_hdac_get_connections); + +#ifdef CONFIG_PM +/** + * snd_hdac_power_up - power up the codec + * @codec: the codec object + * + * This function calls the runtime PM helper to power up the given codec. + * Unlike snd_hdac_power_up_pm(), you should call this only for the code + * path that isn't included in PM path. Otherwise it gets stuck. + */ +void snd_hdac_power_up(struct hdac_device *codec) +{ + pm_runtime_get_sync(&codec->dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_power_up); + +/** + * snd_hdac_power_down - power down the codec + * @codec: the codec object + */ +void snd_hdac_power_down(struct hdac_device *codec) +{ + struct device *dev = &codec->dev; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_power_down); + +/** + * snd_hdac_power_up_pm - power up the codec + * @codec: the codec object + * + * This function can be called in a recursive code path like init code + * which may be called by PM suspend/resume again. OTOH, if a power-up + * call must wake up the sleeper (e.g. in a kctl callback), use + * snd_hdac_power_up() instead. + */ +void snd_hdac_power_up_pm(struct hdac_device *codec) +{ + if (!atomic_inc_not_zero(&codec->in_pm)) + snd_hdac_power_up(codec); +} +EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm); + +/** + * snd_hdac_power_down_pm - power down the codec + * @codec: the codec object + * + * Like snd_hdac_power_up_pm(), this function is used in a recursive + * code path like init code which may be called by PM suspend/resume again. + */ +void snd_hdac_power_down_pm(struct hdac_device *codec) +{ + if (atomic_dec_if_positive(&codec->in_pm) < 0) + snd_hdac_power_down(codec); +} +EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); +#endif + +/* codec vendor labels */ +struct hda_vendor_id { + unsigned int id; + const char *name; +}; + +static struct hda_vendor_id hda_vendor_ids[] = { + { 0x1002, "ATI" }, + { 0x1013, "Cirrus Logic" }, + { 0x1057, "Motorola" }, + { 0x1095, "Silicon Image" }, + { 0x10de, "Nvidia" }, + { 0x10ec, "Realtek" }, + { 0x1102, "Creative" }, + { 0x1106, "VIA" }, + { 0x111d, "IDT" }, + { 0x11c1, "LSI" }, + { 0x11d4, "Analog Devices" }, + { 0x13f6, "C-Media" }, + { 0x14f1, "Conexant" }, + { 0x17e8, "Chrontel" }, + { 0x1854, "LG" }, + { 0x1aec, "Wolfson Microelectronics" }, + { 0x1af4, "QEMU" }, + { 0x434d, "C-Media" }, + { 0x8086, "Intel" }, + { 0x8384, "SigmaTel" }, + {} /* terminator */ +}; + +/* store the codec vendor name */ +static int get_codec_vendor_name(struct hdac_device *codec) +{ + const struct hda_vendor_id *c; + u16 vendor_id = codec->vendor_id >> 16; + + for (c = hda_vendor_ids; c->id; c++) { + if (c->id == vendor_id) { + codec->vendor_name = kstrdup(c->name, GFP_KERNEL); + return codec->vendor_name ? 0 : -ENOMEM; + } + } + + codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id); + return codec->vendor_name ? 0 : -ENOMEM; +} diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c new file mode 100644 index 000000000000..7371e0c3926f --- /dev/null +++ b/sound/hda/hdac_regmap.c @@ -0,0 +1,472 @@ +/* + * Regmap support for HD-audio verbs + * + * A virtual register is translated to one or more hda verbs for write, + * vice versa for read. + * + * A few limitations: + * - Provided for not all verbs but only subset standard non-volatile verbs. + * - For reading, only AC_VERB_GET_* variants can be used. + * - For writing, mapped to the *corresponding* AC_VERB_SET_* variants, + * so can't handle asymmetric verbs for read and write + */ + +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/export.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <sound/core.h> +#include <sound/hdaudio.h> +#include <sound/hda_regmap.h> + +#ifdef CONFIG_PM +#define codec_is_running(codec) \ + (atomic_read(&(codec)->in_pm) || \ + !pm_runtime_suspended(&(codec)->dev)) +#else +#define codec_is_running(codec) true +#endif + +#define get_verb(reg) (((reg) >> 8) & 0xfff) + +static bool hda_volatile_reg(struct device *dev, unsigned int reg) +{ + struct hdac_device *codec = dev_to_hdac_dev(dev); + unsigned int verb = get_verb(reg); + + switch (verb) { + case AC_VERB_GET_PROC_COEF: + return !codec->cache_coef; + case AC_VERB_GET_COEF_INDEX: + case AC_VERB_GET_PROC_STATE: + case AC_VERB_GET_POWER_STATE: + case AC_VERB_GET_PIN_SENSE: + case AC_VERB_GET_HDMI_DIP_SIZE: + case AC_VERB_GET_HDMI_ELDD: + case AC_VERB_GET_HDMI_DIP_INDEX: + case AC_VERB_GET_HDMI_DIP_DATA: + case AC_VERB_GET_HDMI_DIP_XMIT: + case AC_VERB_GET_HDMI_CP_CTRL: + case AC_VERB_GET_HDMI_CHAN_SLOT: + case AC_VERB_GET_DEVICE_SEL: + case AC_VERB_GET_DEVICE_LIST: /* read-only volatile */ + return true; + } + + return false; +} + +static bool hda_writeable_reg(struct device *dev, unsigned int reg) +{ + struct hdac_device *codec = dev_to_hdac_dev(dev); + unsigned int verb = get_verb(reg); + int i; + + for (i = 0; i < codec->vendor_verbs.used; i++) { + unsigned int *v = snd_array_elem(&codec->vendor_verbs, i); + if (verb == *v) + return true; + } + + if (codec->caps_overwriting) + return true; + + switch (verb & 0xf00) { + case AC_VERB_GET_STREAM_FORMAT: + case AC_VERB_GET_AMP_GAIN_MUTE: + return true; + case AC_VERB_GET_PROC_COEF: + return codec->cache_coef; + case 0xf00: + break; + default: + return false; + } + + switch (verb) { + case AC_VERB_GET_CONNECT_SEL: + case AC_VERB_GET_SDI_SELECT: + case AC_VERB_GET_PIN_WIDGET_CONTROL: + case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */ + case AC_VERB_GET_BEEP_CONTROL: + case AC_VERB_GET_EAPD_BTLENABLE: + case AC_VERB_GET_DIGI_CONVERT_1: + case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */ + case AC_VERB_GET_VOLUME_KNOB_CONTROL: + case AC_VERB_GET_GPIO_MASK: + case AC_VERB_GET_GPIO_DIRECTION: + case AC_VERB_GET_GPIO_DATA: /* not for volatile read */ + case AC_VERB_GET_GPIO_WAKE_MASK: + case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK: + case AC_VERB_GET_GPIO_STICKY_MASK: + return true; + } + + return false; +} + +static bool hda_readable_reg(struct device *dev, unsigned int reg) +{ + struct hdac_device *codec = dev_to_hdac_dev(dev); + unsigned int verb = get_verb(reg); + + if (codec->caps_overwriting) + return true; + + switch (verb) { + case AC_VERB_PARAMETERS: + case AC_VERB_GET_CONNECT_LIST: + case AC_VERB_GET_SUBSYSTEM_ID: + return true; + /* below are basically writable, but disabled for reducing unnecessary + * writes at sync + */ + case AC_VERB_GET_CONFIG_DEFAULT: /* usually just read */ + case AC_VERB_GET_CONV: /* managed in PCM code */ + case AC_VERB_GET_CVT_CHAN_COUNT: /* managed in HDMI CA code */ + return true; + } + + return hda_writeable_reg(dev, reg); +} + +/* + * Stereo amp pseudo register: + * for making easier to handle the stereo volume control, we provide a + * fake register to deal both left and right channels by a single + * (pseudo) register access. A verb consisting of SET_AMP_GAIN with + * *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit + * for the left and the upper 8bit for the right channel. + */ +static bool is_stereo_amp_verb(unsigned int reg) +{ + if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE) + return false; + return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) == + (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT); +} + +/* read a pseudo stereo amp register (16bit left+right) */ +static int hda_reg_read_stereo_amp(struct hdac_device *codec, + unsigned int reg, unsigned int *val) +{ + unsigned int left, right; + int err; + + reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT); + err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left); + if (err < 0) + return err; + err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right); + if (err < 0) + return err; + *val = left | (right << 8); + return 0; +} + +/* write a pseudo stereo amp register (16bit left+right) */ +static int hda_reg_write_stereo_amp(struct hdac_device *codec, + unsigned int reg, unsigned int val) +{ + int err; + unsigned int verb, left, right; + + verb = AC_VERB_SET_AMP_GAIN_MUTE << 8; + if (reg & AC_AMP_GET_OUTPUT) + verb |= AC_AMP_SET_OUTPUT; + else + verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8); + reg = (reg & ~0xfffff) | verb; + + left = val & 0xff; + right = (val >> 8) & 0xff; + if (left == right) { + reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT; + return snd_hdac_exec_verb(codec, reg | left, 0, NULL); + } + + err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL); + if (err < 0) + return err; + err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL); + if (err < 0) + return err; + return 0; +} + +/* read a pseudo coef register (16bit) */ +static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg, + unsigned int *val) +{ + unsigned int verb; + int err; + + if (!codec->cache_coef) + return -EINVAL; + /* LSB 8bit = coef index */ + verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8); + err = snd_hdac_exec_verb(codec, verb, 0, NULL); + if (err < 0) + return err; + verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8); + return snd_hdac_exec_verb(codec, verb, 0, val); +} + +/* write a pseudo coef register (16bit) */ +static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg, + unsigned int val) +{ + unsigned int verb; + int err; + + if (!codec->cache_coef) + return -EINVAL; + /* LSB 8bit = coef index */ + verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8); + err = snd_hdac_exec_verb(codec, verb, 0, NULL); + if (err < 0) + return err; + verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) | + (val & 0xffff); + return snd_hdac_exec_verb(codec, verb, 0, NULL); +} + +static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct hdac_device *codec = context; + int verb = get_verb(reg); + int err; + + if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE) + return -EAGAIN; + reg |= (codec->addr << 28); + if (is_stereo_amp_verb(reg)) + return hda_reg_read_stereo_amp(codec, reg, val); + if (verb == AC_VERB_GET_PROC_COEF) + return hda_reg_read_coef(codec, reg, val); + err = snd_hdac_exec_verb(codec, reg, 0, val); + if (err < 0) + return err; + /* special handling for asymmetric reads */ + if (verb == AC_VERB_GET_POWER_STATE) { + if (*val & AC_PWRST_ERROR) + *val = -1; + else /* take only the actual state */ + *val = (*val >> 4) & 0x0f; + } + return 0; +} + +static int hda_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct hdac_device *codec = context; + unsigned int verb; + int i, bytes, err; + + reg &= ~0x00080000U; /* drop GET bit */ + reg |= (codec->addr << 28); + verb = get_verb(reg); + + if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE) + return codec->lazy_cache ? 0 : -EAGAIN; + + if (is_stereo_amp_verb(reg)) + return hda_reg_write_stereo_amp(codec, reg, val); + + if (verb == AC_VERB_SET_PROC_COEF) + return hda_reg_write_coef(codec, reg, val); + + switch (verb & 0xf00) { + case AC_VERB_SET_AMP_GAIN_MUTE: + verb = AC_VERB_SET_AMP_GAIN_MUTE; + if (reg & AC_AMP_GET_LEFT) + verb |= AC_AMP_SET_LEFT >> 8; + else + verb |= AC_AMP_SET_RIGHT >> 8; + if (reg & AC_AMP_GET_OUTPUT) { + verb |= AC_AMP_SET_OUTPUT >> 8; + } else { + verb |= AC_AMP_SET_INPUT >> 8; + verb |= reg & 0xf; + } + break; + } + + switch (verb) { + case AC_VERB_SET_DIGI_CONVERT_1: + bytes = 2; + break; + case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0: + bytes = 4; + break; + default: + bytes = 1; + break; + } + + for (i = 0; i < bytes; i++) { + reg &= ~0xfffff; + reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff); + err = snd_hdac_exec_verb(codec, reg, 0, NULL); + if (err < 0) + return err; + } + + return 0; +} + +static const struct regmap_config hda_regmap_cfg = { + .name = "hdaudio", + .reg_bits = 32, + .val_bits = 32, + .max_register = 0xfffffff, + .writeable_reg = hda_writeable_reg, + .readable_reg = hda_readable_reg, + .volatile_reg = hda_volatile_reg, + .cache_type = REGCACHE_RBTREE, + .reg_read = hda_reg_read, + .reg_write = hda_reg_write, + .use_single_rw = true, +}; + +int snd_hdac_regmap_init(struct hdac_device *codec) +{ + struct regmap *regmap; + + regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + codec->regmap = regmap; + snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8); + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_init); + +void snd_hdac_regmap_exit(struct hdac_device *codec) +{ + if (codec->regmap) { + regmap_exit(codec->regmap); + codec->regmap = NULL; + snd_array_free(&codec->vendor_verbs); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit); + +/** + * snd_hdac_regmap_add_vendor_verb - add a vendor-specific verb to regmap + * @codec: the codec object + * @verb: verb to allow accessing via regmap + * + * Returns zero for success or a negative error code. + */ +int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec, + unsigned int verb) +{ + unsigned int *p = snd_array_new(&codec->vendor_verbs); + + if (!p) + return -ENOMEM; + *p = verb | 0x800; /* set GET bit */ + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb); + +/* + * helper functions + */ + +/* write a pseudo-register value (w/o power sequence) */ +static int reg_raw_write(struct hdac_device *codec, unsigned int reg, + unsigned int val) +{ + if (!codec->regmap) + return hda_reg_write(codec, reg, val); + else + return regmap_write(codec->regmap, reg, val); +} + +/** + * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt + * @codec: the codec object + * @reg: pseudo register + * @val: value to write + * + * Returns zero if successful or a negative error code. + */ +int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, + unsigned int val) +{ + int err; + + err = reg_raw_write(codec, reg, val); + if (err == -EAGAIN) { + snd_hdac_power_up_pm(codec); + err = reg_raw_write(codec, reg, val); + snd_hdac_power_down_pm(codec); + } + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw); + +static int reg_raw_read(struct hdac_device *codec, unsigned int reg, + unsigned int *val) +{ + if (!codec->regmap) + return hda_reg_read(codec, reg, val); + else + return regmap_read(codec->regmap, reg, val); +} + +/** + * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt + * @codec: the codec object + * @reg: pseudo register + * @val: pointer to store the read value + * + * Returns zero if successful or a negative error code. + */ +int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, + unsigned int *val) +{ + int err; + + err = reg_raw_read(codec, reg, val); + if (err == -EAGAIN) { + snd_hdac_power_up_pm(codec); + err = reg_raw_read(codec, reg, val); + snd_hdac_power_down_pm(codec); + } + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw); + +/** + * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt + * @codec: the codec object + * @reg: pseudo register + * @mask: bit mask to udpate + * @val: value to update + * + * Returns zero if successful or a negative error code. + */ +int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg, + unsigned int mask, unsigned int val) +{ + unsigned int orig; + int err; + + val &= mask; + err = snd_hdac_regmap_read_raw(codec, reg, &orig); + if (err < 0) + return err; + val |= orig & ~mask; + if (val == orig) + return 0; + err = snd_hdac_regmap_write_raw(codec, reg, val); + if (err < 0) + return err; + return 1; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw); diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c new file mode 100644 index 000000000000..0a6ce3b84cc4 --- /dev/null +++ b/sound/hda/hdac_sysfs.c @@ -0,0 +1,406 @@ +/* + * sysfs support for HD-audio core device + */ + +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/hdaudio.h> +#include "local.h" + +struct hdac_widget_tree { + struct kobject *root; + struct kobject *afg; + struct kobject **nodes; +}; + +#define CODEC_ATTR(type) \ +static ssize_t type##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct hdac_device *codec = dev_to_hdac_dev(dev); \ + return sprintf(buf, "0x%x\n", codec->type); \ +} \ +static DEVICE_ATTR_RO(type) + +#define CODEC_ATTR_STR(type) \ +static ssize_t type##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct hdac_device *codec = dev_to_hdac_dev(dev); \ + return sprintf(buf, "%s\n", \ + codec->type ? codec->type : ""); \ +} \ +static DEVICE_ATTR_RO(type) + +CODEC_ATTR(type); +CODEC_ATTR(vendor_id); +CODEC_ATTR(subsystem_id); +CODEC_ATTR(revision_id); +CODEC_ATTR(afg); +CODEC_ATTR(mfg); +CODEC_ATTR_STR(vendor_name); +CODEC_ATTR_STR(chip_name); + +static struct attribute *hdac_dev_attrs[] = { + &dev_attr_type.attr, + &dev_attr_vendor_id.attr, + &dev_attr_subsystem_id.attr, + &dev_attr_revision_id.attr, + &dev_attr_afg.attr, + &dev_attr_mfg.attr, + &dev_attr_vendor_name.attr, + &dev_attr_chip_name.attr, + NULL +}; + +static struct attribute_group hdac_dev_attr_group = { + .attrs = hdac_dev_attrs, +}; + +const struct attribute_group *hdac_dev_attr_groups[] = { + &hdac_dev_attr_group, + NULL +}; + +/* + * Widget tree sysfs + * + * This is a tree showing the attributes of each widget. It appears like + * /sys/bus/hdaudioC0D0/widgets/04/caps + */ + +struct widget_attribute; + +struct widget_attribute { + struct attribute attr; + ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf); + ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, + const char *buf, size_t count); +}; + +static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp) +{ + struct device *dev = kobj_to_dev(kobj->parent->parent); + int nid; + ssize_t ret; + + ret = kstrtoint(kobj->name, 16, &nid); + if (ret < 0) + return ret; + *codecp = dev_to_hdac_dev(dev); + return nid; +} + +static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct widget_attribute *wid_attr = + container_of(attr, struct widget_attribute, attr); + struct hdac_device *codec; + int nid; + + if (!wid_attr->show) + return -EIO; + nid = get_codec_nid(kobj, &codec); + if (nid < 0) + return nid; + return wid_attr->show(codec, nid, wid_attr, buf); +} + +static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct widget_attribute *wid_attr = + container_of(attr, struct widget_attribute, attr); + struct hdac_device *codec; + int nid; + + if (!wid_attr->store) + return -EIO; + nid = get_codec_nid(kobj, &codec); + if (nid < 0) + return nid; + return wid_attr->store(codec, nid, wid_attr, buf, count); +} + +static const struct sysfs_ops widget_sysfs_ops = { + .show = widget_attr_show, + .store = widget_attr_store, +}; + +static void widget_release(struct kobject *kobj) +{ + kfree(kobj); +} + +static struct kobj_type widget_ktype = { + .release = widget_release, + .sysfs_ops = &widget_sysfs_ops, +}; + +#define WIDGET_ATTR_RO(_name) \ + struct widget_attribute wid_attr_##_name = __ATTR_RO(_name) +#define WIDGET_ATTR_RW(_name) \ + struct widget_attribute wid_attr_##_name = __ATTR_RW(_name) + +static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%08x\n", get_wcaps(codec, nid)); +} + +static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP)); +} + +static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + unsigned int val; + + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return 0; + if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val)) + return 0; + return sprintf(buf, "0x%08x\n", val); +} + +static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid) +{ + if (nid == codec->afg || nid == codec->mfg) + return true; + switch (get_wcaps_type(get_wcaps(codec, nid))) { + case AC_WID_AUD_OUT: + case AC_WID_AUD_IN: + return true; + default: + return false; + } +} + +static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (!has_pcm_cap(codec, nid)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_PCM)); +} + +static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (!has_pcm_cap(codec, nid)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_STREAM)); +} + +static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP)); +} + +static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP)); +} + +static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE)); +} + +static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP)); +} + +static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + hda_nid_t list[32]; + int i, nconns; + ssize_t ret = 0; + + nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list)); + if (nconns <= 0) + return nconns; + for (i = 0; i < nconns; i++) + ret += sprintf(buf + ret, "%s0x%02x", i ? " " : "", list[i]); + ret += sprintf(buf + ret, "\n"); + return ret; +} + +static WIDGET_ATTR_RO(caps); +static WIDGET_ATTR_RO(pin_caps); +static WIDGET_ATTR_RO(pin_cfg); +static WIDGET_ATTR_RO(pcm_caps); +static WIDGET_ATTR_RO(pcm_formats); +static WIDGET_ATTR_RO(amp_in_caps); +static WIDGET_ATTR_RO(amp_out_caps); +static WIDGET_ATTR_RO(power_caps); +static WIDGET_ATTR_RO(gpio_caps); +static WIDGET_ATTR_RO(connections); + +static struct attribute *widget_node_attrs[] = { + &wid_attr_caps.attr, + &wid_attr_pin_caps.attr, + &wid_attr_pin_cfg.attr, + &wid_attr_pcm_caps.attr, + &wid_attr_pcm_formats.attr, + &wid_attr_amp_in_caps.attr, + &wid_attr_amp_out_caps.attr, + &wid_attr_power_caps.attr, + &wid_attr_connections.attr, + NULL, +}; + +static struct attribute *widget_afg_attrs[] = { + &wid_attr_pcm_caps.attr, + &wid_attr_pcm_formats.attr, + &wid_attr_amp_in_caps.attr, + &wid_attr_amp_out_caps.attr, + &wid_attr_power_caps.attr, + &wid_attr_gpio_caps.attr, + NULL, +}; + +static const struct attribute_group widget_node_group = { + .attrs = widget_node_attrs, +}; + +static const struct attribute_group widget_afg_group = { + .attrs = widget_afg_attrs, +}; + +static void free_widget_node(struct kobject *kobj, + const struct attribute_group *group) +{ + if (kobj) { + sysfs_remove_group(kobj, group); + kobject_put(kobj); + } +} + +static void widget_tree_free(struct hdac_device *codec) +{ + struct hdac_widget_tree *tree = codec->widgets; + struct kobject **p; + + if (!tree) + return; + free_widget_node(tree->afg, &widget_afg_group); + if (tree->nodes) { + for (p = tree->nodes; *p; p++) + free_widget_node(*p, &widget_node_group); + kfree(tree->nodes); + } + if (tree->root) + kobject_put(tree->root); + kfree(tree); + codec->widgets = NULL; +} + +static int add_widget_node(struct kobject *parent, hda_nid_t nid, + const struct attribute_group *group, + struct kobject **res) +{ + struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); + int err; + + if (!kobj) + return -ENOMEM; + kobject_init(kobj, &widget_ktype); + err = kobject_add(kobj, parent, "%02x", nid); + if (err < 0) + return err; + err = sysfs_create_group(kobj, group); + if (err < 0) { + kobject_put(kobj); + return err; + } + + *res = kobj; + return 0; +} + +static int widget_tree_create(struct hdac_device *codec) +{ + struct hdac_widget_tree *tree; + int i, err; + hda_nid_t nid; + + tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL); + if (!tree) + return -ENOMEM; + + tree->root = kobject_create_and_add("widgets", &codec->dev.kobj); + if (!tree->root) + return -ENOMEM; + + tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes), + GFP_KERNEL); + if (!tree->nodes) + return -ENOMEM; + + for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) { + err = add_widget_node(tree->root, nid, &widget_node_group, + &tree->nodes[i]); + if (err < 0) + return err; + } + + if (codec->afg) { + err = add_widget_node(tree->root, codec->afg, + &widget_afg_group, &tree->afg); + if (err < 0) + return err; + } + + kobject_uevent(tree->root, KOBJ_CHANGE); + return 0; +} + +int hda_widget_sysfs_init(struct hdac_device *codec) +{ + int err; + + err = widget_tree_create(codec); + if (err < 0) { + widget_tree_free(codec); + return err; + } + + return 0; +} + +void hda_widget_sysfs_exit(struct hdac_device *codec) +{ + widget_tree_free(codec); +} diff --git a/sound/hda/local.h b/sound/hda/local.h new file mode 100644 index 000000000000..d692f417ddc0 --- /dev/null +++ b/sound/hda/local.h @@ -0,0 +1,23 @@ +/* + * Local helper macros and functions for HD-audio core drivers + */ + +#ifndef __HDAC_LOCAL_H +#define __HDAC_LOCAL_H + +#define get_wcaps(codec, nid) \ + snd_hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) + +/* get the widget type from widget capability bits */ +static inline int get_wcaps_type(unsigned int wcaps) +{ + if (!wcaps) + return -1; /* invalid type */ + return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +} + +extern const struct attribute_group *hdac_dev_attr_groups[]; +int hda_widget_sysfs_init(struct hdac_device *codec); +void hda_widget_sysfs_exit(struct hdac_device *codec); + +#endif /* __HDAC_LOCAL_H */ diff --git a/sound/hda/trace.c b/sound/hda/trace.c new file mode 100644 index 000000000000..ca2d6bd94518 --- /dev/null +++ b/sound/hda/trace.c @@ -0,0 +1,6 @@ +/* + * tracepoint definitions for HD-audio core drivers + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/sound/hda/trace.h b/sound/hda/trace.h new file mode 100644 index 000000000000..33a7eb5573d4 --- /dev/null +++ b/sound/hda/trace.h @@ -0,0 +1,62 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM hda + +#if !defined(__HDAC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __HDAC_TRACE_H + +#include <linux/tracepoint.h> +#include <linux/device.h> +#include <sound/hdaudio.h> + +#ifndef HDAC_MSG_MAX +#define HDAC_MSG_MAX 500 +#endif + +struct hdac_bus; +struct hdac_codec; + +TRACE_EVENT(hda_send_cmd, + TP_PROTO(struct hdac_bus *bus, unsigned int cmd), + TP_ARGS(bus, cmd), + TP_STRUCT__entry(__dynamic_array(char, msg, HDAC_MSG_MAX)), + TP_fast_assign( + snprintf(__get_str(msg), HDAC_MSG_MAX, + "[%s:%d] val=0x%08x", + dev_name((bus)->dev), (cmd) >> 28, cmd); + ), + TP_printk("%s", __get_str(msg)) +); + +TRACE_EVENT(hda_get_response, + TP_PROTO(struct hdac_bus *bus, unsigned int addr, unsigned int res), + TP_ARGS(bus, addr, res), + TP_STRUCT__entry(__dynamic_array(char, msg, HDAC_MSG_MAX)), + TP_fast_assign( + snprintf(__get_str(msg), HDAC_MSG_MAX, + "[%s:%d] val=0x%08x", + dev_name((bus)->dev), addr, res); + ), + TP_printk("%s", __get_str(msg)) +); + +TRACE_EVENT(hda_unsol_event, + TP_PROTO(struct hdac_bus *bus, u32 res, u32 res_ex), + TP_ARGS(bus, res, res_ex), + TP_STRUCT__entry(__dynamic_array(char, msg, HDAC_MSG_MAX)), + TP_fast_assign( + snprintf(__get_str(msg), HDAC_MSG_MAX, + "[%s:%d] res=0x%08x, res_ex=0x%08x", + dev_name((bus)->dev), res_ex & 0x0f, res, res_ex); + ), + TP_printk("%s", __get_str(msg)) +); +#endif /* __HDAC_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include <trace/define_trace.h> diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c index 88844881cbff..2183e9ebaa6d 100644 --- a/sound/i2c/other/ak4113.c +++ b/sound/i2c/other/ak4113.c @@ -73,7 +73,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read, void *private_data, struct ak4113 **r_ak4113) { struct ak4113 *chip; - int err = 0; + int err; unsigned char reg; static struct snd_device_ops ops = { .dev_free = snd_ak4113_dev_free, @@ -109,7 +109,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read, __fail: snd_ak4113_free(chip); - return err < 0 ? err : -EIO; + return err; } EXPORT_SYMBOL_GPL(snd_ak4113_create); diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c index 72332dfada9a..4aa719cad331 100644 --- a/sound/isa/sb/emu8000_synth.c +++ b/sound/isa/sb/emu8000_synth.c @@ -34,8 +34,9 @@ MODULE_LICENSE("GPL"); /* * create a new hardware dependent device for Emu8000 */ -static int snd_emu8000_new_device(struct snd_seq_device *dev) +static int snd_emu8000_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emu8000 *hw; struct snd_emux *emu; @@ -93,8 +94,9 @@ static int snd_emu8000_new_device(struct snd_seq_device *dev) /* * free all resources */ -static int snd_emu8000_delete_device(struct snd_seq_device *dev) +static int snd_emu8000_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emu8000 *hw; if (dev->driver_data == NULL) @@ -114,21 +116,14 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev) * INIT part */ -static int __init alsa_emu8000_init(void) -{ - - static struct snd_seq_dev_ops ops = { - snd_emu8000_new_device, - snd_emu8000_delete_device, - }; - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, - sizeof(struct snd_emu8000*)); -} - -static void __exit alsa_emu8000_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000); -} - -module_init(alsa_emu8000_init) -module_exit(alsa_emu8000_exit) +static struct snd_seq_driver emu8000_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_emu8000_probe, + .remove = snd_emu8000_remove, + }, + .id = SNDRV_SEQ_DEV_ID_EMU8000, + .argsize = sizeof(struct snd_emu8000 *), +}; + +module_snd_seq_driver(emu8000_driver); diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index b5a19708473a..0608a5a4289d 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -79,13 +79,13 @@ wavefront_fx_memset (snd_wavefront_t *dev, if (page < 0 || page > 7) { snd_printk ("FX memset: " "page must be >= 0 and <= 7\n"); - return -(EINVAL); + return -EINVAL; } if (addr < 0 || addr > 0x7f) { snd_printk ("FX memset: " "addr must be >= 0 and <= 7f\n"); - return -(EINVAL); + return -EINVAL; } if (cnt == 1) { @@ -118,7 +118,7 @@ wavefront_fx_memset (snd_wavefront_t *dev, snd_printk ("FX memset " "(0x%x, 0x%x, 0x%lx, %d) incomplete\n", page, addr, (unsigned long) data, cnt); - return -(EIO); + return -EIO; } } diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index 33f5ec14fcfa..69f76ff5693d 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -793,7 +793,7 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header) if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, NULL, buf)) { snd_printk ("download patch failed\n"); - return -(EIO); + return -EIO; } return (0); @@ -831,7 +831,7 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header) if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, NULL, buf)) { snd_printk ("download patch failed\n"); - return -(EIO); + return -EIO; } return (0); @@ -952,7 +952,7 @@ wavefront_send_sample (snd_wavefront_t *dev, if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { snd_printk ("channel selection only " "possible on 16-bit samples"); - return -(EINVAL); + return -EINVAL; } switch (skip) { @@ -1049,7 +1049,7 @@ wavefront_send_sample (snd_wavefront_t *dev, NULL, sample_hdr)) { snd_printk ("sample %sdownload refused.\n", header->size ? "" : "header "); - return -(EIO); + return -EIO; } if (header->size == 0) { @@ -1075,7 +1075,7 @@ wavefront_send_sample (snd_wavefront_t *dev, if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, NULL, NULL)) { snd_printk ("download block " "request refused.\n"); - return -(EIO); + return -EIO; } for (i = 0; i < blocksize; i++) { @@ -1135,12 +1135,12 @@ wavefront_send_sample (snd_wavefront_t *dev, if (dma_ack == -1) { snd_printk ("upload sample " "DMA ack timeout\n"); - return -(EIO); + return -EIO; } else { snd_printk ("upload sample " "DMA ack error 0x%x\n", dma_ack); - return -(EIO); + return -EIO; } } } @@ -1181,7 +1181,7 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header) if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, NULL, alias_hdr)) { snd_printk ("download alias failed.\n"); - return -(EIO); + return -EIO; } dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); @@ -1232,7 +1232,7 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header) msample_hdr)) { snd_printk ("download of multisample failed.\n"); kfree(msample_hdr); - return -(EIO); + return -EIO; } dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); @@ -1254,7 +1254,7 @@ wavefront_fetch_multisample (snd_wavefront_t *dev, if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { snd_printk ("upload multisample failed.\n"); - return -(EIO); + return -EIO; } DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n", @@ -1273,14 +1273,14 @@ wavefront_fetch_multisample (snd_wavefront_t *dev, if ((val = wavefront_read (dev)) == -1) { snd_printk ("upload multisample failed " "during sample loop.\n"); - return -(EIO); + return -EIO; } d[0] = val; if ((val = wavefront_read (dev)) == -1) { snd_printk ("upload multisample failed " "during sample loop.\n"); - return -(EIO); + return -EIO; } d[1] = val; @@ -1315,7 +1315,7 @@ wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header) if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, NULL, drumbuf)) { snd_printk ("download drum failed.\n"); - return -(EIO); + return -EIO; } return (0); diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c index fbcaa5434fd8..1e30e8475431 100644 --- a/sound/mips/au1x00.c +++ b/sound/mips/au1x00.c @@ -633,19 +633,25 @@ static int au1000_ac97_probe(struct platform_device *pdev) au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL); - if (!au1000->stream[PLAYBACK]) + if (!au1000->stream[PLAYBACK]) { + err = -ENOMEM; goto out; + } au1000->stream[PLAYBACK]->dma = -1; au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL); - if (!au1000->stream[CAPTURE]) + if (!au1000->stream[CAPTURE]) { + err = -ENOMEM; goto out; + } au1000->stream[CAPTURE]->dma = -1; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) + if (!r) { + err = -ENODEV; goto out; + } err = -EBUSY; au1000->ac97_res_port = request_mem_region(r->start, resource_size(r), diff --git a/sound/oss/dev_table.c b/sound/oss/dev_table.c index d8cf3e58dc76..6dad51596b70 100644 --- a/sound/oss/dev_table.c +++ b/sound/oss/dev_table.c @@ -58,13 +58,13 @@ int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) { printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name); - return -(EINVAL); + return -EINVAL; } num = sound_alloc_audiodev(); if (num == -1) { printk(KERN_ERR "sound: Too many audio drivers\n"); - return -(EBUSY); + return -EBUSY; } d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver))); sound_nblocks++; @@ -79,7 +79,7 @@ int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, if (d == NULL || op == NULL) { printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name); sound_unload_audiodev(num); - return -(ENOMEM); + return -ENOMEM; } init_waitqueue_head(&op->in_sleeper); init_waitqueue_head(&op->out_sleeper); diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c index 607cee4d545e..b6d19adf8f41 100644 --- a/sound/oss/opl3.c +++ b/sound/oss/opl3.c @@ -666,7 +666,7 @@ static int opl3_start_note (int dev, int voice, int note, int volume) opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); - devc->voc[voice].keyon_byte = data; + devc->voc[voice].keyon_byte = data; opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); if (voice_mode == 4) opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); @@ -717,7 +717,7 @@ static void freq_to_fnum (int freq, int *block, int *fnum) static void opl3_command (int io_addr, unsigned int addr, unsigned int val) { - int i; + int i; /* * The original 2-OP synth requires a quite long delay after writing to a diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c index b47a69026f1b..57f7d25a2cd3 100644 --- a/sound/oss/sb_ess.c +++ b/sound/oss/sb_ess.c @@ -604,7 +604,7 @@ static void ess_audio_output_block_audio2 ess_chgmixer (devc, 0x78, 0x03, 0x03); /* Go */ devc->irq_mode_16 = IMODE_OUTPUT; - devc->intr_active_16 = 1; + devc->intr_active_16 = 1; } static void ess_audio_output_block @@ -1183,17 +1183,12 @@ FKS_test (devc); chip = "ES1688"; } - printk ( KERN_INFO "ESS chip %s %s%s\n" - , chip - , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20 - ? "detected" - : "specified" - ) - , ( devc->sbmo.esstype == ESSTYPE_LIKE20 - ? " (kernel 2.0 compatible)" - : "" - ) - ); + printk(KERN_INFO "ESS chip %s %s%s\n", chip, + (devc->sbmo.esstype == ESSTYPE_DETECT || + devc->sbmo.esstype == ESSTYPE_LIKE20) ? + "detected" : "specified", + devc->sbmo.esstype == ESSTYPE_LIKE20 ? + " (kernel 2.0 compatible)" : ""); sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f); } else { diff --git a/sound/oss/sb_midi.c b/sound/oss/sb_midi.c index f139028e85c0..551ee7557b4e 100644 --- a/sound/oss/sb_midi.c +++ b/sound/oss/sb_midi.c @@ -179,14 +179,14 @@ void sb_dsp_midi_init(sb_devc * devc, struct module *owner) { printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); sound_unload_mididev(dev); - return; + return; } memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations, sizeof(struct midi_operations)); if (owner) - midi_devs[dev]->owner = owner; - + midi_devs[dev]->owner = owner; + midi_devs[dev]->devc = devc; diff --git a/sound/oss/sys_timer.c b/sound/oss/sys_timer.c index 9f039831114c..2226dda0eff0 100644 --- a/sound/oss/sys_timer.c +++ b/sound/oss/sys_timer.c @@ -50,29 +50,24 @@ tmr2ticks(int tmr_value) static void poll_def_tmr(unsigned long dummy) { + if (!opened) + return; + def_tmr.expires = (1) + jiffies; + add_timer(&def_tmr); - if (opened) - { + if (!tmr_running) + return; - { - def_tmr.expires = (1) + jiffies; - add_timer(&def_tmr); - } + spin_lock(&lock); + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); - if (tmr_running) - { - spin_lock(&lock); - tmr_ctr++; - curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); - - if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer(0); - } - spin_unlock(&lock); - } - } + if (curr_ticks >= next_event_time) { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } + + spin_unlock(&lock); } static void diff --git a/sound/oss/v_midi.c b/sound/oss/v_midi.c index f0b4151d9b17..fc0ba276cc8f 100644 --- a/sound/oss/v_midi.c +++ b/sound/oss/v_midi.c @@ -49,13 +49,13 @@ static int v_midi_open (int dev, int mode, unsigned long flags; if (devc == NULL) - return -(ENXIO); + return -ENXIO; spin_lock_irqsave(&devc->lock,flags); if (devc->opened) { spin_unlock_irqrestore(&devc->lock,flags); - return -(EBUSY); + return -EBUSY; } devc->opened = 1; spin_unlock_irqrestore(&devc->lock,flags); diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 5ee2f17c287c..82259ca61e64 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -177,6 +177,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = { { 0x54524123, 0xffffffff, "TR28602", NULL, NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)] { 0x54584e03, 0xffffffff, "TLV320AIC27", NULL, NULL }, { 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL, NULL }, +{ 0x56494120, 0xfffffff0, "VIA1613", patch_vt1613, NULL }, { 0x56494161, 0xffffffff, "VIA1612A", NULL, NULL }, // modified ICE1232 with S/PDIF { 0x56494170, 0xffffffff, "VIA1617A", patch_vt1617a, NULL }, // modified VT1616 with S/PDIF { 0x56494182, 0xffffffff, "VIA1618", patch_vt1618, NULL }, @@ -2901,7 +2902,8 @@ static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr) * Return: Zero if successful, or a negative error code on failure. */ -int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override) +int snd_ac97_tune_hardware(struct snd_ac97 *ac97, + const struct ac97_quirk *quirk, const char *override) { int result; diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index ceaac1c41906..f4234edb878c 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -3352,6 +3352,33 @@ static int patch_cm9780(struct snd_ac97 *ac97) } /* + * VIA VT1613 codec + */ +static const struct snd_kcontrol_new snd_ac97_controls_vt1613[] = { +AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0), +}; + +static int patch_vt1613_specific(struct snd_ac97 *ac97) +{ + return patch_build_controls(ac97, &snd_ac97_controls_vt1613[0], + ARRAY_SIZE(snd_ac97_controls_vt1613)); +}; + +static const struct snd_ac97_build_ops patch_vt1613_ops = { + .build_specific = patch_vt1613_specific +}; + +static int patch_vt1613(struct snd_ac97 *ac97) +{ + ac97->build_ops = &patch_vt1613_ops; + + ac97->flags |= AC97_HAS_NO_VIDEO; + ac97->caps |= AC97_BC_HEADPHONE; + + return 0; +} + +/* * VIA VT1616 codec */ static const struct snd_kcontrol_new snd_ac97_controls_vt1616[] = { diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 850a8c984c25..66ddd981d1d5 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -747,7 +747,7 @@ snd_ad1889_proc_init(struct snd_ad1889 *chip) snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read); } -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x11d4, /* AD */ .subdevice = 0x1889, /* AD1889 */ diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index e5cd7be85355..1039eccbb895 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -2376,7 +2376,7 @@ static int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi, /*------------------------------------------------------------ Sampleclock source controls ------------------------------------------------------------*/ -static const char const *sampleclock_sources[] = { +static const char * const sampleclock_sources[] = { "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header", "SMPTE", "Digital1", "Auto", "Network", "Invalid", "Prev Module", "BLU-Link", diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index d5f15c9bbeda..42a20c806b39 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1390,7 +1390,7 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id) * ac97 mixer section */ -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x103c, .subdevice = 0x006b, diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index a40a2b4c8fd7..33b2a0af1b59 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1385,8 +1385,8 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip, .running) && (!chip->codecs[peer_codecs[codec_type].other2] .running)); - } - if (call_function) + } + if (call_function) snd_azf3328_ctrl_enable_codecs(chip, enable); /* ...and adjust clock, too @@ -2126,7 +2126,8 @@ static struct snd_pcm_ops snd_azf3328_i2s_out_ops = { static int snd_azf3328_pcm(struct snd_azf3328 *chip) { -enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */ + /* pcm devices */ + enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; struct snd_pcm *pcm; int err; diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 1d0f2cad2f5a..6cf464d9043d 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2062,7 +2062,7 @@ static int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol, val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask; if (reg.invert) val = reg.mask - val; - ucontrol->value.integer.value[1] = val; + ucontrol->value.integer.value[1] = val; } spin_unlock_irq(&cm->reg_lock); return 0; diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 802c33f1cc59..963b912550d4 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -43,7 +43,7 @@ static char *ac97_quirk; module_param(ac97_quirk, charp, 0444); MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds."); -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { #if 0 /* Not yet confirmed if all 5536 boards are HP only */ { .subvendor = PCI_VENDOR_ID_AMD, diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index a962de03ebb6..862db9a0b041 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -1283,12 +1283,14 @@ static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol, static int snd_echo_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct echoaudio *chip; + struct echoaudio *chip = snd_kcontrol_chip(kcontrol); + unsigned int out = ucontrol->id.index / num_busses_in(chip); + unsigned int in = ucontrol->id.index % num_busses_in(chip); - chip = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = - chip->monitor_gain[ucontrol->id.index / num_busses_in(chip)] - [ucontrol->id.index % num_busses_in(chip)]; + if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS) + return -EINVAL; + + ucontrol->value.integer.value[0] = chip->monitor_gain[out][in]; return 0; } @@ -1297,12 +1299,14 @@ static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol, { struct echoaudio *chip; int changed, gain; - short out, in; + unsigned int out, in; changed = 0; chip = snd_kcontrol_chip(kcontrol); out = ucontrol->id.index / num_busses_in(chip); in = ucontrol->id.index % num_busses_in(chip); + if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS) + return -EINVAL; gain = ucontrol->value.integer.value[0]; if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) return -EINVAL; diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index b4458a630a7c..54079f5d5673 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -707,6 +707,7 @@ static int emu1010_firmware_thread(void *data) { struct snd_emu10k1 *emu = data; u32 tmp, tmp2, reg; + u32 last_reg = 0; int err; for (;;) { @@ -782,7 +783,15 @@ static int emu1010_firmware_thread(void *data) msleep(10); /* Unmute all. Default is muted after a firmware load */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); + } else if (!reg && last_reg) { + /* Audio Dock removed */ + dev_info(emu->card->dev, + "emu1010: Audio Dock detached\n"); + /* Unmute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); } + + last_reg = reg; } dev_info(emu->card->dev, "emu1010: firmware thread stopping\n"); return 0; @@ -1325,6 +1334,22 @@ static int snd_emu10k1_dev_free(struct snd_device *device) } static struct snd_emu_chip_details emu_chip_details[] = { + /* Audigy 5/Rx SB1550 */ + /* Tested by michael@gernoth.net 28 Mar 2015 */ + /* DSP: CA10300-IAT LF + * DAC: Cirrus Logic CS4382-KQZ + * ADC: Philips 1361T + * AC97: Sigmatel STAC9750 + * CA0151: None + */ + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10241102, + .driver = "Audigy2", .name = "SB Audigy 5/Rx [SB1550]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .spk71 = 1, + .adc_1361t = 1, /* 24 bit capture instead of 16bit */ + .ac97_chip = 1}, /* Audigy4 (Not PRO) SB0610 */ /* Tested by James@superbug.co.uk 4th April 2006 */ /* A_IOCFG bits diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c index 4c41c903a840..5457d5613f6b 100644 --- a/sound/pci/emu10k1/emu10k1_synth.c +++ b/sound/pci/emu10k1/emu10k1_synth.c @@ -29,8 +29,9 @@ MODULE_LICENSE("GPL"); /* * create a new hardware dependent device for Emu10k1 */ -static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) +static int snd_emu10k1_synth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emux *emux; struct snd_emu10k1 *hw; struct snd_emu10k1_synth_arg *arg; @@ -79,8 +80,9 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) return 0; } -static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev) +static int snd_emu10k1_synth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emux *emux; struct snd_emu10k1 *hw; unsigned long flags; @@ -104,21 +106,14 @@ static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev) * INIT part */ -static int __init alsa_emu10k1_synth_init(void) -{ - - static struct snd_seq_dev_ops ops = { - snd_emu10k1_synth_new_device, - snd_emu10k1_synth_delete_device, - }; - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops, - sizeof(struct snd_emu10k1_synth_arg)); -} - -static void __exit alsa_emu10k1_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH); -} - -module_init(alsa_emu10k1_synth_init) -module_exit(alsa_emu10k1_synth_exit) +static struct snd_seq_driver emu10k1_synth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_emu10k1_synth_probe, + .remove = snd_emu10k1_synth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, + .argsize = sizeof(struct snd_emu10k1_synth_arg), +}; + +module_snd_seq_driver(emu10k1_synth_driver); diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 1de33025669a..55e57166256e 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -806,6 +806,108 @@ static struct snd_kcontrol_new snd_emu1010_internal_clock = .put = snd_emu1010_internal_clock_put }; +static int snd_emu1010_optical_out_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[2] = { + "SPDIF", "ADAT" + }; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); +} + +static int snd_emu1010_optical_out_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->emu1010.optical_out; + return 0; +} + +static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int val; + u32 tmp; + int change = 0; + + val = ucontrol->value.enumerated.item[0]; + /* Limit: uinfo->value.enumerated.items = 2; */ + if (val >= 2) + return -EINVAL; + change = (emu->emu1010.optical_out != val); + if (change) { + emu->emu1010.optical_out = val; + tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) | + (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0); + snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp); + } + return change; +} + +static struct snd_kcontrol_new snd_emu1010_optical_out = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Optical Output Mode", + .count = 1, + .info = snd_emu1010_optical_out_info, + .get = snd_emu1010_optical_out_get, + .put = snd_emu1010_optical_out_put +}; + +static int snd_emu1010_optical_in_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[2] = { + "SPDIF", "ADAT" + }; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); +} + +static int snd_emu1010_optical_in_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->emu1010.optical_in; + return 0; +} + +static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int val; + u32 tmp; + int change = 0; + + val = ucontrol->value.enumerated.item[0]; + /* Limit: uinfo->value.enumerated.items = 2; */ + if (val >= 2) + return -EINVAL; + change = (emu->emu1010.optical_in != val); + if (change) { + emu->emu1010.optical_in = val; + tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) | + (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0); + snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp); + } + return change; +} + +static struct snd_kcontrol_new snd_emu1010_optical_in = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Optical Input Mode", + .count = 1, + .info = snd_emu1010_optical_in_info, + .get = snd_emu1010_optical_in_get, + .put = snd_emu1010_optical_in_put +}; + static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2051,6 +2153,14 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, snd_ctl_new1(&snd_emu1010_internal_clock, emu)); if (err < 0) return err; + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_optical_out, emu)); + if (err < 0) + return err; + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_optical_in, emu)); + if (err < 0) + return err; } else if (emu->card_capabilities->emu_model) { /* all other e-mu cards for now */ @@ -2086,6 +2196,14 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, snd_ctl_new1(&snd_emu1010_internal_clock, emu)); if (err < 0) return err; + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_optical_out, emu)); + if (err < 0) + return err; + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_optical_in, emu)); + if (err < 0) + return err; } if ( emu->card_capabilities->i2c_adc) { diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 2ca9f2e93139..53745f4c2bf5 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -241,31 +241,22 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, struct snd_emu10k1 *emu = entry->private_data; u32 value; u32 value2; - unsigned long flags; u32 rate; if (emu->card_capabilities->emu_model) { - spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, 0x38, &value); - spin_unlock_irqrestore(&emu->emu_lock, flags); if ((value & 0x1) == 0) { - spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, 0x2a, &value); snd_emu1010_fpga_read(emu, 0x2b, &value2); - spin_unlock_irqrestore(&emu->emu_lock, flags); rate = 0x1770000 / (((value << 5) | value2)+1); snd_iprintf(buffer, "ADAT Locked : %u\n", rate); } else { snd_iprintf(buffer, "ADAT Unlocked\n"); } - spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, 0x20, &value); - spin_unlock_irqrestore(&emu->emu_lock, flags); if ((value & 0x4) == 0) { - spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, 0x28, &value); snd_emu1010_fpga_read(emu, 0x29, &value2); - spin_unlock_irqrestore(&emu->emu_lock, flags); rate = 0x1770000 / (((value << 5) | value2)+1); snd_iprintf(buffer, "SPDIF Locked : %d\n", rate); } else { @@ -410,14 +401,11 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry, { struct snd_emu10k1 *emu = entry->private_data; u32 value; - unsigned long flags; int i; snd_iprintf(buffer, "EMU1010 Registers:\n\n"); for(i = 0; i < 0x40; i+=1) { - spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, i, &value); - spin_unlock_irqrestore(&emu->emu_lock, flags); snd_iprintf(buffer, "%02X: %08X, %02X\n", i, value, (value >> 8) & 0x7f); } } diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 7f0f2c5a4e97..a5ed1c181784 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -5,6 +5,7 @@ config SND_HDA select SND_PCM select SND_VMASTER select SND_KCTL_JACK + select SND_HDA_CORE config SND_HDA_INTEL tristate "HD Audio PCI" diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 194f30935e77..af78fb33a4fd 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -4,13 +4,12 @@ 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_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.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-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o # for trace-points -CFLAGS_hda_codec.o := -I$(src) CFLAGS_hda_controller.o := -I$(src) snd-hda-codec-generic-objs := hda_generic.o diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 3f8706bb3d16..03b7399bb7f0 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -172,7 +172,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, const hda_nid_t *ignore_nids, unsigned int cond_flags) { - hda_nid_t nid, end_nid; + hda_nid_t nid; short seq, assoc_line_out; struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)]; struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)]; @@ -189,8 +189,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, memset(hp_out, 0, sizeof(hp_out)); assoc_line_out = 0; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wid_caps); unsigned int def_conf; @@ -410,7 +409,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, * debug prints of the parsed results */ codec_info(codec, "autoconfig for %s: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", - codec->chip_name, cfg->line_outs, cfg->line_out_pins[0], + codec->core.chip_name, cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1], cfg->line_out_pins[2], cfg->line_out_pins[3], cfg->line_out_pins[4], cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : @@ -836,33 +835,33 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins) break; codec_dbg(codec, "%s: Apply pincfg for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); snd_hda_apply_pincfgs(codec, fix->v.pins); break; case HDA_FIXUP_VERBS: if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs) break; codec_dbg(codec, "%s: Apply fix-verbs for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); snd_hda_add_verbs(codec, fix->v.verbs); break; case HDA_FIXUP_FUNC: if (!fix->v.func) break; codec_dbg(codec, "%s: Apply fix-func for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); fix->v.func(codec, fix, action); break; case HDA_FIXUP_PINCTLS: if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins) break; codec_dbg(codec, "%s: Apply pinctl for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); set_pin_targets(codec, fix->v.pins); break; default: codec_err(codec, "%s: Invalid fixup type %d\n", - codec->chip_name, fix->type); + codec->core.chip_name, fix->type); break; } if (!fix->chained || fix->chained_before) @@ -912,16 +911,16 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, return; for (pq = pin_quirk; pq->subvendor; pq++) { - if ((codec->subsystem_id & 0xffff0000) != (pq->subvendor << 16)) + if ((codec->core.subsystem_id & 0xffff0000) != (pq->subvendor << 16)) continue; - if (codec->vendor_id != pq->codec) + if (codec->core.vendor_id != pq->codec) continue; if (pin_config_match(codec, pq->pins)) { codec->fixup_id = pq->value; #ifdef CONFIG_SND_DEBUG_VERBOSE codec->fixup_name = pq->name; codec_dbg(codec, "%s: picked fixup %s (pin match)\n", - codec->chip_name, codec->fixup_name); + codec->core.chip_name, codec->fixup_name); #endif codec->fixup_list = fixlist; return; @@ -963,7 +962,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec, codec->fixup_name = NULL; codec->fixup_id = HDA_FIXUP_ID_NO_FIXUP; codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n", - codec->chip_name); + codec->core.chip_name); return; } @@ -974,7 +973,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec, codec->fixup_name = models->name; codec->fixup_list = fixlist; codec_dbg(codec, "%s: picked fixup %s (model specified)\n", - codec->chip_name, codec->fixup_name); + codec->core.chip_name, codec->fixup_name); return; } models++; @@ -987,7 +986,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec, #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; codec_dbg(codec, "%s: picked fixup %s (PCI SSID%s)\n", - codec->chip_name, name, q->subdevice_mask ? "" : " - vendor generic"); + codec->core.chip_name, name, q->subdevice_mask ? "" : " - vendor generic"); #endif } } @@ -996,12 +995,12 @@ void snd_hda_pick_fixup(struct hda_codec *codec, unsigned int vendorid = q->subdevice | (q->subvendor << 16); unsigned int mask = 0xffff0000 | q->subdevice_mask; - if ((codec->subsystem_id & mask) == (vendorid & mask)) { + if ((codec->core.subsystem_id & mask) == (vendorid & mask)) { id = q->value; #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; codec_dbg(codec, "%s: picked fixup %s (codec SSID)\n", - codec->chip_name, name); + codec->core.chip_name, name); #endif break; } diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 1e7de08e77cb..3364dc0fdeab 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -33,30 +33,36 @@ enum { DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */ }; -static void snd_hda_generate_beep(struct work_struct *work) +/* generate or stop tone */ +static void generate_tone(struct hda_beep *beep, int tone) { - struct hda_beep *beep = - container_of(work, struct hda_beep, beep_work); struct hda_codec *codec = beep->codec; - int tone; - if (!beep->enabled) - return; - - tone = beep->tone; if (tone && !beep->playing) { snd_hda_power_up(codec); + if (beep->power_hook) + beep->power_hook(beep, true); beep->playing = 1; } - /* generate tone */ snd_hda_codec_write(codec, beep->nid, 0, AC_VERB_SET_BEEP_CONTROL, tone); if (!tone && beep->playing) { beep->playing = 0; + if (beep->power_hook) + beep->power_hook(beep, false); snd_hda_power_down(codec); } } +static void snd_hda_generate_beep(struct work_struct *work) +{ + struct hda_beep *beep = + container_of(work, struct hda_beep, beep_work); + + if (beep->enabled) + generate_tone(beep, beep->tone); +} + /* (non-standard) Linear beep tone calculation for IDT/STAC codecs * * The tone frequency of beep generator on IDT/STAC codecs is @@ -130,10 +136,7 @@ static void turn_off_beep(struct hda_beep *beep) cancel_work_sync(&beep->beep_work); if (beep->playing) { /* turn off beep */ - snd_hda_codec_write(beep->codec, beep->nid, 0, - AC_VERB_SET_BEEP_CONTROL, 0); - beep->playing = 0; - snd_hda_power_down(beep->codec); + generate_tone(beep, 0); } } @@ -160,15 +163,15 @@ static int snd_hda_do_attach(struct hda_beep *beep) input_dev->name = "HDA Digital PCBeep"; input_dev->phys = beep->phys; input_dev->id.bustype = BUS_PCI; + input_dev->dev.parent = &codec->card->card_dev; - input_dev->id.vendor = codec->vendor_id >> 16; - input_dev->id.product = codec->vendor_id & 0xffff; + input_dev->id.vendor = codec->core.vendor_id >> 16; + input_dev->id.product = codec->core.vendor_id & 0xffff; input_dev->id.version = 0x01; input_dev->evbit[0] = BIT_MASK(EV_SND); input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); input_dev->event = snd_hda_beep_event; - input_dev->dev.parent = &codec->dev; input_set_drvdata(input_dev, beep); beep->dev = input_dev; @@ -224,7 +227,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) if (beep == NULL) return -ENOMEM; snprintf(beep->phys, sizeof(beep->phys), - "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); + "card%d/codec#%d/beep0", codec->card->number, codec->addr); /* enable linear scale */ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 0x01); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index a63b5e077332..46524ff7e79e 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -40,6 +40,7 @@ struct hda_beep { unsigned int playing:1; struct work_struct beep_work; /* scheduled task for beep event */ struct mutex mutex; + void (*power_hook)(struct hda_beep *beep, bool on); }; #ifdef CONFIG_SND_HDA_INPUT_BEEP diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c new file mode 100644 index 000000000000..00aa31c5f08e --- /dev/null +++ b/sound/pci/hda/hda_bind.c @@ -0,0 +1,273 @@ +/* + * HD-audio codec driver binding + * Copyright (c) Takashi Iwai <tiwai@suse.de> + */ + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" + +/* + * find a matching codec preset + */ +static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct hda_codec_driver *driver = + container_of(drv, struct hda_codec_driver, core); + const struct hda_codec_preset *preset; + /* check probe_id instead of vendor_id if set */ + 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) && + (!preset->rev || preset->rev == codec->core.revision_id)) { + codec->preset = preset; + return 1; + } + } + return 0; +} + +/* process an unsolicited event */ +static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + + if (codec->patch_ops.unsol_event) + codec->patch_ops.unsol_event(codec, ev); +} + +/* reset the codec name from the preset */ +static int codec_refresh_name(struct hda_codec *codec, const char *name) +{ + if (name) { + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup(name, GFP_KERNEL); + } + return codec->core.chip_name ? 0 : -ENOMEM; +} + +static int hda_codec_driver_probe(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + struct module *owner = dev->driver->owner; + int err; + + if (WARN_ON(!codec->preset)) + return -EINVAL; + + err = codec_refresh_name(codec, codec->preset->name); + if (err < 0) + goto error; + err = snd_hdac_regmap_init(&codec->core); + if (err < 0) + goto error; + + if (!try_module_get(owner)) { + err = -EINVAL; + goto error; + } + + err = codec->preset->patch(codec); + if (err < 0) + goto error_module; + + err = snd_hda_codec_build_pcms(codec); + if (err < 0) + goto error_module; + err = snd_hda_codec_build_controls(codec); + if (err < 0) + goto error_module; + if (codec->card->registered) { + err = snd_card_register(codec->card); + if (err < 0) + goto error_module; + snd_hda_codec_register(codec); + } + + codec->core.lazy_cache = true; + return 0; + + error_module: + module_put(owner); + + error: + snd_hda_codec_cleanup_for_unbind(codec); + return err; +} + +static int hda_codec_driver_remove(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + + if (codec->patch_ops.free) + codec->patch_ops.free(codec); + snd_hda_codec_cleanup_for_unbind(codec); + module_put(dev->driver->owner); + return 0; +} + +static void hda_codec_driver_shutdown(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + + if (!pm_runtime_suspended(dev) && codec->patch_ops.reboot_notify) + codec->patch_ops.reboot_notify(codec); +} + +int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, + struct module *owner) +{ + drv->core.driver.name = name; + drv->core.driver.owner = owner; + drv->core.driver.bus = &snd_hda_bus_type; + drv->core.driver.probe = hda_codec_driver_probe; + drv->core.driver.remove = hda_codec_driver_remove; + drv->core.driver.shutdown = hda_codec_driver_shutdown; + drv->core.driver.pm = &hda_codec_driver_pm; + drv->core.type = HDA_DEV_LEGACY; + drv->core.match = hda_codec_match; + drv->core.unsol_event = hda_codec_unsol_event; + return driver_register(&drv->core.driver); +} +EXPORT_SYMBOL_GPL(__hda_codec_driver_register); + +void hda_codec_driver_unregister(struct hda_codec_driver *drv) +{ + driver_unregister(&drv->core.driver); +} +EXPORT_SYMBOL_GPL(hda_codec_driver_unregister); + +static inline bool codec_probed(struct hda_codec *codec) +{ + return device_attach(hda_codec_dev(codec)) > 0 && codec->preset; +} + +/* try to auto-load and bind the codec module */ +static void codec_bind_module(struct hda_codec *codec) +{ +#ifdef MODULE + request_module("snd-hda-codec-id:%08x", codec->core.vendor_id); + if (codec_probed(codec)) + return; + request_module("snd-hda-codec-id:%04x*", + (codec->core.vendor_id >> 16) & 0xffff); + if (codec_probed(codec)) + return; +#endif +} + +#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) +/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */ +static bool is_likely_hdmi_codec(struct hda_codec *codec) +{ + hda_nid_t nid; + + for_each_hda_codec_node(nid, codec) { + unsigned int wcaps = get_wcaps(codec, nid); + switch (get_wcaps_type(wcaps)) { + case AC_WID_AUD_IN: + return false; /* HDMI parser supports only HDMI out */ + case AC_WID_AUD_OUT: + if (!(wcaps & AC_WCAP_DIGITAL)) + return false; + break; + } + } + return true; +} +#else +/* no HDMI codec parser support */ +#define is_likely_hdmi_codec(codec) false +#endif /* CONFIG_SND_HDA_CODEC_HDMI */ + +static int codec_bind_generic(struct hda_codec *codec) +{ + if (codec->probe_id) + return -ENODEV; + + if (is_likely_hdmi_codec(codec)) { + codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI; +#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI) + request_module("snd-hda-codec-hdmi"); +#endif + if (codec_probed(codec)) + return 0; + } + + codec->probe_id = HDA_CODEC_ID_GENERIC; +#if IS_MODULE(CONFIG_SND_HDA_GENERIC) + request_module("snd-hda-codec-generic"); +#endif + if (codec_probed(codec)) + return 0; + return -ENODEV; +} + +#if IS_ENABLED(CONFIG_SND_HDA_GENERIC) +#define is_generic_config(codec) \ + (codec->modelname && !strcmp(codec->modelname, "generic")) +#else +#define is_generic_config(codec) 0 +#endif + +/** + * snd_hda_codec_configure - (Re-)configure the HD-audio codec + * @codec: the HDA codec + * + * Start parsing of the given codec tree and (re-)initialize the whole + * patch instance. + * + * Returns 0 if successful or a negative error code. + */ +int snd_hda_codec_configure(struct hda_codec *codec) +{ + int err; + + if (is_generic_config(codec)) + codec->probe_id = HDA_CODEC_ID_GENERIC; + else + codec->probe_id = 0; + + err = snd_hdac_device_register(&codec->core); + if (err < 0) + return err; + + if (!codec->preset) + codec_bind_module(codec); + if (!codec->preset) { + err = codec_bind_generic(codec); + if (err < 0) { + codec_err(codec, "Unable to bind the codec\n"); + goto error; + } + } + + /* audio codec should override the mixer name */ + if (codec->core.afg || !*codec->card->mixername) + snprintf(codec->card->mixername, + sizeof(codec->card->mixername), "%s %s", + codec->core.vendor_name, codec->core.chip_name); + return 0; + + error: + snd_hdac_device_unregister(&codec->core); + return err; +} +EXPORT_SYMBOL_GPL(snd_hda_codec_configure); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2fe86d2e1b09..e70a7fb393dd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -26,6 +26,8 @@ #include <linux/mutex.h> #include <linux/module.h> #include <linux/async.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include <sound/core.h> #include "hda_codec.h" #include <sound/asoundef.h> @@ -37,97 +39,20 @@ #include "hda_jack.h" #include <sound/hda_hwdep.h> -#define CREATE_TRACE_POINTS -#include "hda_trace.h" - -/* - * vendor / preset table - */ - -struct hda_vendor_id { - unsigned int id; - const char *name; -}; - -/* codec vendor labels */ -static struct hda_vendor_id hda_vendor_ids[] = { - { 0x1002, "ATI" }, - { 0x1013, "Cirrus Logic" }, - { 0x1057, "Motorola" }, - { 0x1095, "Silicon Image" }, - { 0x10de, "Nvidia" }, - { 0x10ec, "Realtek" }, - { 0x1102, "Creative" }, - { 0x1106, "VIA" }, - { 0x111d, "IDT" }, - { 0x11c1, "LSI" }, - { 0x11d4, "Analog Devices" }, - { 0x13f6, "C-Media" }, - { 0x14f1, "Conexant" }, - { 0x17e8, "Chrontel" }, - { 0x1854, "LG" }, - { 0x1aec, "Wolfson Microelectronics" }, - { 0x1af4, "QEMU" }, - { 0x434d, "C-Media" }, - { 0x8086, "Intel" }, - { 0x8384, "SigmaTel" }, - {} /* terminator */ -}; - -static DEFINE_MUTEX(preset_mutex); -static LIST_HEAD(hda_preset_tables); - -/** - * snd_hda_add_codec_preset - Add a codec preset to the chain - * @preset: codec preset table to add - */ -int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset) -{ - mutex_lock(&preset_mutex); - list_add_tail(&preset->list, &hda_preset_tables); - mutex_unlock(&preset_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_add_codec_preset); - -/** - * snd_hda_delete_codec_preset - Delete a codec preset from the chain - * @preset: codec preset table to delete - */ -int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset) -{ - mutex_lock(&preset_mutex); - list_del(&preset->list); - mutex_unlock(&preset_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_delete_codec_preset); - #ifdef CONFIG_PM -#define codec_in_pm(codec) ((codec)->in_pm) -static void hda_power_work(struct work_struct *work); -static void hda_keep_power_on(struct hda_codec *codec); -#define hda_codec_is_power_on(codec) ((codec)->power_on) - -static void hda_call_pm_notify(struct hda_codec *codec, bool power_up) -{ - struct hda_bus *bus = codec->bus; - - if ((power_up && codec->pm_up_notified) || - (!power_up && !codec->pm_up_notified)) - return; - if (bus->ops.pm_notify) - bus->ops.pm_notify(bus, power_up); - codec->pm_up_notified = power_up; -} - +#define codec_in_pm(codec) atomic_read(&(codec)->core.in_pm) +#define hda_codec_is_power_on(codec) \ + (!pm_runtime_suspended(hda_codec_dev(codec))) #else #define codec_in_pm(codec) 0 -static inline void hda_keep_power_on(struct hda_codec *codec) {} #define hda_codec_is_power_on(codec) 1 -#define hda_call_pm_notify(codec, state) {} #endif +#define codec_has_epss(codec) \ + ((codec)->core.power_caps & AC_PWRST_EPSS) +#define codec_has_clkstop(codec) \ + ((codec)->core.power_caps & AC_PWRST_CLKSTOP) + /** * snd_hda_get_jack_location - Give a location string of the jack * @cfg: pin default config value @@ -199,67 +124,32 @@ const char *snd_hda_get_jack_type(u32 cfg) EXPORT_SYMBOL_GPL(snd_hda_get_jack_type); /* - * Compose a 32bit command word to be sent to the HD-audio controller - */ -static inline unsigned int -make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int flags, - unsigned int verb, unsigned int parm) -{ - u32 val; - - if ((codec->addr & ~0xf) || (nid & ~0x7f) || - (verb & ~0xfff) || (parm & ~0xffff)) { - codec_err(codec, "hda-codec: out of range cmd %x:%x:%x:%x\n", - codec->addr, nid, verb, parm); - return ~0; - } - - val = (u32)codec->addr << 28; - val |= (u32)nid << 20; - val |= verb << 8; - val |= parm; - return val; -} - -/* - * Send and receive a verb + * Send and receive a verb - passed to exec_verb override for hdac_device */ -static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, - int flags, unsigned int *res) +static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, + unsigned int flags, unsigned int *res) { + struct hda_codec *codec = container_of(dev, struct hda_codec, core); struct hda_bus *bus = codec->bus; int err; if (cmd == ~0) return -1; - if (res) - *res = -1; again: - snd_hda_power_up(codec); - mutex_lock(&bus->cmd_mutex); + snd_hda_power_up_pm(codec); + mutex_lock(&bus->core.cmd_mutex); if (flags & HDA_RW_NO_RESPONSE_FALLBACK) bus->no_response_fallback = 1; - for (;;) { - trace_hda_send_cmd(codec, cmd); - err = bus->ops.command(bus, cmd); - if (err != -EAGAIN) - break; - /* process pending verbs */ - bus->ops.get_response(bus, codec->addr); - } - if (!err && res) { - *res = bus->ops.get_response(bus, codec->addr); - trace_hda_get_response(codec, *res); - } + err = snd_hdac_bus_exec_verb_unlocked(&bus->core, codec->core.addr, + cmd, res); bus->no_response_fallback = 0; - mutex_unlock(&bus->cmd_mutex); - snd_hda_power_down(codec); - if (!codec_in_pm(codec) && res && *res == -1 && bus->rirb_error) { + mutex_unlock(&bus->core.cmd_mutex); + snd_hda_power_down_pm(codec); + if (!codec_in_pm(codec) && res && err < 0 && bus->rirb_error) { if (bus->response_reset) { codec_dbg(codec, "resetting BUS due to fatal communication error\n"); - trace_hda_bus_reset(bus); bus->ops.bus_reset(bus); } goto again; @@ -286,9 +176,9 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm) { - unsigned cmd = make_codec_cmd(codec, nid, flags, verb, parm); + unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm); unsigned int res; - if (codec_exec_verb(codec, cmd, flags, &res)) + if (snd_hdac_exec_verb(&codec->core, cmd, flags, &res)) return -1; return res; } @@ -309,10 +199,8 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_read); int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm) { - unsigned int cmd = make_codec_cmd(codec, nid, flags, verb, parm); - unsigned int res; - return codec_exec_verb(codec, cmd, flags, - codec->bus->sync_write ? &res : NULL); + unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm); + return snd_hdac_exec_verb(&codec->core, cmd, flags, NULL); } EXPORT_SYMBOL_GPL(snd_hda_codec_write); @@ -331,30 +219,6 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) } EXPORT_SYMBOL_GPL(snd_hda_sequence_write); -/** - * snd_hda_get_sub_nodes - get the range of sub nodes - * @codec: the HDA codec - * @nid: NID to parse - * @start_id: the pointer to store the start NID - * - * Parse the NID and store the start NID of its sub-nodes. - * Returns the number of sub-nodes. - */ -int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *start_id) -{ - unsigned int parm; - - parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT); - if (parm == -1) { - *start_id = 0; - return 0; - } - *start_id = (parm >> 16) & 0x7fff; - return (int)(parm & 0x7fff); -} -EXPORT_SYMBOL_GPL(snd_hda_get_sub_nodes); - /* connection list element */ struct hda_conn_list { struct list_head list; @@ -495,128 +359,6 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_get_connections); -/* return CONNLIST_LEN parameter of the given widget */ -static unsigned int get_num_conns(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int parm; - - if (!(wcaps & AC_WCAP_CONN_LIST) && - get_wcaps_type(wcaps) != AC_WID_VOL_KNB) - return 0; - - parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); - if (parm == -1) - parm = 0; - return parm; -} - -int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_get_raw_connections(codec, nid, NULL, 0); -} - -/** - * snd_hda_get_raw_connections - copy connection list without cache - * @codec: the HDA codec - * @nid: NID to parse - * @conn_list: connection list array - * @max_conns: max. number of connections to store - * - * Like snd_hda_get_connections(), copy the connection list but without - * checking through the connection-list cache. - * Currently called only from hda_proc.c, so not exported. - */ -int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns) -{ - unsigned int parm; - int i, conn_len, conns; - unsigned int shift, num_elems, mask; - hda_nid_t prev_nid; - int null_count = 0; - - parm = get_num_conns(codec, nid); - if (!parm) - return 0; - - if (parm & AC_CLIST_LONG) { - /* long form */ - shift = 16; - num_elems = 2; - } else { - /* short form */ - shift = 8; - num_elems = 4; - } - conn_len = parm & AC_CLIST_LENGTH; - mask = (1 << (shift-1)) - 1; - - if (!conn_len) - return 0; /* no connection */ - - if (conn_len == 1) { - /* single connection */ - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, 0); - if (parm == -1 && codec->bus->rirb_error) - return -EIO; - if (conn_list) - conn_list[0] = parm & mask; - return 1; - } - - /* multi connection */ - conns = 0; - prev_nid = 0; - for (i = 0; i < conn_len; i++) { - int range_val; - hda_nid_t val, n; - - if (i % num_elems == 0) { - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, i); - if (parm == -1 && codec->bus->rirb_error) - return -EIO; - } - range_val = !!(parm & (1 << (shift-1))); /* ranges */ - val = parm & mask; - if (val == 0 && null_count++) { /* no second chance */ - codec_dbg(codec, - "invalid CONNECT_LIST verb %x[%i]:%x\n", - nid, i, parm); - return 0; - } - parm >>= shift; - if (range_val) { - /* ranges between the previous and this one */ - if (!prev_nid || prev_nid >= val) { - codec_warn(codec, - "invalid dep_range_val %x:%x\n", - prev_nid, val); - continue; - } - for (n = prev_nid + 1; n <= val; n++) { - if (conn_list) { - if (conns >= max_conns) - return -ENOSPC; - conn_list[conns] = n; - } - conns++; - } - } else { - if (conn_list) { - if (conns >= max_conns) - return -ENOSPC; - conn_list[conns] = val; - } - conns++; - } - prev_nid = val; - } - return conns; -} - /** * snd_hda_override_conn_list - add/modify the connection-list to cache * @codec: the HDA codec @@ -741,90 +483,6 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, return devices; } -/** - * snd_hda_queue_unsol_event - add an unsolicited event to queue - * @bus: the BUS - * @res: unsolicited event (lower 32bit of RIRB entry) - * @res_ex: codec addr and flags (upper 32bit or RIRB entry) - * - * Adds the given event to the queue. The events are processed in - * the workqueue asynchronously. Call this function in the interrupt - * hanlder when RIRB receives an unsolicited event. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) -{ - struct hda_bus_unsolicited *unsol; - unsigned int wp; - - if (!bus || !bus->workq) - return 0; - - trace_hda_unsol_event(bus, res, res_ex); - unsol = bus->unsol; - if (!unsol) - return 0; - - wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE; - unsol->wp = wp; - - wp <<= 1; - unsol->queue[wp] = res; - unsol->queue[wp + 1] = res_ex; - - queue_work(bus->workq, &unsol->work); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_queue_unsol_event); - -/* - * process queued unsolicited events - */ -static void process_unsol_events(struct work_struct *work) -{ - struct hda_bus_unsolicited *unsol = - container_of(work, struct hda_bus_unsolicited, work); - struct hda_bus *bus = unsol->bus; - struct hda_codec *codec; - unsigned int rp, caddr, res; - - while (unsol->rp != unsol->wp) { - rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE; - unsol->rp = rp; - rp <<= 1; - res = unsol->queue[rp]; - caddr = unsol->queue[rp + 1]; - if (!(caddr & (1 << 4))) /* no unsolicited event? */ - continue; - codec = bus->caddr_tbl[caddr & 0x0f]; - if (codec && codec->patch_ops.unsol_event) - codec->patch_ops.unsol_event(codec, res); - } -} - -/* - * initialize unsolicited queue - */ -static int init_unsol_queue(struct hda_bus *bus) -{ - struct hda_bus_unsolicited *unsol; - - if (bus->unsol) /* already initialized */ - return 0; - - unsol = kzalloc(sizeof(*unsol), GFP_KERNEL); - if (!unsol) { - dev_err(bus->card->dev, "can't allocate unsolicited queue\n"); - return -ENOMEM; - } - INIT_WORK(&unsol->work, process_unsol_events); - unsol->bus = bus; - bus->unsol = unsol; - return 0; -} - /* * destructor */ @@ -832,16 +490,9 @@ static void snd_hda_bus_free(struct hda_bus *bus) { if (!bus) return; - - WARN_ON(!list_empty(&bus->codec_list)); - if (bus->workq) - flush_workqueue(bus->workq); - kfree(bus->unsol); if (bus->ops.private_free) bus->ops.private_free(bus); - if (bus->workq) - destroy_workqueue(bus->workq); - + snd_hdac_bus_exit(&bus->core); kfree(bus); } @@ -858,17 +509,35 @@ static int snd_hda_bus_dev_disconnect(struct snd_device *device) 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 - * @temp: the template for hda_bus information * @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, - const struct hda_bus_template *temp, - struct hda_bus **busp) + struct hda_bus **busp) { struct hda_bus *bus; int err; @@ -877,40 +546,21 @@ int snd_hda_bus_new(struct snd_card *card, .dev_free = snd_hda_bus_dev_free, }; - if (snd_BUG_ON(!temp)) - return -EINVAL; - if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response)) - return -EINVAL; - if (busp) *busp = NULL; bus = kzalloc(sizeof(*bus), GFP_KERNEL); - if (bus == NULL) { - dev_err(card->dev, "can't allocate struct hda_bus\n"); + 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; - bus->private_data = temp->private_data; - bus->pci = temp->pci; - bus->modelname = temp->modelname; - bus->power_save = temp->power_save; - bus->ops = temp->ops; - - mutex_init(&bus->cmd_mutex); mutex_init(&bus->prepare_mutex); - INIT_LIST_HEAD(&bus->codec_list); - - snprintf(bus->workq_name, sizeof(bus->workq_name), - "hd-audio%d", card->number); - bus->workq = create_singlethread_workqueue(bus->workq_name); - if (!bus->workq) { - dev_err(card->dev, "cannot create workqueue %s\n", - bus->workq_name); - kfree(bus); - return -ENOMEM; - } err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); if (err < 0) { @@ -923,140 +573,6 @@ int snd_hda_bus_new(struct snd_card *card, } EXPORT_SYMBOL_GPL(snd_hda_bus_new); -#if IS_ENABLED(CONFIG_SND_HDA_GENERIC) -#define is_generic_config(codec) \ - (codec->modelname && !strcmp(codec->modelname, "generic")) -#else -#define is_generic_config(codec) 0 -#endif - -#ifdef MODULE -#define HDA_MODREQ_MAX_COUNT 2 /* two request_modules()'s */ -#else -#define HDA_MODREQ_MAX_COUNT 0 /* all presets are statically linked */ -#endif - -/* - * find a matching codec preset - */ -static const struct hda_codec_preset * -find_codec_preset(struct hda_codec *codec) -{ - struct hda_codec_preset_list *tbl; - const struct hda_codec_preset *preset; - unsigned int mod_requested = 0; - - again: - mutex_lock(&preset_mutex); - list_for_each_entry(tbl, &hda_preset_tables, list) { - if (!try_module_get(tbl->owner)) { - codec_err(codec, "cannot module_get\n"); - continue; - } - for (preset = tbl->preset; preset->id; preset++) { - u32 mask = preset->mask; - if (preset->afg && preset->afg != codec->afg) - continue; - if (preset->mfg && preset->mfg != codec->mfg) - continue; - if (!mask) - mask = ~0; - if (preset->id == (codec->vendor_id & mask) && - (!preset->rev || - preset->rev == codec->revision_id)) { - mutex_unlock(&preset_mutex); - codec->owner = tbl->owner; - return preset; - } - } - module_put(tbl->owner); - } - mutex_unlock(&preset_mutex); - - if (mod_requested < HDA_MODREQ_MAX_COUNT) { - if (!mod_requested) - request_module("snd-hda-codec-id:%08x", - codec->vendor_id); - else - request_module("snd-hda-codec-id:%04x*", - (codec->vendor_id >> 16) & 0xffff); - mod_requested++; - goto again; - } - return NULL; -} - -/* - * get_codec_name - store the codec name - */ -static int get_codec_name(struct hda_codec *codec) -{ - const struct hda_vendor_id *c; - const char *vendor = NULL; - u16 vendor_id = codec->vendor_id >> 16; - char tmp[16]; - - if (codec->vendor_name) - goto get_chip_name; - - for (c = hda_vendor_ids; c->id; c++) { - if (c->id == vendor_id) { - vendor = c->name; - break; - } - } - if (!vendor) { - sprintf(tmp, "Generic %04x", vendor_id); - vendor = tmp; - } - codec->vendor_name = kstrdup(vendor, GFP_KERNEL); - if (!codec->vendor_name) - return -ENOMEM; - - get_chip_name: - if (codec->chip_name) - return 0; - - if (codec->preset && codec->preset->name) - codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL); - else { - sprintf(tmp, "ID %x", codec->vendor_id & 0xffff); - codec->chip_name = kstrdup(tmp, GFP_KERNEL); - } - if (!codec->chip_name) - return -ENOMEM; - return 0; -} - -/* - * look for an AFG and MFG nodes - */ -static void setup_fg_nodes(struct hda_codec *codec) -{ - int i, total_nodes, function_id; - hda_nid_t nid; - - total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); - for (i = 0; i < total_nodes; i++, nid++) { - function_id = snd_hda_param_read(codec, nid, - AC_PAR_FUNCTION_TYPE); - switch (function_id & 0xff) { - case AC_GRP_AUDIO_FUNCTION: - codec->afg = nid; - codec->afg_function_id = function_id & 0xff; - codec->afg_unsol = (function_id >> 8) & 1; - break; - case AC_GRP_MODEM_FUNCTION: - codec->mfg = nid; - codec->mfg_function_id = function_id & 0xff; - codec->mfg_unsol = (function_id >> 8) & 1; - break; - default: - break; - } - } -} - /* * read widget caps for each widget and store in cache */ @@ -1065,25 +581,22 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) int i; hda_nid_t nid; - codec->num_nodes = snd_hda_get_sub_nodes(codec, fg_node, - &codec->start_nid); - codec->wcaps = kmalloc(codec->num_nodes * 4, GFP_KERNEL); + codec->wcaps = kmalloc(codec->core.num_nodes * 4, GFP_KERNEL); if (!codec->wcaps) return -ENOMEM; - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) - codec->wcaps[i] = snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); + nid = codec->core.start_nid; + for (i = 0; i < codec->core.num_nodes; i++, nid++) + codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core, + nid, AC_PAR_AUDIO_WIDGET_CAP); return 0; } /* read all pin default configurations and save codec->init_pins */ static int read_pin_defaults(struct hda_codec *codec) { - int i; - hda_nid_t nid = codec->start_nid; + hda_nid_t nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { struct hda_pincfg *pin; unsigned int wcaps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wcaps); @@ -1290,14 +803,10 @@ static void hda_jackpoll_work(struct work_struct *work) if (!codec->jackpoll_interval) return; - queue_delayed_work(codec->bus->workq, &codec->jackpoll_work, - codec->jackpoll_interval); + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); } -static void init_hda_cache(struct hda_cache_rec *cache, - unsigned int record_size); -static void free_hda_cache(struct hda_cache_rec *cache); - /* release all pincfg lists */ static void free_init_pincfgs(struct hda_codec *codec) { @@ -1339,70 +848,117 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid) } /* - * Dynamic symbol binding for the codec parsers + * PCM device */ +static void release_pcm(struct kref *kref) +{ + struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref); + + if (pcm->pcm) + snd_device_free(pcm->codec->card, pcm->pcm); + clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits); + kfree(pcm->name); + kfree(pcm); +} -#define load_parser(codec, sym) \ - ((codec)->parser = (int (*)(struct hda_codec *))symbol_request(sym)) +void snd_hda_codec_pcm_put(struct hda_pcm *pcm) +{ + kref_put(&pcm->kref, release_pcm); +} +EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put); -static void unload_parser(struct hda_codec *codec) +struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, + const char *fmt, ...) { - if (codec->parser) - symbol_put_addr(codec->parser); - codec->parser = NULL; + struct hda_pcm *pcm; + va_list args; + + va_start(args, fmt); + pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return NULL; + + pcm->codec = codec; + kref_init(&pcm->kref); + pcm->name = kvasprintf(GFP_KERNEL, fmt, args); + if (!pcm->name) { + kfree(pcm); + return NULL; + } + + list_add_tail(&pcm->list, &codec->pcm_list_head); + return pcm; } +EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new); /* * codec destructor */ -static void snd_hda_codec_free(struct hda_codec *codec) +static void codec_release_pcms(struct hda_codec *codec) { - if (!codec) - return; + struct hda_pcm *pcm, *n; + + list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) { + list_del_init(&pcm->list); + if (pcm->pcm) + snd_device_disconnect(codec->card, pcm->pcm); + snd_hda_codec_pcm_put(pcm); + } +} + +void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) +{ + if (codec->registered) { + /* pm_runtime_put() is called in snd_hdac_device_exit() */ + pm_runtime_get_noresume(hda_codec_dev(codec)); + pm_runtime_disable(hda_codec_dev(codec)); + codec->registered = 0; + } + cancel_delayed_work_sync(&codec->jackpoll_work); + if (!codec->in_freeing) + snd_hda_ctls_clear(codec); + codec_release_pcms(codec); + snd_hda_detach_beep_device(codec); + memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); snd_hda_jack_tbl_clear(codec); - free_init_pincfgs(codec); -#ifdef CONFIG_PM - cancel_delayed_work(&codec->power_work); - flush_workqueue(codec->bus->workq); -#endif - list_del(&codec->list); - snd_array_free(&codec->mixers); - snd_array_free(&codec->nids); + codec->proc_widget_hook = NULL; + codec->spec = NULL; + + /* free only driver_pins so that init_pins + user_pins are restored */ + snd_array_free(&codec->driver_pins); snd_array_free(&codec->cvt_setups); snd_array_free(&codec->spdif_out); + snd_array_free(&codec->verbs); + codec->preset = NULL; + codec->slave_dig_outs = NULL; + codec->spdif_status_reset = 0; + snd_array_free(&codec->mixers); + snd_array_free(&codec->nids); remove_conn_list(codec); - codec->bus->caddr_tbl[codec->addr] = NULL; - if (codec->patch_ops.free) - codec->patch_ops.free(codec); - hda_call_pm_notify(codec, false); /* cancel leftover refcounts */ - snd_hda_sysfs_clear(codec); - unload_parser(codec); - module_put(codec->owner); - free_hda_cache(&codec->amp_cache); - free_hda_cache(&codec->cmd_cache); - kfree(codec->vendor_name); - kfree(codec->chip_name); - kfree(codec->modelname); - kfree(codec->wcaps); - codec->bus->num_codecs--; - put_device(&codec->dev); + snd_hdac_regmap_exit(&codec->core); } -static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, - hda_nid_t fg, unsigned int power_state); - static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state); -static int snd_hda_codec_dev_register(struct snd_device *device) +/* also called from hda_bind.c */ +void snd_hda_codec_register(struct hda_codec *codec) { - struct hda_codec *codec = device->device_data; - int err = device_add(&codec->dev); + if (codec->registered) + return; + if (device_is_registered(hda_codec_dev(codec))) { + snd_hda_register_beep_device(codec); + pm_runtime_enable(hda_codec_dev(codec)); + /* it was powered up in snd_hda_codec_new(), now all done */ + snd_hda_power_down(codec); + codec->registered = 1; + } +} - if (err < 0) - return err; - snd_hda_register_beep_device(codec); +static int snd_hda_codec_dev_register(struct snd_device *device) +{ + snd_hda_codec_register(device->device_data); return 0; } @@ -1411,20 +967,29 @@ static int snd_hda_codec_dev_disconnect(struct snd_device *device) struct hda_codec *codec = device->device_data; snd_hda_detach_beep_device(codec); - device_del(&codec->dev); return 0; } static int snd_hda_codec_dev_free(struct snd_device *device) { - snd_hda_codec_free(device->device_data); + struct hda_codec *codec = device->device_data; + + codec->in_freeing = 1; + snd_hdac_device_unregister(&codec->core); + put_device(hda_codec_dev(codec)); return 0; } -/* just free the container */ static void snd_hda_codec_dev_release(struct device *dev) { - kfree(container_of(dev, struct hda_codec, dev)); + struct hda_codec *codec = dev_to_hda_codec(dev); + + free_init_pincfgs(codec); + snd_hdac_device_exit(&codec->core); + snd_hda_sysfs_clear(codec); + kfree(codec->modelname); + kfree(codec->wcaps); + kfree(codec); } /** @@ -1435,9 +1000,8 @@ static void snd_hda_codec_dev_release(struct device *dev) * * Returns 0 if successful, or a negative error code. */ -int snd_hda_codec_new(struct hda_bus *bus, - unsigned int codec_addr, - struct hda_codec **codecp) +int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec **codecp) { struct hda_codec *codec; char component[31]; @@ -1454,35 +1018,27 @@ int snd_hda_codec_new(struct hda_bus *bus, if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) return -EINVAL; - if (bus->caddr_tbl[codec_addr]) { - dev_err(bus->card->dev, - "address 0x%x is already occupied\n", - codec_addr); - return -EBUSY; - } - codec = kzalloc(sizeof(*codec), GFP_KERNEL); - if (codec == NULL) { - dev_err(bus->card->dev, "can't allocate struct hda_codec\n"); + if (!codec) return -ENOMEM; + + sprintf(component, "hdaudioC%dD%d", card->number, codec_addr); + err = snd_hdac_device_init(&codec->core, &bus->core, component, + codec_addr); + if (err < 0) { + kfree(codec); + return err; } - device_initialize(&codec->dev); - codec->dev.parent = &bus->card->card_dev; - codec->dev.class = sound_class; - codec->dev.release = snd_hda_codec_dev_release; - codec->dev.groups = snd_hda_dev_attr_groups; - dev_set_name(&codec->dev, "hdaudioC%dD%d", bus->card->number, - codec_addr); - dev_set_drvdata(&codec->dev, codec); /* for sysfs */ + codec->core.dev.release = snd_hda_codec_dev_release; + codec->core.type = HDA_DEV_LEGACY; + codec->core.exec_verb = codec_exec_verb; codec->bus = bus; + codec->card = card; codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); mutex_init(&codec->control_mutex); - mutex_init(&codec->hash_mutex); - init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); - init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32); snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); @@ -1492,19 +1048,14 @@ int snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); INIT_LIST_HEAD(&codec->conn_list); + INIT_LIST_HEAD(&codec->pcm_list_head); INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); codec->depop_delay = -1; codec->fixup_id = HDA_FIXUP_ID_NOT_SET; #ifdef CONFIG_PM - spin_lock_init(&codec->power_lock); - INIT_DELAYED_WORK(&codec->power_work, hda_power_work); - /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. - * the caller has to power down appropriatley after initialization - * phase. - */ - hda_keep_power_on(codec); + codec->power_jiffies = jiffies; #endif snd_hda_sysfs_init(codec); @@ -1517,59 +1068,14 @@ int snd_hda_codec_new(struct hda_bus *bus, } } - list_add_tail(&codec->list, &bus->codec_list); - bus->num_codecs++; - - bus->caddr_tbl[codec_addr] = codec; - - codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_VENDOR_ID); - if (codec->vendor_id == -1) - /* read again, hopefully the access method was corrected - * in the last read... - */ - codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_VENDOR_ID); - codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_SUBSYSTEM_ID); - codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_REV_ID); - - setup_fg_nodes(codec); - if (!codec->afg && !codec->mfg) { - dev_err(bus->card->dev, "no AFG or MFG node found\n"); - err = -ENODEV; - goto error; - } - - fg = codec->afg ? codec->afg : codec->mfg; + fg = codec->core.afg ? codec->core.afg : codec->core.mfg; err = read_widget_caps(codec, fg); - if (err < 0) { - dev_err(bus->card->dev, "cannot malloc\n"); + if (err < 0) goto error; - } err = read_pin_defaults(codec); if (err < 0) goto error; - if (!codec->subsystem_id) { - codec->subsystem_id = - snd_hda_codec_read(codec, fg, 0, - AC_VERB_GET_SUBSYSTEM_ID, 0); - } - -#ifdef CONFIG_PM - codec->d3_stop_clk = snd_hda_codec_get_supported_ps(codec, fg, - AC_PWRST_CLKSTOP); -#endif - codec->epss = snd_hda_codec_get_supported_ps(codec, fg, - AC_PWRST_EPSS); -#ifdef CONFIG_PM - if (!codec->d3_stop_clk || !codec->epss) - bus->power_keep_link_on = 1; -#endif - - /* power-up all before initialization */ hda_set_power_state(codec, AC_PWRST_D0); @@ -1577,11 +1083,11 @@ int snd_hda_codec_new(struct hda_bus *bus, snd_hda_create_hwdep(codec); - sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, - codec->subsystem_id, codec->revision_id); - snd_component_add(codec->bus->card, component); + sprintf(component, "HDA:%08x,%08x,%08x", codec->core.vendor_id, + codec->core.subsystem_id, codec->core.revision_id); + snd_component_add(card, component); - err = snd_device_new(bus->card, SNDRV_DEV_CODEC, codec, &dev_ops); + err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops); if (err < 0) goto error; @@ -1590,7 +1096,7 @@ int snd_hda_codec_new(struct hda_bus *bus, return 0; error: - snd_hda_codec_free(codec); + put_device(hda_codec_dev(codec)); return err; } EXPORT_SYMBOL_GPL(snd_hda_codec_new); @@ -1607,16 +1113,18 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec) hda_nid_t fg; int err; + err = snd_hdac_refresh_widgets(&codec->core); + if (err < 0) + return err; + /* Assume the function group node does not change, * only the widget nodes may change. */ kfree(codec->wcaps); - fg = codec->afg ? codec->afg : codec->mfg; + fg = codec->core.afg ? codec->core.afg : codec->core.mfg; err = read_widget_caps(codec, fg); - if (err < 0) { - codec_err(codec, "cannot malloc\n"); + if (err < 0) return err; - } snd_array_free(&codec->init_pins); err = read_pin_defaults(codec); @@ -1625,98 +1133,6 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_codec_update_widgets); - -#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) -/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */ -static bool is_likely_hdmi_codec(struct hda_codec *codec) -{ - hda_nid_t nid = codec->start_nid; - int i; - - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - switch (get_wcaps_type(wcaps)) { - case AC_WID_AUD_IN: - return false; /* HDMI parser supports only HDMI out */ - case AC_WID_AUD_OUT: - if (!(wcaps & AC_WCAP_DIGITAL)) - return false; - break; - } - } - return true; -} -#else -/* no HDMI codec parser support */ -#define is_likely_hdmi_codec(codec) false -#endif /* CONFIG_SND_HDA_CODEC_HDMI */ - -/** - * snd_hda_codec_configure - (Re-)configure the HD-audio codec - * @codec: the HDA codec - * - * Start parsing of the given codec tree and (re-)initialize the whole - * patch instance. - * - * Returns 0 if successful or a negative error code. - */ -int snd_hda_codec_configure(struct hda_codec *codec) -{ - int (*patch)(struct hda_codec *) = NULL; - int err; - - codec->preset = find_codec_preset(codec); - if (!codec->vendor_name || !codec->chip_name) { - err = get_codec_name(codec); - if (err < 0) - return err; - } - - if (!is_generic_config(codec) && codec->preset) - patch = codec->preset->patch; - if (!patch) { - unload_parser(codec); /* to be sure */ - if (is_likely_hdmi_codec(codec)) { -#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI) - patch = load_parser(codec, snd_hda_parse_hdmi_codec); -#elif IS_BUILTIN(CONFIG_SND_HDA_CODEC_HDMI) - patch = snd_hda_parse_hdmi_codec; -#endif - } - if (!patch) { -#if IS_MODULE(CONFIG_SND_HDA_GENERIC) - patch = load_parser(codec, snd_hda_parse_generic_codec); -#elif IS_BUILTIN(CONFIG_SND_HDA_GENERIC) - patch = snd_hda_parse_generic_codec; -#endif - } - if (!patch) { - codec_err(codec, "No codec parser is available\n"); - return -ENODEV; - } - } - - err = patch(codec); - if (err < 0) { - unload_parser(codec); - return err; - } - - if (codec->patch_ops.unsol_event) { - err = init_unsol_queue(codec->bus); - if (err < 0) - return err; - } - - /* audio codec should override the mixer name */ - if (codec->afg || !*codec->bus->card->mixername) - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_configure); - /* update the stream-id if changed */ static void update_pcm_stream_id(struct hda_codec *codec, struct hda_cvt_setup *p, hda_nid_t nid, @@ -1782,6 +1198,8 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, if (!p) return; + if (codec->patch_ops.stream_pm) + codec->patch_ops.stream_pm(codec, nid, true); if (codec->pcm_format_first) update_pcm_format(codec, p, nid, format); update_pcm_stream_id(codec, p, nid, stream_tag, channel_id); @@ -1793,7 +1211,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, /* make other inactive cvts with the same stream-tag dirty */ type = get_wcaps_type(get_wcaps(codec, nid)); - list_for_each_entry(c, &codec->bus->codec_list, list) { + list_for_each_codec(c, codec->bus) { for (i = 0; i < c->cvt_setups.used; i++) { p = snd_array_elem(&c->cvt_setups, i); if (!p->active && p->stream_tag == stream_tag && @@ -1850,6 +1268,8 @@ static void really_cleanup_stream(struct hda_codec *codec, ); memset(q, 0, sizeof(*q)); q->nid = nid; + if (codec->patch_ops.stream_pm) + codec->patch_ops.stream_pm(codec, nid, false); } /* clean up the all conflicting obsolete streams */ @@ -1858,7 +1278,7 @@ static void purify_inactive_streams(struct hda_codec *codec) struct hda_codec *c; int i; - list_for_each_entry(c, &codec->bus->codec_list, list) { + list_for_each_codec(c, codec->bus) { for (i = 0; i < c->cvt_setups.used; i++) { struct hda_cvt_setup *p; p = snd_array_elem(&c->cvt_setups, i); @@ -1886,127 +1306,6 @@ static void hda_cleanup_all_streams(struct hda_codec *codec) * amp access functions */ -/* FIXME: more better hash key? */ -#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24)) -#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24)) -#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24)) -#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24)) -#define INFO_AMP_CAPS (1<<0) -#define INFO_AMP_VOL(ch) (1 << (1 + (ch))) - -/* initialize the hash table */ -static void init_hda_cache(struct hda_cache_rec *cache, - unsigned int record_size) -{ - memset(cache, 0, sizeof(*cache)); - memset(cache->hash, 0xff, sizeof(cache->hash)); - snd_array_init(&cache->buf, record_size, 64); -} - -static void free_hda_cache(struct hda_cache_rec *cache) -{ - snd_array_free(&cache->buf); -} - -/* query the hash. allocate an entry if not found. */ -static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key) -{ - u16 idx = key % (u16)ARRAY_SIZE(cache->hash); - u16 cur = cache->hash[idx]; - struct hda_cache_head *info; - - while (cur != 0xffff) { - info = snd_array_elem(&cache->buf, cur); - if (info->key == key) - return info; - cur = info->next; - } - return NULL; -} - -/* query the hash. allocate an entry if not found. */ -static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, - u32 key) -{ - struct hda_cache_head *info = get_hash(cache, key); - if (!info) { - u16 idx, cur; - /* add a new hash entry */ - info = snd_array_new(&cache->buf); - if (!info) - return NULL; - cur = snd_array_index(&cache->buf, info); - info->key = key; - info->val = 0; - info->dirty = 0; - idx = key % (u16)ARRAY_SIZE(cache->hash); - info->next = cache->hash[idx]; - cache->hash[idx] = cur; - } - return info; -} - -/* query and allocate an amp hash entry */ -static inline struct hda_amp_info * -get_alloc_amp_hash(struct hda_codec *codec, u32 key) -{ - return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); -} - -/* overwrite the value with the key in the caps hash */ -static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val) -{ - struct hda_amp_info *info; - - mutex_lock(&codec->hash_mutex); - info = get_alloc_amp_hash(codec, key); - if (!info) { - mutex_unlock(&codec->hash_mutex); - return -EINVAL; - } - info->amp_caps = val; - info->head.val |= INFO_AMP_CAPS; - mutex_unlock(&codec->hash_mutex); - return 0; -} - -/* query the value from the caps hash; if not found, fetch the current - * value from the given function and store in the hash - */ -static unsigned int -query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key, - unsigned int (*func)(struct hda_codec *, hda_nid_t, int)) -{ - struct hda_amp_info *info; - unsigned int val; - - mutex_lock(&codec->hash_mutex); - info = get_alloc_amp_hash(codec, key); - if (!info) { - mutex_unlock(&codec->hash_mutex); - return 0; - } - if (!(info->head.val & INFO_AMP_CAPS)) { - mutex_unlock(&codec->hash_mutex); /* for reentrance */ - val = func(codec, nid, dir); - write_caps_hash(codec, key, val); - } else { - val = info->amp_caps; - mutex_unlock(&codec->hash_mutex); - } - return val; -} - -static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, - int direction) -{ - if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) - nid = codec->afg; - return snd_hda_param_read(codec, nid, - direction == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); -} - /** * query_amp_caps - query AMP capabilities * @codec: the HD-auio codec @@ -2021,9 +1320,11 @@ static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, */ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { - return query_caps_hash(codec, nid, direction, - HDA_HASH_KEY(nid, direction, 0), - read_amp_cap); + if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) + nid = codec->core.afg; + return snd_hda_param_read(codec, nid, + direction == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); } EXPORT_SYMBOL_GPL(query_amp_caps); @@ -2064,183 +1365,14 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps) { - return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps); -} -EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); - -static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid, - int dir) -{ - return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); -} - -/** - * snd_hda_query_pin_caps - Query PIN capabilities - * @codec: the HD-auio codec - * @nid: the NID to query - * - * Query PIN capabilities for the given widget. - * Returns the obtained capability bits. - * - * When cap bits have been already read, this doesn't read again but - * returns the cached value. - */ -u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid), - read_pin_cap); -} -EXPORT_SYMBOL_GPL(snd_hda_query_pin_caps); - -/** - * snd_hda_override_pin_caps - Override the pin capabilities - * @codec: the CODEC - * @nid: the NID to override - * @caps: the capability bits to set - * - * Override the cached PIN capabilitiy bits value by the given one. - * - * Returns zero if successful or a negative error code. - */ -int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, - unsigned int caps) -{ - return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps); -} -EXPORT_SYMBOL_GPL(snd_hda_override_pin_caps); - -/* read or sync the hash value with the current value; - * call within hash_mutex - */ -static struct hda_amp_info * -update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index, bool init_only) -{ - struct hda_amp_info *info; - unsigned int parm, val = 0; - bool val_read = false; - - retry: - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); - if (!info) - return NULL; - if (!(info->head.val & INFO_AMP_VOL(ch))) { - if (!val_read) { - mutex_unlock(&codec->hash_mutex); - parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; - parm |= direction == HDA_OUTPUT ? - AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; - parm |= index; - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, parm); - val &= 0xff; - val_read = true; - mutex_lock(&codec->hash_mutex); - goto retry; - } - info->vol[ch] = val; - info->head.val |= INFO_AMP_VOL(ch); - } else if (init_only) - return NULL; - return info; -} - -/* - * write the current volume in info to the h/w - */ -static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps, - hda_nid_t nid, int ch, int direction, int index, - int val) -{ - u32 parm; - - parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; - parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; - parm |= index << AC_AMP_SET_INDEX_SHIFT; - if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) && - (amp_caps & AC_AMPCAP_MIN_MUTE)) - ; /* set the zero value as a fake mute */ - else - parm |= val; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); -} - -/** - * snd_hda_codec_amp_read - Read AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @index: the index value (only for input direction) - * - * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. - */ -int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index) -{ - struct hda_amp_info *info; - unsigned int val = 0; - - mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, index, false); - if (info) - val = info->vol[ch]; - mutex_unlock(&codec->hash_mutex); - return val; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_amp_read); - -static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val, - bool init_only) -{ - struct hda_amp_info *info; - unsigned int caps; - unsigned int cache_only; - - if (snd_BUG_ON(mask & ~0xff)) - mask &= 0xff; - val &= mask; - - mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, idx, init_only); - if (!info) { - mutex_unlock(&codec->hash_mutex); - return 0; - } - val |= info->vol[ch] & ~mask; - if (info->vol[ch] == val) { - mutex_unlock(&codec->hash_mutex); - return 0; - } - info->vol[ch] = val; - cache_only = info->head.dirty = codec->cached_write; - caps = info->amp_caps; - mutex_unlock(&codec->hash_mutex); - if (!cache_only) - put_vol_mute(codec, caps, nid, ch, direction, idx, val); - return 1; -} + unsigned int parm; -/** - * snd_hda_codec_amp_update - update the AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP value with a bit mask. - * Returns 0 if the value is unchanged, 1 if changed. - */ -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val) -{ - return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false); + snd_hda_override_wcaps(codec, nid, + get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD); + parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP; + return snd_hdac_override_parm(&codec->core, nid, parm, caps); } -EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update); +EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); /** * snd_hda_codec_amp_stereo - update the AMP stereo values @@ -2285,7 +1417,16 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo); int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, int dir, int idx, int mask, int val) { - return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true); + int orig; + + if (!codec->core.regmap) + return -EINVAL; + regcache_cache_only(codec->core.regmap, true); + orig = snd_hda_codec_amp_read(codec, nid, ch, dir, idx); + regcache_cache_only(codec->core.regmap, false); + if (orig >= 0) + return 0; + return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val); } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init); @@ -2314,49 +1455,6 @@ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo); -/** - * snd_hda_codec_resume_amp - Resume all AMP commands from the cache - * @codec: HD-audio codec - * - * Resume the all amp commands from the cache. - */ -void snd_hda_codec_resume_amp(struct hda_codec *codec) -{ - int i; - - mutex_lock(&codec->hash_mutex); - codec->cached_write = 0; - for (i = 0; i < codec->amp_cache.buf.used; i++) { - struct hda_amp_info *buffer; - u32 key; - hda_nid_t nid; - unsigned int idx, dir, ch; - struct hda_amp_info info; - - buffer = snd_array_elem(&codec->amp_cache.buf, i); - if (!buffer->head.dirty) - continue; - buffer->head.dirty = 0; - info = *buffer; - key = info.head.key; - if (!key) - continue; - nid = key & 0xff; - idx = (key >> 16) & 0xff; - dir = (key >> 24) & 0xff; - for (ch = 0; ch < 2; ch++) { - if (!(info.head.val & INFO_AMP_VOL(ch))) - continue; - mutex_unlock(&codec->hash_mutex); - put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx, - info.vol[ch]); - mutex_lock(&codec->hash_mutex); - } - } - mutex_unlock(&codec->hash_mutex); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_resume_amp); - static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int ofs) { @@ -2478,14 +1576,12 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, long *valp = ucontrol->value.integer.value; int change = 0; - snd_hda_power_up(codec); if (chs & 1) { change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp); valp++; } if (chs & 2) change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp); - snd_hda_power_down(codec); return change; } EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put); @@ -2572,7 +1668,7 @@ find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx) if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) return NULL; strcpy(id.name, name); - return snd_ctl_find_id(codec->bus->card, &id); + return snd_ctl_find_id(codec->card, &id); } /** @@ -2636,7 +1732,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, nid = kctl->id.subdevice & 0xffff; if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG)) kctl->id.subdevice = 0; - err = snd_ctl_add(codec->bus->card, kctl); + err = snd_ctl_add(codec->card, kctl); if (err < 0) return err; item = snd_array_new(&codec->mixers); @@ -2689,7 +1785,7 @@ void snd_hda_ctls_clear(struct hda_codec *codec) int i; struct hda_nid_item *items = codec->mixers.list; for (i = 0; i < codec->mixers.used; i++) - snd_ctl_remove(codec->bus->card, items[i].kctl); + snd_ctl_remove(codec->card, items[i].kctl); snd_array_free(&codec->mixers); snd_array_free(&codec->nids); } @@ -2712,10 +1808,9 @@ int snd_hda_lock_devices(struct hda_bus *bus) if (!list_empty(&card->ctl_files)) goto err_clear; - list_for_each_entry(codec, &bus->codec_list, list) { - int pcm; - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - struct hda_pcm *cpcm = &codec->pcm_info[pcm]; + list_for_each_codec(codec, bus) { + struct hda_pcm *cpcm; + list_for_each_entry(cpcm, &codec->pcm_list_head, list) { if (!cpcm->pcm) continue; if (cpcm->pcm->streams[0].substream_opened || @@ -2742,7 +1837,6 @@ void snd_hda_unlock_devices(struct hda_bus *bus) { struct snd_card *card = bus->card; - card = bus->card; spin_lock(&card->files_lock); card->shutdown = 0; spin_unlock(&card->files_lock); @@ -2762,51 +1856,12 @@ EXPORT_SYMBOL_GPL(snd_hda_unlock_devices); int snd_hda_codec_reset(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; - struct snd_card *card = bus->card; - int i; if (snd_hda_lock_devices(bus) < 0) return -EBUSY; /* OK, let it free */ - cancel_delayed_work_sync(&codec->jackpoll_work); -#ifdef CONFIG_PM - cancel_delayed_work_sync(&codec->power_work); - flush_workqueue(bus->workq); -#endif - snd_hda_ctls_clear(codec); - /* release PCMs */ - for (i = 0; i < codec->num_pcms; i++) { - if (codec->pcm_info[i].pcm) { - snd_device_free(card, codec->pcm_info[i].pcm); - clear_bit(codec->pcm_info[i].device, - bus->pcm_dev_bits); - } - } - snd_hda_detach_beep_device(codec); - if (codec->patch_ops.free) - codec->patch_ops.free(codec); - memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); - snd_hda_jack_tbl_clear(codec); - codec->proc_widget_hook = NULL; - codec->spec = NULL; - free_hda_cache(&codec->amp_cache); - free_hda_cache(&codec->cmd_cache); - init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); - init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); - /* free only driver_pins so that init_pins + user_pins are restored */ - snd_array_free(&codec->driver_pins); - snd_array_free(&codec->cvt_setups); - snd_array_free(&codec->spdif_out); - snd_array_free(&codec->verbs); - codec->num_pcms = 0; - codec->pcm_info = NULL; - codec->preset = NULL; - codec->slave_dig_outs = NULL; - codec->spdif_status_reset = 0; - unload_parser(codec); - module_put(codec->owner); - codec->owner = NULL; + snd_hdac_device_unregister(&codec->core); /* allow device access again */ snd_hda_unlock_devices(bus); @@ -3153,7 +2208,6 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, long *valp = ucontrol->value.integer.value; int change = 0; - snd_hda_power_up(codec); if (chs & 1) { change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, HDA_AMP_MUTE, @@ -3165,7 +2219,6 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, HDA_AMP_MUTE, *valp ? 0 : HDA_AMP_MUTE); hda_call_check_power_status(codec, nid); - snd_hda_power_down(codec); return change; } EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put); @@ -3466,25 +2519,35 @@ static unsigned int convert_to_spdif_status(unsigned short val) /* set digital convert verbs both for the given NID and its slaves */ static void set_dig_out(struct hda_codec *codec, hda_nid_t nid, - int verb, int val) + int mask, int val) { const hda_nid_t *d; - snd_hda_codec_write_cache(codec, nid, 0, verb, val); + snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1, + mask, val); d = codec->slave_dig_outs; if (!d) return; for (; *d; d++) - snd_hda_codec_write_cache(codec, *d, 0, verb, val); + snd_hdac_regmap_update(&codec->core, nid, + AC_VERB_SET_DIGI_CONVERT_1, mask, val); } static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid, int dig1, int dig2) { - if (dig1 != -1) - set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1); - if (dig2 != -1) - set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2); + unsigned int mask = 0; + unsigned int val = 0; + + if (dig1 != -1) { + mask |= 0xff; + val = dig1; + } + if (dig2 != -1) { + mask |= 0xff00; + val |= dig2 << 8; + } + set_dig_out(codec, nid, mask, val); } static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, @@ -3617,6 +2680,7 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; int idx = 0; + int val = 0; const int spdif_index = 16; struct hda_spdif_out *spdif; struct hda_bus *bus = codec->bus; @@ -3657,8 +2721,9 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, return err; } spdif->nid = cvt_nid; - spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); + snd_hdac_regmap_read(&codec->core, cvt_nid, + AC_VERB_GET_DIGI_CONVERT_1, &val); + spdif->ctls = val; spdif->status = convert_to_spdif_status(spdif->ctls); return 0; } @@ -3802,8 +2867,8 @@ static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol, change = codec->spdif_in_enable != val; if (change) { codec->spdif_in_enable = val; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, val); + snd_hdac_regmap_write(&codec->core, nid, + AC_VERB_SET_DIGI_CONVERT_1, val); } mutex_unlock(&codec->spdif_mutex); return change; @@ -3814,10 +2879,11 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; - unsigned short val; + unsigned int val; unsigned int sbits; - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); + snd_hdac_regmap_read(&codec->core, nid, + AC_VERB_GET_DIGI_CONVERT_1, &val); sbits = convert_to_spdif_status(val); ucontrol->value.iec958.status[0] = sbits; ucontrol->value.iec958.status[1] = sbits >> 8; @@ -3883,153 +2949,6 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls); -/* - * command cache - */ - -/* build a 31bit cache key with the widget id and the command parameter */ -#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid) -#define get_cmd_cache_nid(key) ((key) & 0xff) -#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff) - -/** - * snd_hda_codec_write_cache - send a single command with caching - * @codec: the HDA codec - * @nid: NID to send the command - * @flags: optional bit flags - * @verb: the verb to send - * @parm: the parameter for the verb - * - * Send a single command without waiting for response. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, - int flags, unsigned int verb, unsigned int parm) -{ - int err; - struct hda_cache_head *c; - u32 key; - unsigned int cache_only; - - cache_only = codec->cached_write; - if (!cache_only) { - err = snd_hda_codec_write(codec, nid, flags, verb, parm); - if (err < 0) - return err; - } - - /* parm may contain the verb stuff for get/set amp */ - verb = verb | (parm >> 8); - parm &= 0xff; - key = build_cmd_cache_key(nid, verb); - mutex_lock(&codec->bus->cmd_mutex); - c = get_alloc_hash(&codec->cmd_cache, key); - if (c) { - c->val = parm; - c->dirty = cache_only; - } - mutex_unlock(&codec->bus->cmd_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_write_cache); - -/** - * snd_hda_codec_update_cache - check cache and write the cmd only when needed - * @codec: the HDA codec - * @nid: NID to send the command - * @flags: optional bit flags - * @verb: the verb to send - * @parm: the parameter for the verb - * - * This function works like snd_hda_codec_write_cache(), but it doesn't send - * command if the parameter is already identical with the cached value. - * If not, it sends the command and refreshes the cache. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, - int flags, unsigned int verb, unsigned int parm) -{ - struct hda_cache_head *c; - u32 key; - - /* parm may contain the verb stuff for get/set amp */ - verb = verb | (parm >> 8); - parm &= 0xff; - key = build_cmd_cache_key(nid, verb); - mutex_lock(&codec->bus->cmd_mutex); - c = get_hash(&codec->cmd_cache, key); - if (c && c->val == parm) { - mutex_unlock(&codec->bus->cmd_mutex); - return 0; - } - mutex_unlock(&codec->bus->cmd_mutex); - return snd_hda_codec_write_cache(codec, nid, flags, verb, parm); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_update_cache); - -/** - * snd_hda_codec_resume_cache - Resume the all commands from the cache - * @codec: HD-audio codec - * - * Execute all verbs recorded in the command caches to resume. - */ -void snd_hda_codec_resume_cache(struct hda_codec *codec) -{ - int i; - - mutex_lock(&codec->hash_mutex); - codec->cached_write = 0; - for (i = 0; i < codec->cmd_cache.buf.used; i++) { - struct hda_cache_head *buffer; - u32 key; - - buffer = snd_array_elem(&codec->cmd_cache.buf, i); - key = buffer->key; - if (!key) - continue; - if (!buffer->dirty) - continue; - buffer->dirty = 0; - mutex_unlock(&codec->hash_mutex); - snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0, - get_cmd_cache_cmd(key), buffer->val); - mutex_lock(&codec->hash_mutex); - } - mutex_unlock(&codec->hash_mutex); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_resume_cache); - -/** - * snd_hda_sequence_write_cache - sequence writes with caching - * @codec: the HDA codec - * @seq: VERB array to send - * - * Send the commands sequentially from the given array. - * Thte commands are recorded on cache for power-save and resume. - * The array must be terminated with NID=0. - */ -void snd_hda_sequence_write_cache(struct hda_codec *codec, - const struct hda_verb *seq) -{ - for (; seq->nid; seq++) - snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb, - seq->param); -} -EXPORT_SYMBOL_GPL(snd_hda_sequence_write_cache); - -/** - * snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs - * @codec: HD-audio codec - */ -void snd_hda_codec_flush_cache(struct hda_codec *codec) -{ - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache); - /** * snd_hda_codec_set_power_to_all - Set the power state to all widgets * @codec: the HDA codec @@ -4043,10 +2962,9 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache); void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state) { - hda_nid_t nid = codec->start_nid; - int i; + hda_nid_t nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wcaps = get_wcaps(codec, nid); unsigned int state = power_state; if (!(wcaps & AC_WCAP_POWER)) @@ -4063,22 +2981,6 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all); /* - * supported power states check - */ -static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - int sup = snd_hda_param_read(codec, fg, AC_PAR_POWER_STATE); - - if (sup == -1) - return false; - if (sup & power_state) - return true; - else - return false; -} - -/* * wait until the state is reached, returns the current state */ static unsigned int hda_sync_power_state(struct hda_codec *codec, @@ -4117,7 +3019,7 @@ unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) { - if (nid == codec->afg || nid == codec->mfg) + if (nid == codec->core.afg || nid == codec->core.mfg) return power_state; if (power_state == AC_PWRST_D3 && get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN && @@ -4137,7 +3039,7 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter); static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state) { - hda_nid_t fg = codec->afg ? codec->afg : codec->mfg; + hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg; int count; unsigned int state; int flags = 0; @@ -4145,7 +3047,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, /* this delay seems necessary to avoid click noise at power-down */ if (power_state == AC_PWRST_D3) { if (codec->depop_delay < 0) - msleep(codec->epss ? 10 : 100); + msleep(codec_has_epss(codec) ? 10 : 100); else if (codec->depop_delay > 0) msleep(codec->depop_delay); flags = HDA_RW_NO_RESPONSE_FALLBACK; @@ -4179,14 +3081,13 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, */ static void sync_power_up_states(struct hda_codec *codec) { - hda_nid_t nid = codec->start_nid; - int i; + hda_nid_t nid; /* don't care if no filter is used */ if (!codec->power_filter) return; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wcaps = get_wcaps(codec, nid); unsigned int target; if (!(wcaps & AC_WCAP_POWER)) @@ -4212,63 +3113,54 @@ static inline void hda_exec_init_verbs(struct hda_codec *codec) {} #endif #ifdef CONFIG_PM +/* update the power on/off account with the current jiffies */ +static void update_power_acct(struct hda_codec *codec, bool on) +{ + unsigned long delta = jiffies - codec->power_jiffies; + + if (on) + codec->power_on_acct += delta; + else + codec->power_off_acct += delta; + codec->power_jiffies += delta; +} + +void snd_hda_update_power_acct(struct hda_codec *codec) +{ + update_power_acct(codec, hda_codec_is_power_on(codec)); +} + /* * call suspend and power-down; used both from PM and power-save * this function returns the power state in the end */ -static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq) +static unsigned int hda_call_codec_suspend(struct hda_codec *codec) { unsigned int state; - codec->in_pm = 1; + atomic_inc(&codec->core.in_pm); if (codec->patch_ops.suspend) codec->patch_ops.suspend(codec); hda_cleanup_all_streams(codec); state = hda_set_power_state(codec, AC_PWRST_D3); - /* Cancel delayed work if we aren't currently running from it. */ - if (!in_wq) - cancel_delayed_work_sync(&codec->power_work); - spin_lock(&codec->power_lock); - snd_hda_update_power_acct(codec); - trace_hda_power_down(codec); - codec->power_on = 0; - codec->power_transition = 0; - codec->power_jiffies = jiffies; - spin_unlock(&codec->power_lock); - codec->in_pm = 0; + update_power_acct(codec, true); + atomic_dec(&codec->core.in_pm); return state; } -/* mark all entries of cmd and amp caches dirty */ -static void hda_mark_cmd_cache_dirty(struct hda_codec *codec) -{ - int i; - for (i = 0; i < codec->cmd_cache.buf.used; i++) { - struct hda_cache_head *cmd; - cmd = snd_array_elem(&codec->cmd_cache.buf, i); - cmd->dirty = 1; - } - for (i = 0; i < codec->amp_cache.buf.used; i++) { - struct hda_amp_info *amp; - amp = snd_array_elem(&codec->amp_cache.buf, i); - amp->head.dirty = 1; - } -} - /* * kick up codec; used both from PM and power-save */ static void hda_call_codec_resume(struct hda_codec *codec) { - codec->in_pm = 1; + atomic_inc(&codec->core.in_pm); - hda_mark_cmd_cache_dirty(codec); + if (codec->core.regmap) + regcache_mark_dirty(codec->core.regmap); + + codec->power_jiffies = jiffies; - /* set as if powered on for avoiding re-entering the resume - * in the resume / power-save sequence - */ - hda_keep_power_on(codec); hda_set_power_state(codec, AC_PWRST_D0); restore_shutup_pins(codec); hda_exec_init_verbs(codec); @@ -4278,72 +3170,71 @@ static void hda_call_codec_resume(struct hda_codec *codec) else { if (codec->patch_ops.init) codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); + if (codec->core.regmap) + regcache_sync(codec->core.regmap); } if (codec->jackpoll_interval) hda_jackpoll_work(&codec->jackpoll_work.work); else snd_hda_jack_report_sync(codec); - - codec->in_pm = 0; - snd_hda_power_down(codec); /* flag down before returning */ + atomic_dec(&codec->core.in_pm); } -#endif /* CONFIG_PM */ +static int hda_codec_runtime_suspend(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + struct hda_pcm *pcm; + unsigned int state; -/** - * snd_hda_build_controls - build mixer controls - * @bus: the BUS - * - * Creates mixer controls for each codec included in the bus. - * - * Returns 0 if successful, otherwise a negative error code. - */ -int snd_hda_build_controls(struct hda_bus *bus) + cancel_delayed_work_sync(&codec->jackpoll_work); + list_for_each_entry(pcm, &codec->pcm_list_head, list) + snd_pcm_suspend_all(pcm->pcm); + state = hda_call_codec_suspend(codec); + if (codec_has_clkstop(codec) && codec_has_epss(codec) && + (state & AC_PWRST_CLK_STOP_OK)) + snd_hdac_codec_link_down(&codec->core); + return 0; +} + +static int hda_codec_runtime_resume(struct device *dev) { - struct hda_codec *codec; + struct hda_codec *codec = dev_to_hda_codec(dev); - list_for_each_entry(codec, &bus->codec_list, list) { - int err = snd_hda_codec_build_controls(codec); - if (err < 0) { - codec_err(codec, - "cannot build controls for #%d (error %d)\n", - codec->addr, err); - err = snd_hda_codec_reset(codec); - if (err < 0) { - codec_err(codec, - "cannot revert codec\n"); - return err; - } - } - } + snd_hdac_codec_link_up(&codec->core); + hda_call_codec_resume(codec); + pm_runtime_mark_last_busy(dev); return 0; } -EXPORT_SYMBOL_GPL(snd_hda_build_controls); +#endif /* CONFIG_PM */ + +/* referred in hda_bind.c */ +const struct dev_pm_ops hda_codec_driver_pm = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, + NULL) +}; /* * add standard channel maps if not specified */ static int add_std_chmaps(struct hda_codec *codec) { - int i, str, err; + struct hda_pcm *pcm; + int str, err; - for (i = 0; i < codec->num_pcms; i++) { + list_for_each_entry(pcm, &codec->pcm_list_head, list) { for (str = 0; str < 2; str++) { - struct snd_pcm *pcm = codec->pcm_info[i].pcm; - struct hda_pcm_stream *hinfo = - &codec->pcm_info[i].stream[str]; + struct hda_pcm_stream *hinfo = &pcm->stream[str]; struct snd_pcm_chmap *chmap; const struct snd_pcm_chmap_elem *elem; - if (codec->pcm_info[i].own_chmap) - continue; - if (!pcm || !hinfo->substreams) + if (!pcm || pcm->own_chmap || + !hinfo->substreams) continue; elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps; - err = snd_pcm_add_chmap_ctls(pcm, str, elem, + err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem, hinfo->channels_max, 0, &chmap); if (err < 0) @@ -4499,43 +3390,29 @@ unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format); -static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid, - int dir) +static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) { unsigned int val = 0; - if (nid != codec->afg && + 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->afg, AC_PAR_PCM); + val = snd_hda_param_read(codec, codec->core.afg, AC_PAR_PCM); if (!val || val == -1) return 0; return val; } -static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid), - get_pcm_param); -} - -static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid, - int dir) +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->afg, AC_PAR_STREAM); + streams = snd_hda_param_read(codec, codec->core.afg, AC_PAR_STREAM); if (!streams || streams == -1) return 0; return streams; } -static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid), - get_stream_param); -} - /** * snd_hda_query_supported_pcm - query the supported PCM rates and formats * @codec: the HDA codec @@ -4792,7 +3669,11 @@ int snd_hda_codec_prepare(struct hda_codec *codec, { int ret; mutex_lock(&codec->bus->prepare_mutex); - ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream); + if (hinfo->ops.prepare) + ret = hinfo->ops.prepare(hinfo, codec, stream, format, + substream); + else + ret = -ENODEV; if (ret >= 0) purify_inactive_streams(codec); mutex_unlock(&codec->bus->prepare_mutex); @@ -4813,7 +3694,8 @@ void snd_hda_codec_cleanup(struct hda_codec *codec, struct snd_pcm_substream *substream) { mutex_lock(&codec->bus->prepare_mutex); - hinfo->ops.cleanup(hinfo, codec, substream); + if (hinfo->ops.cleanup) + hinfo->ops.cleanup(hinfo, codec, substream); mutex_unlock(&codec->bus->prepare_mutex); } EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup); @@ -4871,112 +3753,84 @@ static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type) return -EAGAIN; } -/* - * attach a new PCM stream - */ -static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm) +/* call build_pcms ops of the given codec and set up the default parameters */ +int snd_hda_codec_parse_pcms(struct hda_codec *codec) { - struct hda_bus *bus = codec->bus; - struct hda_pcm_stream *info; - int stream, err; + struct hda_pcm *cpcm; + int err; - if (snd_BUG_ON(!pcm->name)) - return -EINVAL; - for (stream = 0; stream < 2; stream++) { - info = &pcm->stream[stream]; - if (info->substreams) { + if (!list_empty(&codec->pcm_list_head)) + return 0; /* already parsed */ + + if (!codec->patch_ops.build_pcms) + return 0; + + err = codec->patch_ops.build_pcms(codec); + if (err < 0) { + codec_err(codec, "cannot build PCMs for #%d (error %d)\n", + codec->core.addr, err); + return err; + } + + list_for_each_entry(cpcm, &codec->pcm_list_head, list) { + int stream; + + for (stream = 0; stream < 2; stream++) { + struct hda_pcm_stream *info = &cpcm->stream[stream]; + + if (!info->substreams) + continue; err = set_pcm_default_values(codec, info); - if (err < 0) + if (err < 0) { + codec_warn(codec, + "fail to setup default for PCM %s\n", + cpcm->name); return err; + } } } - return bus->ops.attach_pcm(bus, codec, pcm); + + return 0; } /* assign all PCMs of the given codec */ int snd_hda_codec_build_pcms(struct hda_codec *codec) { - unsigned int pcm; - int err; + struct hda_bus *bus = codec->bus; + struct hda_pcm *cpcm; + int dev, err; - if (!codec->num_pcms) { - if (!codec->patch_ops.build_pcms) - return 0; - err = codec->patch_ops.build_pcms(codec); - if (err < 0) { - codec_err(codec, - "cannot build PCMs for #%d (error %d)\n", - codec->addr, err); - err = snd_hda_codec_reset(codec); - if (err < 0) { - codec_err(codec, - "cannot revert codec\n"); - return 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); + return err; } - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - struct hda_pcm *cpcm = &codec->pcm_info[pcm]; - int dev; + /* attach a new PCM streams */ + list_for_each_entry(cpcm, &codec->pcm_list_head, list) { + if (cpcm->pcm) + continue; /* already attached */ if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) continue; /* no substreams assigned */ - if (!cpcm->pcm) { - dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type); - if (dev < 0) - continue; /* no fatal error */ - cpcm->device = dev; - err = snd_hda_attach_pcm(codec, cpcm); - if (err < 0) { - codec_err(codec, - "cannot attach PCM stream %d for codec #%d\n", - dev, codec->addr); - continue; /* no fatal error */ - } + dev = get_empty_pcm_device(bus, cpcm->pcm_type); + if (dev < 0) + continue; /* no fatal error */ + cpcm->device = dev; + err = bus->ops.attach_pcm(bus, codec, cpcm); + if (err < 0) { + codec_err(codec, + "cannot attach PCM stream %d for codec #%d\n", + dev, codec->core.addr); + continue; /* no fatal error */ } } - return 0; -} -/** - * snd_hda_build_pcms - build PCM information - * @bus: the BUS - * - * Create PCM information for each codec included in the bus. - * - * The build_pcms codec patch is requested to set up codec->num_pcms and - * codec->pcm_info properly. The array is referred by the top-level driver - * to create its PCM instances. - * The allocated codec->pcm_info should be released in codec->patch_ops.free - * callback. - * - * At least, substreams, channels_min and channels_max must be filled for - * each stream. substreams = 0 indicates that the stream doesn't exist. - * When rates and/or formats are zero, the supported values are queried - * from the given nid. The nid is used also by the default ops.prepare - * and ops.cleanup callbacks. - * - * The driver needs to call ops.open in its open callback. Similarly, - * ops.close is supposed to be called in the close callback. - * ops.prepare should be called in the prepare or hw_params callback - * with the proper parameters for set up. - * ops.cleanup should be called in hw_free for clean up of streams. - * - * This function returns 0 if successful, or a negative error code. - */ -int snd_hda_build_pcms(struct hda_bus *bus) -{ - struct hda_codec *codec; - - list_for_each_entry(codec, &bus->codec_list, list) { - int err = snd_hda_codec_build_pcms(codec); - if (err < 0) - return err; - } return 0; } -EXPORT_SYMBOL_GPL(snd_hda_build_pcms); /** * snd_hda_add_new_ctls - create controls from the array @@ -5013,8 +3867,8 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, * the codec addr; if it still fails (or it's the * primary codec), then try another control index */ - if (!addr && codec->addr) - addr = codec->addr; + if (!addr && codec->core.addr) + addr = codec->core.addr; else if (!idx && !knew->index) { idx = find_empty_mixer_ctl_idx(codec, knew->name, 0); @@ -5029,127 +3883,37 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls); #ifdef CONFIG_PM -static void hda_power_work(struct work_struct *work) -{ - struct hda_codec *codec = - container_of(work, struct hda_codec, power_work.work); - struct hda_bus *bus = codec->bus; - unsigned int state; - - spin_lock(&codec->power_lock); - if (codec->power_transition > 0) { /* during power-up sequence? */ - spin_unlock(&codec->power_lock); - return; - } - if (!codec->power_on || codec->power_count) { - codec->power_transition = 0; - spin_unlock(&codec->power_lock); - return; - } - spin_unlock(&codec->power_lock); - - state = hda_call_codec_suspend(codec, true); - if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) - hda_call_pm_notify(codec, false); -} - -static void hda_keep_power_on(struct hda_codec *codec) -{ - spin_lock(&codec->power_lock); - codec->power_count++; - codec->power_on = 1; - codec->power_jiffies = jiffies; - spin_unlock(&codec->power_lock); - hda_call_pm_notify(codec, true); -} - -/* update the power on/off account with the current jiffies */ -void snd_hda_update_power_acct(struct hda_codec *codec) -{ - unsigned long delta = jiffies - codec->power_jiffies; - if (codec->power_on) - codec->power_on_acct += delta; - else - codec->power_off_acct += delta; - codec->power_jiffies += delta; -} - -/* Transition to powered up, if wait_power_down then wait for a pending - * transition to D3 to complete. A pending D3 transition is indicated - * with power_transition == -1. */ -/* call this with codec->power_lock held! */ -static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down) -{ - /* Return if power_on or transitioning to power_on, unless currently - * powering down. */ - if ((codec->power_on || codec->power_transition > 0) && - !(wait_power_down && codec->power_transition < 0)) - return; - spin_unlock(&codec->power_lock); - - cancel_delayed_work_sync(&codec->power_work); - - spin_lock(&codec->power_lock); - /* If the power down delayed work was cancelled above before starting, - * then there is no need to go through power up here. - */ - if (codec->power_on) { - if (codec->power_transition < 0) - codec->power_transition = 0; - return; - } - - trace_hda_power_up(codec); - snd_hda_update_power_acct(codec); - codec->power_on = 1; - codec->power_jiffies = jiffies; - codec->power_transition = 1; /* avoid reentrance */ - spin_unlock(&codec->power_lock); - - hda_call_codec_resume(codec); - - spin_lock(&codec->power_lock); - codec->power_transition = 0; -} - -#define power_save(codec) \ - ((codec)->bus->power_save ? *(codec)->bus->power_save : 0) - -/* Transition to powered down */ -static void __snd_hda_power_down(struct hda_codec *codec) +static void codec_set_power_save(struct hda_codec *codec, int delay) { - if (!codec->power_on || codec->power_count || codec->power_transition) - return; + struct device *dev = hda_codec_dev(codec); - if (power_save(codec)) { - codec->power_transition = -1; /* avoid reentrance */ - queue_delayed_work(codec->bus->workq, &codec->power_work, - msecs_to_jiffies(power_save(codec) * 1000)); + if (delay > 0) { + pm_runtime_set_autosuspend_delay(dev, delay); + pm_runtime_use_autosuspend(dev); + pm_runtime_allow(dev); + if (!pm_runtime_suspended(dev)) + pm_runtime_mark_last_busy(dev); + } else { + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_forbid(dev); } } /** - * snd_hda_power_save - Power-up/down/sync the codec - * @codec: HD-audio codec - * @delta: the counter delta to change - * @d3wait: sync for D3 transition complete + * snd_hda_set_power_save - reprogram autosuspend for the given delay + * @bus: HD-audio bus + * @delay: autosuspend delay in msec, 0 = off * - * Change the power-up counter via @delta, and power up or down the hardware - * appropriately. For the power-down, queue to the delayed action. - * Passing zero to @delta means to synchronize the power state. + * Synchronize the runtime PM autosuspend state from the power_save option. */ -void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait) +void snd_hda_set_power_save(struct hda_bus *bus, int delay) { - spin_lock(&codec->power_lock); - codec->power_count += delta; - trace_hda_power_count(codec); - if (delta > 0) - __snd_hda_power_up(codec, d3wait); - else - __snd_hda_power_down(codec); - spin_unlock(&codec->power_lock); + struct hda_codec *c; + + list_for_each_codec(c, bus) + codec_set_power_save(c, delay); } -EXPORT_SYMBOL_GPL(snd_hda_power_save); +EXPORT_SYMBOL_GPL(snd_hda_set_power_save); /** * snd_hda_check_amp_list_power - Check the amp list and update the power @@ -5187,7 +3951,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, if (!(v & HDA_AMP_MUTE) && v > 0) { if (!check->power_on) { check->power_on = 1; - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); } return 1; } @@ -5195,7 +3959,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, } if (check->power_on) { check->power_on = 0; - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); } return 0; } @@ -5203,88 +3967,6 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power); #endif /* - * Channel mode helper - */ - -/** - * snd_hda_ch_mode_info - Info callback helper for the channel mode enum - * @codec: the HDA codec - * @uinfo: pointer to get/store the data - * @chmode: channel mode array - * @num_chmodes: channel mode array size - */ -int snd_hda_ch_mode_info(struct hda_codec *codec, - struct snd_ctl_elem_info *uinfo, - const struct hda_channel_mode *chmode, - int num_chmodes) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = num_chmodes; - if (uinfo->value.enumerated.item >= num_chmodes) - uinfo->value.enumerated.item = num_chmodes - 1; - sprintf(uinfo->value.enumerated.name, "%dch", - chmode[uinfo->value.enumerated.item].channels); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_ch_mode_info); - -/** - * snd_hda_ch_mode_get - Get callback helper for the channel mode enum - * @codec: the HDA codec - * @ucontrol: pointer to get/store the data - * @chmode: channel mode array - * @num_chmodes: channel mode array size - * @max_channels: max number of channels - */ -int snd_hda_ch_mode_get(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int max_channels) -{ - int i; - - for (i = 0; i < num_chmodes; i++) { - if (max_channels == chmode[i].channels) { - ucontrol->value.enumerated.item[0] = i; - break; - } - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_ch_mode_get); - -/** - * snd_hda_ch_mode_put - Put callback helper for the channel mode enum - * @codec: the HDA codec - * @ucontrol: pointer to get/store the data - * @chmode: channel mode array - * @num_chmodes: channel mode array size - * @max_channelsp: pointer to store the max channels - */ -int snd_hda_ch_mode_put(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int *max_channelsp) -{ - unsigned int mode; - - mode = ucontrol->value.enumerated.item[0]; - if (mode >= num_chmodes) - return -EINVAL; - if (*max_channelsp == chmode[mode].channels) - return 0; - /* change the current channel setting */ - *max_channelsp = chmode[mode].channels; - if (chmode[mode].sequence) - snd_hda_sequence_write_cache(codec, chmode[mode].sequence); - return 1; -} -EXPORT_SYMBOL_GPL(snd_hda_ch_mode_put); - -/* * input MUX helper */ @@ -5418,24 +4100,6 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) } /** - * snd_hda_bus_reboot_notify - call the reboot notifier of each codec - * @bus: HD-audio bus - */ -void snd_hda_bus_reboot_notify(struct hda_bus *bus) -{ - struct hda_codec *codec; - - if (!bus) - return; - list_for_each_entry(codec, &bus->codec_list, list) { - if (hda_codec_is_power_on(codec) && - codec->patch_ops.reboot_notify) - codec->patch_ops.reboot_notify(codec); - } -} -EXPORT_SYMBOL_GPL(snd_hda_bus_reboot_notify); - -/** * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode * @codec: the HDA codec * @mout: hda_multi_out object @@ -5825,123 +4489,26 @@ int snd_hda_add_imux_item(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_add_imux_item); - -#ifdef CONFIG_PM -/* - * power management - */ - - -static void hda_async_suspend(void *data, async_cookie_t cookie) -{ - hda_call_codec_suspend(data, false); -} - -static void hda_async_resume(void *data, async_cookie_t cookie) -{ - hda_call_codec_resume(data); -} - /** - * snd_hda_suspend - suspend the codecs - * @bus: the HDA bus - * - * Returns 0 if successful. + * snd_hda_bus_reset - Reset the bus + * @bus: HD-audio bus */ -int snd_hda_suspend(struct hda_bus *bus) +void snd_hda_bus_reset(struct hda_bus *bus) { struct hda_codec *codec; - ASYNC_DOMAIN_EXCLUSIVE(domain); - list_for_each_entry(codec, &bus->codec_list, list) { + list_for_each_codec(codec, bus) { + /* FIXME: maybe a better way needed for forced reset */ cancel_delayed_work_sync(&codec->jackpoll_work); +#ifdef CONFIG_PM if (hda_codec_is_power_on(codec)) { - if (bus->num_codecs > 1) - async_schedule_domain(hda_async_suspend, codec, - &domain); - else - hda_call_codec_suspend(codec, false); - } - } - - if (bus->num_codecs > 1) - async_synchronize_full_domain(&domain); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_suspend); - -/** - * snd_hda_resume - resume the codecs - * @bus: the HDA bus - * - * Returns 0 if successful. - */ -int snd_hda_resume(struct hda_bus *bus) -{ - struct hda_codec *codec; - ASYNC_DOMAIN_EXCLUSIVE(domain); - - list_for_each_entry(codec, &bus->codec_list, list) { - if (bus->num_codecs > 1) - async_schedule_domain(hda_async_resume, codec, &domain); - else + hda_call_codec_suspend(codec); hda_call_codec_resume(codec); + } +#endif } - - if (bus->num_codecs > 1) - async_synchronize_full_domain(&domain); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_resume); -#endif /* CONFIG_PM */ - -/* - * generic arrays - */ - -/** - * snd_array_new - get a new element from the given array - * @array: the array object - * - * Get a new element from the given array. If it exceeds the - * pre-allocated array size, re-allocate the array. - * - * Returns NULL if allocation failed. - */ -void *snd_array_new(struct snd_array *array) -{ - if (snd_BUG_ON(!array->elem_size)) - return NULL; - if (array->used >= array->alloced) { - int num = array->alloced + array->alloc_align; - int size = (num + 1) * array->elem_size; - void *nlist; - if (snd_BUG_ON(num >= 4096)) - return NULL; - nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO); - if (!nlist) - return NULL; - array->list = nlist; - array->alloced = num; - } - return snd_array_elem(array, array->used++); -} -EXPORT_SYMBOL_GPL(snd_array_new); - -/** - * snd_array_free - free the given array elements - * @array: the array object - */ -void snd_array_free(struct snd_array *array) -{ - kfree(array->list); - array->used = 0; - array->alloced = 0; - array->list = NULL; } -EXPORT_SYMBOL_GPL(snd_array_free); +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 9c8820f21f94..9075ac28dc4b 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -21,41 +21,14 @@ #ifndef __SOUND_HDA_CODEC_H #define __SOUND_HDA_CODEC_H +#include <linux/kref.h> #include <sound/info.h> #include <sound/control.h> #include <sound/pcm.h> #include <sound/hwdep.h> +#include <sound/hdaudio.h> #include <sound/hda_verbs.h> - -/* - * generic arrays - */ -struct snd_array { - unsigned int used; - unsigned int alloced; - unsigned int elem_size; - unsigned int alloc_align; - void *list; -}; - -void *snd_array_new(struct snd_array *array); -void snd_array_free(struct snd_array *array); -static inline void snd_array_init(struct snd_array *array, unsigned int size, - unsigned int align) -{ - array->elem_size = size; - array->alloc_align = align; -} - -static inline void *snd_array_elem(struct snd_array *array, unsigned int idx) -{ - return array->list + idx * array->elem_size; -} - -static inline unsigned int snd_array_index(struct snd_array *array, void *ptr) -{ - return (unsigned long)(ptr - array->list) / array->elem_size; -} +#include <sound/hda_regmap.h> /* * Structures @@ -66,10 +39,6 @@ struct hda_beep; struct hda_codec; struct hda_pcm; struct hda_pcm_stream; -struct hda_bus_unsolicited; - -/* NID type */ -typedef u16 hda_nid_t; /* bus operators */ struct hda_bus_ops { @@ -84,10 +53,6 @@ struct hda_bus_ops { struct hda_pcm *pcm); /* reset bus for retry verb */ void (*bus_reset)(struct hda_bus *bus); -#ifdef CONFIG_PM - /* notify power-up/down from codec to controller */ - void (*pm_notify)(struct hda_bus *bus, bool power_up); -#endif #ifdef CONFIG_SND_HDA_DSP_LOADER /* prepare DSP transfer */ int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format, @@ -101,15 +66,6 @@ struct hda_bus_ops { #endif }; -/* template to pass to the bus constructor */ -struct hda_bus_template { - void *private_data; - struct pci_dev *pci; - const char *modelname; - int *power_save; - struct hda_bus_ops ops; -}; - /* * codec bus * @@ -117,42 +73,28 @@ struct hda_bus_template { * A hda_bus contains several codecs in the list codec_list. */ struct hda_bus { + struct hdac_bus core; + struct snd_card *card; - /* copied from template */ void *private_data; struct pci_dev *pci; const char *modelname; - int *power_save; struct hda_bus_ops ops; - /* codec linked list */ - struct list_head codec_list; - unsigned int num_codecs; - /* link caddr -> codec */ - struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; - - struct mutex cmd_mutex; struct mutex prepare_mutex; - /* unsolicited event queue */ - struct hda_bus_unsolicited *unsol; - char workq_name[16]; - struct workqueue_struct *workq; /* common workqueue for codecs */ - /* assigned PCMs */ DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); /* misc op flags */ unsigned int needs_damn_long_delay :1; unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ - unsigned int sync_write:1; /* sync after verb write */ /* 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 power_keep_link_on:1; /* don't power off HDA link */ unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ int primary_dig_out_type; /* primary digital out PCM type */ @@ -175,15 +117,22 @@ struct hda_codec_preset { int (*patch)(struct hda_codec *codec); }; -struct hda_codec_preset_list { +#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101 +#define HDA_CODEC_ID_GENERIC 0x00000201 + +struct hda_codec_driver { + struct hdac_driver core; const struct hda_codec_preset *preset; - struct module *owner; - struct list_head list; }; -/* initial hook */ -int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset); -int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset); +int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, + struct module *owner); +#define hda_codec_driver_register(drv) \ + __hda_codec_driver_register(drv, KBUILD_MODNAME, THIS_MODULE) +void hda_codec_driver_unregister(struct hda_codec_driver *drv); +#define module_hda_codec_driver(drv) \ + module_driver(drv, hda_codec_driver_register, \ + hda_codec_driver_unregister) /* ops set by the preset patch */ struct hda_codec_ops { @@ -200,25 +149,7 @@ struct hda_codec_ops { int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); #endif void (*reboot_notify)(struct hda_codec *codec); -}; - -/* record for amp information cache */ -struct hda_cache_head { - u32 key:31; /* hash key */ - u32 dirty:1; - u16 val; /* assigned value */ - u16 next; -}; - -struct hda_amp_info { - struct hda_cache_head head; - u32 amp_caps; /* amp capabilities */ - u16 vol[2]; /* current volume & mute */ -}; - -struct hda_cache_rec { - u16 hash[64]; /* hash table for index */ - struct snd_array buf; /* record entries */ + void (*stream_pm)(struct hda_codec *codec, hda_nid_t nid, bool on); }; /* PCM callbacks */ @@ -267,41 +198,29 @@ struct hda_pcm { int device; /* device number to assign */ struct snd_pcm *pcm; /* assigned PCM instance */ bool own_chmap; /* codec driver provides own channel maps */ + /* private: */ + struct hda_codec *codec; + struct kref kref; + struct list_head list; }; /* codec information */ struct hda_codec { - struct device dev; + struct hdac_device core; struct hda_bus *bus; + struct snd_card *card; unsigned int addr; /* codec addr*/ - struct list_head list; /* list point */ - - hda_nid_t afg; /* AFG node id */ - hda_nid_t mfg; /* MFG node id */ - - /* ids */ - u8 afg_function_id; - u8 mfg_function_id; - u8 afg_unsol; - u8 mfg_unsol; - u32 vendor_id; - u32 subsystem_id; - u32 revision_id; + u32 probe_id; /* overridden id for probing */ /* detected preset */ const struct hda_codec_preset *preset; - struct module *owner; - int (*parser)(struct hda_codec *codec); - const char *vendor_name; /* codec vendor name */ - const char *chip_name; /* codec chip name */ const char *modelname; /* model name for preset */ /* set by patch */ struct hda_codec_ops patch_ops; /* PCM to create, set by patch_ops.build_pcms callback */ - unsigned int num_pcms; - struct hda_pcm *pcm_info; + struct list_head pcm_list_head; /* codec specific info */ void *spec; @@ -311,21 +230,15 @@ struct hda_codec { unsigned int beep_mode; /* widget capabilities cache */ - unsigned int num_nodes; - hda_nid_t start_nid; u32 *wcaps; struct snd_array mixers; /* list of assigned mixer elements */ struct snd_array nids; /* list of mapped mixer elements */ - struct hda_cache_rec amp_cache; /* cache for amp access */ - struct hda_cache_rec cmd_cache; /* cache for other commands */ - struct list_head conn_list; /* linked-list of connection-list */ struct mutex spdif_mutex; struct mutex control_mutex; - struct mutex hash_mutex; struct snd_array spdif_out; unsigned int spdif_in_enable; /* SPDIF input enable? */ const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ @@ -345,6 +258,8 @@ struct hda_codec { #endif /* misc flags */ + unsigned int in_freeing:1; /* being released */ + unsigned int registered:1; /* codec was registered */ unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each * status change * (e.g. Realtek codecs) @@ -362,22 +277,14 @@ struct hda_codec { unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */ unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */ unsigned int pcm_format_first:1; /* PCM format must be set first */ - unsigned int epss:1; /* supporting EPSS? */ unsigned int cached_write:1; /* write only to caches */ 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 */ #ifdef CONFIG_PM - unsigned int power_on :1; /* current (global) power-state */ - unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ - unsigned int pm_up_notified:1; /* PM notified to controller */ - unsigned int in_pm:1; /* suspend/resume being performed */ - int power_transition; /* power-state in transition */ - int power_count; /* current (global) power refcount */ - struct delayed_work power_work; /* delayed task for powerdown */ unsigned long power_on_acct; unsigned long power_off_acct; unsigned long power_jiffies; - spinlock_t power_lock; #endif /* filter the requested power state per nid */ @@ -409,10 +316,11 @@ struct hda_codec { struct snd_array verbs; }; -/* direction */ -enum { - HDA_INPUT, HDA_OUTPUT -}; +#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, core.dev) +#define hda_codec_dev(_dev) (&(_dev)->core.dev) + +#define list_for_each_codec(c, bus) \ + list_for_each_entry(c, &(bus)->core.codec_list, core.list) /* snd_hda_codec_read/write optional flags */ #define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0) @@ -420,10 +328,9 @@ enum { /* * constructors */ -int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, - struct hda_bus **busp); -int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, - struct hda_codec **codecp); +int snd_hda_bus_new(struct snd_card *card, 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); int snd_hda_codec_update_widgets(struct hda_codec *codec); @@ -436,9 +343,9 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm); #define snd_hda_param_read(codec, nid, param) \ - snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param) -int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *start_id); + snd_hdac_read_parm(&(codec)->core, nid, param) +#define snd_hda_get_sub_nodes(codec, nid, start_nid) \ + snd_hdac_get_sub_nodes(&(codec)->core, nid, start_nid) int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); static inline int @@ -446,9 +353,12 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid) { return snd_hda_get_connections(codec, nid, NULL, 0); } -int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns); + +#define snd_hda_get_raw_connections(codec, nid, list, max_conns) \ + snd_hdac_get_connections(&(codec)->core, nid, list, max_conns) +#define snd_hda_get_num_raw_conns(codec, nid) \ + snd_hdac_get_connections(&(codec)->core, nid, NULL, 0); + int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, const hda_nid_t **listp); int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, @@ -470,18 +380,22 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq); /* unsolicited event */ -int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); +static inline void +snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) +{ + snd_hdac_bus_queue_event(&bus->core, res, res_ex); +} /* cached write */ -int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, - int flags, unsigned int verb, unsigned int parm); -void snd_hda_sequence_write_cache(struct hda_codec *codec, - const struct hda_verb *seq); -int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, - int flags, unsigned int verb, unsigned int parm); -void snd_hda_codec_resume_cache(struct hda_codec *codec); -/* both for cmd & amp caches */ -void snd_hda_codec_flush_cache(struct hda_codec *codec); +static inline int +snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, + int flags, unsigned int verb, unsigned int parm) +{ + return snd_hdac_regmap_write(&codec->core, nid, verb, parm); +} + +#define snd_hda_codec_update_cache(codec, nid, flags, verb, parm) \ + snd_hda_codec_write_cache(codec, nid, flags, verb, parm) /* the struct for codec->pin_configs */ struct hda_pincfg { @@ -512,15 +426,24 @@ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid); /* * Mixer */ -int snd_hda_build_controls(struct hda_bus *bus); int snd_hda_codec_build_controls(struct hda_codec *codec); /* * PCM */ -int snd_hda_build_pcms(struct hda_bus *bus); +int snd_hda_codec_parse_pcms(struct hda_codec *codec); int snd_hda_codec_build_pcms(struct hda_codec *codec); +__printf(2, 3) +struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, + const char *fmt, ...); + +static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm) +{ + kref_get(&pcm->kref); +} +void snd_hda_codec_pcm_put(struct hda_pcm *pcm); + int snd_hda_codec_prepare(struct hda_codec *codec, struct hda_pcm_stream *hinfo, unsigned int stream, @@ -552,20 +475,17 @@ extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[]; * Misc */ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); -void snd_hda_bus_reboot_notify(struct hda_bus *bus); void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); 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); /* * power management */ -#ifdef CONFIG_PM -int snd_hda_suspend(struct hda_bus *bus); -int snd_hda_resume(struct hda_bus *bus); -#endif +extern const struct dev_pm_ops hda_codec_driver_pm; static inline int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) @@ -587,65 +507,17 @@ const char *snd_hda_get_jack_location(u32 cfg); /* * power saving */ +#define snd_hda_power_up(codec) snd_hdac_power_up(&(codec)->core) +#define snd_hda_power_up_pm(codec) snd_hdac_power_up_pm(&(codec)->core) +#define snd_hda_power_down(codec) snd_hdac_power_down(&(codec)->core) +#define snd_hda_power_down_pm(codec) snd_hdac_power_down_pm(&(codec)->core) #ifdef CONFIG_PM -void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait); +void snd_hda_set_power_save(struct hda_bus *bus, int delay); void snd_hda_update_power_acct(struct hda_codec *codec); #else -static inline void snd_hda_power_save(struct hda_codec *codec, int delta, - bool d3wait) {} +static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {} #endif -/** - * snd_hda_power_up - Power-up the codec - * @codec: HD-audio codec - * - * Increment the power-up counter and power up the hardware really when - * not turned on yet. - */ -static inline void snd_hda_power_up(struct hda_codec *codec) -{ - snd_hda_power_save(codec, 1, false); -} - -/** - * snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending - * D3 transition to complete. This differs from snd_hda_power_up() when - * power_transition == -1. snd_hda_power_up sees this case as a nop, - * snd_hda_power_up_d3wait waits for the D3 transition to complete then powers - * back up. - * @codec: HD-audio codec - * - * Cancel any power down operation hapenning on the work queue, then power up. - */ -static inline void snd_hda_power_up_d3wait(struct hda_codec *codec) -{ - snd_hda_power_save(codec, 1, true); -} - -/** - * snd_hda_power_down - Power-down the codec - * @codec: HD-audio codec - * - * Decrement the power-up counter and schedules the power-off work if - * the counter rearches to zero. - */ -static inline void snd_hda_power_down(struct hda_codec *codec) -{ - snd_hda_power_save(codec, -1, false); -} - -/** - * snd_hda_power_sync - Synchronize the power-save status - * @codec: HD-audio codec - * - * Synchronize the actual power state with the power account; - * called when power_save parameter is changed - */ -static inline void snd_hda_power_sync(struct hda_codec *codec) -{ - snd_hda_power_save(codec, 0, false); -} - #ifdef CONFIG_SND_HDA_PATCH_LOADER /* * patch firmware diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 17c2637d842c..26ce990592a0 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -27,10 +27,8 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/slab.h> -#include <linux/reboot.h> #include <sound/core.h> #include <sound/initval.h> -#include "hda_priv.h" #include "hda_controller.h" #define CREATE_TRACE_POINTS @@ -259,11 +257,18 @@ static void azx_timecounter_init(struct snd_pcm_substream *substream, tc->cycle_last = last; } +static inline struct hda_pcm_stream * +to_hda_pcm_stream(struct snd_pcm_substream *substream) +{ + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); + return &apcm->info->stream[substream->stream]; +} + static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream, u64 nsec) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); u64 codec_frames, codec_nsecs; if (!hinfo->ops.get_delay) @@ -399,7 +404,7 @@ static int azx_setup_periods(struct azx *chip, static int azx_pcm_close(struct snd_pcm_substream *substream) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + 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; @@ -410,9 +415,11 @@ static int azx_pcm_close(struct snd_pcm_substream *substream) azx_dev->running = 0; spin_unlock_irqrestore(&chip->reg_lock, flags); azx_release_device(azx_dev); - hinfo->ops.close(hinfo, apcm->codec, substream); + if (hinfo->ops.close) + hinfo->ops.close(hinfo, apcm->codec, substream); snd_hda_power_down(apcm->codec); mutex_unlock(&chip->open_mutex); + snd_hda_codec_pcm_put(apcm->info); return 0; } @@ -441,7 +448,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream) struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx_dev *azx_dev = get_azx_dev(substream); struct azx *chip = apcm->chip; - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); int err; /* reset BDL address */ @@ -468,7 +475,7 @@ static int azx_pcm_prepare(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); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + 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; int err; @@ -708,7 +715,7 @@ unsigned int azx_get_position(struct azx *chip, if (substream->runtime) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[stream]; + struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); if (chip->get_delay[stream]) delay += chip->get_delay[stream](chip, azx_dev, pos); @@ -732,17 +739,32 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) azx_get_position(chip, azx_dev)); } -static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream, - struct timespec *ts) +static int azx_get_time_info(struct snd_pcm_substream *substream, + struct timespec *system_ts, struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report) { struct azx_dev *azx_dev = get_azx_dev(substream); u64 nsec; - nsec = timecounter_read(&azx_dev->azx_tc); - nsec = div_u64(nsec, 3); /* can be optimized */ - nsec = azx_adjust_codec_delay(substream, nsec); + if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && + (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) { + + snd_pcm_gettime(substream->runtime, system_ts); - *ts = ns_to_timespec(nsec); + nsec = timecounter_read(&azx_dev->azx_tc); + nsec = div_u64(nsec, 3); /* can be optimized */ + if (audio_tstamp_config->report_delay) + nsec = azx_adjust_codec_delay(substream, nsec); + + *audio_ts = ns_to_timespec(nsec); + + audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; + audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */ + audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */ + + } else + audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; return 0; } @@ -756,7 +778,8 @@ static struct snd_pcm_hardware azx_pcm_hw = { /* SNDRV_PCM_INFO_RESUME |*/ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_HAS_WALL_CLOCK | + SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ + SNDRV_PCM_INFO_HAS_LINK_ATIME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, @@ -775,7 +798,7 @@ static struct snd_pcm_hardware azx_pcm_hw = { static int azx_pcm_open(struct snd_pcm_substream *substream) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev; struct snd_pcm_runtime *runtime = substream->runtime; @@ -783,11 +806,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) int err; int buff_step; + snd_hda_codec_pcm_get(apcm->info); mutex_lock(&chip->open_mutex); azx_dev = azx_assign_device(chip, substream); if (azx_dev == NULL) { - mutex_unlock(&chip->open_mutex); - return -EBUSY; + err = -EBUSY; + goto unlock; } runtime->hw = azx_pcm_hw; runtime->hw.channels_min = hinfo->channels_min; @@ -821,13 +845,14 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) buff_step); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, buff_step); - snd_hda_power_up_d3wait(apcm->codec); - err = hinfo->ops.open(hinfo, apcm->codec, substream); + snd_hda_power_up(apcm->codec); + if (hinfo->ops.open) + err = hinfo->ops.open(hinfo, apcm->codec, substream); + else + err = -ENODEV; if (err < 0) { azx_release_device(azx_dev); - snd_hda_power_down(apcm->codec); - mutex_unlock(&chip->open_mutex); - return err; + goto powerdown; } snd_pcm_limit_hw_rates(runtime); /* sanity check */ @@ -836,16 +861,18 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) snd_BUG_ON(!runtime->hw.formats) || snd_BUG_ON(!runtime->hw.rates)) { azx_release_device(azx_dev); - hinfo->ops.close(hinfo, apcm->codec, substream); - snd_hda_power_down(apcm->codec); - mutex_unlock(&chip->open_mutex); - return -EINVAL; + if (hinfo->ops.close) + hinfo->ops.close(hinfo, apcm->codec, substream); + err = -EINVAL; + goto powerdown; } - /* disable WALLCLOCK timestamps for capture streams + /* disable LINK_ATIME timestamps for capture streams until we figure out how to handle digital inputs */ - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */ + runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME; + } spin_lock_irqsave(&chip->reg_lock, flags); azx_dev->substream = substream; @@ -856,6 +883,13 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) snd_pcm_set_sync(substream); mutex_unlock(&chip->open_mutex); return 0; + + powerdown: + snd_hda_power_down(apcm->codec); + unlock: + mutex_unlock(&chip->open_mutex); + snd_hda_codec_pcm_put(apcm->info); + return err; } static int azx_pcm_mmap(struct snd_pcm_substream *substream, @@ -877,7 +911,7 @@ static struct snd_pcm_ops azx_pcm_ops = { .prepare = azx_pcm_prepare, .trigger = azx_pcm_trigger, .pointer = azx_pcm_pointer, - .wall_clock = azx_get_wallclock_tstamp, + .get_time_info = azx_get_time_info, .mmap = azx_pcm_mmap, .page = snd_pcm_sgbuf_ops_page, }; @@ -887,6 +921,7 @@ static void azx_pcm_free(struct snd_pcm *pcm) struct azx_pcm *apcm = pcm->private_data; if (apcm) { list_del(&apcm->list); + apcm->info->pcm = NULL; kfree(apcm); } } @@ -923,6 +958,7 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, apcm->chip = chip; apcm->pcm = pcm; apcm->codec = codec; + apcm->info = cpcm; pcm->private_data = apcm; pcm->private_free = azx_pcm_free; if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM) @@ -930,7 +966,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, list_add_tail(&apcm->list, &chip->pcm_list); cpcm->pcm = pcm; for (s = 0; s < 2; s++) { - apcm->hinfo[s] = &cpcm->stream[s]; if (cpcm->stream[s].substreams) snd_pcm_set_ops(pcm, s, &azx_pcm_ops); } @@ -941,9 +976,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, chip->card->dev, size, MAX_PREALLOC_SIZE); - /* link to codec */ - for (s = 0; s < 2; s++) - pcm->streams[s].dev.parent = &codec->dev; return 0; } @@ -952,14 +984,9 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, */ static int azx_alloc_cmd_io(struct azx *chip) { - int err; - /* single page (at least 4096 bytes) must suffice for both ringbuffes */ - err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, - PAGE_SIZE, &chip->rb); - if (err < 0) - dev_err(chip->card->dev, "cannot allocate CORB/RIRB\n"); - return err; + return chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, + PAGE_SIZE, &chip->rb); } static void azx_init_cmd_io(struct azx *chip) @@ -1445,7 +1472,6 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus, int azx_alloc_stream_pages(struct azx *chip) { int i, err; - struct snd_card *card = chip->card; for (i = 0; i < chip->num_streams; i++) { dsp_lock_init(&chip->azx_dev[i]); @@ -1453,18 +1479,14 @@ int azx_alloc_stream_pages(struct azx *chip) err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, BDL_SIZE, &chip->azx_dev[i].bdl); - if (err < 0) { - dev_err(card->dev, "cannot allocate BDL\n"); + 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) { - dev_err(card->dev, "cannot allocate posbuf\n"); + if (err < 0) return -ENOMEM; - } /* allocate CORB/RIRB */ err = azx_alloc_cmd_io(chip); @@ -1676,7 +1698,7 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) int i; #ifdef CONFIG_PM - if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME) + if (azx_has_pm_runtime(chip)) if (!pm_runtime_active(chip->card->dev)) return IRQ_NONE; #endif @@ -1742,12 +1764,12 @@ static int probe_codec(struct azx *chip, int addr) (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res; - mutex_lock(&chip->bus->cmd_mutex); + mutex_lock(&chip->bus->core.cmd_mutex); chip->probing = 1; azx_send_cmd(chip->bus, cmd); res = azx_get_response(chip->bus, addr); chip->probing = 0; - mutex_unlock(&chip->bus->cmd_mutex); + mutex_unlock(&chip->bus->core.cmd_mutex); if (res == -1) return -EIO; dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr); @@ -1761,34 +1783,11 @@ static void azx_bus_reset(struct hda_bus *bus) bus->in_reset = 1; azx_stop_chip(chip); azx_init_chip(chip, true); -#ifdef CONFIG_PM - if (chip->initialized) { - struct azx_pcm *p; - list_for_each_entry(p, &chip->pcm_list, list) - snd_pcm_suspend_all(p->pcm); - snd_hda_suspend(chip->bus); - snd_hda_resume(chip->bus); - } -#endif + if (chip->initialized) + snd_hda_bus_reset(chip->bus); bus->in_reset = 0; } -#ifdef CONFIG_PM -/* power-up/down the controller */ -static void azx_power_notify(struct hda_bus *bus, bool power_up) -{ - struct azx *chip = bus->private_data; - - if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) - return; - - if (power_up) - pm_runtime_get_sync(chip->card->dev); - else - pm_runtime_put_sync(chip->card->dev); -} -#endif - static int get_jackpoll_interval(struct azx *chip) { int i; @@ -1810,41 +1809,59 @@ static int get_jackpoll_interval(struct azx *chip) return j; } -/* Codec initialization */ -int azx_codec_create(struct azx *chip, const char *model, - unsigned int max_slots, - int *power_save_to) -{ - struct hda_bus_template bus_temp; - int c, codecs, err; - - memset(&bus_temp, 0, sizeof(bus_temp)); - bus_temp.private_data = chip; - bus_temp.modelname = model; - bus_temp.pci = chip->pci; - bus_temp.ops.command = azx_send_cmd; - bus_temp.ops.get_response = azx_get_response; - bus_temp.ops.attach_pcm = azx_attach_pcm_stream; - bus_temp.ops.bus_reset = azx_bus_reset; -#ifdef CONFIG_PM - bus_temp.power_save = power_save_to; - bus_temp.ops.pm_notify = azx_power_notify; -#endif +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 - bus_temp.ops.load_dsp_prepare = azx_load_dsp_prepare; - bus_temp.ops.load_dsp_trigger = azx_load_dsp_trigger; - bus_temp.ops.load_dsp_cleanup = azx_load_dsp_cleanup; + .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) +{ + struct hda_bus *bus; + int err; - err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus); + err = snd_hda_bus_new(chip->card, &bus); if (err < 0) return err; + chip->bus = bus; + bus->private_data = chip; + bus->pci = chip->pci; + bus->modelname = model; + bus->ops = bus_ops; + if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) { dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n"); - chip->bus->needs_damn_long_delay = 1; + bus->needs_damn_long_delay = 1; + } + + /* AMD chipsets often cause the communication stalls upon certain + * sequence like the pin-detection. It seems that forcing the synced + * access works around the stall. Grrr... + */ + if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) { + dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n"); + bus->core.sync_write = 1; + bus->allow_bus_reset = 1; } + return 0; +} +EXPORT_SYMBOL_GPL(azx_bus_create); + +/* Probe codecs */ +int azx_probe_codecs(struct azx *chip, unsigned int max_slots) +{ + struct hda_bus *bus = chip->bus; + int c, codecs, err; + codecs = 0; if (!max_slots) max_slots = AZX_DEFAULT_CODECS; @@ -1872,21 +1889,11 @@ int azx_codec_create(struct azx *chip, const char *model, } } - /* AMD chipsets often cause the communication stalls upon certain - * sequence like the pin-detection. It seems that forcing the synced - * access works around the stall. Grrr... - */ - if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) { - dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n"); - chip->bus->sync_write = 1; - chip->bus->allow_bus_reset = 1; - } - /* Then create codec instances */ for (c = 0; c < max_slots; c++) { if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { struct hda_codec *codec; - err = snd_hda_codec_new(chip->bus, c, &codec); + err = snd_hda_codec_new(bus, bus->card, c, &codec); if (err < 0) continue; codec->jackpoll_interval = get_jackpoll_interval(chip); @@ -1900,26 +1907,19 @@ int azx_codec_create(struct azx *chip, const char *model, } return 0; } -EXPORT_SYMBOL_GPL(azx_codec_create); +EXPORT_SYMBOL_GPL(azx_probe_codecs); /* configure each codec instance */ int azx_codec_configure(struct azx *chip) { struct hda_codec *codec; - list_for_each_entry(codec, &chip->bus->codec_list, list) { + list_for_each_codec(codec, chip->bus) { snd_hda_codec_configure(codec); } return 0; } EXPORT_SYMBOL_GPL(azx_codec_configure); -/* mixer creation - all stuff is implemented in hda module */ -int azx_mixer_create(struct azx *chip) -{ - return snd_hda_build_controls(chip->bus); -} -EXPORT_SYMBOL_GPL(azx_mixer_create); - static bool is_input_stream(struct azx *chip, unsigned char index) { @@ -1966,30 +1966,5 @@ int azx_init_stream(struct azx *chip) } EXPORT_SYMBOL_GPL(azx_init_stream); -/* - * reboot notifier for hang-up problem at power-down - */ -static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf) -{ - struct azx *chip = container_of(nb, struct azx, reboot_notifier); - snd_hda_bus_reboot_notify(chip->bus); - azx_stop_chip(chip); - return NOTIFY_OK; -} - -void azx_notifier_register(struct azx *chip) -{ - chip->reboot_notifier.notifier_call = azx_halt; - register_reboot_notifier(&chip->reboot_notifier); -} -EXPORT_SYMBOL_GPL(azx_notifier_register); - -void azx_notifier_unregister(struct azx *chip) -{ - if (chip->reboot_notifier.notifier_call) - unregister_reboot_notifier(&chip->reboot_notifier); -} -EXPORT_SYMBOL_GPL(azx_notifier_unregister); - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Common HDA driver functions"); diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index c90d10fd4d8f..be1b7ded8d82 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -15,10 +15,396 @@ #ifndef __SOUND_HDA_CONTROLLER_H #define __SOUND_HDA_CONTROLLER_H +#include <linux/timecounter.h> +#include <linux/interrupt.h> #include <sound/core.h> +#include <sound/pcm.h> #include <sound/initval.h> #include "hda_codec.h" -#include "hda_priv.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_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 */ +#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */ +#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */ +#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */ +#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */ +#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */ +#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */ +#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ +#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */ +#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */ +#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ +#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ +#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ +#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */ +/* 22 unused */ +#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ +#define AZX_DCAPS_REVERSE_ASSIGN (1 << 24) /* Assign devices in reverse order */ +#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ +#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ +#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ +#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ +#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ +#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ + +enum { + AZX_SNOOP_TYPE_NONE, + AZX_SNOOP_TYPE_SCH, + AZX_SNOOP_TYPE_ATI, + 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; + unsigned int irq_pending:1; + unsigned int prepared:1; + unsigned int locked:1; + /* + * For VIA: + * A flag to ensure DMA position is 0 + * when link position is not greater than FIFO size + */ + 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 */ +}; + +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); + int (*substream_free_pages)(struct azx *chip, + struct snd_pcm_substream *substream); + void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream, + struct vm_area_struct *area); + /* Check if current position is acceptable */ + int (*position_check)(struct azx *chip, struct azx_dev *azx_dev); +}; + +struct azx_pcm { + struct azx *chip; + struct snd_pcm *pcm; + struct hda_codec *codec; + struct hda_pcm *info; + struct list_head list; +}; + +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 snd_card *card; + struct pci_dev *pci; + int dev_index; + + /* chip type specific */ + int driver_type; + unsigned int driver_caps; + int playback_streams; + int playback_index_offset; + int capture_streams; + int capture_index_offset; + int num_streams; + const int *jackpoll_ms; /* per-card jack poll interval */ + + /* Register interaction. */ + const struct hda_controller_ops *ops; + + /* position adjustment callbacks */ + 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 + + /* flags */ + 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; + unsigned int probing:1; /* codec probing phase */ + unsigned int snoop:1; + unsigned int align_buffer_size:1; + 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 +}; + +#ifdef CONFIG_X86 +#define azx_snoop(chip) ((chip)->snoop) +#else +#define azx_snoop(chip) true +#endif + +/* + * macros for easy use + */ + +#define azx_writel(chip, reg, value) \ + ((chip)->ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg)) +#define azx_readl(chip, reg) \ + ((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg)) +#define azx_writew(chip, reg, value) \ + ((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg)) +#define azx_readw(chip, reg) \ + ((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg)) +#define azx_writeb(chip, reg, value) \ + ((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg)) +#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)) + +#define azx_has_pm_runtime(chip) \ + (!AZX_DCAPS_PM_RUNTIME || ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME)) /* PCM setup */ static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream) @@ -43,14 +429,9 @@ void azx_enter_link_reset(struct azx *chip); irqreturn_t azx_interrupt(int irq, void *dev_id); /* Codec interface */ -int azx_codec_create(struct azx *chip, const char *model, - unsigned int max_slots, - int *power_save_to); +int azx_bus_create(struct azx *chip, const char *model); +int azx_probe_codecs(struct azx *chip, unsigned int max_slots); int azx_codec_configure(struct azx *chip); -int azx_mixer_create(struct azx *chip); int azx_init_stream(struct azx *chip); -void azx_notifier_register(struct azx *chip); -void azx_notifier_unregister(struct azx *chip); - #endif /* __SOUND_HDA_CONTROLLER_H */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8ec5289f8e05..3d2597b7037b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -140,6 +140,9 @@ static void parse_user_hints(struct hda_codec *codec) val = snd_hda_get_bool_hint(codec, "single_adc_amp"); if (val >= 0) codec->single_adc_amp = !!val; + val = snd_hda_get_bool_hint(codec, "power_save_node"); + if (val >= 0) + codec->power_save_node = !!val; val = snd_hda_get_bool_hint(codec, "auto_mute"); if (val >= 0) @@ -648,12 +651,24 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, unsigned int dir, unsigned int idx) { struct hda_gen_spec *spec = codec->spec; + int type = get_wcaps_type(get_wcaps(codec, nid)); int i, n; + if (nid == codec->core.afg) + return true; + for (n = 0; n < spec->paths.used; n++) { struct nid_path *path = snd_array_elem(&spec->paths, n); if (!path->active) continue; + if (codec->power_save_node) { + if (!path->stream_enabled) + continue; + /* ignore unplugged paths except for DAC/ADC */ + if (!(path->pin_enabled || path->pin_fixed) && + type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN) + continue; + } for (i = 0; i < path->depth; i++) { if (path->path[i] == nid) { if (dir == HDA_OUTPUT || path->idx[i] == idx) @@ -807,6 +822,44 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, } } +/* sync power of each widget in the the given path */ +static hda_nid_t path_power_update(struct hda_codec *codec, + struct nid_path *path, + bool allow_powerdown) +{ + hda_nid_t nid, changed = 0; + int i, state; + + for (i = 0; i < path->depth; i++) { + nid = path->path[i]; + if (!(get_wcaps(codec, nid) & AC_WCAP_POWER)) + continue; + if (nid == codec->core.afg) + continue; + if (!allow_powerdown || is_active_nid_for_any(codec, nid)) + state = AC_PWRST_D0; + else + state = AC_PWRST_D3; + if (!snd_hda_check_power_state(codec, nid, state)) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_POWER_STATE, state); + changed = nid; + if (state == AC_PWRST_D0) + snd_hdac_regmap_sync_node(&codec->core, nid); + } + } + return changed; +} + +/* do sync with the last power state change */ +static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid) +{ + if (nid) { + msleep(10); + snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + } +} + /** * snd_hda_activate_path - activate or deactivate the given path * @codec: the HDA codec @@ -825,15 +878,13 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, if (!enable) path->active = false; + /* make sure the widget is powered up */ + if (enable && (spec->power_down_unused || codec->power_save_node)) + path_power_update(codec, path, codec->power_save_node); + for (i = path->depth - 1; i >= 0; i--) { hda_nid_t nid = path->path[i]; - if (enable && spec->power_down_unused) { - /* make sure the widget is powered up */ - if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - } + if (enable && path->multi[i]) snd_hda_codec_update_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, @@ -853,28 +904,10 @@ EXPORT_SYMBOL_GPL(snd_hda_activate_path); static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path) { struct hda_gen_spec *spec = codec->spec; - bool changed = false; - int i; - if (!spec->power_down_unused || path->active) + if (!(spec->power_down_unused || codec->power_save_node) || path->active) return; - - for (i = 0; i < path->depth; i++) { - hda_nid_t nid = path->path[i]; - if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D3) && - !is_active_nid_for_any(codec, nid)) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - changed = true; - } - } - - if (changed) { - msleep(10); - snd_hda_codec_read(codec, path->path[0], 0, - AC_VERB_GET_POWER_STATE, 0); - } + sync_power_state_change(codec, path_power_update(codec, path, true)); } /* turn on/off EAPD on the given pin */ @@ -1574,6 +1607,7 @@ static int check_aamix_out_path(struct hda_codec *codec, int path_idx) return 0; /* print_nid_path(codec, "output-aamix", path); */ path->active = false; /* unused as default */ + path->pin_fixed = true; /* static route */ return snd_hda_get_path_idx(codec, path); } @@ -1863,12 +1897,11 @@ static void debug_show_configs(struct hda_codec *codec, static void fill_all_dac_nids(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - int i; - hda_nid_t nid = codec->start_nid; + hda_nid_t nid; spec->num_all_dacs = 0; memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) continue; if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { @@ -2998,6 +3031,7 @@ static int new_analog_input(struct hda_codec *codec, int input_idx, } path->active = true; + path->stream_enabled = true; /* no DAC/ADC involved */ err = add_loopback_list(spec, mix_nid, idx); if (err < 0) return err; @@ -3009,6 +3043,8 @@ static int new_analog_input(struct hda_codec *codec, int input_idx, if (path) { print_nid_path(codec, "loopback-merge", path); path->active = true; + path->pin_fixed = true; /* static route */ + path->stream_enabled = true; /* no DAC/ADC involved */ spec->loopback_merge_path = snd_hda_get_path_idx(codec, path); } @@ -3030,10 +3066,9 @@ static int fill_adc_nids(struct hda_codec *codec) hda_nid_t nid; hda_nid_t *adc_nids = spec->adc_nids; int max_nums = ARRAY_SIZE(spec->adc_nids); - int i, nums = 0; + int nums = 0; - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int caps = get_wcaps(codec, nid); int type = get_wcaps_type(caps); @@ -3346,11 +3381,6 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, imux = &spec->input_mux; adc_idx = kcontrol->id.index; mutex_lock(&codec->control_mutex); - /* we use the cache-only update at first since multiple input paths - * may shared the same amp; by updating only caches, the redundant - * writes to hardware can be reduced. - */ - codec->cached_write = 1; for (i = 0; i < imux->num_items; i++) { path = get_input_path(codec, adc_idx, i); if (!path || !path->ctls[type]) @@ -3358,12 +3388,9 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, kcontrol->private_value = path->ctls[type]; err = func(kcontrol, ucontrol); if (err < 0) - goto error; + break; } - error: - codec->cached_write = 0; mutex_unlock(&codec->control_mutex); - snd_hda_codec_flush_cache(codec); /* flush the updates */ if (err >= 0 && spec->cap_sync_hook) spec->cap_sync_hook(codec, kcontrol, ucontrol); return err; @@ -3810,6 +3837,7 @@ static void parse_digital(struct hda_codec *codec) continue; print_nid_path(codec, "digout", path); path->active = true; + path->pin_fixed = true; /* no jack detection */ spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); set_pin_target(codec, pin, PIN_OUT, false); if (!nums) { @@ -3826,8 +3854,7 @@ static void parse_digital(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) { pin = spec->autocfg.dig_in_pin; - dig_nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + for_each_hda_codec_node(dig_nid, codec) { unsigned int wcaps = get_wcaps(codec, dig_nid); if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) continue; @@ -3837,6 +3864,7 @@ static void parse_digital(struct hda_codec *codec) if (path) { print_nid_path(codec, "digin", path); path->active = true; + path->pin_fixed = true; /* no jack */ spec->dig_in_nid = dig_nid; spec->digin_path = snd_hda_get_path_idx(codec, path); set_pin_target(codec, pin, PIN_IN, false); @@ -3896,6 +3924,238 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, return 1; } +/* power up/down widgets in the all paths that match with the given NID + * as terminals (either start- or endpoint) + * + * returns the last changed NID, or zero if unchanged. + */ +static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid, + int pin_state, int stream_state) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t last, changed = 0; + struct nid_path *path; + int n; + + for (n = 0; n < spec->paths.used; n++) { + path = snd_array_elem(&spec->paths, n); + if (path->path[0] == nid || + path->path[path->depth - 1] == nid) { + bool pin_old = path->pin_enabled; + bool stream_old = path->stream_enabled; + + if (pin_state >= 0) + path->pin_enabled = pin_state; + if (stream_state >= 0) + path->stream_enabled = stream_state; + if ((!path->pin_fixed && path->pin_enabled != pin_old) + || path->stream_enabled != stream_old) { + last = path_power_update(codec, path, true); + if (last) + changed = last; + } + } + } + return changed; +} + +/* check the jack status for power control */ +static bool detect_pin_state(struct hda_codec *codec, hda_nid_t pin) +{ + if (!is_jack_detectable(codec, pin)) + return true; + return snd_hda_jack_detect_state(codec, pin) != HDA_JACK_NOT_PRESENT; +} + +/* power up/down the paths of the given pin according to the jack state; + * power = 0/1 : only power up/down if it matches with the jack state, + * < 0 : force power up/down to follow the jack sate + * + * returns the last changed NID, or zero if unchanged. + */ +static hda_nid_t set_pin_power_jack(struct hda_codec *codec, hda_nid_t pin, + int power) +{ + bool on; + + if (!codec->power_save_node) + return 0; + + on = detect_pin_state(codec, pin); + + if (power >= 0 && on != power) + return 0; + return set_path_power(codec, pin, on, -1); +} + +static void pin_power_callback(struct hda_codec *codec, + struct hda_jack_callback *jack, + bool on) +{ + if (jack && jack->tbl->nid) + sync_power_state_change(codec, + set_pin_power_jack(codec, jack->tbl->nid, on)); +} + +/* callback only doing power up -- called at first */ +static void pin_power_up_callback(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + pin_power_callback(codec, jack, true); +} + +/* callback only doing power down -- called at last */ +static void pin_power_down_callback(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + pin_power_callback(codec, jack, false); +} + +/* set up the power up/down callbacks */ +static void add_pin_power_ctls(struct hda_codec *codec, int num_pins, + const hda_nid_t *pins, bool on) +{ + int i; + hda_jack_callback_fn cb = + on ? pin_power_up_callback : pin_power_down_callback; + + for (i = 0; i < num_pins && pins[i]; i++) { + if (is_jack_detectable(codec, pins[i])) + snd_hda_jack_detect_enable_callback(codec, pins[i], cb); + else + set_path_power(codec, pins[i], true, -1); + } +} + +/* enabled power callback to each available I/O pin with jack detections; + * the digital I/O pins are excluded because of the unreliable detectsion + */ +static void add_all_pin_power_ctls(struct hda_codec *codec, bool on) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + if (!codec->power_save_node) + return; + add_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins, on); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + add_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins, on); + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + add_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, on); + for (i = 0; i < cfg->num_inputs; i++) + add_pin_power_ctls(codec, 1, &cfg->inputs[i].pin, on); +} + +/* sync path power up/down with the jack states of given pins */ +static void sync_pin_power_ctls(struct hda_codec *codec, int num_pins, + const hda_nid_t *pins) +{ + int i; + + for (i = 0; i < num_pins && pins[i]; i++) + if (is_jack_detectable(codec, pins[i])) + set_pin_power_jack(codec, pins[i], -1); +} + +/* sync path power up/down with pins; called at init and resume */ +static void sync_all_pin_power_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + if (!codec->power_save_node) + return; + sync_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + sync_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins); + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + sync_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins); + for (i = 0; i < cfg->num_inputs; i++) + sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin); +} + +/* add fake paths if not present yet */ +static int add_fake_paths(struct hda_codec *codec, hda_nid_t nid, + int num_pins, const hda_nid_t *pins) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + int i; + + for (i = 0; i < num_pins; i++) { + if (!pins[i]) + break; + if (get_nid_path(codec, nid, pins[i], 0)) + continue; + path = snd_array_new(&spec->paths); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + path->depth = 2; + path->path[0] = nid; + path->path[1] = pins[i]; + path->active = true; + } + return 0; +} + +/* create fake paths to all outputs from beep */ +static int add_fake_beep_paths(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid = spec->beep_nid; + int err; + + if (!codec->power_save_node || !nid) + return 0; + err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins); + if (err < 0) + return err; + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins); + if (err < 0) + return err; + } + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = add_fake_paths(codec, nid, cfg->speaker_outs, + cfg->speaker_pins); + if (err < 0) + return err; + } + return 0; +} + +/* power up/down beep widget and its output paths */ +static void beep_power_hook(struct hda_beep *beep, bool on) +{ + set_path_power(beep->codec, beep->nid, -1, on); +} + +/** + * snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0 + * @codec: the HDA codec + * @pin: NID of pin to fix + */ +int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + + path = snd_array_new(&spec->paths); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + path->depth = 1; + path->path[0] = pin; + path->active = true; + path->pin_fixed = true; + path->stream_enabled = true; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power); /* * Jack detections for HP auto-mute and mic-switch @@ -3933,6 +4193,10 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, if (!nid) break; + oldval = snd_hda_codec_get_pin_target(codec, nid); + if (oldval & PIN_IN) + continue; /* no mute for inputs */ + if (spec->auto_mute_via_amp) { struct nid_path *path; hda_nid_t mute_nid; @@ -3947,29 +4211,32 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, spec->mute_bits |= (1ULL << mute_nid); else spec->mute_bits &= ~(1ULL << mute_nid); - set_pin_eapd(codec, nid, !mute); continue; + } else { + /* don't reset VREF value in case it's controlling + * the amp (see alc861_fixup_asus_amp_vref_0f()) + */ + if (spec->keep_vref_in_automute) + val = oldval & ~PIN_HP; + else + val = 0; + if (!mute) + val |= oldval; + /* here we call update_pin_ctl() so that the pinctl is + * changed without changing the pinctl target value; + * the original target value will be still referred at + * the init / resume again + */ + update_pin_ctl(codec, nid, val); } - oldval = snd_hda_codec_get_pin_target(codec, nid); - if (oldval & PIN_IN) - continue; /* no mute for inputs */ - /* don't reset VREF value in case it's controlling - * the amp (see alc861_fixup_asus_amp_vref_0f()) - */ - if (spec->keep_vref_in_automute) - val = oldval & ~PIN_HP; - else - val = 0; - if (!mute) - val |= oldval; - /* here we call update_pin_ctl() so that the pinctl is changed - * without changing the pinctl target value; - * the original target value will be still referred at the - * init / resume again - */ - update_pin_ctl(codec, nid, val); set_pin_eapd(codec, nid, !mute); + if (codec->power_save_node) { + bool on = !mute; + if (on) + on = detect_pin_state(codec, nid); + set_path_power(codec, nid, on, -1); + } } } @@ -4436,7 +4703,11 @@ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) { - if (power_state != AC_PWRST_D0 || nid == codec->afg) + struct hda_gen_spec *spec = codec->spec; + + if (!spec->power_down_unused && !codec->power_save_node) + return power_state; + if (power_state != AC_PWRST_D0 || nid == codec->core.afg) return power_state; if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER) return power_state; @@ -4466,6 +4737,21 @@ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) } /** + * snd_hda_gen_stream_pm - Stream power management callback + * @codec: the HDA codec + * @nid: audio widget + * @on: power on/off flag + * + * Set this in patch_ops.stream_pm. Only valid with power_save_node flag. + */ +void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on) +{ + if (codec->power_save_node) + set_path_power(codec, nid, -1, on); +} +EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm); + +/** * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and * set up the hda_gen_spec * @codec: the HDA codec @@ -4549,6 +4835,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, if (err < 0) return err; + /* add power-down pin callbacks at first */ + add_all_pin_power_ctls(codec, false); + spec->const_channel_count = spec->ext_channel_count; /* check the multiple speaker and headphone pins */ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) @@ -4618,6 +4907,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, } } + /* add power-up pin callbacks at last */ + add_all_pin_power_ctls(codec, true); + /* mute all aamix input initially */ if (spec->mixer_nid) mute_all_mixer_nid(codec, spec->mixer_nid); @@ -4625,13 +4917,20 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, dig_only: parse_digital(codec); - if (spec->power_down_unused) - codec->power_filter = snd_hda_gen_path_power_filter; + if (spec->power_down_unused || codec->power_save_node) + if (!codec->power_filter) + codec->power_filter = snd_hda_gen_path_power_filter; if (!spec->no_analog && spec->beep_nid) { err = snd_hda_attach_beep_device(codec, spec->beep_nid); if (err < 0) return err; + if (codec->beep && codec->power_save_node) { + err = add_fake_beep_paths(codec); + if (err < 0) + return err; + codec->beep->power_hook = beep_power_hook; + } } return 1; @@ -4675,7 +4974,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) err = snd_hda_create_dig_out_ctls(codec, spec->multiout.dig_out_nid, spec->multiout.dig_out_nid, - spec->pcm_rec[1].pcm_type); + spec->pcm_rec[1]->pcm_type); if (err < 0) return err; if (!spec->no_analog) { @@ -5137,6 +5436,33 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, strlcat(str, sfx, len); } +/* copy PCM stream info from @default_str, and override non-NULL entries + * from @spec_str and @nid + */ +static void setup_pcm_stream(struct hda_pcm_stream *str, + const struct hda_pcm_stream *default_str, + const struct hda_pcm_stream *spec_str, + hda_nid_t nid) +{ + *str = *default_str; + if (nid) + str->nid = nid; + if (spec_str) { + if (spec_str->substreams) + str->substreams = spec_str->substreams; + if (spec_str->channels_min) + str->channels_min = spec_str->channels_min; + if (spec_str->channels_max) + str->channels_max = spec_str->channels_max; + if (spec_str->rates) + str->rates = spec_str->rates; + if (spec_str->formats) + str->formats = spec_str->formats; + if (spec_str->maxbps) + str->maxbps = spec_str->maxbps; + } +} + /** * snd_hda_gen_build_pcms - build PCM streams based on the parsed results * @codec: the HDA codec @@ -5146,27 +5472,25 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, int snd_hda_gen_build_pcms(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - const struct hda_pcm_stream *p; + struct hda_pcm *info; bool have_multi_adcs; - codec->num_pcms = 1; - codec->pcm_info = info; - if (spec->no_analog) goto skip_analog; fill_pcm_stream_name(spec->stream_name_analog, sizeof(spec->stream_name_analog), - " Analog", codec->chip_name); - info->name = spec->stream_name_analog; + " Analog", codec->core.chip_name); + info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog); + if (!info) + return -ENOMEM; + spec->pcm_rec[0] = info; if (spec->multiout.num_dacs > 0) { - p = spec->stream_analog_playback; - if (!p) - p = &pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], + &pcm_analog_playback, + spec->stream_analog_playback, + spec->multiout.dac_nids[0]); info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && @@ -5175,15 +5499,11 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) snd_pcm_2_1_chmaps; } if (spec->num_adc_nids) { - p = spec->stream_analog_capture; - if (!p) { - if (spec->dyn_adc_switch) - p = &dyn_adc_pcm_analog_capture; - else - p = &pcm_analog_capture; - } - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], + (spec->dyn_adc_switch ? + &dyn_adc_pcm_analog_capture : &pcm_analog_capture), + spec->stream_analog_capture, + spec->adc_nids[0]); } skip_analog: @@ -5191,29 +5511,27 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) if (spec->multiout.dig_out_nid || spec->dig_in_nid) { fill_pcm_stream_name(spec->stream_name_digital, sizeof(spec->stream_name_digital), - " Digital", codec->chip_name); - codec->num_pcms = 2; + " Digital", codec->core.chip_name); + info = snd_hda_codec_pcm_new(codec, "%s", + spec->stream_name_digital); + if (!info) + return -ENOMEM; codec->slave_dig_outs = spec->multiout.slave_dig_outs; - info = spec->pcm_rec + 1; - info->name = spec->stream_name_digital; + spec->pcm_rec[1] = info; if (spec->dig_out_type) info->pcm_type = spec->dig_out_type; else info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - p = spec->stream_digital_playback; - if (!p) - p = &pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - p = spec->stream_digital_capture; - if (!p) - p = &pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } + if (spec->multiout.dig_out_nid) + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], + &pcm_digital_playback, + spec->stream_digital_playback, + spec->multiout.dig_out_nid); + if (spec->dig_in_nid) + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], + &pcm_digital_capture, + spec->stream_digital_capture, + spec->dig_in_nid); } if (spec->no_analog) @@ -5228,35 +5546,30 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) if (spec->alt_dac_nid || have_multi_adcs) { fill_pcm_stream_name(spec->stream_name_alt_analog, sizeof(spec->stream_name_alt_analog), - " Alt Analog", codec->chip_name); - codec->num_pcms = 3; - info = spec->pcm_rec + 2; - info->name = spec->stream_name_alt_analog; - if (spec->alt_dac_nid) { - p = spec->stream_analog_alt_playback; - if (!p) - p = &pcm_analog_alt_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->alt_dac_nid; - } else { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; - } + " Alt Analog", codec->core.chip_name); + info = snd_hda_codec_pcm_new(codec, "%s", + spec->stream_name_alt_analog); + if (!info) + return -ENOMEM; + spec->pcm_rec[2] = info; + if (spec->alt_dac_nid) + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], + &pcm_analog_alt_playback, + spec->stream_analog_alt_playback, + spec->alt_dac_nid); + else + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], + &pcm_null_stream, NULL, 0); if (have_multi_adcs) { - p = spec->stream_analog_alt_capture; - if (!p) - p = &pcm_analog_alt_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->adc_nids[1]; + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], + &pcm_analog_alt_capture, + spec->stream_analog_alt_capture, + spec->adc_nids[1]); info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids - 1; } else { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], + &pcm_null_stream, NULL, 0); } } @@ -5452,8 +5765,6 @@ int snd_hda_gen_init(struct hda_codec *codec) snd_hda_apply_verbs(codec); - codec->cached_write = 1; - init_multi_out(codec); init_extra_out(codec); init_multi_io(codec); @@ -5464,10 +5775,12 @@ int snd_hda_gen_init(struct hda_codec *codec) clear_unsol_on_unused_pins(codec); + sync_all_pin_power_ctls(codec); + /* call init functions of standard auto-mute helpers */ update_automute_all(codec); - snd_hda_codec_flush_cache(codec); + regcache_sync(codec->core.regmap); if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) snd_hda_sync_vmaster_hook(&spec->vmaster_mute); @@ -5524,13 +5837,11 @@ static const struct hda_codec_ops generic_patch_ops = { #endif }; -/** +/* * snd_hda_parse_generic_codec - Generic codec parser * @codec: the HDA codec - * - * This should be called from the HDA codec core. */ -int snd_hda_parse_generic_codec(struct hda_codec *codec) +static int snd_hda_parse_generic_codec(struct hda_codec *codec) { struct hda_gen_spec *spec; int err; @@ -5556,7 +5867,17 @@ error: snd_hda_gen_free(codec); return err; } -EXPORT_SYMBOL_GPL(snd_hda_parse_generic_codec); + +static const struct hda_codec_preset snd_hda_preset_generic[] = { + { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec }, + {} /* terminator */ +}; + +static struct hda_codec_driver generic_driver = { + .preset = snd_hda_preset_generic, +}; + +module_hda_codec_driver(generic_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic HD-audio codec parser"); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 3d852660443a..56e4139b9032 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -46,7 +46,10 @@ struct nid_path { unsigned char idx[MAX_NID_PATH_DEPTH]; unsigned char multi[MAX_NID_PATH_DEPTH]; unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ - bool active; + bool active:1; /* activated by driver */ + bool pin_enabled:1; /* pins are enabled */ + bool pin_fixed:1; /* path with fixed pin */ + bool stream_enabled:1; /* stream is active */ }; /* mic/line-in auto switching entry */ @@ -144,7 +147,7 @@ struct hda_gen_spec { int const_channel_count; /* channel count for all */ /* PCM information */ - struct hda_pcm pcm_rec[3]; /* used in build_pcms() */ + struct hda_pcm *pcm_rec[3]; /* used in build_pcms() */ /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; @@ -340,5 +343,7 @@ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid); unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state); +void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on); +int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin); #endif /* __SOUND_HDA_GENERIC_H */ diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 11b5a42b4ec8..57df06e76968 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -101,7 +101,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec) int err; sprintf(hwname, "HDA Codec %d", codec->addr); - err = snd_hwdep_new(codec->bus->card, hwname, codec->addr, &hwdep); + err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep); if (err < 0) return err; codec->hwdep = hwdep; @@ -116,9 +116,6 @@ int snd_hda_create_hwdep(struct hda_codec *codec) hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; #endif - /* link to codec */ - hwdep->dev.parent = &codec->dev; - /* for sysfs */ hwdep->dev.groups = snd_hda_dev_attr_groups; dev_set_drvdata(&hwdep->dev, codec); diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c index 714894527e06..52a85d87c23c 100644 --- a/sound/pci/hda/hda_i915.c +++ b/sound/pci/hda/hda_i915.c @@ -22,7 +22,7 @@ #include <linux/component.h> #include <drm/i915_component.h> #include <sound/core.h> -#include "hda_priv.h" +#include "hda_controller.h" #include "hda_intel.h" /* Intel HSW/BDW display HDA controller Extended Mode registers. diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index a8a1e14272a1..e1c210515581 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -62,7 +62,6 @@ #include <linux/firmware.h> #include "hda_codec.h" #include "hda_controller.h" -#include "hda_priv.h" #include "hda_intel.h" /* position fix mode */ @@ -174,7 +173,6 @@ static struct kernel_param_ops param_ops_xint = { #define param_check_xint param_check_int static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; -static int *power_save_addr = &power_save; module_param(power_save, xint, 0644); MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " "(in second, 0 = disable)."); @@ -187,7 +185,7 @@ static bool power_save_controller = 1; module_param(power_save_controller, bool, 0644); MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); #else -static int *power_save_addr; +#define power_save 0 #endif /* CONFIG_PM */ static int align_buffer_size = -1; @@ -299,8 +297,12 @@ enum { AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ AZX_DCAPS_SNOOP_TYPE(SCH)) +#define AZX_DCAPS_INTEL_BRASWELL \ + (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_I915_POWERWELL) + #define AZX_DCAPS_INTEL_SKYLAKE \ - (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG) + (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG |\ + AZX_DCAPS_I915_POWERWELL) /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -530,10 +532,10 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) if (ok == 1) { azx_dev->irq_pending = 0; return ok; - } else if (ok == 0 && chip->bus && chip->bus->workq) { + } else if (ok == 0) { /* bogus IRQ, process it later */ azx_dev->irq_pending = 1; - queue_work(chip->bus->workq, &hda->irq_pending_work); + schedule_work(&hda->irq_pending_work); } return 0; } @@ -741,7 +743,6 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) { struct hda_intel *hda; struct azx *chip; - struct hda_codec *c; int prev = power_save; int ret = param_set_int(val, kp); @@ -753,8 +754,7 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) chip = &hda->chip; if (!chip->bus || chip->disabled) continue; - list_for_each_entry(c, &chip->bus->codec_list, list) - snd_hda_power_sync(c); + snd_hda_set_power_save(chip->bus, power_save * 1000); } mutex_unlock(&card_list_lock); return 0; @@ -773,7 +773,6 @@ static int azx_suspend(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; struct hda_intel *hda; - struct azx_pcm *p; if (!card) return 0; @@ -785,10 +784,6 @@ static int azx_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); azx_clear_irq_pending(chip); - list_for_each_entry(p, &chip->pcm_list, list) - snd_pcm_suspend_all(p->pcm); - if (chip->initialized) - snd_hda_suspend(chip->bus); azx_stop_chip(chip); azx_enter_link_reset(chip); if (chip->irq >= 0) { @@ -831,7 +826,6 @@ static int azx_resume(struct device *dev) azx_init_chip(chip, true); - snd_hda_resume(chip->bus); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } @@ -852,7 +846,7 @@ static int azx_runtime_suspend(struct device *dev) if (chip->disabled || hda->init_failed) return 0; - if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) + if (!azx_has_pm_runtime(chip)) return 0; /* enable controller wake up event */ @@ -885,7 +879,7 @@ static int azx_runtime_resume(struct device *dev) if (chip->disabled || hda->init_failed) return 0; - if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) + if (!azx_has_pm_runtime(chip)) return 0; if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { @@ -901,10 +895,10 @@ static int azx_runtime_resume(struct device *dev) bus = chip->bus; if (status && bus) { - list_for_each_entry(codec, &bus->codec_list, list) + list_for_each_codec(codec, bus) if (status & (1 << codec->addr)) - queue_delayed_work(codec->bus->workq, - &codec->jackpoll_work, codec->jackpoll_interval); + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); } /* disable controller Wake Up event*/ @@ -928,8 +922,8 @@ static int azx_runtime_idle(struct device *dev) if (chip->disabled || hda->init_failed) return 0; - if (!power_save_controller || - !(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) + if (!power_save_controller || !azx_has_pm_runtime(chip) || + chip->bus->core.codec_powered) return -EBUSY; return 0; @@ -1071,14 +1065,11 @@ static int azx_free(struct azx *chip) struct hda_intel *hda = container_of(chip, struct hda_intel, chip); int i; - if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME) - && chip->running) + if (azx_has_pm_runtime(chip) && chip->running) pm_runtime_get_noresume(&pci->dev); azx_del_card_list(chip); - azx_notifier_unregister(chip); - hda->init_failed = 1; /* to be sure */ complete_all(&hda->probe_wait); @@ -1394,7 +1385,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, hda = kzalloc(sizeof(*hda), GFP_KERNEL); if (!hda) { - dev_err(card->dev, "Cannot allocate hda\n"); pci_disable_device(pci); return -ENOMEM; } @@ -1575,10 +1565,8 @@ static int azx_first_init(struct azx *chip) 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) { - dev_err(card->dev, "cannot malloc azx_dev\n"); + if (!chip->azx_dev) return -ENOMEM; - } err = azx_alloc_stream_pages(chip); if (err < 0) @@ -1615,19 +1603,6 @@ static int azx_first_init(struct azx *chip) return 0; } -static void power_down_all_codecs(struct azx *chip) -{ -#ifdef CONFIG_PM - /* The codecs were powered up in snd_hda_codec_new(). - * Now all initialization done, so turn them down if possible - */ - struct hda_codec *codec; - list_for_each_entry(codec, &chip->bus->codec_list, list) { - snd_hda_power_down(codec); - } -#endif -} - #ifdef CONFIG_SND_HDA_PATCH_LOADER /* callback from request_firmware_nowait() */ static void azx_firmware_cb(const struct firmware *fw, void *context) @@ -1896,12 +1871,14 @@ static int azx_probe_continue(struct azx *chip) #endif /* create codec instances */ - err = azx_codec_create(chip, model[dev], - azx_max_codecs[chip->driver_type], - power_save_addr); + 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, @@ -1920,25 +1897,14 @@ static int azx_probe_continue(struct azx *chip) goto out_free; } - /* create PCM streams */ - err = snd_hda_build_pcms(chip->bus); - if (err < 0) - goto out_free; - - /* create mixer controls */ - err = azx_mixer_create(chip); - if (err < 0) - goto out_free; - err = snd_card_register(chip->card); if (err < 0) goto out_free; chip->running = 1; - power_down_all_codecs(chip); - azx_notifier_register(chip); azx_add_card_list(chip); - if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME) || hda->use_vga_switcheroo) + 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: @@ -1956,6 +1922,18 @@ static void azx_remove(struct pci_dev *pci) snd_card_free(card); } +static void azx_shutdown(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct azx *chip; + + if (!card) + return; + chip = card->private_data; + if (chip && chip->running) + azx_stop_chip(chip); +} + /* PCI IDs */ static const struct pci_device_id azx_ids[] = { /* CPT */ @@ -2017,7 +1995,7 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, /* Braswell */ { PCI_DEVICE(0x8086, 0x2284), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BRASWELL }, /* ICH6 */ { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, @@ -2178,6 +2156,7 @@ static struct pci_driver azx_driver = { .id_table = azx_ids, .probe = azx_probe, .remove = azx_remove, + .shutdown = azx_shutdown, .driver = { .pm = AZX_PM_OPS, }, diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h index 348611835476..d5231f7216a7 100644 --- a/sound/pci/hda/hda_intel.h +++ b/sound/pci/hda/hda_intel.h @@ -17,7 +17,7 @@ #define __SOUND_HDA_INTEL_H #include <drm/i915_component.h> -#include "hda_priv.h" +#include "hda_controller.h" struct hda_intel { struct azx chip; diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index e664307617bd..d7cfe7b8c32b 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -135,7 +135,7 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_INPUT_JACK /* free jack instances manually when clearing/reconfiguring */ if (!codec->bus->shutdown && jack->jack) - snd_device_free(codec->bus->card, jack->jack); + snd_device_free(codec->card, jack->jack); #endif for (cb = jack->callback; cb; cb = next) { next = cb->next; @@ -340,7 +340,7 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) if (!jack->kctl || jack->block_report) continue; state = get_jack_plug_state(jack->pin_sense); - snd_kctl_jack_report(codec->bus->card, jack->kctl, state); + snd_kctl_jack_report(codec->card, jack->kctl, state); #ifdef CONFIG_SND_HDA_INPUT_JACK if (jack->jack) snd_jack_report(jack->jack, @@ -412,11 +412,11 @@ static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, jack->phantom_jack = !!phantom_jack; state = snd_hda_jack_detect(codec, nid); - snd_kctl_jack_report(codec->bus->card, kctl, state); + 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->bus->card, name, jack->type, + err = snd_jack_new(codec->card, name, jack->type, &jack->jack); if (err < 0) return err; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 62658f2f8c9f..3b567f42296b 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -127,18 +127,16 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); #endif /* lowlevel accessor with caching; use carefully */ -int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index); -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val); +#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \ + snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx) +#define snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val) \ + snd_hdac_regmap_update_amp(&(codec)->core, nid, ch, dir, idx, mask, val) int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); -void snd_hda_codec_resume_amp(struct hda_codec *codec); - void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv); struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, @@ -150,6 +148,8 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, #define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \ __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL) int snd_hda_codec_reset(struct hda_codec *codec); +void snd_hda_codec_register(struct hda_codec *codec); +void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); enum { HDA_VMUTE_OFF, @@ -273,29 +273,6 @@ int snd_hda_add_imux_item(struct hda_codec *codec, int index, int *type_index_ret); /* - * Channel mode helper - */ -struct hda_channel_mode { - int channels; - const struct hda_verb *sequence; -}; - -int snd_hda_ch_mode_info(struct hda_codec *codec, - struct snd_ctl_elem_info *uinfo, - const struct hda_channel_mode *chmode, - int num_chmodes); -int snd_hda_ch_mode_get(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int max_channels); -int snd_hda_ch_mode_put(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int *max_channelsp); - -/* * Multi-channel / digital-out PCM helper */ @@ -351,12 +328,6 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout); /* - * generic codec parser - */ -int snd_hda_parse_generic_codec(struct hda_codec *codec); -int snd_hda_parse_hdmi_codec(struct hda_codec *codec); - -/* * generic proc interface */ #ifdef CONFIG_PROC_FS @@ -466,23 +437,6 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, const struct snd_hda_pin_quirk *pin_quirk, const struct hda_fixup *fixlist); - -/* - * unsolicited event handler - */ - -#define HDA_UNSOL_QUEUE_SIZE 64 - -struct hda_bus_unsolicited { - /* ring buffer */ - u32 queue[HDA_UNSOL_QUEUE_SIZE * 2]; - unsigned int rp, wp; - - /* workqueue */ - struct work_struct work; - struct hda_bus *bus; -}; - /* helper macros to retrieve pin default-config values */ #define get_defcfg_connect(cfg) \ ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) @@ -560,15 +514,18 @@ int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid); int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, unsigned int val); +#define for_each_hda_codec_node(nid, codec) \ + for ((nid) = (codec)->core.start_nid; (nid) < (codec)->core.end_nid; (nid)++) + /* * get widget capabilities */ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) { - if (nid < codec->start_nid || - nid >= codec->start_nid + codec->num_nodes) + if (nid < codec->core.start_nid || + nid >= codec->core.start_nid + codec->core.num_nodes) return 0; - return codec->wcaps[nid - codec->start_nid]; + return codec->wcaps[nid - codec->core.start_nid]; } /* get the widget type from widget capability bits */ @@ -592,17 +549,49 @@ static inline unsigned int get_wcaps_channels(u32 wcaps) static inline void snd_hda_override_wcaps(struct hda_codec *codec, hda_nid_t nid, u32 val) { - if (nid >= codec->start_nid && - nid < codec->start_nid + codec->num_nodes) - codec->wcaps[nid - codec->start_nid] = val; + if (nid >= codec->core.start_nid && + nid < codec->core.start_nid + codec->core.num_nodes) + codec->wcaps[nid - codec->core.start_nid] = val; } u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps); -u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, - unsigned int caps); +/** + * snd_hda_query_pin_caps - Query PIN capabilities + * @codec: the HD-auio codec + * @nid: the NID to query + * + * Query PIN capabilities for the given widget. + * Returns the obtained capability bits. + * + * When cap bits have been already read, this doesn't read again but + * returns the cached value. + */ +static inline u32 +snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + +} + +/** + * snd_hda_override_pin_caps - Override the pin capabilities + * @codec: the CODEC + * @nid: the NID to override + * @caps: the capability bits to set + * + * Override the cached PIN capabilitiy bits value by the given one. + * + * Returns zero if successful or a negative error code. + */ +static inline int +snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, + unsigned int caps) +{ + return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps); +} + bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int bits); @@ -800,9 +789,13 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); /* */ -#define codec_err(codec, fmt, args...) dev_err(&(codec)->dev, fmt, ##args) -#define codec_warn(codec, fmt, args...) dev_warn(&(codec)->dev, fmt, ##args) -#define codec_info(codec, fmt, args...) dev_info(&(codec)->dev, fmt, ##args) -#define codec_dbg(codec, fmt, args...) dev_dbg(&(codec)->dev, fmt, ##args) +#define codec_err(codec, fmt, args...) \ + dev_err(hda_codec_dev(codec), fmt, ##args) +#define codec_warn(codec, fmt, args...) \ + dev_warn(hda_codec_dev(codec), fmt, ##args) +#define codec_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define codec_dbg(codec, fmt, args...) \ + dev_dbg(hda_codec_dev(codec), fmt, ##args) #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h deleted file mode 100644 index daf458299753..000000000000 --- a/sound/pci/hda/hda_priv.h +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Common defines for the alsa driver code base for HD Audio. - * - * 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. - */ - -#ifndef __SOUND_HDA_PRIV_H -#define __SOUND_HDA_PRIV_H - -#include <linux/timecounter.h> -#include <sound/core.h> -#include <sound/pcm.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_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 */ -#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */ -#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */ -#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */ -#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */ -#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */ -#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */ -#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ -#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */ -#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */ -#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ -#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ -#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ -#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */ -/* 22 unused */ -#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ -#define AZX_DCAPS_REVERSE_ASSIGN (1 << 24) /* Assign devices in reverse order */ -#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ -#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ -#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ -#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ -#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ -#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ - -enum { - AZX_SNOOP_TYPE_NONE , - AZX_SNOOP_TYPE_SCH, - AZX_SNOOP_TYPE_ATI, - 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; - unsigned int irq_pending:1; - unsigned int prepared:1; - unsigned int locked:1; - /* - * For VIA: - * A flag to ensure DMA position is 0 - * when link position is not greater than FIFO size - */ - 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 */ -}; - -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); - int (*substream_free_pages)(struct azx *chip, - struct snd_pcm_substream *substream); - void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream, - struct vm_area_struct *area); - /* Check if current position is acceptable */ - int (*position_check)(struct azx *chip, struct azx_dev *azx_dev); -}; - -struct azx_pcm { - struct azx *chip; - struct snd_pcm *pcm; - struct hda_codec *codec; - struct hda_pcm_stream *hinfo[2]; - struct list_head list; -}; - -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 snd_card *card; - struct pci_dev *pci; - int dev_index; - - /* chip type specific */ - int driver_type; - unsigned int driver_caps; - int playback_streams; - int playback_index_offset; - int capture_streams; - int capture_index_offset; - int num_streams; - const int *jackpoll_ms; /* per-card jack poll interval */ - - /* Register interaction. */ - const struct hda_controller_ops *ops; - - /* position adjustment callbacks */ - 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 - - /* flags */ - 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; - unsigned int probing:1; /* codec probing phase */ - unsigned int snoop:1; - unsigned int align_buffer_size:1; - unsigned int region_requested:1; - unsigned int disabled:1; /* disabled by VGA-switcher */ - - /* for debugging */ - unsigned int last_cmd[AZX_MAX_CODECS]; - - /* reboot notifier (for mysterious hangup problem at power-down) */ - struct notifier_block reboot_notifier; - -#ifdef CONFIG_SND_HDA_DSP_LOADER - struct azx_dev saved_azx_dev; -#endif -}; - -#ifdef CONFIG_X86 -#define azx_snoop(chip) ((chip)->snoop) -#else -#define azx_snoop(chip) true -#endif - -/* - * macros for easy use - */ - -#define azx_writel(chip, reg, value) \ - ((chip)->ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg)) -#define azx_readl(chip, reg) \ - ((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg)) -#define azx_writew(chip, reg, value) \ - ((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg)) -#define azx_readw(chip, reg) \ - ((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg)) -#define azx_writeb(chip, reg, value) \ - ((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg)) -#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)) - -#endif /* __SOUND_HDA_PRIV_H */ diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 05e19f78b4cb..ee6230767c64 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -32,6 +32,10 @@ static int dump_coef = -1; module_param(dump_coef, int, 0644); MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)"); +/* always use noncached version */ +#define param_read(codec, nid, parm) \ + snd_hdac_read_parm_uncached(&(codec)->core, nid, parm) + static char *bits_names(unsigned int bits, char *names[], int size) { int i, n; @@ -99,10 +103,10 @@ static void print_nid_array(struct snd_info_buffer *buffer, static void print_nid_pcms(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - int pcm, type; + int type; struct hda_pcm *cpcm; - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - cpcm = &codec->pcm_info[pcm]; + + list_for_each_entry(cpcm, &codec->pcm_list_head, list) { for (type = 0; type < 2; type++) { if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) continue; @@ -119,9 +123,8 @@ static void print_amp_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, int dir) { unsigned int caps; - caps = snd_hda_param_read(codec, nid, - dir == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); + caps = param_read(codec, nid, dir == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); if (caps == -1 || caps == 0) { snd_iprintf(buffer, "N/A\n"); return; @@ -225,8 +228,8 @@ static void print_pcm_formats(struct snd_info_buffer *buffer, static void print_pcm_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM); - unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM); + unsigned int pcm = param_read(codec, nid, AC_PAR_PCM); + unsigned int stream = param_read(codec, nid, AC_PAR_STREAM); if (pcm == -1 || stream == -1) { snd_iprintf(buffer, "N/A\n"); return; @@ -273,7 +276,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer, static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; unsigned int caps, val; - caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + caps = param_read(codec, nid, AC_PAR_PIN_CAP); snd_iprintf(buffer, " Pincap 0x%08x:", caps); if (caps & AC_PINCAP_IN) snd_iprintf(buffer, " IN"); @@ -289,7 +292,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer, snd_iprintf(buffer, " Balanced"); if (caps & AC_PINCAP_HDMI) { /* Realtek uses this bit as a different meaning */ - if ((codec->vendor_id >> 16) == 0x10ec) + if ((codec->core.vendor_id >> 16) == 0x10ec) snd_iprintf(buffer, " R/L"); else { if (caps & AC_PINCAP_HBR) @@ -401,8 +404,7 @@ static void print_pin_ctls(struct snd_info_buffer *buffer, static void print_vol_knob(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - unsigned int cap = snd_hda_param_read(codec, nid, - AC_PAR_VOL_KNB_CAP); + unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP); snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ", (cap >> 7) & 1, cap & 0x7f); cap = snd_hda_codec_read(codec, nid, 0, @@ -487,7 +489,7 @@ static void print_power_state(struct snd_info_buffer *buffer, [ilog2(AC_PWRST_EPSS)] = "EPSS", }; - int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE); + int sup = param_read(codec, nid, AC_PAR_POWER_STATE); int pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); if (sup != -1) @@ -531,8 +533,7 @@ static void print_proc_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { unsigned int i, ncoeff, oldindex; - unsigned int proc_caps = snd_hda_param_read(codec, nid, - AC_PAR_PROC_CAP); + unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP); ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT; snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n", proc_caps & AC_PCAP_BENIGN, ncoeff); @@ -597,7 +598,7 @@ static void print_gpio(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { unsigned int gpio = - snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); unsigned int enable, direction, wake, unsol, sticky, data; int i, max; snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, " @@ -667,13 +668,9 @@ static void print_device_list(struct snd_info_buffer *buffer, } } -static void print_codec_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) +static void print_codec_core_info(struct hdac_device *codec, + struct snd_info_buffer *buffer) { - struct hda_codec *codec = entry->private_data; - hda_nid_t nid; - int i, nodes; - snd_iprintf(buffer, "Codec: "); if (codec->vendor_name && codec->chip_name) snd_iprintf(buffer, "%s %s\n", @@ -695,34 +692,43 @@ static void print_codec_info(struct snd_info_entry *entry, snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg); else snd_iprintf(buffer, "No Modem Function Group found\n"); +} + +static void print_codec_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hda_codec *codec = entry->private_data; + hda_nid_t nid, fg; + int i, nodes; - if (! codec->afg) + print_codec_core_info(&codec->core, buffer); + fg = codec->core.afg; + if (!fg) return; snd_hda_power_up(codec); snd_iprintf(buffer, "Default PCM:\n"); - print_pcm_caps(buffer, codec, codec->afg); + print_pcm_caps(buffer, codec, fg); snd_iprintf(buffer, "Default Amp-In caps: "); - print_amp_caps(buffer, codec, codec->afg, HDA_INPUT); + print_amp_caps(buffer, codec, fg, HDA_INPUT); snd_iprintf(buffer, "Default Amp-Out caps: "); - print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT); - snd_iprintf(buffer, "State of AFG node 0x%02x:\n", codec->afg); - print_power_state(buffer, codec, codec->afg); + print_amp_caps(buffer, codec, fg, HDA_OUTPUT); + snd_iprintf(buffer, "State of AFG node 0x%02x:\n", fg); + print_power_state(buffer, codec, fg); - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + nodes = snd_hda_get_sub_nodes(codec, fg, &nid); if (! nid || nodes < 0) { snd_iprintf(buffer, "Invalid AFG subtree\n"); snd_hda_power_down(codec); return; } - print_gpio(buffer, codec, codec->afg); + print_gpio(buffer, codec, fg); if (codec->proc_widget_hook) - codec->proc_widget_hook(buffer, codec, codec->afg); + codec->proc_widget_hook(buffer, codec, fg); for (i = 0; i < nodes; i++, nid++) { unsigned int wid_caps = - snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); + param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); unsigned int wid_type = get_wcaps_type(wid_caps); hda_nid_t *conn = NULL; int conn_len = 0; @@ -860,8 +866,8 @@ int snd_hda_codec_proc_new(struct hda_codec *codec) struct snd_info_entry *entry; int err; - snprintf(name, sizeof(name), "codec#%d", codec->addr); - err = snd_card_proc_new(codec->bus->card, name, &entry); + snprintf(name, sizeof(name), "codec#%d", codec->core.addr); + err = snd_card_proc_new(codec->card, name, &entry); if (err < 0) return err; diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index ccc962a1699f..a6e3d9b511ab 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -48,33 +48,33 @@ static DEVICE_ATTR_RO(power_on_acct); static DEVICE_ATTR_RO(power_off_acct); #endif /* CONFIG_PM */ -#define CODEC_INFO_SHOW(type) \ +#define CODEC_INFO_SHOW(type, field) \ static ssize_t type##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct hda_codec *codec = dev_get_drvdata(dev); \ - return sprintf(buf, "0x%x\n", codec->type); \ + return sprintf(buf, "0x%x\n", codec->field); \ } -#define CODEC_INFO_STR_SHOW(type) \ +#define CODEC_INFO_STR_SHOW(type, field) \ static ssize_t type##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct hda_codec *codec = dev_get_drvdata(dev); \ return sprintf(buf, "%s\n", \ - codec->type ? codec->type : ""); \ + codec->field ? codec->field : ""); \ } -CODEC_INFO_SHOW(vendor_id); -CODEC_INFO_SHOW(subsystem_id); -CODEC_INFO_SHOW(revision_id); -CODEC_INFO_SHOW(afg); -CODEC_INFO_SHOW(mfg); -CODEC_INFO_STR_SHOW(vendor_name); -CODEC_INFO_STR_SHOW(chip_name); -CODEC_INFO_STR_SHOW(modelname); +CODEC_INFO_SHOW(vendor_id, core.vendor_id); +CODEC_INFO_SHOW(subsystem_id, core.subsystem_id); +CODEC_INFO_SHOW(revision_id, core.revision_id); +CODEC_INFO_SHOW(afg, core.afg); +CODEC_INFO_SHOW(mfg, core.mfg); +CODEC_INFO_STR_SHOW(vendor_name, core.vendor_name); +CODEC_INFO_STR_SHOW(chip_name, core.chip_name); +CODEC_INFO_STR_SHOW(modelname, modelname); static ssize_t pin_configs_show(struct hda_codec *codec, struct snd_array *list, @@ -149,7 +149,7 @@ static int reconfig_codec(struct hda_codec *codec) err = snd_hda_codec_build_controls(codec); if (err < 0) goto error; - err = snd_card_register(codec->bus->card); + err = snd_card_register(codec->card); error: snd_hda_power_down(codec); return err; @@ -170,7 +170,7 @@ static char *kstrndup_noeol(const char *src, size_t len) return s; } -#define CODEC_INFO_STORE(type) \ +#define CODEC_INFO_STORE(type, field) \ static ssize_t type##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ @@ -180,11 +180,11 @@ static ssize_t type##_store(struct device *dev, \ int err = kstrtoul(buf, 0, &val); \ if (err < 0) \ return err; \ - codec->type = val; \ + codec->field = val; \ return count; \ } -#define CODEC_INFO_STR_STORE(type) \ +#define CODEC_INFO_STR_STORE(type, field) \ static ssize_t type##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ @@ -193,17 +193,17 @@ static ssize_t type##_store(struct device *dev, \ char *s = kstrndup_noeol(buf, 64); \ if (!s) \ return -ENOMEM; \ - kfree(codec->type); \ - codec->type = s; \ + kfree(codec->field); \ + codec->field = s; \ return count; \ } -CODEC_INFO_STORE(vendor_id); -CODEC_INFO_STORE(subsystem_id); -CODEC_INFO_STORE(revision_id); -CODEC_INFO_STR_STORE(vendor_name); -CODEC_INFO_STR_STORE(chip_name); -CODEC_INFO_STR_STORE(modelname); +CODEC_INFO_STORE(vendor_id, core.vendor_id); +CODEC_INFO_STORE(subsystem_id, core.subsystem_id); +CODEC_INFO_STORE(revision_id, core.revision_id); +CODEC_INFO_STR_STORE(vendor_name, core.vendor_name); +CODEC_INFO_STR_STORE(chip_name, core.chip_name); +CODEC_INFO_STR_STORE(modelname, modelname); #define CODEC_ACTION_STORE(type) \ static ssize_t type##_store(struct device *dev, \ @@ -552,10 +552,10 @@ static void parse_codec_mode(char *buf, struct hda_bus *bus, *codecp = NULL; if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { - list_for_each_entry(codec, &bus->codec_list, list) { - if ((vendorid <= 0 || codec->vendor_id == vendorid) && - (subid <= 0 || codec->subsystem_id == subid) && - codec->addr == caddr) { + list_for_each_codec(codec, bus) { + if ((vendorid <= 0 || codec->core.vendor_id == vendorid) && + (subid <= 0 || codec->core.subsystem_id == subid) && + codec->core.addr == caddr) { *codecp = codec; break; } @@ -595,8 +595,8 @@ static void parse_model_mode(char *buf, struct hda_bus *bus, static void parse_chip_name_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - kfree((*codecp)->chip_name); - (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL); + kfree((*codecp)->core.chip_name); + (*codecp)->core.chip_name = kstrdup(buf, GFP_KERNEL); } #define DEFINE_PARSE_ID_MODE(name) \ @@ -605,7 +605,7 @@ static void parse_##name##_mode(char *buf, struct hda_bus *bus, \ { \ unsigned long val; \ if (!kstrtoul(buf, 0, &val)) \ - (*codecp)->name = val; \ + (*codecp)->core.name = val; \ } DEFINE_PARSE_ID_MODE(vendor_id); diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 375e94f4cf52..2e4fd5c56d3b 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -37,7 +37,6 @@ #include "hda_codec.h" #include "hda_controller.h" -#include "hda_priv.h" /* Defines for Nvidia Tegra HDA support */ #define HDA_BAR0 0x8000 @@ -82,7 +81,7 @@ module_param(power_save, bint, 0644); MODULE_PARM_DESC(power_save, "Automatic power-saving timeout (in seconds, 0 = disable)."); #else -static int power_save = 0; +#define power_save 0 #endif /* @@ -250,14 +249,9 @@ static int hda_tegra_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; - struct azx_pcm *p; struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - list_for_each_entry(p, &chip->pcm_list, list) - snd_pcm_suspend_all(p->pcm); - if (chip->initialized) - snd_hda_suspend(chip->bus); azx_stop_chip(chip); azx_enter_link_reset(chip); @@ -278,7 +272,6 @@ static int hda_tegra_resume(struct device *dev) azx_init_chip(chip, 1); - snd_hda_resume(chip->bus); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; @@ -297,8 +290,6 @@ static int hda_tegra_dev_free(struct snd_device *device) int i; struct azx *chip = device->device_data; - azx_notifier_unregister(chip); - if (chip->initialized) { for (i = 0; i < chip->num_streams; i++) azx_stream_stop(chip, &chip->azx_dev[i]); @@ -344,17 +335,6 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) return 0; } -/* - * The codecs were powered up in snd_hda_codec_new(). - * Now all initialization done, so turn them down if possible - */ -static void power_down_all_codecs(struct azx *chip) -{ - struct hda_codec *codec; - list_for_each_entry(codec, &chip->bus->codec_list, list) - snd_hda_power_down(codec); -} - static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) { struct snd_card *card = chip->card; @@ -503,21 +483,15 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free; /* create codec instances */ - err = azx_codec_create(chip, NULL, 0, &power_save); + err = azx_bus_create(chip, NULL); if (err < 0) goto out_free; - err = azx_codec_configure(chip); + err = azx_probe_codecs(chip, 0); if (err < 0) goto out_free; - /* create PCM streams */ - err = snd_hda_build_pcms(chip->bus); - if (err < 0) - goto out_free; - - /* create mixer controls */ - err = azx_mixer_create(chip); + err = azx_codec_configure(chip); if (err < 0) goto out_free; @@ -526,8 +500,7 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free; chip->running = 1; - power_down_all_codecs(chip); - azx_notifier_register(chip); + snd_hda_set_power_save(chip->bus, power_save * 1000); return 0; @@ -541,6 +514,18 @@ static int hda_tegra_remove(struct platform_device *pdev) return snd_card_free(dev_get_drvdata(&pdev->dev)); } +static void hda_tegra_shutdown(struct platform_device *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip; + + if (!card) + return; + chip = card->private_data; + if (chip && chip->running) + azx_stop_chip(chip); +} + static struct platform_driver tegra_platform_hda = { .driver = { .name = "tegra-hda", @@ -549,6 +534,7 @@ static struct platform_driver tegra_platform_hda = { }, .probe = hda_tegra_probe, .remove = hda_tegra_remove, + .shutdown = hda_tegra_shutdown, }; module_platform_driver(tegra_platform_hda); diff --git a/sound/pci/hda/hda_trace.h b/sound/pci/hda/hda_trace.h deleted file mode 100644 index 3a1c63161eb1..000000000000 --- a/sound/pci/hda/hda_trace.h +++ /dev/null @@ -1,143 +0,0 @@ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM hda -#define TRACE_INCLUDE_FILE hda_trace - -#if !defined(_TRACE_HDA_H) || defined(TRACE_HEADER_MULTI_READ) -#define _TRACE_HDA_H - -#include <linux/tracepoint.h> - -struct hda_bus; -struct hda_codec; - -DECLARE_EVENT_CLASS(hda_cmd, - - TP_PROTO(struct hda_codec *codec, unsigned int val), - - TP_ARGS(codec, val), - - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( unsigned int, addr ) - __field( unsigned int, val ) - ), - - TP_fast_assign( - __entry->card = (codec)->bus->card->number; - __entry->addr = (codec)->addr; - __entry->val = (val); - ), - - TP_printk("[%d:%d] val=%x", __entry->card, __entry->addr, __entry->val) -); - -DEFINE_EVENT(hda_cmd, hda_send_cmd, - TP_PROTO(struct hda_codec *codec, unsigned int val), - TP_ARGS(codec, val) -); - -DEFINE_EVENT(hda_cmd, hda_get_response, - TP_PROTO(struct hda_codec *codec, unsigned int val), - TP_ARGS(codec, val) -); - -TRACE_EVENT(hda_bus_reset, - - TP_PROTO(struct hda_bus *bus), - - TP_ARGS(bus), - - TP_STRUCT__entry( - __field( unsigned int, card ) - ), - - TP_fast_assign( - __entry->card = (bus)->card->number; - ), - - TP_printk("[%d]", __entry->card) -); - -#ifdef CONFIG_PM -DECLARE_EVENT_CLASS(hda_power, - - TP_PROTO(struct hda_codec *codec), - - TP_ARGS(codec), - - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( unsigned int, addr ) - ), - - TP_fast_assign( - __entry->card = (codec)->bus->card->number; - __entry->addr = (codec)->addr; - ), - - TP_printk("[%d:%d]", __entry->card, __entry->addr) -); - -DEFINE_EVENT(hda_power, hda_power_down, - TP_PROTO(struct hda_codec *codec), - TP_ARGS(codec) -); - -DEFINE_EVENT(hda_power, hda_power_up, - TP_PROTO(struct hda_codec *codec), - TP_ARGS(codec) -); - -TRACE_EVENT(hda_power_count, - TP_PROTO(struct hda_codec *codec), - TP_ARGS(codec), - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( unsigned int, addr ) - __field( int, power_count ) - __field( int, power_on ) - __field( int, power_transition ) - ), - - TP_fast_assign( - __entry->card = (codec)->bus->card->number; - __entry->addr = (codec)->addr; - __entry->power_count = (codec)->power_count; - __entry->power_on = (codec)->power_on; - __entry->power_transition = (codec)->power_transition; - ), - - TP_printk("[%d:%d] power_count=%d, power_on=%d, power_transition=%d", - __entry->card, __entry->addr, __entry->power_count, - __entry->power_on, __entry->power_transition) -); -#endif /* CONFIG_PM */ - -TRACE_EVENT(hda_unsol_event, - - TP_PROTO(struct hda_bus *bus, u32 res, u32 res_ex), - - TP_ARGS(bus, res, res_ex), - - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( u32, res ) - __field( u32, res_ex ) - ), - - TP_fast_assign( - __entry->card = (bus)->card->number; - __entry->res = res; - __entry->res_ex = res_ex; - ), - - TP_printk("[%d] res=%x, res_ex=%x", __entry->card, - __entry->res, __entry->res_ex) -); - -#endif /* _TRACE_HDA_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/local.h b/sound/pci/hda/local.h new file mode 100644 index 000000000000..28cb7f98982e --- /dev/null +++ b/sound/pci/hda/local.h @@ -0,0 +1,39 @@ +/* + */ + +#ifndef __HDAC_LOCAL_H +#define __HDAC_LOCAL_H + +int hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm); + +#define get_wcaps(codec, nid) \ + hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) +/* get the widget type from widget capability bits */ +static inline int get_wcaps_type(unsigned int wcaps) +{ + if (!wcaps) + return -1; /* invalid type */ + return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +} + +#define get_pin_caps(codec, nid) \ + hdac_read_parm(codec, nid, AC_PAR_PIN_CAP) + +static inline +unsigned int get_pin_cfg(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int val; + + if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val)) + return -1; + return val; +} + +#define get_amp_caps(codec, nid, dir) \ + hdac_read_parm(codec, nid, (dir) == HDA_OUTPUT ? \ + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP) + +#define get_power_caps(codec, nid) \ + hdac_read_parm(codec, nid, AC_PAR_POWER_STATE) + +#endif /* __HDAC_LOCAL_H */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index d285904cdb64..231f89029779 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -99,7 +99,7 @@ static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, static void ad198x_power_eapd(struct hda_codec *codec) { /* We currently only handle front, HP */ - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x11d41882: case 0x11d4882a: case 0x11d41884: @@ -777,7 +777,6 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol, return 0; mutex_lock(&codec->control_mutex); - codec->cached_write = 1; path = snd_hda_get_path_from_idx(codec, spec->smux_paths[spec->cur_smux]); if (path) @@ -786,9 +785,7 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol, if (path) snd_hda_activate_path(codec, path, true, true); spec->cur_smux = val; - codec->cached_write = 0; mutex_unlock(&codec->control_mutex); - snd_hda_codec_flush_cache(codec); /* flush the updates */ return 1; } @@ -1004,18 +1001,17 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct ad198x_spec *spec = codec->spec; - static const struct hda_verb gpio_init_verbs[] = { - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, - {}, - }; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook; spec->gen.own_eapd_ctl = 1; - snd_hda_sequence_write_cache(codec, gpio_init_verbs); + snd_hda_codec_write_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_MASK, 0x02); + snd_hda_codec_write_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_DIRECTION, 0x02); + snd_hda_codec_write_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 0x02); break; case HDA_FIXUP_ACT_PROBE: if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) @@ -1194,20 +1190,8 @@ MODULE_ALIAS("snd-hda-codec-id:11d4*"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Analog Devices HD-audio codec"); -static struct hda_codec_preset_list analog_list = { +static struct hda_codec_driver analog_driver = { .preset = snd_hda_preset_analog, - .owner = THIS_MODULE, }; -static int __init patch_analog_init(void) -{ - return snd_hda_add_codec_preset(&analog_list); -} - -static void __exit patch_analog_exit(void) -{ - snd_hda_delete_codec_preset(&analog_list); -} - -module_init(patch_analog_init) -module_exit(patch_analog_exit) +module_hda_codec_driver(analog_driver); diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 5e65999e0d8e..447302695195 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -98,20 +98,8 @@ MODULE_ALIAS("snd-hda-codec-id:1102000d"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec"); -static struct hda_codec_preset_list ca0110_list = { +static struct hda_codec_driver ca0110_driver = { .preset = snd_hda_preset_ca0110, - .owner = THIS_MODULE, }; -static int __init patch_ca0110_init(void) -{ - return snd_hda_add_codec_preset(&ca0110_list); -} - -static void __exit patch_ca0110_exit(void) -{ - snd_hda_delete_codec_preset(&ca0110_list); -} - -module_init(patch_ca0110_init) -module_exit(patch_ca0110_exit) +module_hda_codec_driver(ca0110_driver); diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index e0383eea9880..4a4e7b282e4f 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -719,7 +719,6 @@ struct ca0132_spec { unsigned int num_inputs; hda_nid_t shared_mic_nid; hda_nid_t shared_out_nid; - struct hda_pcm pcm_rec[5]; /* PCM information */ /* chip access */ struct mutex chipio_mutex; /* chip access mutex */ @@ -3132,7 +3131,7 @@ static int ca0132_select_out(struct hda_codec *codec) codec_dbg(codec, "ca0132_select_out\n"); - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; @@ -3216,7 +3215,7 @@ static int ca0132_select_out(struct hda_codec *codec) } exit: - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return err < 0 ? err : 0; } @@ -3294,7 +3293,7 @@ static int ca0132_select_mic(struct hda_codec *codec) codec_dbg(codec, "ca0132_select_mic\n"); - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID]; @@ -3327,7 +3326,7 @@ static int ca0132_select_mic(struct hda_codec *codec) ca0132_effects_set(codec, VOICE_FOCUS, 0); } - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return 0; } @@ -4036,12 +4035,11 @@ static struct hda_pcm_stream ca0132_pcm_digital_capture = { static int ca0132_build_pcms(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->pcm_info = info; - codec->num_pcms = 0; + struct hda_pcm *info; - info->name = "CA0132 Analog"; + info = snd_hda_codec_pcm_new(codec, "CA0132 Analog"); + if (!info) + return -ENOMEM; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = @@ -4049,27 +4047,27 @@ static int ca0132_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; - codec->num_pcms++; - info++; - info->name = "CA0132 Analog Mic-In2"; + info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2"); + if (!info) + return -ENOMEM; info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1]; - codec->num_pcms++; - info++; - info->name = "CA0132 What U Hear"; + info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear"); + if (!info) + return -ENOMEM; info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2]; - codec->num_pcms++; if (!spec->dig_out && !spec->dig_in) return 0; - info++; - info->name = "CA0132 Digital"; + info = snd_hda_codec_pcm_new(codec, "CA0132 Digital"); + if (!info) + return -ENOMEM; info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->dig_out) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = @@ -4081,7 +4079,6 @@ static int ca0132_build_pcms(struct hda_codec *codec) ca0132_pcm_digital_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; } - codec->num_pcms++; return 0; } @@ -4246,13 +4243,9 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; int i; - hda_nid_t nid; codec_dbg(codec, "ca0132_refresh_widget_caps.\n"); - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) - codec->wcaps[i] = snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); + snd_hda_codec_update_widgets(codec); for (i = 0; i < spec->multiout.num_dacs; i++) refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT); @@ -4352,7 +4345,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) const struct dsp_image_seg *dsp_os_image; const struct firmware *fw_entry; - if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0) + if (request_firmware(&fw_entry, EFX_FILE, codec->card->dev) != 0) return false; dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); @@ -4413,8 +4406,7 @@ static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) * state machine run. */ cancel_delayed_work_sync(&spec->unsol_hp_work); - queue_delayed_work(codec->bus->workq, &spec->unsol_hp_work, - msecs_to_jiffies(500)); + schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500)); cb->tbl->block_report = 1; } @@ -4554,7 +4546,7 @@ static int ca0132_init(struct hda_codec *codec) spec->dsp_state = DSP_DOWNLOAD_INIT; spec->curr_chip_addx = INVALID_CHIP_ADDRESS; - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); ca0132_init_unsol(codec); @@ -4585,7 +4577,7 @@ static int ca0132_init(struct hda_codec *codec) snd_hda_jack_report_sync(codec); - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return 0; } @@ -4702,20 +4694,8 @@ MODULE_ALIAS("snd-hda-codec-id:11020011"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Creative Sound Core3D codec"); -static struct hda_codec_preset_list ca0132_list = { +static struct hda_codec_driver ca0132_driver = { .preset = snd_hda_preset_ca0132, - .owner = THIS_MODULE, }; -static int __init patch_ca0132_init(void) -{ - return snd_hda_add_codec_preset(&ca0132_list); -} - -static void __exit patch_ca0132_exit(void) -{ - snd_hda_delete_codec_preset(&ca0132_list); -} - -module_init(patch_ca0132_init) -module_exit(patch_ca0132_exit) +module_hda_codec_driver(ca0132_driver); diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index dd2b3d92071f..50e9dd675579 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -1221,20 +1221,8 @@ MODULE_ALIAS("snd-hda-codec-id:10134213"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); -static struct hda_codec_preset_list cirrus_list = { +static struct hda_codec_driver cirrus_driver = { .preset = snd_hda_preset_cirrus, - .owner = THIS_MODULE, }; -static int __init patch_cirrus_init(void) -{ - return snd_hda_add_codec_preset(&cirrus_list); -} - -static void __exit patch_cirrus_exit(void) -{ - snd_hda_delete_codec_preset(&cirrus_list); -} - -module_init(patch_cirrus_init) -module_exit(patch_cirrus_exit) +module_hda_codec_driver(cirrus_driver); diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index c895a8f21192..617d9012e78a 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -137,20 +137,8 @@ MODULE_ALIAS("snd-hda-codec-id:434d4980"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("C-Media HD-audio codec"); -static struct hda_codec_preset_list cmedia_list = { +static struct hda_codec_driver cmedia_driver = { .preset = snd_hda_preset_cmedia, - .owner = THIS_MODULE, }; -static int __init patch_cmedia_init(void) -{ - return snd_hda_add_codec_preset(&cmedia_list); -} - -static void __exit patch_cmedia_exit(void) -{ - snd_hda_delete_codec_preset(&cmedia_list); -} - -module_init(patch_cmedia_init) -module_exit(patch_cmedia_exit) +module_hda_codec_driver(cmedia_driver); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index da67ea8645a6..f8f0dfbef149 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -103,10 +103,9 @@ static int add_beep_ctls(struct hda_codec *codec) static void cx_auto_parse_beep(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t nid; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) + for_each_hda_codec_node(nid, codec) if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { set_beep_amp(spec, nid, 0, HDA_OUTPUT); break; @@ -120,10 +119,9 @@ static void cx_auto_parse_beep(struct hda_codec *codec) static void cx_auto_parse_eapd(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t nid; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { + for_each_hda_codec_node(nid, codec) { if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) continue; if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) @@ -304,6 +302,7 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec, switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC; + snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410); break; case HDA_FIXUP_ACT_PROBE: spec->gen.cap_sync_hook = cxt_update_headset_mode_hook; @@ -411,15 +410,11 @@ static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_callback *jack) { struct conexant_spec *spec = codec->spec; - int saved_cached_write = codec->cached_write; - codec->cached_write = 1; /* in DC mode, we don't handle automic */ if (!spec->dc_enable) snd_hda_gen_mic_autoswitch(codec, jack); olpc_xo_update_mic_pins(codec); - snd_hda_codec_flush_cache(codec); - codec->cached_write = saved_cached_write; if (spec->dc_enable) olpc_xo_update_mic_boost(codec); } @@ -848,7 +843,7 @@ static int patch_conexant_auto(struct hda_codec *codec) struct conexant_spec *spec; int err; - codec_info(codec, "%s: BIOS auto-probing.\n", codec->chip_name); + codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name); spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) @@ -862,7 +857,7 @@ static int patch_conexant_auto(struct hda_codec *codec) if (spec->dynamic_eapd) spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x14f15045: codec->single_adc_amp = 1; spec->gen.mixer_nid = 0x17; @@ -896,7 +891,7 @@ static int patch_conexant_auto(struct hda_codec *codec) * others may use EAPD really as an amp switch, so it might be * not good to expose it blindly. */ - switch (codec->subsystem_id >> 16) { + switch (codec->core.subsystem_id >> 16) { case 0x103c: spec->gen.vmaster_mute_enum = 1; break; @@ -919,10 +914,10 @@ static int patch_conexant_auto(struct hda_codec *codec) * which falls into the single-cmd mode. * Better to make reset, then. */ - if (!codec->bus->sync_write) { + if (!codec->bus->core.sync_write) { codec_info(codec, "Enable sync_write for stable communication\n"); - codec->bus->sync_write = 1; + codec->bus->core.sync_write = 1; codec->bus->allow_bus_reset = 1; } @@ -1018,20 +1013,8 @@ MODULE_ALIAS("snd-hda-codec-id:14f151d7"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); -static struct hda_codec_preset_list conexant_list = { +static struct hda_codec_driver conexant_driver = { .preset = snd_hda_preset_conexant, - .owner = THIS_MODULE, }; -static int __init patch_conexant_init(void) -{ - return snd_hda_add_codec_preset(&conexant_list); -} - -static void __exit patch_conexant_exit(void) -{ - snd_hda_delete_codec_preset(&conexant_list); -} - -module_init(patch_conexant_init) -module_exit(patch_conexant_exit) +module_hda_codec_driver(conexant_driver); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b422e406a9cb..5f44f60a6389 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -45,14 +45,14 @@ static bool static_hdmi_pcm; module_param(static_hdmi_pcm, bool, 0644); MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); -#define is_haswell(codec) ((codec)->vendor_id == 0x80862807) -#define is_broadwell(codec) ((codec)->vendor_id == 0x80862808) -#define is_skylake(codec) ((codec)->vendor_id == 0x80862809) +#define is_haswell(codec) ((codec)->core.vendor_id == 0x80862807) +#define is_broadwell(codec) ((codec)->core.vendor_id == 0x80862808) +#define is_skylake(codec) ((codec)->core.vendor_id == 0x80862809) #define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \ || is_skylake(codec)) -#define is_valleyview(codec) ((codec)->vendor_id == 0x80862882) -#define is_cherryview(codec) ((codec)->vendor_id == 0x80862883) +#define is_valleyview(codec) ((codec)->core.vendor_id == 0x80862882) +#define is_cherryview(codec) ((codec)->core.vendor_id == 0x80862883) #define is_valleyview_plus(codec) (is_valleyview(codec) || is_cherryview(codec)) struct hdmi_spec_per_cvt { @@ -86,7 +86,6 @@ 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 */ - char pcm_name[8]; /* filled in build_pcm callbacks */ #ifdef CONFIG_PROC_FS struct snd_info_entry *proc_entry; #endif @@ -132,7 +131,7 @@ struct hdmi_spec { int num_pins; struct snd_array pins; /* struct hdmi_spec_per_pin */ - struct snd_array pcm_rec; /* struct hda_pcm */ + struct hda_pcm *pcm_rec[16]; unsigned int channels_max; /* max over all cvts */ struct hdmi_eld temp_eld; @@ -355,8 +354,7 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) #define get_cvt(spec, idx) \ ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx)) -#define get_pcm_rec(spec, idx) \ - ((struct hda_pcm *)snd_array_elem(&spec->pcm_rec, idx)) +#define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx]) static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid) { @@ -579,7 +577,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) int err; snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); - err = snd_card_proc_new(codec->bus->card, name, &entry); + err = snd_card_proc_new(codec->card, name, &entry); if (err < 0) return err; @@ -594,7 +592,7 @@ 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->bus->card, per_pin->proc_entry); + snd_device_free(per_pin->codec->card, per_pin->proc_entry); per_pin->proc_entry = NULL; } } @@ -1393,13 +1391,12 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec, hda_nid_t pin_nid, int mux_idx) { struct hdmi_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t nid; int cvt_idx, curr; struct hdmi_spec_per_cvt *per_cvt; /* configure all pins, including "no physical connection" ones */ - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wid_caps); @@ -1548,7 +1545,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) bool eld_changed = false; bool ret; - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); present = snd_hda_pin_sense(codec, pin_nid); mutex_lock(&per_pin->lock); @@ -1578,9 +1575,8 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) update_eld = true; } else if (repoll) { - queue_delayed_work(codec->bus->workq, - &per_pin->work, - msecs_to_jiffies(300)); + schedule_delayed_work(&per_pin->work, + msecs_to_jiffies(300)); goto unlock; } } @@ -1624,7 +1620,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) } if (eld_changed) - snd_ctl_notify(codec->bus->card, + snd_ctl_notify(codec->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &per_pin->eld_ctl->id); unlock: @@ -1635,7 +1631,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) jack->block_report = !ret; mutex_unlock(&per_pin->lock); - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return ret; } @@ -1731,7 +1727,7 @@ static int hdmi_parse_codec(struct hda_codec *codec) hda_nid_t nid; int i, nodes; - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &nid); if (!nid || nodes < 0) { codec_warn(codec, "HDMI: failed to get afg sub nodes\n"); return -EINVAL; @@ -2056,11 +2052,10 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) struct hdmi_spec_per_pin *per_pin; per_pin = get_pin(spec, pin_idx); - sprintf(per_pin->pcm_name, "HDMI %d", pin_idx); - info = snd_array_new(&spec->pcm_rec); + info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx); if (!info) return -ENOMEM; - info->name = per_pin->pcm_name; + spec->pcm_rec[pin_idx] = info; info->pcm_type = HDA_PCM_TYPE_HDMI; info->own_chmap = true; @@ -2070,9 +2065,6 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) /* other pstr fields are set in open */ } - codec->num_pcms = spec->num_pins; - codec->pcm_info = spec->pcm_rec.list; - return 0; } @@ -2125,13 +2117,15 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) /* add channel maps */ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hda_pcm *pcm; struct snd_pcm_chmap *chmap; struct snd_kcontrol *kctl; int i; - if (!codec->pcm_info[pin_idx].pcm) + pcm = spec->pcm_rec[pin_idx]; + if (!pcm || !pcm->pcm) break; - err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm, + err = snd_pcm_add_chmap_ctls(pcm->pcm, SNDRV_PCM_STREAM_PLAYBACK, NULL, 0, pin_idx, &chmap); if (err < 0) @@ -2186,14 +2180,12 @@ static void hdmi_array_init(struct hdmi_spec *spec, int nums) { snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums); snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums); - snd_array_init(&spec->pcm_rec, sizeof(struct hda_pcm), nums); } static void hdmi_array_free(struct hdmi_spec *spec) { snd_array_free(&spec->pins); snd_array_free(&spec->cvts); - snd_array_free(&spec->pcm_rec); } static void generic_hdmi_free(struct hda_codec *codec) @@ -2204,11 +2196,10 @@ static void generic_hdmi_free(struct hda_codec *codec) for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - cancel_delayed_work(&per_pin->work); + cancel_delayed_work_sync(&per_pin->work); eld_proc_free(per_pin); } - flush_workqueue(codec->bus->workq); hdmi_array_free(spec); kfree(spec); } @@ -2220,8 +2211,7 @@ static int generic_hdmi_resume(struct hda_codec *codec) int pin_idx; codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); + regcache_sync(codec->core.regmap); for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); @@ -2308,6 +2298,7 @@ static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec) /* enable DP1.2 mode */ vendor_param |= INTEL_EN_DP12; + snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB); snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0, INTEL_SET_VENDOR_VERB, vendor_param); } @@ -2381,11 +2372,10 @@ static int simple_playback_build_pcms(struct hda_codec *codec) chans = get_wcaps(codec, per_cvt->cvt_nid); chans = get_wcaps_channels(chans); - info = snd_array_new(&spec->pcm_rec); + info = snd_hda_codec_pcm_new(codec, "HDMI 0"); if (!info) return -ENOMEM; - info->name = get_pin(spec, 0)->pcm_name; - sprintf(info->name, "HDMI 0"); + spec->pcm_rec[0] = info; info->pcm_type = HDA_PCM_TYPE_HDMI; pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; *pstr = spec->pcm_playback; @@ -2393,9 +2383,6 @@ static int simple_playback_build_pcms(struct hda_codec *codec) if (pstr->channels_max <= 2 && chans && chans <= 16) pstr->channels_max = chans; - codec->num_pcms = 1; - codec->pcm_info = info; - return 0; } @@ -2940,7 +2927,8 @@ static int patch_nvhdmi(struct hda_codec *codec) */ #define is_amdhdmi_rev3_or_later(codec) \ - ((codec)->vendor_id == 0x1002aa01 && ((codec)->revision_id & 0xff00) >= 0x0300) + ((codec)->core.vendor_id == 0x1002aa01 && \ + ((codec)->core.revision_id & 0xff00) >= 0x0300) #define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec) /* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ @@ -3301,15 +3289,6 @@ static int patch_via_hdmi(struct hda_codec *codec) } /* - * called from hda_codec.c for generic HDMI support - */ -int snd_hda_parse_hdmi_codec(struct hda_codec *codec) -{ - return patch_generic_hdmi(codec); -} -EXPORT_SYMBOL_GPL(snd_hda_parse_hdmi_codec); - -/* * patch entries */ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { @@ -3373,6 +3352,8 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi }, { .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi }, +/* special ID for generic HDMI */ +{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi }, {} /* terminator */ }; @@ -3442,20 +3423,8 @@ MODULE_ALIAS("snd-hda-codec-intelhdmi"); MODULE_ALIAS("snd-hda-codec-nvhdmi"); MODULE_ALIAS("snd-hda-codec-atihdmi"); -static struct hda_codec_preset_list intel_list = { +static struct hda_codec_driver hdmi_driver = { .preset = snd_hda_preset_hdmi, - .owner = THIS_MODULE, }; -static int __init patch_hdmi_init(void) -{ - return snd_hda_add_codec_preset(&intel_list); -} - -static void __exit patch_hdmi_exit(void) -{ - snd_hda_delete_codec_preset(&intel_list); -} - -module_init(patch_hdmi_init) -module_exit(patch_hdmi_exit) +module_hda_codec_driver(hdmi_driver); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 74382137b9f5..b18b9c67b262 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -299,7 +299,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) coef = alc_get_coef0(codec); - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0262: alc_update_coef_idx(codec, 0x7, 0, 1<<5); break; @@ -432,7 +432,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) snd_hda_sequence_write(codec, alc_gpio3_init_verbs); break; case ALC_INIT_DEFAULT: - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0260: alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010); break; @@ -498,18 +498,18 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec) if (!codec->bus->pci) return -1; - ass = codec->subsystem_id & 0xffff; + ass = codec->core.subsystem_id & 0xffff; if (ass != codec->bus->pci->subsystem_device && (ass & 1)) goto do_sku; nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) + if (codec->core.vendor_id == 0x10ec0260) nid = 0x17; ass = snd_hda_codec_get_pincfg(codec, nid); if (!(ass & 1)) { codec_info(codec, "%s: SKU not ready 0x%08x\n", - codec->chip_name, ass); + codec->core.chip_name, ass); return -1; } @@ -585,7 +585,7 @@ static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) goto do_sku; } - ass = codec->subsystem_id & 0xffff; + ass = codec->core.subsystem_id & 0xffff; if (codec->bus->pci && ass != codec->bus->pci->subsystem_device && (ass & 1)) goto do_sku; @@ -600,7 +600,7 @@ static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) * 0 : override */ nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) + if (codec->core.vendor_id == 0x10ec0260) nid = 0x17; ass = snd_hda_codec_get_pincfg(codec, nid); codec_dbg(codec, @@ -621,7 +621,7 @@ static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) return 0; do_sku: codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", - ass & 0xffff, codec->vendor_id); + ass & 0xffff, codec->core.vendor_id); /* * 0 : override * 1 : Swap Jack @@ -799,8 +799,7 @@ static int alc_resume(struct hda_codec *codec) if (!spec->no_depop_delay) msleep(150); /* to avoid pop noise */ codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); + regcache_sync(codec->core.regmap); hda_call_check_power_status(codec, 0x01); return 0; } @@ -826,9 +825,9 @@ static const struct hda_codec_ops alc_patch_ops = { /* replace the codec chip_name with the given string */ static int alc_codec_rename(struct hda_codec *codec, const char *name) { - kfree(codec->chip_name); - codec->chip_name = kstrdup(name, GFP_KERNEL); - if (!codec->chip_name) { + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup(name, GFP_KERNEL); + if (!codec->core.chip_name) { alc_free(codec); return -ENOMEM; } @@ -904,7 +903,7 @@ static int alc_codec_rename_from_preset(struct hda_codec *codec) const struct alc_codec_rename_pci_table *q; for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->vendor_id) + if (p->vendor_id != codec->core.vendor_id) continue; if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) return alc_codec_rename(codec, p->name); @@ -913,7 +912,7 @@ static int alc_codec_rename_from_preset(struct hda_codec *codec) if (!codec->bus->pci) return 0; for (q = rename_pci_tbl; q->codec_vendor_id; q++) { - if (q->codec_vendor_id != codec->vendor_id) + if (q->codec_vendor_id != codec->core.vendor_id) continue; if (q->pci_subvendor != codec->bus->pci->subsystem_vendor) continue; @@ -1785,7 +1784,7 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) { unsigned int gpiostate, gpiomask, gpiodir; - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + gpiostate = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0); if (!muted) @@ -1793,23 +1792,23 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) else gpiostate &= ~(1 << pin); - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + gpiomask = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_MASK, 0); gpiomask |= (1 << pin); - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + gpiodir = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DIRECTION, 0); gpiodir |= (1 << pin); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, gpiodir); msleep(1); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, gpiostate); } @@ -2269,7 +2268,7 @@ static int patch_alc882(struct hda_codec *codec) spec = codec->spec; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0882: case 0x10ec0885: case 0x10ec0900: @@ -2602,53 +2601,12 @@ static int patch_alc268(struct hda_codec *codec) * ALC269 */ -static int playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ - /* NID is set in alc_build_pcms */ - .ops = { - .open = playback_pcm_open, - .prepare = playback_pcm_prepare, - .cleanup = playback_pcm_cleanup - }, }; static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ - /* NID is set in alc_build_pcms */ }; /* different alc269-variants */ @@ -2912,6 +2870,8 @@ static void alc283_init(struct hda_codec *codec) if (!hp_pin) return; + + msleep(30); hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); /* Index 0x43 Direct Drive HP AMP LPM Control 1 */ @@ -3099,8 +3059,7 @@ static int alc269_resume(struct hda_codec *codec) msleep(200); } - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); + regcache_sync(codec->core.regmap); hda_call_check_power_status(codec, 0x01); /* on some machine, the BIOS will clear the codec gpio data when enter @@ -3108,7 +3067,7 @@ static int alc269_resume(struct hda_codec *codec) * in the driver. */ if (spec->gpio_led) - snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_SET_GPIO_DATA, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_led); if (spec->has_alc5505_dsp) @@ -3153,8 +3112,8 @@ static void alc271_fixup_dmic(struct hda_codec *codec, }; unsigned int cfg; - if (strcmp(codec->chip_name, "ALC271X") && - strcmp(codec->chip_name, "ALC269VB")) + if (strcmp(codec->core.chip_name, "ALC271X") && + strcmp(codec->core.chip_name, "ALC269VB")) return; cfg = snd_hda_codec_get_pincfg(codec, 0x12); if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) @@ -3264,7 +3223,7 @@ static unsigned int led_power_filter(struct hda_codec *codec, snd_hda_set_pin_ctl(codec, nid, snd_hda_codec_get_pin_target(codec, nid)); - return AC_PWRST_D0; + return snd_hda_gen_path_power_filter(codec, nid, power_state); } static void alc269_fixup_hp_mute_led(struct hda_codec *codec, @@ -3520,9 +3479,9 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, } snd_hda_add_verbs(codec, gpio_init); - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); - snd_hda_jack_detect_enable_callback(codec, codec->afg, + snd_hda_jack_detect_enable_callback(codec, codec->core.afg, gpio2_mic_hotkey_event); spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; @@ -3583,6 +3542,14 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) WRITE_COEF(0x32, 0x42a3), {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; static struct coef_fw coef0292[] = { WRITE_COEF(0x76, 0x000e), WRITE_COEF(0x6c, 0x2400), @@ -3605,14 +3572,19 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: + case 0x10ec0256: alc_process_coef_fw(codec, coef0255); break; case 0x10ec0233: case 0x10ec0283: alc_process_coef_fw(codec, coef0233); break; + case 0x10ec0286: + case 0x10ec0288: + alc_process_coef_fw(codec, coef0288); + break; case 0x10ec0292: alc_process_coef_fw(codec, coef0292); break; @@ -3642,6 +3614,14 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, WRITE_COEF(0x26, 0x008c), {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x50, 0x2000, 0), + UPDATE_COEF(0x56, 0x0006, 0), + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), + UPDATE_COEF(0x66, 0x0008, 0x0008), + UPDATE_COEF(0x67, 0x2000, 0x2000), + {} + }; static struct coef_fw coef0292[] = { WRITE_COEF(0x19, 0xa208), WRITE_COEF(0x2e, 0xacf0), @@ -3660,8 +3640,9 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: + case 0x10ec0256: alc_write_coef_idx(codec, 0x45, 0xc489); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); alc_process_coef_fw(codec, coef0255); @@ -3674,6 +3655,13 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, alc_process_coef_fw(codec, coef0233); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); break; + case 0x10ec0286: + case 0x10ec0288: + alc_update_coef_idx(codec, 0x4f, 0x000c, 0); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0288); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; case 0x10ec0292: snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); alc_process_coef_fw(codec, coef0292); @@ -3709,6 +3697,14 @@ static void alc_headset_mode_default(struct hda_codec *codec) WRITE_COEF(0x32, 0x4ea3), {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), /* Set to TRS type */ + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; static struct coef_fw coef0292[] = { WRITE_COEF(0x76, 0x000e), WRITE_COEF(0x6c, 0x2400), @@ -3729,14 +3725,20 @@ static void alc_headset_mode_default(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: + case 0x10ec0256: alc_process_coef_fw(codec, coef0255); break; case 0x10ec0233: case 0x10ec0283: alc_process_coef_fw(codec, coef0233); break; + case 0x10ec0286: + case 0x10ec0288: + alc_process_coef_fw(codec, coef0288); + break; + break; case 0x10ec0292: alc_process_coef_fw(codec, coef0292); break; @@ -3765,6 +3767,13 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) WRITE_COEF(0x32, 0x4ea3), {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; static struct coef_fw coef0292[] = { WRITE_COEF(0x6b, 0xd429), WRITE_COEF(0x76, 0x0008), @@ -3783,14 +3792,21 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: + case 0x10ec0256: alc_process_coef_fw(codec, coef0255); break; case 0x10ec0233: case 0x10ec0283: alc_process_coef_fw(codec, coef0233); break; + case 0x10ec0286: + case 0x10ec0288: + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); + msleep(300); + alc_process_coef_fw(codec, coef0288); + break; case 0x10ec0292: alc_process_coef_fw(codec, coef0292); break; @@ -3819,6 +3835,13 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) WRITE_COEF(0x32, 0x4ea3), {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; static struct coef_fw coef0292[] = { WRITE_COEF(0x6b, 0xe429), WRITE_COEF(0x76, 0x0008), @@ -3837,14 +3860,21 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: + case 0x10ec0256: alc_process_coef_fw(codec, coef0255); break; case 0x10ec0233: case 0x10ec0283: alc_process_coef_fw(codec, coef0233); break; + case 0x10ec0286: + case 0x10ec0288: + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); + msleep(300); + alc_process_coef_fw(codec, coef0288); + break; case 0x10ec0292: alc_process_coef_fw(codec, coef0292); break; @@ -3869,6 +3899,10 @@ static void alc_determine_headset_type(struct hda_codec *codec) conteol) */ {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */ + {} + }; static struct coef_fw coef0293[] = { UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */ WRITE_COEF(0x45, 0xD429), /* Set to ctia type */ @@ -3882,8 +3916,9 @@ static void alc_determine_headset_type(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: + case 0x10ec0256: alc_process_coef_fw(codec, coef0255); msleep(300); val = alc_read_coef_idx(codec, 0x46); @@ -3896,6 +3931,13 @@ static void alc_determine_headset_type(struct hda_codec *codec) val = alc_read_coef_idx(codec, 0x46); is_ctia = (val & 0x0070) == 0x0070; break; + case 0x10ec0286: + case 0x10ec0288: + alc_process_coef_fw(codec, coef0288); + msleep(350); + val = alc_read_coef_idx(codec, 0x50); + is_ctia = (val & 0x0070) == 0x0070; + break; case 0x10ec0292: alc_write_coef_idx(codec, 0x6b, 0xd429); msleep(300); @@ -4079,6 +4121,29 @@ static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec, alc_fixup_headset_mode(codec, fix, action); } +static void alc288_update_headset_jack_cb(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + int present; + + alc_update_headset_jack_cb(codec, jack); + /* Headset Mic enable or disable, only for Dell Dino */ + present = spec->gen.hp_jack_present ? 0x40 : 0; + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + present); +} + +static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_headset_mode(codec, fix, action); + if (action == HDA_FIXUP_ACT_PROBE) { + struct alc_spec *spec = codec->spec; + spec->gen.hp_automute_hook = alc288_update_headset_jack_cb; + } +} + static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -4119,9 +4184,9 @@ static unsigned int alc_power_filter_xps13(struct hda_codec *codec, /* Avoid pop noises when headphones are plugged in */ if (spec->gen.hp_jack_present) - if (nid == codec->afg || nid == 0x02 || nid == 0x15) + if (nid == codec->core.afg || nid == 0x02 || nid == 0x15) return AC_PWRST_D0; - return power_state; + return snd_hda_gen_path_power_filter(codec, nid, power_state); } static void alc_fixup_dell_xps13(struct hda_codec *codec, @@ -4364,6 +4429,7 @@ enum { ALC269_FIXUP_QUANTA_MUTE, ALC269_FIXUP_LIFEBOOK, ALC269_FIXUP_LIFEBOOK_EXTMIC, + ALC269_FIXUP_LIFEBOOK_HP_PIN, ALC269_FIXUP_AMIC, ALC269_FIXUP_DMIC, ALC269VB_FIXUP_AMIC, @@ -4418,6 +4484,9 @@ enum { ALC286_FIXUP_HP_GPIO_LED, ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, ALC280_FIXUP_HP_DOCK_PINS, + ALC288_FIXUP_DELL_HEADSET_MODE, + ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC288_FIXUP_DELL_XPS_13_GPIO6, }; static const struct hda_fixup alc269_fixups[] = { @@ -4517,6 +4586,13 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, + [ALC269_FIXUP_LIFEBOOK_HP_PIN] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x21, 0x0221102f }, /* HP out */ + { } + }, + }, [ALC269_FIXUP_AMIC] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -4906,6 +4982,33 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC280_FIXUP_HP_GPIO4 }, + [ALC288_FIXUP_DELL_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_dell_alc288, + .chained = true, + .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED + }, + [ALC288_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 = ALC288_FIXUP_DELL_HEADSET_MODE + }, + [ALC288_FIXUP_DELL_XPS_13_GPIO6] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x01, AC_VERB_SET_GPIO_MASK, 0x40}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x40}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, + { } + }, + .chained = true, + .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -5010,6 +5113,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x10cf, 0x15dc, "Lifebook T731", ALC269_FIXUP_LIFEBOOK_HP_PIN), SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_BXBT2807_MIC), @@ -5030,12 +5134,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { 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), + SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), @@ -5132,6 +5238,13 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {0x1b, 0x411111f0}, \ {0x1e, 0x411111f0} +#define ALC288_STANDARD_PINS \ + {0x17, 0x411111f0}, \ + {0x18, 0x411111f0}, \ + {0x19, 0x411111f0}, \ + {0x1a, 0x411111f0}, \ + {0x1e, 0x411111f0} + #define ALC290_STANDARD_PINS \ {0x12, 0x99a30130}, \ {0x13, 0x40000000}, \ @@ -5217,6 +5330,16 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x17, 0x40000000}, {0x1d, 0x40700001}, {0x21, 0x02211050}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x12, 0x90a60140}, + {0x13, 0x40000000}, + {0x14, 0x90170110}, + {0x19, 0x411111f0}, + {0x1a, 0x411111f0}, + {0x1b, 0x411111f0}, + {0x1d, 0x40700001}, + {0x1e, 0x411111f0}, + {0x21, 0x02211020}), SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4, {0x12, 0x90a60130}, {0x13, 0x40000000}, @@ -5317,6 +5440,13 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x19, 0x03a11020}, {0x1d, 0x40e00001}, {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL_XPS_13_GPIO6, + ALC288_STANDARD_PINS, + {0x12, 0x90a60120}, + {0x13, 0x40000000}, + {0x14, 0x90170110}, + {0x1d, 0x4076832d}, + {0x21, 0x0321101f}), SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC290_STANDARD_PINS, {0x14, 0x411111f0}, @@ -5457,6 +5587,7 @@ static int patch_alc269(struct hda_codec *codec) spec = codec->spec; spec->gen.shared_mic_vref_pin = 0x18; + codec->power_save_node = 1; snd_hda_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); @@ -5470,7 +5601,7 @@ static int patch_alc269(struct hda_codec *codec) if (has_cdefine_beep(codec)) spec->gen.beep_nid = 0x01; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0269: spec->codec_variant = ALC269_TYPE_ALC269VA; switch (alc_get_coef0(codec) & 0x00f0) { @@ -5553,6 +5684,7 @@ static int patch_alc269(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 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; @@ -5814,9 +5946,9 @@ static int alc662_parse_auto_config(struct hda_codec *codec) static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; const hda_nid_t *ssids; - if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || - codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670 || - codec->vendor_id == 0x10ec0671) + if (codec->core.vendor_id == 0x10ec0272 || codec->core.vendor_id == 0x10ec0663 || + codec->core.vendor_id == 0x10ec0665 || codec->core.vendor_id == 0x10ec0670 || + codec->core.vendor_id == 0x10ec0671) ssids = alc663_ssids; else ssids = alc662_ssids; @@ -5851,7 +5983,7 @@ static void alc_fixup_bass_chmap(struct hda_codec *codec, { if (action == HDA_FIXUP_ACT_BUILD) { struct alc_spec *spec = codec->spec; - spec->gen.pcm_rec[0].stream[0].chmap = asus_pcm_2_1_chmaps; + spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps; } } @@ -5861,7 +5993,7 @@ static unsigned int gpio_led_power_filter(struct hda_codec *codec, unsigned int power_state) { struct alc_spec *spec = codec->spec; - if (nid == codec->afg && power_state == AC_PWRST_D3 && spec->gpio_led) + if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_led) return AC_PWRST_D0; return power_state; } @@ -6359,7 +6491,7 @@ static int patch_alc662(struct hda_codec *codec) alc_fix_pll_init(codec, 0x20, 0x04, 15); - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0668: spec->init_hook = alc668_restore_default_value; break; @@ -6389,7 +6521,7 @@ static int patch_alc662(struct hda_codec *codec) goto error; if (!spec->gen.no_analog && spec->gen.beep_nid) { - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0662: set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); break; @@ -6522,20 +6654,8 @@ MODULE_ALIAS("snd-hda-codec-id:10ec*"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek HD-audio codec"); -static struct hda_codec_preset_list realtek_list = { +static struct hda_codec_driver realtek_driver = { .preset = snd_hda_preset_realtek, - .owner = THIS_MODULE, }; -static int __init patch_realtek_init(void) -{ - return snd_hda_add_codec_preset(&realtek_list); -} - -static void __exit patch_realtek_exit(void) -{ - snd_hda_delete_codec_preset(&realtek_list); -} - -module_init(patch_realtek_init) -module_exit(patch_realtek_exit) +module_hda_codec_driver(realtek_driver); diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index 3208ad69583e..5104bebb2286 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -83,7 +83,6 @@ struct si3054_spec { unsigned international; - struct hda_pcm pcm; }; @@ -199,15 +198,15 @@ static const struct hda_pcm_stream si3054_pcm = { static int si3054_build_pcms(struct hda_codec *codec) { - struct si3054_spec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm; - codec->num_pcms = 1; - codec->pcm_info = info; - info->name = "Si3054 Modem"; + struct hda_pcm *info; + + info = snd_hda_codec_pcm_new(codec, "Si3054 Modem"); + if (!info) + return -ENOMEM; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm; info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->mfg; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->core.mfg; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->core.mfg; info->pcm_type = HDA_PCM_TYPE_MODEM; return 0; } @@ -223,8 +222,12 @@ static int si3054_init(struct hda_codec *codec) unsigned wait_count; u16 val; + if (snd_hdac_regmap_add_vendor_verb(&codec->core, + SI3054_VERB_WRITE_NODE)) + return -ENOMEM; + snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0); - snd_hda_codec_write(codec, codec->mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0); + snd_hda_codec_write(codec, codec->core.mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0); SET_REG(codec, SI3054_LINE_RATE, 9600); SET_REG(codec, SI3054_LINE_LEVEL, SI3054_DTAG_MASK|SI3054_ATAG_MASK); SET_REG(codec, SI3054_EXTENDED_MID, 0); @@ -319,20 +322,8 @@ MODULE_ALIAS("snd-hda-codec-id:18540018"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Si3054 HD-audio modem codec"); -static struct hda_codec_preset_list si3054_list = { +static struct hda_codec_driver si3054_driver = { .preset = snd_hda_preset_si3054, - .owner = THIS_MODULE, }; -static int __init patch_si3054_init(void) -{ - return snd_hda_add_codec_preset(&si3054_list); -} - -static void __exit patch_si3054_exit(void) -{ - snd_hda_delete_codec_preset(&si3054_list); -} - -module_init(patch_si3054_init) -module_exit(patch_si3054_exit) +module_hda_codec_driver(si3054_driver); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 87eff3173ce9..43c99ce4a520 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -299,32 +299,33 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, unsigned int dir_mask, unsigned int data) { unsigned int gpiostate, gpiomask, gpiodir; + hda_nid_t fg = codec->core.afg; codec_dbg(codec, "%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + gpiostate = snd_hda_codec_read(codec, fg, 0, AC_VERB_GET_GPIO_DATA, 0); gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + gpiomask = snd_hda_codec_read(codec, fg, 0, AC_VERB_GET_GPIO_MASK, 0); gpiomask |= mask; - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + gpiodir = snd_hda_codec_read(codec, fg, 0, AC_VERB_GET_GPIO_DIRECTION, 0); gpiodir |= dir_mask; /* Configure GPIOx as CMOS */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); + snd_hda_codec_write(codec, fg, 0, 0x7e7, 0); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_read(codec, codec->afg, 0, + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ msleep(1); - snd_hda_codec_read(codec, codec->afg, 0, + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } @@ -387,7 +388,7 @@ static unsigned int stac_vref_led_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) { - if (nid == codec->afg && power_state == AC_PWRST_D3) + if (nid == codec->core.afg && power_state == AC_PWRST_D3) return AC_PWRST_D1; return snd_hda_gen_path_power_filter(codec, nid, power_state); } @@ -432,7 +433,7 @@ static void stac_update_outputs(struct hda_codec *codec) if (spec->gpio_mute) spec->gen.master_mute = - !(snd_hda_codec_read(codec, codec->afg, 0, + !(snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); snd_hda_gen_update_outputs(codec); @@ -476,7 +477,7 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, if (val != spec->power_map_bits) { spec->power_map_bits = val; if (do_write) - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_IDT_SET_POWER_MAP, val); } } @@ -508,7 +509,8 @@ static void jack_update_power(struct hda_codec *codec, false); } - snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_IDT_SET_POWER_MAP, + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_IDT_SET_POWER_MAP, spec->power_map_bits); } @@ -517,10 +519,10 @@ static void stac_vref_event(struct hda_codec *codec, { unsigned int data; - data = snd_hda_codec_read(codec, codec->afg, 0, + data = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0); /* toggle VREF state based on GPIOx status */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, + snd_hda_codec_write(codec, codec->core.afg, 0, 0x7e0, !!(data & (1 << event->private_data))); } @@ -622,7 +624,7 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, /* Only return the bits defined by the shift value of the * first two bytes of the mask */ - dac_mode = snd_hda_codec_read(codec, codec->afg, 0, + dac_mode = snd_hda_codec_read(codec, codec->core.afg, 0, kcontrol->private_value & 0xFFFF, 0x0); dac_mode >>= spec->aloopback_shift; @@ -634,7 +636,7 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, dac_mode &= ~idx_val; } - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, kcontrol->private_value >> 16, dac_mode); return 1; @@ -658,11 +660,11 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, /* check whether it's a HP laptop with a docking port */ static bool hp_bnb2011_with_dock(struct hda_codec *codec) { - if (codec->vendor_id != 0x111d7605 && - codec->vendor_id != 0x111d76d1) + if (codec->core.vendor_id != 0x111d7605 && + codec->core.vendor_id != 0x111d76d1) return false; - switch (codec->subsystem_id) { + switch (codec->core.subsystem_id) { case 0x103c1618: case 0x103c1619: case 0x103c161a: @@ -733,7 +735,7 @@ static void set_hp_led_gpio(struct hda_codec *codec) if (spec->gpio_led) return; - gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + gpio = snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); gpio &= AC_GPIO_IO_COUNT; if (gpio > 3) spec->gpio_led = 0x08; /* GPIO 3 */ @@ -777,7 +779,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) &spec->gpio_led_polarity, &spec->gpio_led) == 2) { unsigned int max_gpio; - max_gpio = snd_hda_param_read(codec, codec->afg, + max_gpio = snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); max_gpio &= AC_GPIO_IO_COUNT; if (spec->gpio_led < max_gpio) @@ -807,7 +809,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) * we statically set the GPIO - if not a B-series system * and default polarity is provided */ - if (!hp_blike_system(codec->subsystem_id) && + if (!hp_blike_system(codec->core.subsystem_id) && (default_polarity == 0 || default_polarity == 1)) { set_hp_led_gpio(codec); spec->gpio_led_polarity = default_polarity; @@ -1048,12 +1050,9 @@ static const struct hda_verb stac92hd71bxx_core_init[] = { {} }; -static const struct hda_verb stac92hd71bxx_unmute_core_init[] = { +static const hda_nid_t stac92hd71bxx_unmute_nids[] = { /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */ - { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {} + 0x0f, 0x0a, 0x0d, 0 }; static const struct hda_verb stac925x_core_init[] = { @@ -2132,8 +2131,10 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ +#ifdef CONFIG_PM /* resetting controller clears GPIO, so we need to keep on */ - codec->bus->power_keep_link_on = 1; + codec->core.power_caps &= ~AC_PWRST_CLKSTOP; +#endif } } @@ -3029,9 +3030,9 @@ static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, return; /* Enable VREF power saving on GPIO1 detect */ - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); - jack = snd_hda_jack_detect_enable_callback(codec, codec->afg, + jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg, stac_vref_event); if (!IS_ERR(jack)) jack->private_data = 0x02; @@ -3091,7 +3092,7 @@ static void stac92hd71bxx_fixup_hp(struct hda_codec *codec, if (action != HDA_FIXUP_ACT_PRE_PROBE) return; - if (hp_blike_system(codec->subsystem_id)) { + if (hp_blike_system(codec->core.subsystem_id)) { unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || @@ -3790,7 +3791,7 @@ static void stac927x_fixup_dell_dmic(struct hda_codec *codec, if (action != HDA_FIXUP_ACT_PRE_PROBE) return; - if (codec->subsystem_id != 0x1028022f) { + if (codec->core.subsystem_id != 0x1028022f) { /* GPIO2 High = Enable EAPD */ spec->eapd_mask = spec->gpio_mask = 0x04; spec->gpio_dir = spec->gpio_data = 0x04; @@ -4051,9 +4052,9 @@ static void stac9205_fixup_dell_m43(struct hda_codec *codec, snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs); /* Enable unsol response for GPIO4/Dock HP connection */ - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); - jack = snd_hda_jack_detect_enable_callback(codec, codec->afg, + jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg, stac_vref_event); if (!IS_ERR(jack)) jack->private_data = 0x01; @@ -4223,6 +4224,12 @@ static int stac_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + if (spec->vref_mute_led_nid) { + err = snd_hda_gen_fix_pin_power(codec, spec->vref_mute_led_nid); + if (err < 0) + return err; + } + /* setup analog beep controls */ if (spec->anabeep_nid > 0) { err = stac_auto_create_beep_ctls(codec, @@ -4259,6 +4266,10 @@ static int stac_parse_auto_config(struct hda_codec *codec) if (spec->aloopback_ctl && snd_hda_get_bool_hint(codec, "loopback") == 1) { + unsigned int wr_verb = + spec->aloopback_ctl->private_value >> 16; + if (snd_hdac_regmap_add_vendor_verb(&codec->core, wr_verb)) + return -ENOMEM; if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl)) return -ENOMEM; } @@ -4294,7 +4305,7 @@ static int stac_init(struct hda_codec *codec) /* sync the power-map */ if (spec->num_pwrs) - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_IDT_SET_POWER_MAP, spec->power_map_bits); @@ -4330,7 +4341,7 @@ static void stac_shutup(struct hda_codec *codec) static void stac92hd_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - if (nid == codec->afg) + if (nid == codec->core.afg) snd_iprintf(buffer, "Power-Map: 0x%02x\n", snd_hda_codec_read(codec, nid, 0, AC_VERB_IDT_GET_POWER_MAP, 0)); @@ -4341,7 +4352,7 @@ static void analog_loop_proc_hook(struct snd_info_buffer *buffer, unsigned int verb) { snd_iprintf(buffer, "Analog Loopback: 0x%02x\n", - snd_hda_codec_read(codec, codec->afg, 0, verb, 0)); + snd_hda_codec_read(codec, codec->core.afg, 0, verb, 0)); } /* stac92hd71bxx, stac92hd73xx */ @@ -4349,21 +4360,21 @@ static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { stac92hd_proc_hook(buffer, codec, nid); - if (nid == codec->afg) + if (nid == codec->core.afg) analog_loop_proc_hook(buffer, codec, 0xfa0); } static void stac9205_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - if (nid == codec->afg) + if (nid == codec->core.afg) analog_loop_proc_hook(buffer, codec, 0xfe0); } static void stac927x_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - if (nid == codec->afg) + if (nid == codec->core.afg) analog_loop_proc_hook(buffer, codec, 0xfeb); } #else @@ -4392,6 +4403,7 @@ static const struct hda_codec_ops stac_patch_ops = { #ifdef CONFIG_PM .suspend = stac_suspend, #endif + .stream_pm = snd_hda_gen_stream_pm, .reboot_notify = stac_shutup, }; @@ -4485,6 +4497,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec) return err; spec = codec->spec; + codec->power_save_node = 1; spec->linear_tone_beep = 0; spec->gen.mixer_nid = 0x1d; spec->have_spdif_mux = 1; @@ -4587,9 +4600,11 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) if (err < 0) return err; - codec->epss = 0; /* longer delay needed for D3 */ + /* longer delay needed for D3 */ + codec->core.power_caps &= ~AC_PWRST_EPSS; spec = codec->spec; + codec->power_save_node = 1; spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; spec->gen.power_down_unused = 1; @@ -4636,9 +4651,11 @@ static int patch_stac92hd95(struct hda_codec *codec) if (err < 0) return err; - codec->epss = 0; /* longer delay needed for D3 */ + /* longer delay needed for D3 */ + codec->core.power_caps &= ~AC_PWRST_EPSS; spec = codec->spec; + codec->power_save_node = 1; spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; spec->gen.power_down_unused = 1; @@ -4672,7 +4689,7 @@ static int patch_stac92hd95(struct hda_codec *codec) static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; - const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init; + const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids; int err; err = alloc_stac_spec(codec); @@ -4680,6 +4697,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) return err; spec = codec->spec; + codec->power_save_node = 1; spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; spec->gen.power_down_unused = 1; @@ -4693,23 +4711,23 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec->gpio_dir = 0x01; spec->gpio_data = 0x01; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x111d76b6: /* 4 Port without Analog Mixer */ case 0x111d76b7: - unmute_init++; + unmute_nids++; break; case 0x111d7608: /* 5 Port with Analog Mixer */ - if ((codec->revision_id & 0xf) == 0 || - (codec->revision_id & 0xf) == 1) + if ((codec->core.revision_id & 0xf) == 0 || + (codec->core.revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ /* disable VSW */ - unmute_init++; + unmute_nids++; snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0); snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); break; case 0x111d7603: /* 6 Port with Analog Mixer */ - if ((codec->revision_id & 0xf) == 1) + if ((codec->core.revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ break; @@ -4718,8 +4736,12 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB) snd_hda_add_verbs(codec, stac92hd71bxx_core_init); - if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) - snd_hda_sequence_write_cache(codec, unmute_init); + if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) { + const hda_nid_t *p; + for (p = unmute_nids; *p; p++) + snd_hda_codec_amp_init_stereo(codec, *p, HDA_INPUT, 0, + 0xff, 0x00); + } spec->aloopback_ctl = &stac92hd71bxx_loopback; spec->aloopback_mask = 0x50; @@ -5091,20 +5113,8 @@ MODULE_ALIAS("snd-hda-codec-id:111d*"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec"); -static struct hda_codec_preset_list sigmatel_list = { +static struct hda_codec_driver sigmatel_driver = { .preset = snd_hda_preset_sigmatel, - .owner = THIS_MODULE, }; -static int __init patch_sigmatel_init(void) -{ - return snd_hda_add_codec_preset(&sigmatel_list); -} - -static void __exit patch_sigmatel_exit(void) -{ - snd_hda_delete_codec_preset(&sigmatel_list); -} - -module_init(patch_sigmatel_init) -module_exit(patch_sigmatel_exit) +module_hda_codec_driver(sigmatel_driver); diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 3de6d3d779c9..31a95cca015d 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -99,7 +99,6 @@ struct via_spec { /* HP mode source */ unsigned int dmic_enabled; - unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; /* analog low-power control */ @@ -109,8 +108,7 @@ struct via_spec { int hp_work_active; int vt1708_jack_detect; - void (*set_widgets_power_state)(struct hda_codec *codec); - unsigned int dac_stream_tag[4]; + unsigned int beep_amp; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); @@ -133,17 +131,18 @@ static struct via_spec *via_new_spec(struct hda_codec *codec) /* VT1708BCE & VT1708S are almost same */ if (spec->codec_type == VT1708BCE) spec->codec_type = VT1708S; - spec->no_pin_power_ctl = 1; spec->gen.indep_hp = 1; spec->gen.keep_eapd_on = 1; spec->gen.pcm_playback_hook = via_playback_pcm_hook; spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; + codec->power_save_node = 1; + spec->gen.power_down_unused = 1; return spec; } static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) { - u32 vendor_id = codec->vendor_id; + u32 vendor_id = codec->core.vendor_id; u16 ven_id = vendor_id >> 16; u16 dev_id = vendor_id & 0xffff; enum VIA_HDA_CODEC codec_type; @@ -222,98 +221,13 @@ static void vt1708_update_hp_work(struct hda_codec *codec) if (!spec->hp_work_active) { codec->jackpoll_interval = msecs_to_jiffies(100); snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); - queue_delayed_work(codec->bus->workq, - &codec->jackpoll_work, 0); + schedule_delayed_work(&codec->jackpoll_work, 0); spec->hp_work_active = true; } } else if (!hp_detect_with_aa(codec)) vt1708_stop_hp_work(codec); } -static void set_widgets_power_state(struct hda_codec *codec) -{ -#if 0 /* FIXME: the assumed connections don't match always with the - * actual routes by the generic parser, so better to disable - * the control for safety. - */ - struct via_spec *spec = codec->spec; - if (spec->set_widgets_power_state) - spec->set_widgets_power_state(codec); -#endif -} - -static void update_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int parm) -{ - if (snd_hda_check_power_state(codec, nid, parm)) - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); -} - -static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int parm, unsigned int index) -{ - struct via_spec *spec = codec->spec; - unsigned int format; - - if (snd_hda_check_power_state(codec, nid, parm)) - return; - format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); - if (format && (spec->dac_stream_tag[index] != format)) - spec->dac_stream_tag[index] = format; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); - if (parm == AC_PWRST_D0) { - format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); - if (!format && (spec->dac_stream_tag[index] != format)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, - spec->dac_stream_tag[index]); - } -} - -static bool smart51_enabled(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - return spec->gen.ext_channel_count > 2; -} - -static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->gen.multi_ios; i++) - if (spec->gen.multi_io[i].pin == pin) - return true; - return false; -} - -static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int *affected_parm) -{ - unsigned parm; - unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); - unsigned no_presence = (def_conf & AC_DEFCFG_MISC) - >> AC_DEFCFG_MISC_SHIFT - & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ - struct via_spec *spec = codec->spec; - unsigned present = 0; - - no_presence |= spec->no_pin_power_ctl; - if (!no_presence) - present = snd_hda_jack_detect(codec, nid); - if ((smart51_enabled(codec) && is_smart51_pins(codec, nid)) - || ((no_presence || present) - && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { - *affected_parm = AC_PWRST_D0; /* if it's connected */ - parm = AC_PWRST_D0; - } else - parm = AC_PWRST_D3; - - update_power_state(codec, nid, parm); -} - static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -324,8 +238,7 @@ static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; + ucontrol->value.enumerated.item[0] = codec->power_save_node; return 0; } @@ -334,12 +247,12 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - unsigned int val = !ucontrol->value.enumerated.item[0]; + bool val = !!ucontrol->value.enumerated.item[0]; - if (val == spec->no_pin_power_ctl) + if (val == codec->power_save_node) return 0; - spec->no_pin_power_ctl = val; - set_widgets_power_state(codec); + codec->power_save_node = val; + spec->gen.power_down_unused = val; analog_low_current_mode(codec); return 1; } @@ -355,6 +268,59 @@ static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { {} /* terminator */ }; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static inline void set_beep_amp(struct via_spec *spec, hda_nid_t nid, + int idx, int dir) +{ + spec->gen.beep_nid = nid; + spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); +} + +/* additional beep mixers; the actual parameters are overwritten at build */ +static const struct snd_kcontrol_new cxt_beep_mixer[] = { + HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), + { } /* end */ +}; + +/* create beep controls if needed */ +static int add_beep_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + if (spec->beep_amp) { + const struct snd_kcontrol_new *knew; + for (knew = cxt_beep_mixer; knew->name; knew++) { + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = spec->beep_amp; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; + } + } + return 0; +} + +static void auto_parse_beep(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t nid; + + for_each_hda_codec_node(nid, codec) + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { + set_beep_amp(spec, nid, 0, HDA_OUTPUT); + break; + } +} +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#define add_beep_ctls(codec) 0 +#define auto_parse_beep(codec) +#endif /* check AA path's mute status */ static bool is_aa_path_mute(struct hda_codec *codec) @@ -384,7 +350,7 @@ static void __analog_low_current_mode(struct hda_codec *codec, bool force) bool enable; unsigned int verb, parm; - if (spec->no_pin_power_ctl) + if (!codec->power_save_node) enable = false; else enable = is_aa_path_mute(codec) && !spec->gen.active_streams; @@ -424,7 +390,7 @@ static void __analog_low_current_mode(struct hda_codec *codec, bool force) return; /* other codecs are not supported */ } /* send verb */ - snd_hda_codec_write(codec, codec->afg, 0, verb, parm); + snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm); } static void analog_low_current_mode(struct hda_codec *codec) @@ -441,8 +407,11 @@ static int via_build_controls(struct hda_codec *codec) if (err < 0) return err; - if (spec->set_widgets_power_state) - spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; + err = add_beep_ctls(codec); + if (err < 0) + return err; + + spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); @@ -486,7 +455,6 @@ static int via_suspend(struct hda_codec *codec) static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct via_spec *spec = codec->spec; - set_widgets_power_state(codec); analog_low_current_mode(codec); vt1708_update_hp_work(codec); return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); @@ -504,6 +472,7 @@ 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, .check_power_status = via_check_power_status, @@ -574,34 +543,6 @@ static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { {} /* terminator */ }; -static void via_jack_powerstate_event(struct hda_codec *codec, - struct hda_jack_callback *tbl) -{ - set_widgets_power_state(codec); -} - -static void via_set_jack_unsol_events(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - hda_nid_t pin; - int i; - - for (i = 0; i < cfg->line_outs; i++) { - pin = cfg->line_out_pins[i]; - if (pin && is_jack_detectable(codec, pin)) - snd_hda_jack_detect_enable_callback(codec, pin, - via_jack_powerstate_event); - } - - for (i = 0; i < cfg->num_inputs; i++) { - pin = cfg->line_out_pins[i]; - if (pin && is_jack_detectable(codec, pin)) - snd_hda_jack_detect_enable_callback(codec, pin, - via_jack_powerstate_event); - } -} - static const struct badness_table via_main_out_badness = { .no_primary_dac = 0x10000, .no_dac = 0x4000, @@ -631,11 +572,15 @@ static int via_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + auto_parse_beep(codec); + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); if (err < 0) return err; - via_set_jack_unsol_events(codec); + /* disable widget PM at start for compatibility */ + codec->power_save_node = 0; + spec->gen.power_down_unused = 0; return 0; } @@ -648,7 +593,6 @@ static int via_init(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->init_verbs[i]); /* init power states */ - set_widgets_power_state(codec); __analog_low_current_mode(codec, true); snd_hda_gen_init(codec); @@ -676,15 +620,17 @@ static int vt1708_build_pcms(struct hda_codec *codec) int i, err; err = snd_hda_gen_build_pcms(codec); - if (err < 0 || codec->vendor_id != 0x11061708) + if (err < 0 || codec->core.vendor_id != 0x11061708) return err; /* We got noisy outputs on the right channel on VT1708 when * 24bit samples are used. Until any workaround is found, * disable the 24bit format, so far. */ - for (i = 0; i < codec->num_pcms; i++) { - struct hda_pcm *info = &spec->gen.pcm_rec[i]; + for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) { + struct hda_pcm *info = spec->gen.pcm_rec[i]; + if (!info) + continue; if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || info->pcm_type != HDA_PCM_TYPE_AUDIO) continue; @@ -766,78 +712,6 @@ static int patch_vt1709(struct hda_codec *codec) return 0; } -static void set_widgets_power_state_vt1708B(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - int is_8ch = 0; - if ((spec->codec_type != VT1708B_4CH) && - (codec->vendor_id != 0x11064397)) - is_8ch = 1; - - /* SW0 (17h) = stereo mixer */ - imux_is_smixer = - (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) - == ((spec->codec_type == VT1708S) ? 5 : 0)); - /* inputs */ - /* PW 1/2/5 (1ah/1bh/1eh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1a, &parm); - set_pin_power_state(codec, 0x1b, &parm); - set_pin_power_state(codec, 0x1e, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* SW0 (17h), AIW 0/1 (13h/14h) */ - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x13, parm); - update_power_state(codec, 0x14, parm); - - /* outputs */ - /* PW0 (19h), SW1 (18h), AOW1 (11h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x19, &parm); - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1b, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x11, parm); - - /* PW6 (22h), SW2 (26h), AOW2 (24h) */ - if (is_8ch) { - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x22, &parm); - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x26, parm); - update_power_state(codec, 0x24, parm); - } else if (codec->vendor_id == 0x11064397) { - /* PW7(23h), SW2(27h), AOW2(25h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x23, &parm); - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x27, parm); - update_power_state(codec, 0x25, parm); - } - - /* PW 3/4/7 (1ch/1dh/23h) */ - parm = AC_PWRST_D3; - /* force to D0 for internal Speaker */ - set_pin_power_state(codec, 0x1c, &parm); - set_pin_power_state(codec, 0x1d, &parm); - if (is_8ch) - set_pin_power_state(codec, 0x23, &parm); - - /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ - update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, parm); - if (is_8ch) { - update_power_state(codec, 0x25, parm); - update_power_state(codec, 0x27, parm); - } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled) - update_power_state(codec, 0x25, parm); -} - static int patch_vt1708S(struct hda_codec *codec); static int patch_vt1708B(struct hda_codec *codec) { @@ -862,9 +736,6 @@ static int patch_vt1708B(struct hda_codec *codec) } codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; - return 0; } @@ -905,19 +776,19 @@ static int patch_vt1708S(struct hda_codec *codec) /* correct names for VT1708BCE */ if (get_codec_type(codec) == VT1708BCE) { - kfree(codec->chip_name); - codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL); + snprintf(codec->card->mixername, + sizeof(codec->card->mixername), + "%s %s", codec->core.vendor_name, codec->core.chip_name); } /* correct names for VT1705 */ - if (codec->vendor_id == 0x11064397) { - kfree(codec->chip_name); - codec->chip_name = kstrdup("VT1705", GFP_KERNEL); - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); + if (codec->core.vendor_id == 0x11064397) { + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL); + snprintf(codec->card->mixername, + sizeof(codec->card->mixername), + "%s %s", codec->core.vendor_name, codec->core.chip_name); } /* automatic parse from the BIOS config */ @@ -930,8 +801,6 @@ static int patch_vt1708S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; return 0; } @@ -945,36 +814,6 @@ static const struct hda_verb vt1702_init_verbs[] = { { } }; -static void set_widgets_power_state_vt1702(struct hda_codec *codec) -{ - int imux_is_smixer = - snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; - unsigned int parm; - /* inputs */ - /* PW 1/2/5 (14h/15h/18h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x14, &parm); - set_pin_power_state(codec, 0x15, &parm); - set_pin_power_state(codec, 0x18, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ - /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ - update_power_state(codec, 0x13, parm); - update_power_state(codec, 0x12, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x20, parm); - - /* outputs */ - /* PW 3/4 (16h/17h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x17, &parm); - set_pin_power_state(codec, 0x16, &parm); - /* MW0 (1ah), AOW 0/1 (10h/1dh) */ - update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x1d, parm); -} - static int patch_vt1702(struct hda_codec *codec) { struct via_spec *spec; @@ -1004,8 +843,6 @@ static int patch_vt1702(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1702; return 0; } @@ -1020,71 +857,6 @@ static const struct hda_verb vt1718S_init_verbs[] = { { } }; -static void set_widgets_power_state_vt1718S(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm, parm2; - /* MUX6 (1eh) = stereo mixer */ - imux_is_smixer = - snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x27, &parm); - update_power_state(codec, 0x1a, parm); - parm2 = parm; /* for pin 0x0b */ - - /* PW2 (26h), AOW2 (ah) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x26, &parm); - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x2b, &parm); - update_power_state(codec, 0xa, parm); - - /* PW0 (24h), AOW0 (8h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - if (!spec->gen.indep_hp_enabled) /* check for redirected HP */ - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x8, parm); - if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) - parm = parm2; - update_power_state(codec, 0xb, parm); - /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ - update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm); - - /* PW1 (25h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x2a, &parm); - update_power_state(codec, 0x9, parm); - - if (spec->gen.indep_hp_enabled) { - /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x1b, parm); - update_power_state(codec, 0x34, parm); - update_power_state(codec, 0xc, parm); - } -} - /* Add a connection to the primary DAC from AA-mixer for some codecs * This isn't listed from the raw info, but the chip has a secret connection. */ @@ -1105,8 +877,7 @@ static int add_secret_dac_path(struct hda_codec *codec) } /* find the primary DAC and add to the connection list */ - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int caps = get_wcaps(codec, nid); if (get_wcaps_type(caps) == AC_WID_AUD_OUT && !(caps & AC_WCAP_DIGITAL)) { @@ -1145,9 +916,6 @@ static int patch_vt1718S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1718S; - return 0; } @@ -1187,7 +955,6 @@ static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_CONNECT_SEL, index); spec->dmic_enabled = index; - set_widgets_power_state(codec); return 1; } @@ -1222,95 +989,6 @@ static const struct hda_verb vt1716S_init_verbs[] = { { } }; -static void set_widgets_power_state_vt1716S(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - unsigned int mono_out, present; - /* SW0 (17h) = stereo mixer */ - imux_is_smixer = - (snd_hda_codec_read(codec, 0x17, 0, - AC_VERB_GET_CONNECT_SEL, 0x00) == 5); - /* inputs */ - /* PW 1/2/5 (1ah/1bh/1eh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1a, &parm); - set_pin_power_state(codec, 0x1b, &parm); - set_pin_power_state(codec, 0x1e, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* SW0 (17h), AIW0(13h) */ - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x13, parm); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1e, &parm); - /* PW11 (22h) */ - if (spec->dmic_enabled) - set_pin_power_state(codec, 0x22, &parm); - else - update_power_state(codec, 0x22, AC_PWRST_D3); - - /* SW2(26h), AIW1(14h) */ - update_power_state(codec, 0x26, parm); - update_power_state(codec, 0x14, parm); - - /* outputs */ - /* PW0 (19h), SW1 (18h), AOW1 (11h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x19, &parm); - /* Smart 5.1 PW2(1bh) */ - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1b, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x11, parm); - - /* PW7 (23h), SW3 (27h), AOW3 (25h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x23, &parm); - /* Smart 5.1 PW1(1ah) */ - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x27, parm); - - /* Smart 5.1 PW5(1eh) */ - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1e, &parm); - update_power_state(codec, 0x25, parm); - - /* Mono out */ - /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ - present = snd_hda_jack_detect(codec, 0x1c); - - if (present) - mono_out = 0; - else { - present = snd_hda_jack_detect(codec, 0x1d); - if (!spec->gen.indep_hp_enabled && present) - mono_out = 0; - else - mono_out = 1; - } - parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; - update_power_state(codec, 0x28, parm); - update_power_state(codec, 0x29, parm); - update_power_state(codec, 0x2a, parm); - - /* PW 3/4 (1ch/1dh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1c, &parm); - set_pin_power_state(codec, 0x1d, &parm); - /* HP Independent Mode, power on AOW3 */ - if (spec->gen.indep_hp_enabled) - update_power_state(codec, 0x25, parm); - - /* force to D0 for internal Speaker */ - /* MW0 (16h), AOW0 (10h) */ - update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm); -} - static int patch_vt1716S(struct hda_codec *codec) { struct via_spec *spec; @@ -1338,8 +1016,6 @@ static int patch_vt1716S(struct hda_codec *codec) spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1716S; return 0; } @@ -1365,98 +1041,6 @@ static const struct hda_verb vt1802_init_verbs[] = { { } }; -static void set_widgets_power_state_vt2002P(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - unsigned int present; - /* MUX9 (1eh) = stereo mixer */ - imux_is_smixer = - snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - parm = AC_PWRST_D0; - /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* AOW0 (8h)*/ - update_power_state(codec, 0x8, parm); - - if (spec->codec_type == VT1802) { - /* PW4 (28h), MW4 (18h), MUX4(38h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x38, parm); - } else { - /* PW4 (26h), MW4 (1ch), MUX4(37h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x26, &parm); - update_power_state(codec, 0x1c, parm); - update_power_state(codec, 0x37, parm); - } - - if (spec->codec_type == VT1802) { - /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x15, parm); - update_power_state(codec, 0x35, parm); - } else { - /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x19, parm); - update_power_state(codec, 0x35, parm); - } - - if (spec->gen.indep_hp_enabled) - update_power_state(codec, 0x9, AC_PWRST_D0); - - /* Class-D */ - /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ - present = snd_hda_jack_detect(codec, 0x25); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - parm = present ? AC_PWRST_D3 : AC_PWRST_D0; - if (spec->codec_type == VT1802) - update_power_state(codec, 0x14, parm); - else - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x34, parm); - - /* Mono Out */ - present = snd_hda_jack_detect(codec, 0x26); - - parm = present ? AC_PWRST_D3 : AC_PWRST_D0; - if (spec->codec_type == VT1802) { - /* PW15 (33h), MW8(1ch), MUX8(3ch) */ - update_power_state(codec, 0x33, parm); - update_power_state(codec, 0x1c, parm); - update_power_state(codec, 0x3c, parm); - } else { - /* PW15 (31h), MW8(17h), MUX8(3bh) */ - update_power_state(codec, 0x31, parm); - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x3b, parm); - } - /* MW9 (21h) */ - if (imux_is_smixer || !is_aa_path_mute(codec)) - update_power_state(codec, 0x21, AC_PWRST_D0); - else - update_power_state(codec, 0x21, AC_PWRST_D3); -} - /* * pin fix-up */ @@ -1540,8 +1124,6 @@ static int patch_vt2002P(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt2002P; return 0; } @@ -1555,81 +1137,6 @@ static const struct hda_verb vt1812_init_verbs[] = { { } }; -static void set_widgets_power_state_vt1812(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - unsigned int parm; - unsigned int present; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - parm = AC_PWRST_D0; - /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* AOW0 (8h)*/ - update_power_state(codec, 0x8, AC_PWRST_D0); - - /* PW4 (28h), MW4 (18h), MUX4(38h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x38, parm); - - /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x15, parm); - update_power_state(codec, 0x35, parm); - if (spec->gen.indep_hp_enabled) - update_power_state(codec, 0x9, AC_PWRST_D0); - - /* Internal Speaker */ - /* PW0 (24h), MW0(14h), MUX0(34h) */ - present = snd_hda_jack_detect(codec, 0x25); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - if (present) { - update_power_state(codec, 0x14, AC_PWRST_D3); - update_power_state(codec, 0x34, AC_PWRST_D3); - } else { - update_power_state(codec, 0x14, AC_PWRST_D0); - update_power_state(codec, 0x34, AC_PWRST_D0); - } - - - /* Mono Out */ - /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ - present = snd_hda_jack_detect(codec, 0x28); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x31, &parm); - if (present) { - update_power_state(codec, 0x1c, AC_PWRST_D3); - update_power_state(codec, 0x3c, AC_PWRST_D3); - update_power_state(codec, 0x3e, AC_PWRST_D3); - } else { - update_power_state(codec, 0x1c, AC_PWRST_D0); - update_power_state(codec, 0x3c, AC_PWRST_D0); - update_power_state(codec, 0x3e, AC_PWRST_D0); - } - - /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x33, &parm); - update_power_state(codec, 0x1d, parm); - update_power_state(codec, 0x3d, parm); - -} - /* patch for vt1812 */ static int patch_vt1812(struct hda_codec *codec) { @@ -1656,8 +1163,6 @@ static int patch_vt1812(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1812; return 0; } @@ -1673,84 +1178,6 @@ static const struct hda_verb vt3476_init_verbs[] = { { } }; -static void set_widgets_power_state_vt3476(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm, parm2; - /* MUX10 (1eh) = stereo mixer */ - imux_is_smixer = - snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* PW3 (27h), MW3(37h), AOW3 (bh) */ - if (spec->codec_type == VT1705CF) { - parm = AC_PWRST_D3; - update_power_state(codec, 0x27, parm); - update_power_state(codec, 0x37, parm); - } else { - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x27, &parm); - update_power_state(codec, 0x37, parm); - } - - /* PW2 (26h), MW2(36h), AOW2 (ah) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x26, &parm); - update_power_state(codec, 0x36, parm); - if (smart51_enabled(codec)) { - /* PW7(2bh), MW7(3bh), MUX7(1Bh) */ - set_pin_power_state(codec, 0x2b, &parm); - update_power_state(codec, 0x3b, parm); - update_power_state(codec, 0x1b, parm); - } - update_conv_power_state(codec, 0xa, parm, 2); - - /* PW1 (25h), MW1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x35, parm); - if (smart51_enabled(codec)) { - /* PW6(2ah), MW6(3ah), MUX6(1ah) */ - set_pin_power_state(codec, 0x2a, &parm); - update_power_state(codec, 0x3a, parm); - update_power_state(codec, 0x1a, parm); - } - update_conv_power_state(codec, 0x9, parm, 1); - - /* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x38, parm); - update_power_state(codec, 0x18, parm); - if (spec->gen.indep_hp_enabled) - update_conv_power_state(codec, 0xb, parm, 3); - parm2 = parm; /* for pin 0x0b */ - - /* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - update_power_state(codec, 0x34, parm); - if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) - parm = parm2; - update_conv_power_state(codec, 0x8, parm, 0); - /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ - update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm); -} - static int patch_vt3476(struct hda_codec *codec) { struct via_spec *spec; @@ -1774,9 +1201,6 @@ static int patch_vt3476(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt3476; - return 0; } @@ -1884,23 +1308,11 @@ static const struct hda_codec_preset snd_hda_preset_via[] = { MODULE_ALIAS("snd-hda-codec-id:1106*"); -static struct hda_codec_preset_list via_list = { +static struct hda_codec_driver via_driver = { .preset = snd_hda_preset_via, - .owner = THIS_MODULE, }; MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("VIA HD-audio codec"); -static int __init patch_via_init(void) -{ - return snd_hda_add_codec_preset(&via_list); -} - -static void __exit patch_via_exit(void) -{ - snd_hda_delete_codec_preset(&via_list); -} - -module_init(patch_via_init) -module_exit(patch_via_exit) +module_hda_codec_driver(via_driver); diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index 6ba0b5517c40..0a4ad5feb82e 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -21,7 +21,7 @@ static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context, static bool is_thinkpad(struct hda_codec *codec) { bool found = false; - if (codec->subsystem_id >> 16 != 0x17aa) + if (codec->core.subsystem_id >> 16 != 0x17aa) return false; if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found) return true; diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c index bcf30a387b87..9906119e0954 100644 --- a/sound/pci/ice1712/wtm.c +++ b/sound/pci/ice1712/wtm.c @@ -29,12 +29,19 @@ #include <linux/interrupt.h> #include <linux/init.h> #include <sound/core.h> +#include <sound/tlv.h> +#include <linux/slab.h> #include "ice1712.h" #include "envy24ht.h" #include "wtm.h" #include "stac946x.h" +struct wtm_spec { + /* rate change needs atomic mute/unmute of all dacs*/ + struct mutex mute_mutex; +}; + /* * 2*ADC 6*DAC no1 ringbuffer r/w on i2c bus @@ -68,15 +75,65 @@ static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg) /* * DAC mute control */ +static void stac9460_dac_mute_all(struct snd_ice1712 *ice, unsigned char mute, + unsigned short int *change_mask) +{ + unsigned char new, old; + int id, idx, change; + + /*stac9460 1*/ + for (id = 0; id < 7; id++) { + if (*change_mask & (0x01 << id)) { + if (id == 0) + idx = STAC946X_MASTER_VOLUME; + else + idx = STAC946X_LF_VOLUME - 1 + id; + old = stac9460_get(ice, idx); + new = (~mute << 7 & 0x80) | (old & ~0x80); + change = (new != old); + if (change) { + stac9460_put(ice, idx, new); + *change_mask = *change_mask | (0x01 << id); + } else { + *change_mask = *change_mask & ~(0x01 << id); + } + } + } + + /*stac9460 2*/ + for (id = 0; id < 3; id++) { + if (*change_mask & (0x01 << (id + 7))) { + if (id == 0) + idx = STAC946X_MASTER_VOLUME; + else + idx = STAC946X_LF_VOLUME - 1 + id; + old = stac9460_2_get(ice, idx); + new = (~mute << 7 & 0x80) | (old & ~0x80); + change = (new != old); + if (change) { + stac9460_2_put(ice, idx, new); + *change_mask = *change_mask | (0x01 << id); + } else { + *change_mask = *change_mask & ~(0x01 << id); + } + } + } +} + + + #define stac9460_dac_mute_info snd_ctl_boolean_mono_info static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct wtm_spec *spec = ice->spec; unsigned char val; int idx, id; + mutex_lock(&spec->mute_mutex); + if (kcontrol->private_value) { idx = STAC946X_MASTER_VOLUME; id = 0; @@ -89,6 +146,8 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, else val = stac9460_2_get(ice, idx - 6); ucontrol->value.integer.value[0] = (~val >> 7) & 0x1; + + mutex_unlock(&spec->mute_mutex); return 0; } @@ -338,8 +397,14 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, /* * MIC / LINE switch fonction */ +static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[2] = { "Line In", "Mic" }; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); +} -#define stac9460_mic_sw_info snd_ctl_boolean_mono_info static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -353,7 +418,7 @@ static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol, val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE); else val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE); - ucontrol->value.integer.value[0] = ~val>>7 & 0x1; + ucontrol->value.enumerated.item[0] = (val >> 7) & 0x1; return 0; } @@ -369,7 +434,7 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE); else old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE); - new = (~ucontrol->value.integer.value[0] << 7 & 0x80) | (old & ~0x80); + new = (ucontrol->value.enumerated.item[0] << 7 & 0x80) | (old & ~0x80); change = (new != old); if (change) { if (id == 0) @@ -380,17 +445,63 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, return change; } + +/* + * Handler for setting correct codec rate - called when rate change is detected + */ +static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate) +{ + unsigned char old, new; + unsigned short int changed; + struct wtm_spec *spec = ice->spec; + + if (rate == 0) /* no hint - S/PDIF input is master, simply return */ + return; + else if (rate <= 48000) + new = 0x08; /* 256x, base rate mode */ + else if (rate <= 96000) + new = 0x11; /* 256x, mid rate mode */ + else + new = 0x12; /* 128x, high rate mode */ + + old = stac9460_get(ice, STAC946X_MASTER_CLOCKING); + if (old == new) + return; + /* change detected, setting master clock, muting first */ + /* due to possible conflicts with mute controls - mutexing */ + mutex_lock(&spec->mute_mutex); + /* we have to remember current mute status for each DAC */ + changed = 0xFFFF; + stac9460_dac_mute_all(ice, 0, &changed); + /*printk(KERN_DEBUG "Rate change: %d, new MC: 0x%02x\n", rate, new);*/ + stac9460_put(ice, STAC946X_MASTER_CLOCKING, new); + stac9460_2_put(ice, STAC946X_MASTER_CLOCKING, new); + udelay(10); + /* unmuting - only originally unmuted dacs - + * i.e. those changed when muting */ + stac9460_dac_mute_all(ice, 1, &changed); + mutex_unlock(&spec->mute_mutex); +} + + +/*Limits value in dB for fader*/ +static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0); + /* * Control tabs */ static struct snd_kcontrol_new stac9640_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Master Playback Switch", .info = stac9460_dac_mute_info, .get = stac9460_dac_mute_get, .put = stac9460_dac_mute_put, - .private_value = 1 + .private_value = 1, + .tlv = { .p = db_scale_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -402,7 +513,7 @@ static struct snd_kcontrol_new stac9640_controls[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "MIC/Line switch", + .name = "MIC/Line Input Enum", .count = 2, .info = stac9460_mic_sw_info, .get = stac9460_mic_sw_get, @@ -419,11 +530,15 @@ static struct snd_kcontrol_new stac9640_controls[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "DAC Volume", .count = 8, .info = stac9460_dac_vol_info, .get = stac9460_dac_vol_get, .put = stac9460_dac_vol_put, + .tlv = { .p = db_scale_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -435,12 +550,15 @@ static struct snd_kcontrol_new stac9640_controls[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "ADC Volume", .count = 2, .info = stac9460_adc_vol_info, .get = stac9460_adc_vol_get, .put = stac9460_adc_vol_put, - + .tlv = { .p = db_scale_adc } } }; @@ -463,41 +581,53 @@ static int wtm_add_controls(struct snd_ice1712 *ice) static int wtm_init(struct snd_ice1712 *ice) { - static unsigned short stac_inits_prodigy[] = { + static unsigned short stac_inits_wtm[] = { STAC946X_RESET, 0, + STAC946X_MASTER_CLOCKING, 0x11, (unsigned short)-1 }; unsigned short *p; + struct wtm_spec *spec; /*WTM 192M*/ ice->num_total_dacs = 8; ice->num_total_adcs = 4; ice->force_rdma1 = 1; + /*init mutex for dac mute conflict*/ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + mutex_init(&spec->mute_mutex); + + /*initialize codec*/ - p = stac_inits_prodigy; + p = stac_inits_wtm; for (; *p != (unsigned short)-1; p += 2) { stac9460_put(ice, p[0], p[1]); stac9460_2_put(ice, p[0], p[1]); } + ice->gpio.set_pro_rate = stac9460_set_rate_val; return 0; } static unsigned char wtm_eeprom[] = { - 0x47, /*SYSCONF: clock 192KHz, 4ADC, 8DAC */ - 0x80, /* ACLINK : I2S */ - 0xf8, /* I2S: vol; 96k, 24bit, 192k */ - 0xc1 /*SPDIF: out-en, spidf ext out*/, - 0x9f, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x7f, /* GPIO_DIR2 */ - 0x9f, /* GPIO_MASK */ - 0xff, /* GPIO_MASK1 */ - 0x7f, /* GPIO_MASK2 */ - 0x16, /* GPIO_STATE */ - 0x80, /* GPIO_STATE1 */ - 0x00, /* GPIO_STATE2 */ + [ICE_EEP2_SYSCONF] = 0x67, /*SYSCONF: clock 192KHz, mpu401, + 4ADC, 8DAC */ + [ICE_EEP2_ACLINK] = 0x80, /* ACLINK : I2S */ + [ICE_EEP2_I2S] = 0xf8, /* I2S: vol; 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc1, /*SPDIF: out-en, spidf ext out*/ + [ICE_EEP2_GPIO_DIR] = 0x9f, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0x7f, + [ICE_EEP2_GPIO_MASK] = 0x9f, + [ICE_EEP2_GPIO_MASK1] = 0xff, + [ICE_EEP2_GPIO_MASK2] = 0x7f, + [ICE_EEP2_GPIO_STATE] = 0x16, + [ICE_EEP2_GPIO_STATE1] = 0x80, + [ICE_EEP2_GPIO_STATE2] = 0x00, }; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 2c5484eeb963..749069aa6997 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1795,7 +1795,7 @@ static struct ac97_pcm ac97_pcm_defs[] = { }, }; -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x0e11, .subdevice = 0x000e, diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index ca67f896d117..cb666c73712d 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6043,23 +6043,30 @@ hdspm_hw_constraints_aes32_sample_rates = { .mask = 0 }; -static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) +static int snd_hdspm_open(struct snd_pcm_substream *substream) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); spin_lock_irq(&hdspm->lock); - snd_pcm_set_sync(substream); + runtime->hw = (playback) ? snd_hdspm_playback_subinfo : + snd_hdspm_capture_subinfo; + if (playback) { + if (hdspm->capture_substream == NULL) + hdspm_stop_audio(hdspm); - runtime->hw = snd_hdspm_playback_subinfo; - - if (hdspm->capture_substream == NULL) - hdspm_stop_audio(hdspm); + hdspm->playback_pid = current->pid; + hdspm->playback_substream = substream; + } else { + if (hdspm->playback_substream == NULL) + hdspm_stop_audio(hdspm); - hdspm->playback_pid = current->pid; - hdspm->playback_substream = substream; + hdspm->capture_pid = current->pid; + hdspm->capture_substream = substream; + } spin_unlock_irq(&hdspm->lock); @@ -6094,108 +6101,42 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) &hdspm_hw_constraints_aes32_sample_rates); } else { snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_hdspm_hw_rule_rate_out_channels, hdspm, + (playback ? + snd_hdspm_hw_rule_rate_out_channels : + snd_hdspm_hw_rule_rate_in_channels), hdspm, SNDRV_PCM_HW_PARAM_CHANNELS, -1); } snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_out_channels, hdspm, + (playback ? snd_hdspm_hw_rule_out_channels : + snd_hdspm_hw_rule_in_channels), hdspm, SNDRV_PCM_HW_PARAM_CHANNELS, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_out_channels_rate, hdspm, + (playback ? snd_hdspm_hw_rule_out_channels_rate : + snd_hdspm_hw_rule_in_channels_rate), hdspm, SNDRV_PCM_HW_PARAM_RATE, -1); return 0; } -static int snd_hdspm_playback_release(struct snd_pcm_substream *substream) +static int snd_hdspm_release(struct snd_pcm_substream *substream) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); spin_lock_irq(&hdspm->lock); - hdspm->playback_pid = -1; - hdspm->playback_substream = NULL; - - spin_unlock_irq(&hdspm->lock); - - return 0; -} - - -static int snd_hdspm_capture_open(struct snd_pcm_substream *substream) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - - spin_lock_irq(&hdspm->lock); - snd_pcm_set_sync(substream); - runtime->hw = snd_hdspm_capture_subinfo; - - if (hdspm->playback_substream == NULL) - hdspm_stop_audio(hdspm); - - hdspm->capture_pid = current->pid; - hdspm->capture_substream = substream; - - spin_unlock_irq(&hdspm->lock); - - snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); - - switch (hdspm->io_type) { - case AIO: - case RayDAT: - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - 32, 4096); - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - 16384, 16384); - break; - - default: - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - 64, 8192); - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_PERIODS, - 2, 2); - break; - } - - if (AES32 == hdspm->io_type) { - runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - &hdspm_hw_constraints_aes32_sample_rates); + if (playback) { + hdspm->playback_pid = -1; + hdspm->playback_substream = NULL; } else { - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_hdspm_hw_rule_rate_in_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + hdspm->capture_pid = -1; + hdspm->capture_substream = NULL; } - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_in_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_in_channels_rate, hdspm, - SNDRV_PCM_HW_PARAM_RATE, -1); - - return 0; -} - -static int snd_hdspm_capture_release(struct snd_pcm_substream *substream) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - - spin_lock_irq(&hdspm->lock); - - hdspm->capture_pid = -1; - hdspm->capture_substream = NULL; - spin_unlock_irq(&hdspm->lock); + return 0; } @@ -6413,21 +6354,9 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, return 0; } -static struct snd_pcm_ops snd_hdspm_playback_ops = { - .open = snd_hdspm_playback_open, - .close = snd_hdspm_playback_release, - .ioctl = snd_hdspm_ioctl, - .hw_params = snd_hdspm_hw_params, - .hw_free = snd_hdspm_hw_free, - .prepare = snd_hdspm_prepare, - .trigger = snd_hdspm_trigger, - .pointer = snd_hdspm_hw_pointer, - .page = snd_pcm_sgbuf_ops_page, -}; - -static struct snd_pcm_ops snd_hdspm_capture_ops = { - .open = snd_hdspm_capture_open, - .close = snd_hdspm_capture_release, +static struct snd_pcm_ops snd_hdspm_ops = { + .open = snd_hdspm_open, + .close = snd_hdspm_release, .ioctl = snd_hdspm_ioctl, .hw_params = snd_hdspm_hw_params, .hw_free = snd_hdspm_hw_free, @@ -6521,9 +6450,9 @@ static int snd_hdspm_create_pcm(struct snd_card *card, strcpy(pcm->name, hdspm->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_hdspm_playback_ops); + &snd_hdspm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_hdspm_capture_ops); + &snd_hdspm_ops); pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 8622283e89f3..3dd038bdb204 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1812,7 +1812,7 @@ static void snd_via82xx_mixer_free_ac97(struct snd_ac97 *ac97) chip->ac97 = NULL; } -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x1106, .subdevice = 0x4161, diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 13146d701413..0095a80a997f 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -240,7 +240,7 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec, */ spin_lock_irq(&chip->reg_lock); snd_pmac_dma_stop(rec); - st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); + chip->extra_dma.cmds->command = cpu_to_le16(DBDMA_STOP); snd_pmac_dma_set_command(rec, &chip->extra_dma); snd_pmac_dma_run(rec, RUN); spin_unlock_irq(&chip->reg_lock); @@ -251,15 +251,15 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec, */ offset = runtime->dma_addr; for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) { - st_le32(&cp->phy_addr, offset); - st_le16(&cp->req_count, rec->period_size); - /*st_le16(&cp->res_count, 0);*/ - st_le16(&cp->xfer_status, 0); + cp->phy_addr = cpu_to_le32(offset); + cp->req_count = cpu_to_le16(rec->period_size); + /*cp->res_count = cpu_to_le16(0);*/ + cp->xfer_status = cpu_to_le16(0); offset += rec->period_size; } /* make loop */ - st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); - st_le32(&cp->cmd_dep, rec->cmd.addr); + cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS); + cp->cmd_dep = cpu_to_le32(rec->cmd.addr); snd_pmac_dma_stop(rec); snd_pmac_dma_set_command(rec, &rec->cmd); @@ -328,7 +328,7 @@ static snd_pcm_uframes_t snd_pmac_pcm_pointer(struct snd_pmac *chip, #if 1 /* hmm.. how can we get the current dma pointer?? */ int stat; volatile struct dbdma_cmd __iomem *cp = &rec->cmd.cmds[rec->cur_period]; - stat = ld_le16(&cp->xfer_status); + stat = le16_to_cpu(cp->xfer_status); if (stat & (ACTIVE|DEAD)) { count = in_le16(&cp->res_count); if (count) @@ -427,26 +427,26 @@ static inline void snd_pmac_pcm_dead_xfer(struct pmac_stream *rec, memcpy((void *)emergency_dbdma.cmds, (void *)cp, sizeof(struct dbdma_cmd)); emergency_in_use = 1; - st_le16(&cp->xfer_status, 0); - st_le16(&cp->req_count, rec->period_size); + cp->xfer_status = cpu_to_le16(0); + cp->req_count = cpu_to_le16(rec->period_size); cp = emergency_dbdma.cmds; } /* now bump the values to reflect the amount we haven't yet shifted */ - req = ld_le16(&cp->req_count); - res = ld_le16(&cp->res_count); - phy = ld_le32(&cp->phy_addr); + req = le16_to_cpu(cp->req_count); + res = le16_to_cpu(cp->res_count); + phy = le32_to_cpu(cp->phy_addr); phy += (req - res); - st_le16(&cp->req_count, res); - st_le16(&cp->res_count, 0); - st_le16(&cp->xfer_status, 0); - st_le32(&cp->phy_addr, phy); + cp->req_count = cpu_to_le16(res); + cp->res_count = cpu_to_le16(0); + cp->xfer_status = cpu_to_le16(0); + cp->phy_addr = cpu_to_le32(phy); - st_le32(&cp->cmd_dep, rec->cmd.addr + cp->cmd_dep = cpu_to_le32(rec->cmd.addr + sizeof(struct dbdma_cmd)*((rec->cur_period+1)%rec->nperiods)); - st_le16(&cp->command, OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS); + cp->command = cpu_to_le16(OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS); /* point at our patched up command block */ out_le32(&rec->dma->cmdptr, emergency_dbdma.addr); @@ -475,7 +475,7 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) else cp = &rec->cmd.cmds[rec->cur_period]; - stat = ld_le16(&cp->xfer_status); + stat = le16_to_cpu(cp->xfer_status); if (stat & DEAD) { snd_pmac_pcm_dead_xfer(rec, cp); @@ -489,9 +489,9 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) break; /*printk(KERN_DEBUG "update frag %d\n", rec->cur_period);*/ - st_le16(&cp->xfer_status, 0); - st_le16(&cp->req_count, rec->period_size); - /*st_le16(&cp->res_count, 0);*/ + cp->xfer_status = cpu_to_le16(0); + cp->req_count = cpu_to_le16(rec->period_size); + /*cp->res_count = cpu_to_le16(0);*/ rec->cur_period++; if (rec->cur_period >= rec->nperiods) { rec->cur_period = 0; @@ -760,11 +760,11 @@ void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long add struct pmac_stream *rec = &chip->playback; snd_pmac_dma_stop(rec); - st_le16(&chip->extra_dma.cmds->req_count, bytes); - st_le16(&chip->extra_dma.cmds->xfer_status, 0); - st_le32(&chip->extra_dma.cmds->cmd_dep, chip->extra_dma.addr); - st_le32(&chip->extra_dma.cmds->phy_addr, addr); - st_le16(&chip->extra_dma.cmds->command, OUTPUT_MORE + BR_ALWAYS); + chip->extra_dma.cmds->req_count = cpu_to_le16(bytes); + chip->extra_dma.cmds->xfer_status = cpu_to_le16(0); + chip->extra_dma.cmds->cmd_dep = cpu_to_le32(chip->extra_dma.addr); + chip->extra_dma.cmds->phy_addr = cpu_to_le32(addr); + chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE + BR_ALWAYS); out_le32(&chip->awacs->control, (in_le32(&chip->awacs->control) & ~0x1f00) | (speed << 8)); @@ -776,7 +776,7 @@ void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long add void snd_pmac_beep_dma_stop(struct snd_pmac *chip) { snd_pmac_dma_stop(&chip->playback); - st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); + chip->extra_dma.cmds->command = cpu_to_le16(DBDMA_STOP); snd_pmac_pcm_set_format(chip); /* reset format */ } diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index dcc79aa0236b..3ba52da18bc6 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -47,6 +47,7 @@ source "sound/soc/kirkwood/Kconfig" source "sound/soc/intel/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" +source "sound/soc/qcom/Kconfig" source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/sh/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 5b3c8f67c8db..974ba708b482 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ obj-$(CONFIG_SND_SOC) += kirkwood/ obj-$(CONFIG_SND_SOC) += pxa/ +obj-$(CONFIG_SND_SOC) += qcom/ obj-$(CONFIG_SND_SOC) += rockchip/ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sh/ diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 1579e994acf8..e7d08806f3e9 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -25,7 +25,8 @@ config SND_ATMEL_SOC_SSC config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" - depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC + 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 select SND_SOC_WM8731 @@ -35,7 +36,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 && ATMEL_SSC && SND_ATMEL_SOC && I2C + depends on ARCH_AT91 || COMPILE_TEST + depends on ATMEL_SSC && SND_ATMEL_SOC && I2C select SND_ATMEL_SOC_SSC select SND_ATMEL_SOC_DMA select SND_SOC_WM8904 @@ -45,7 +47,8 @@ config SND_ATMEL_SOC_WM8904 config SND_AT91_SOC_SAM9X5_WM8731 tristate "SoC Audio support for WM8731-based at91sam9x5 board" - depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC + 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 select SND_SOC_WM8731 diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 466a821da98c..b327e5cc8de3 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -1,10 +1,8 @@ # AT91 Platform Support -snd-soc-atmel-pcm-objs := atmel-pcm.o snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o -obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index b8e7bad05eb1..b6625c8c411b 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -54,7 +54,7 @@ static const struct snd_pcm_hardware atmel_pcm_dma_hardware = { .period_bytes_max = 2 * 0xffff, /* if 2 bytes format */ .periods_min = 8, .periods_max = 1024, /* no limit */ - .buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE, + .buffer_bytes_max = 512 * 1024, }; /** @@ -119,7 +119,7 @@ static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream, static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = { .prepare_slave_config = atmel_pcm_configure_dma, .pcm_hardware = &atmel_pcm_dma_hardware, - .prealloc_buffer_size = ATMEL_SSC_DMABUF_SIZE, + .prealloc_buffer_size = 64 * 1024, }; int atmel_pcm_dma_platform_register(struct device *dev) diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index a366b3503c28..da861b44413f 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -47,6 +47,85 @@ #include "atmel-pcm.h" +static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, + int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = ATMEL_SSC_DMABUF_SIZE; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_coherent(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n", + (void *)buf->area, (void *)(long)buf->addr, size); + + if (!buf->area) + return -ENOMEM; + + buf->bytes = size; + return 0; +} + +static int atmel_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return remap_pfn_range(vma, vma->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret; + + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n"); + ret = atmel_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n"); + ret = atmel_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + out: + return ret; +} + +static void atmel_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + dma_free_coherent(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + /*--------------------------------------------------------------------------*\ * Hardware definition \*--------------------------------------------------------------------------*/ diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c deleted file mode 100644 index 8ae3fa5ac60a..000000000000 --- a/sound/soc/atmel/atmel-pcm.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC. - * - * Copyright (C) 2005 SAN People - * Copyright (C) 2008 Atmel - * - * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com> - * - * Based on at91-pcm. by: - * Frank Mandarino <fmandarino@endrelia.com> - * Copyright 2006 Endrelia Technologies Inc. - * - * Based on pxa2xx-pcm.c by: - * - * Author: Nicolas Pitre - * Created: Nov 30, 2004 - * Copyright: (C) 2004 MontaVista Software, 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. - * - * 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/module.h> -#include <linux/dma-mapping.h> -#include <sound/pcm.h> -#include <sound/soc.h> -#include "atmel-pcm.h" - -static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, - int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = ATMEL_SSC_DMABUF_SIZE; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_coherent(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n", - (void *)buf->area, (void *)(long)buf->addr, size); - - if (!buf->area) - return -ENOMEM; - - buf->bytes = size; - return 0; -} - -int atmel_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - return remap_pfn_range(vma, vma->vm_start, - substream->dma_buffer.addr >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot); -} -EXPORT_SYMBOL_GPL(atmel_pcm_mmap); - -int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret; - - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n"); - ret = atmel_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n"); - ret = atmel_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - out: - return ret; -} -EXPORT_SYMBOL_GPL(atmel_pcm_new); - -void atmel_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - dma_free_coherent(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} -EXPORT_SYMBOL_GPL(atmel_pcm_free); - diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h index 12ae814eff21..6eaf081cad50 100644 --- a/sound/soc/atmel/atmel-pcm.h +++ b/sound/soc/atmel/atmel-pcm.h @@ -83,11 +83,6 @@ struct atmel_pcm_dma_params { #define ssc_readx(base, reg) (__raw_readl((base) + (reg))) #define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg)) -int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd); -void atmel_pcm_free(struct snd_pcm *pcm); -int atmel_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma); - #if defined(CONFIG_SND_ATMEL_SOC_PDC) || \ defined(CONFIG_SND_ATMEL_SOC_PDC_MODULE) int atmel_pcm_pdc_platform_register(struct device *dev); diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index fb0b7e8b08ff..841d05946b88 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -187,6 +187,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +/* + * When the bit clock is input, limit the maximum rate according to the + * Serial Clock Ratio Considerations section from the SSC documentation: + * + * The Transmitter and the Receiver can be programmed to operate + * with the clock signals provided on either the TK or RK pins. + * This allows the SSC to support many slave-mode data transfers. + * In this case, the maximum clock speed allowed on the RK pin is: + * - Peripheral clock divided by 2 if Receiver Frame Synchro is input + * - Peripheral clock divided by 3 if Receiver Frame Synchro is output + * In addition, the maximum clock speed allowed on the TK pin is: + * - Peripheral clock divided by 6 if Transmit Frame Synchro is input + * - Peripheral clock divided by 2 if Transmit Frame Synchro is output + * + * When the bit clock is output, limit the rate according to the + * SSC divider restrictions. + */ +static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct atmel_ssc_info *ssc_p = rule->private; + struct ssc_device *ssc = ssc_p->ssc; + struct snd_interval *i = hw_param_interval(params, rule->var); + struct snd_interval t; + struct snd_ratnum r = { + .den_min = 1, + .den_max = 4095, + .den_step = 1, + }; + unsigned int num = 0, den = 0; + int frame_size; + int mck_div = 2; + int ret; + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) + return frame_size; + + switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFS: + if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE) + && ssc->clk_from_rk_pin) + /* Receiver Frame Synchro (i.e. capture) + * is output (format is _CFS) and the RK pin + * is used for input (format is _CBM_). + */ + mck_div = 3; + break; + + case SND_SOC_DAIFMT_CBM_CFM: + if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK) + && !ssc->clk_from_rk_pin) + /* Transmit Frame Synchro (i.e. playback) + * is input (format is _CFM) and the TK pin + * is used for input (format _CBM_ but not + * using the RK pin). + */ + mck_div = 6; + break; + } + + switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + r.num = ssc_p->mck_rate / mck_div / frame_size; + + ret = snd_interval_ratnum(i, 1, &r, &num, &den); + if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { + params->rate_num = num; + params->rate_den = den; + } + break; + + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBM_CFM: + t.min = 8000; + t.max = ssc_p->mck_rate / mck_div / frame_size; + t.openmin = t.openmax = 0; + t.integer = 0; + ret = snd_interval_refine(i, &t); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} /*-------------------------------------------------------------------------*\ * DAI functions @@ -200,6 +288,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; + int ret; pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", ssc_readl(ssc_p->ssc->regs, SR)); @@ -207,6 +296,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, /* Enable PMC peripheral clock for this SSC */ pr_debug("atmel_ssc_dai: Starting clock\n"); clk_enable(ssc_p->ssc->clk); + ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk); /* Reset the SSC to keep it at a clean status */ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); @@ -219,6 +309,17 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, dir_mask = SSC_DIR_MASK_CAPTURE; } + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + atmel_ssc_hw_rule_rate, + ssc_p, + SNDRV_PCM_HW_PARAM_FRAME_BITS, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret < 0) { + dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret); + return ret; + } + dma_params = &ssc_dma_params[dai->id][dir]; dma_params->ssc = ssc_p->ssc; dma_params->substream = substream; @@ -783,8 +884,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) # define atmel_ssc_resume NULL #endif /* CONFIG_PM */ -#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000) - #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) @@ -804,12 +903,16 @@ static struct snd_soc_dai_driver atmel_ssc_dai = { .playback = { .channels_min = 1, .channels_max = 2, - .rates = ATMEL_SSC_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 384000, .formats = ATMEL_SSC_FORMATS,}, .capture = { .channels_min = 1, .channels_max = 2, - .rates = ATMEL_SSC_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 384000, .formats = ATMEL_SSC_FORMATS,}, .ops = &atmel_ssc_dai_ops, }; diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h index b1f08d511495..80b153857a88 100644 --- a/sound/soc/atmel/atmel_ssc_dai.h +++ b/sound/soc/atmel/atmel_ssc_dai.h @@ -115,6 +115,7 @@ struct atmel_ssc_info { unsigned short rcmr_period; struct atmel_pcm_dma_params *dma_params[2]; struct atmel_ssc_state ssc_state; + unsigned long mck_rate; }; int atmel_ssc_set_audio(int ssc_id); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index ea9f0e31f9d4..061c46587628 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -70,6 +70,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX98090 if I2C select SND_SOC_MAX98095 if I2C select SND_SOC_MAX98357A if GPIOLIB + select SND_SOC_MAX98925 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9768 if I2C select SND_SOC_MAX9877 if I2C @@ -141,7 +142,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8770 if SPI_MASTER select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8782 - select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8804_I2C if I2C + select SND_SOC_WM8804_SPI if SPI_MASTER select SND_SOC_WM8900 if I2C select SND_SOC_WM8903 if I2C select SND_SOC_WM8904 if I2C @@ -460,6 +462,9 @@ config SND_SOC_MAX98095 config SND_SOC_MAX98357A tristate +config SND_SOC_MAX98925 + tristate + config SND_SOC_MAX9850 tristate @@ -744,8 +749,19 @@ config SND_SOC_WM8782 tristate config SND_SOC_WM8804 - tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver" - depends on SND_SOC_I2C_AND_SPI + tristate + +config SND_SOC_WM8804_I2C + tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver I2C" + depends on I2C + select SND_SOC_WM8804 + select REGMAP_I2C + +config SND_SOC_WM8804_SPI + tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver SPI" + depends on SPI_MASTER + select SND_SOC_WM8804 + select REGMAP_SPI config SND_SOC_WM8900 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 69b8666d187a..abe2d7edf65c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -65,6 +65,7 @@ snd-soc-max98088-objs := max98088.o snd-soc-max98090-objs := max98090.o snd-soc-max98095-objs := max98095.o snd-soc-max98357a-objs := max98357a.o +snd-soc-max98925-objs := max98925.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o @@ -145,6 +146,8 @@ snd-soc-wm8770-objs := wm8770.o snd-soc-wm8776-objs := wm8776.o snd-soc-wm8782-objs := wm8782.o snd-soc-wm8804-objs := wm8804.o +snd-soc-wm8804-i2c-objs := wm8804-i2c.o +snd-soc-wm8804-spi-objs := wm8804-spi.o snd-soc-wm8900-objs := wm8900.o snd-soc-wm8903-objs := wm8903.o snd-soc-wm8904-objs := wm8904.o @@ -247,6 +250,7 @@ obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o +obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o @@ -323,6 +327,8 @@ obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o +obj-$(CONFIG_SND_SOC_WM8804_I2C) += snd-soc-wm8804-i2c.o +obj-$(CONFIG_SND_SOC_WM8804_SPI) += snd-soc-wm8804-spi.o obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 7895689588da..88ca9cb0ce79 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2003,7 +2003,6 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec, return 0; } -EXPORT_SYMBOL_GPL(ab8500_audio_setup_mics); static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec, enum ear_cm_voltage ear_cmv) @@ -2036,7 +2035,6 @@ static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec, return 0; } -EXPORT_SYMBOL_GPL(ab8500_audio_set_ear_cmv); static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai, unsigned int delay) diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index 70ab35744aba..7ad8e156e2df 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -938,22 +938,15 @@ int adau1977_probe(struct device *dev, struct regmap *regmap, adau1977->dvdd_reg = NULL; } - adau1977->reset_gpio = devm_gpiod_get(dev, "reset"); - if (IS_ERR(adau1977->reset_gpio)) { - ret = PTR_ERR(adau1977->reset_gpio); - if (ret != -ENOENT && ret != -ENOSYS) - return PTR_ERR(adau1977->reset_gpio); - adau1977->reset_gpio = NULL; - } + adau1977->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(adau1977->reset_gpio)) + return PTR_ERR(adau1977->reset_gpio); dev_set_drvdata(dev, adau1977); - if (adau1977->reset_gpio) { - ret = gpiod_direction_output(adau1977->reset_gpio, 0); - if (ret) - return ret; + if (adau1977->reset_gpio) ndelay(100); - } ret = adau1977_power_enable(adau1977); if (ret) diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c index 16ce9f9fefa1..298dedc05140 100644 --- a/sound/soc/codecs/ak4554.c +++ b/sound/soc/codecs/ak4554.c @@ -84,7 +84,7 @@ static int ak4554_soc_remove(struct platform_device *pdev) return 0; } -static struct of_device_id ak4554_of_match[] = { +static const struct of_device_id ak4554_of_match[] = { { .compatible = "asahi-kasei,ak4554" }, {}, }; diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index dde8b49c19ad..13585e88f597 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -97,6 +97,9 @@ #define PMMP (1 << 2) /* MPWR pin Power Management */ #define MGAIN0 (1 << 0) /* MIC amp gain*/ +/* SG_SL2 */ +#define LOPS (1 << 6) /* Stero Line-out Power Save Mode */ + /* TIMER */ #define ZTM(param) ((param & 0x3) << 4) /* ALC Zero Crossing TimeOut */ #define WTM(param) (((param & 0x4) << 4) | ((param & 0x3) << 2)) @@ -168,6 +171,29 @@ static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = { SOC_DAPM_SINGLE("DACL", SG_SL1, 4, 1, 0), }; +/* event handlers */ +static int ak4642_lout_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_PRE_PMD: + case SND_SOC_DAPM_PRE_PMU: + /* Power save mode ON */ + snd_soc_update_bits(codec, SG_SL2, LOPS, LOPS); + break; + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + /* Power save mode OFF */ + mdelay(300); + snd_soc_update_bits(codec, SG_SL2, LOPS, 0); + break; + } + + return 0; +} + static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = { /* Outputs */ @@ -182,12 +208,15 @@ static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = { SND_SOC_DAPM_PGA("DACH", MD_CTL4, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("LINEOUT Mixer", PW_MGMT1, 3, 0, + SND_SOC_DAPM_MIXER_E("LINEOUT Mixer", PW_MGMT1, 3, 0, &ak4642_lout_mixer_controls[0], - ARRAY_SIZE(ak4642_lout_mixer_controls)), + ARRAY_SIZE(ak4642_lout_mixer_controls), + ak4642_lout_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), /* DAC */ - SND_SOC_DAPM_DAC("DAC", "HiFi Playback", PW_MGMT1, 2, 0), + SND_SOC_DAPM_DAC("DAC", NULL, PW_MGMT1, 2, 0), }; static const struct snd_soc_dapm_route ak4642_intercon[] = { @@ -205,6 +234,8 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = { {"DACH", NULL, "DAC"}, {"LINEOUT Mixer", "DACL", "DAC"}, + + { "DAC", NULL, "Playback" }, }; /* @@ -468,13 +499,13 @@ static struct snd_soc_dai_driver ak4642_dai = { .name = "ak4642-hifi", .playback = { .stream_name = "Playback", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE }, .capture = { .stream_name = "Capture", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE }, diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 29202610dd0d..57da0ceda03f 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -19,7 +19,6 @@ #include <sound/tlv.h> #include <linux/mfd/arizona/core.h> -#include <linux/mfd/arizona/gpio.h> #include <linux/mfd/arizona/registers.h> #include "arizona.h" @@ -281,6 +280,7 @@ 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"); break; default: @@ -1729,6 +1729,7 @@ static int arizona_calc_fratio(struct arizona_fll *fll, switch (fll->arizona->type) { case WM5110: + case WM8280: if (fll->arizona->rev < 3 || sync) return init_ratio; break; @@ -1901,7 +1902,7 @@ static int arizona_is_enabled_fll(struct arizona_fll *fll) static int arizona_enable_fll(struct arizona_fll *fll) { struct arizona *arizona = fll->arizona; - int ret; + unsigned long time_left; bool use_sync = false; int already_enabled = arizona_is_enabled_fll(fll); struct arizona_fll_cfg cfg; @@ -1977,9 +1978,9 @@ static int arizona_enable_fll(struct arizona_fll *fll) regmap_update_bits_async(arizona->regmap, fll->base + 1, ARIZONA_FLL1_FREERUN, 0); - ret = wait_for_completion_timeout(&fll->ok, + time_left = wait_for_completion_timeout(&fll->ok, msecs_to_jiffies(250)); - if (ret == 0) + if (time_left == 0) arizona_fll_warn(fll, "Timed out waiting for lock\n"); return 0; diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index f2b8aad21274..60598b230341 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -437,20 +437,13 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client, } /* Reset the Device */ - cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev, - "reset-gpios"); - if (IS_ERR(cs35l32->reset_gpio)) { - ret = PTR_ERR(cs35l32->reset_gpio); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - cs35l32->reset_gpio = NULL; - } else { - ret = gpiod_direction_output(cs35l32->reset_gpio, 0); - if (ret) - return ret; + cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs35l32->reset_gpio)) + return PTR_ERR(cs35l32->reset_gpio); + + if (cs35l32->reset_gpio) gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); - } /* initialize codec */ ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, ®); diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index ce6086835ebd..cac48ddf3ba6 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -605,21 +605,14 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, return ret; } - cs4265->reset_gpio = devm_gpiod_get(&i2c_client->dev, - "reset-gpios"); - if (IS_ERR(cs4265->reset_gpio)) { - ret = PTR_ERR(cs4265->reset_gpio); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - cs4265->reset_gpio = NULL; - } else { - ret = gpiod_direction_output(cs4265->reset_gpio, 0); - if (ret) - return ret; + cs4265->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs4265->reset_gpio)) + return PTR_ERR(cs4265->reset_gpio); + + if (cs4265->reset_gpio) { mdelay(1); gpiod_set_value_cansleep(cs4265->reset_gpio, 1); - } i2c_set_clientdata(i2c_client, cs4265); diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 7d3a6accaf9a..e770ee6f36da 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -561,10 +561,10 @@ static int cs4271_codec_probe(struct snd_soc_codec *codec) if (gpio_is_valid(cs4271->gpio_nreset)) { /* Reset codec */ gpio_direction_output(cs4271->gpio_nreset, 0); - udelay(1); + mdelay(1); gpio_set_value(cs4271->gpio_nreset, 1); /* Give the codec time to wake up */ - udelay(1); + mdelay(1); } ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 0b10979513c4..0f334bc1b63c 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -420,7 +420,7 @@ static int cx20442_platform_probe(struct platform_device *pdev) &cx20442_codec_dev, &cx20442_dai, 1); } -static int __exit cx20442_platform_remove(struct platform_device *pdev) +static int cx20442_platform_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); return 0; @@ -431,7 +431,7 @@ static struct platform_driver cx20442_platform_driver = { .name = "cx20442-codec", }, .probe = cx20442_platform_probe, - .remove = __exit_p(cx20442_platform_remove), + .remove = cx20442_platform_remove, }; module_platform_driver(cx20442_platform_driver); diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index b112b1c2c394..3e33ef2acf3c 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2605,8 +2605,24 @@ err_enable: return ret; } +static void max98090_i2c_shutdown(struct i2c_client *i2c) +{ + struct max98090_priv *max98090 = dev_get_drvdata(&i2c->dev); + + /* + * Enable volume smoothing, disable zero cross. This will cause + * a quick 40ms ramp to mute on shutdown. + */ + regmap_write(max98090->regmap, + M98090_REG_LEVEL_CONTROL, M98090_VSENN_MASK); + regmap_write(max98090->regmap, + M98090_REG_DEVICE_SHUTDOWN, 0x00); + msleep(40); +} + static int max98090_i2c_remove(struct i2c_client *client) { + max98090_i2c_shutdown(client); snd_soc_unregister_codec(&client->dev); return 0; } @@ -2696,6 +2712,7 @@ static struct i2c_driver max98090_i2c_driver = { .acpi_match_table = ACPI_PTR(max98090_acpi_match), }, .probe = max98090_i2c_probe, + .shutdown = max98090_i2c_shutdown, .remove = max98090_i2c_remove, .id_table = max98090_i2c_id, }; diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c index e9e6efbc21dd..bf3e933ee895 100644 --- a/sound/soc/codecs/max98357a.c +++ b/sound/soc/codecs/max98357a.c @@ -26,8 +26,6 @@ #include <sound/soc-dai.h> #include <sound/soc-dapm.h> -#define DRV_NAME "max98357a" - static int max98357a_daiops_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -87,9 +85,9 @@ static struct snd_soc_dai_ops max98357a_dai_ops = { }; static struct snd_soc_dai_driver max98357a_dai_driver = { - .name = DRV_NAME, + .name = "HiFi", .playback = { - .stream_name = DRV_NAME "-playback", + .stream_name = "HiFi Playback", .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32, @@ -127,7 +125,7 @@ static int max98357a_platform_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id max98357a_device_id[] = { - { .compatible = "maxim," DRV_NAME, }, + { .compatible = "maxim,max98357a" }, {} }; MODULE_DEVICE_TABLE(of, max98357a_device_id); @@ -135,7 +133,7 @@ MODULE_DEVICE_TABLE(of, max98357a_device_id); static struct platform_driver max98357a_platform_driver = { .driver = { - .name = DRV_NAME, + .name = "max98357a", .of_match_table = of_match_ptr(max98357a_device_id), }, .probe = max98357a_platform_probe, @@ -145,4 +143,3 @@ module_platform_driver(max98357a_platform_driver); MODULE_DESCRIPTION("Maxim MAX98357A Codec Driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c new file mode 100644 index 000000000000..9b5a17de4690 --- /dev/null +++ b/sound/soc/codecs/max98925.c @@ -0,0 +1,655 @@ +/* + * max98925.c -- ALSA SoC Stereo MAX98925 driver + * Copyright 2013-15 Maxim Integrated Products + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "max98925.h" + +static const char *const dai_text[] = { + "Left", "Right", "LeftRight", "LeftRightDiv2", +}; + +static const char * const max98925_boost_voltage_text[] = { + "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V", + "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V" +}; + +static SOC_ENUM_SINGLE_DECL(max98925_boost_voltage, + MAX98925_CONFIGURATION, M98925_BST_VOUT_SHIFT, + max98925_boost_voltage_text); + +static const char *const hpf_text[] = { + "Disable", "DC Block", "100Hz", "200Hz", "400Hz", "800Hz", +}; + +static const struct reg_default max98925_reg[] = { + { 0x0B, 0x00 }, /* IRQ Enable0 */ + { 0x0C, 0x00 }, /* IRQ Enable1 */ + { 0x0D, 0x00 }, /* IRQ Enable2 */ + { 0x0E, 0x00 }, /* IRQ Clear0 */ + { 0x0F, 0x00 }, /* IRQ Clear1 */ + { 0x10, 0x00 }, /* IRQ Clear2 */ + { 0x11, 0xC0 }, /* Map0 */ + { 0x12, 0x00 }, /* Map1 */ + { 0x13, 0x00 }, /* Map2 */ + { 0x14, 0xF0 }, /* Map3 */ + { 0x15, 0x00 }, /* Map4 */ + { 0x16, 0xAB }, /* Map5 */ + { 0x17, 0x89 }, /* Map6 */ + { 0x18, 0x00 }, /* Map7 */ + { 0x19, 0x00 }, /* Map8 */ + { 0x1A, 0x06 }, /* DAI Clock Mode 1 */ + { 0x1B, 0xC0 }, /* DAI Clock Mode 2 */ + { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */ + { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */ + { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */ + { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */ + { 0x20, 0x50 }, /* Format */ + { 0x21, 0x00 }, /* TDM Slot Select */ + { 0x22, 0x00 }, /* DOUT Configuration VMON */ + { 0x23, 0x00 }, /* DOUT Configuration IMON */ + { 0x24, 0x00 }, /* DOUT Configuration VBAT */ + { 0x25, 0x00 }, /* DOUT Configuration VBST */ + { 0x26, 0x00 }, /* DOUT Configuration FLAG */ + { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */ + { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */ + { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */ + { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */ + { 0x2B, 0x02 }, /* DOUT Drive Strength */ + { 0x2C, 0x90 }, /* Filters */ + { 0x2D, 0x00 }, /* Gain */ + { 0x2E, 0x02 }, /* Gain Ramping */ + { 0x2F, 0x00 }, /* Speaker Amplifier */ + { 0x30, 0x0A }, /* Threshold */ + { 0x31, 0x00 }, /* ALC Attack */ + { 0x32, 0x80 }, /* ALC Atten and Release */ + { 0x33, 0x00 }, /* ALC Infinite Hold Release */ + { 0x34, 0x92 }, /* ALC Configuration */ + { 0x35, 0x01 }, /* Boost Converter */ + { 0x36, 0x00 }, /* Block Enable */ + { 0x37, 0x00 }, /* Configuration */ + { 0x38, 0x00 }, /* Global Enable */ + { 0x3A, 0x00 }, /* Boost Limiter */ +}; + +static const struct soc_enum max98925_dai_enum = + SOC_ENUM_SINGLE(MAX98925_GAIN, 5, ARRAY_SIZE(dai_text), dai_text); + +static const struct soc_enum max98925_hpf_enum = + SOC_ENUM_SINGLE(MAX98925_FILTERS, 0, ARRAY_SIZE(hpf_text), hpf_text); + +static const struct snd_kcontrol_new max98925_hpf_sel_mux = + SOC_DAPM_ENUM("Rc Filter MUX Mux", max98925_hpf_enum); + +static const struct snd_kcontrol_new max98925_dai_sel_mux = + SOC_DAPM_ENUM("DAI IN MUX Mux", max98925_dai_enum); + +static int max98925_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(max98925->regmap, + MAX98925_BLOCK_ENABLE, + M98925_BST_EN_MASK | + M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK, + M98925_BST_EN_MASK | + M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(max98925->regmap, + MAX98925_BLOCK_ENABLE, M98925_BST_EN_MASK | + M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK, 0); + break; + default: + return 0; + } + return 0; +} + +static const struct snd_soc_dapm_widget max98925_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("DAI IN MUX", SND_SOC_NOPM, 0, 0, + &max98925_dai_sel_mux), + SND_SOC_DAPM_MUX("Rc Filter MUX", SND_SOC_NOPM, 0, 0, + &max98925_hpf_sel_mux), + SND_SOC_DAPM_DAC_E("Amp Enable", NULL, MAX98925_BLOCK_ENABLE, + M98925_SPK_EN_SHIFT, 0, max98925_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("Global Enable", MAX98925_GLOBAL_ENABLE, + M98925_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("BE_OUT"), +}; + +static const struct snd_soc_dapm_route max98925_audio_map[] = { + {"DAI IN MUX", "Left", "DAI_OUT"}, + {"DAI IN MUX", "Right", "DAI_OUT"}, + {"DAI IN MUX", "LeftRight", "DAI_OUT"}, + {"DAI IN MUX", "LeftRightDiv2", "DAI_OUT"}, + {"Rc Filter MUX", "Disable", "DAI IN MUX"}, + {"Rc Filter MUX", "DC Block", "DAI IN MUX"}, + {"Rc Filter MUX", "100Hz", "DAI IN MUX"}, + {"Rc Filter MUX", "200Hz", "DAI IN MUX"}, + {"Rc Filter MUX", "400Hz", "DAI IN MUX"}, + {"Rc Filter MUX", "800Hz", "DAI IN MUX"}, + {"Amp Enable", NULL, "Rc Filter MUX"}, + {"BE_OUT", NULL, "Amp Enable"}, + {"BE_OUT", NULL, "Global Enable"}, +}; + +static bool max98925_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98925_VBAT_DATA: + case MAX98925_VBST_DATA: + case MAX98925_LIVE_STATUS0: + case MAX98925_LIVE_STATUS1: + case MAX98925_LIVE_STATUS2: + case MAX98925_STATE0: + case MAX98925_STATE1: + case MAX98925_STATE2: + case MAX98925_FLAG0: + case MAX98925_FLAG1: + case MAX98925_FLAG2: + case MAX98925_REV_VERSION: + return true; + default: + return false; + } +} + +static bool max98925_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98925_IRQ_CLEAR0: + case MAX98925_IRQ_CLEAR1: + case MAX98925_IRQ_CLEAR2: + case MAX98925_ALC_HOLD_RLS: + return false; + default: + return true; + } +} + +static DECLARE_TLV_DB_SCALE(max98925_spk_tlv, -600, 100, 0); + +static const struct snd_kcontrol_new max98925_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", MAX98925_GAIN, + M98925_SPK_GAIN_SHIFT, (1<<M98925_SPK_GAIN_WIDTH)-1, 0, + max98925_spk_tlv), + SOC_SINGLE("Ramp Switch", MAX98925_GAIN_RAMPING, + M98925_SPK_RMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ZCD Switch", MAX98925_GAIN_RAMPING, + M98925_SPK_ZCD_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Switch", MAX98925_THRESHOLD, + M98925_ALC_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Threshold", MAX98925_THRESHOLD, M98925_ALC_TH_SHIFT, + (1<<M98925_ALC_TH_WIDTH)-1, 0), + SOC_ENUM("Boost Output Voltage", max98925_boost_voltage), +}; + +/* codec sample rate and n/m dividers parameter table */ +static const struct { + int rate; + int sr; + int divisors[3][2]; +} rate_table[] = { + { + .rate = 8000, + .sr = 0, + .divisors = { {1, 375}, {5, 1764}, {1, 384} } + }, + { + .rate = 11025, + .sr = 1, + .divisors = { {147, 40000}, {1, 256}, {147, 40960} } + }, + { + .rate = 12000, + .sr = 2, + .divisors = { {1, 250}, {5, 1176}, {1, 256} } + }, + { + .rate = 16000, + .sr = 3, + .divisors = { {2, 375}, {5, 882}, {1, 192} } + }, + { + .rate = 22050, + .sr = 4, + .divisors = { {147, 20000}, {1, 128}, {147, 20480} } + }, + { + .rate = 24000, + .sr = 5, + .divisors = { {1, 125}, {5, 588}, {1, 128} } + }, + { + .rate = 32000, + .sr = 6, + .divisors = { {4, 375}, {5, 441}, {1, 96} } + }, + { + .rate = 44100, + .sr = 7, + .divisors = { {147, 10000}, {1, 64}, {147, 10240} } + }, + { + .rate = 48000, + .sr = 8, + .divisors = { {2, 125}, {5, 294}, {1, 64} } + }, +}; + +static inline int max98925_rate_value(struct snd_soc_codec *codec, + int rate, int clock, int *value, int *n, int *m) +{ + int ret = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i].rate >= rate) { + *value = rate_table[i].sr; + *n = rate_table[i].divisors[clock][0]; + *m = rate_table[i].divisors[clock][1]; + ret = 0; + break; + } + } + dev_dbg(codec->dev, "%s: sample rate is %d, returning %d\n", + __func__, rate_table[i].rate, *value); + return ret; +} + +static void max98925_set_sense_data(struct max98925_priv *max98925) +{ + /* set VMON slots */ + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_VMON, + M98925_DAI_VMON_EN_MASK, M98925_DAI_VMON_EN_MASK); + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_VMON, + M98925_DAI_VMON_SLOT_MASK, + max98925->v_slot << M98925_DAI_VMON_SLOT_SHIFT); + /* set IMON slots */ + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_IMON, + M98925_DAI_IMON_EN_MASK, M98925_DAI_IMON_EN_MASK); + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_IMON, + M98925_DAI_IMON_SLOT_MASK, + max98925->i_slot << M98925_DAI_IMON_SLOT_SHIFT); +} + +static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); + unsigned int invert = 0; + + dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt); + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* set DAI to slave mode */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_MAS_MASK, 0); + max98925_set_sense_data(max98925); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* + * set left channel DAI to master mode, + * right channel always slave + */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_MAS_MASK, M98925_DAI_MAS_MASK); + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(codec->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + invert = M98925_DAI_WCI_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + invert = M98925_DAI_BCI_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + invert = M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK; + break; + default: + dev_err(codec->dev, "DAI invert mode unsupported"); + return -EINVAL; + } + + regmap_update_bits(max98925->regmap, MAX98925_FORMAT, + M98925_DAI_BCI_MASK, invert); + return 0; +} + +static int max98925_set_clock(struct max98925_priv *max98925, + struct snd_pcm_hw_params *params) +{ + unsigned int dai_sr = 0, clock, mdll, n, m; + struct snd_soc_codec *codec = max98925->codec; + int rate = params_rate(params); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98925->ch_size; + + switch (blr_clk_ratio) { + case 32: + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_32); + break; + case 48: + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_48); + break; + case 64: + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_64); + break; + default: + return -EINVAL; + } + + switch (max98925->sysclk) { + case 6000000: + clock = 0; + mdll = M98925_MDLL_MULT_MCLKx16; + break; + case 11289600: + clock = 1; + mdll = M98925_MDLL_MULT_MCLKx8; + break; + case 12000000: + clock = 0; + mdll = M98925_MDLL_MULT_MCLKx8; + break; + case 12288000: + clock = 2; + mdll = M98925_MDLL_MULT_MCLKx8; + break; + default: + dev_info(max98925->codec->dev, "unsupported sysclk %d\n", + max98925->sysclk); + return -EINVAL; + } + + if (max98925_rate_value(codec, rate, clock, &dai_sr, &n, &m)) + return -EINVAL; + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_SR_MASK, dai_sr << M98925_DAI_SR_SHIFT); + /* set DAI m divider */ + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_M_MSBS, m >> 8); + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_M_LSBS, m & 0xFF); + /* set DAI n divider */ + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_N_MSBS, n >> 8); + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_N_LSBS, n & 0xFF); + /* set MDLL */ + regmap_update_bits(max98925->regmap, MAX98925_DAI_CLK_MODE1, + M98925_MDLL_MULT_MASK, mdll << M98925_MDLL_MULT_SHIFT); + return 0; +} + +static int max98925_dai_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 max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); + + switch (snd_pcm_format_width(params_format(params))) { + case 16: + regmap_update_bits(max98925->regmap, + MAX98925_FORMAT, + M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_16); + max98925->ch_size = 16; + break; + case 24: + regmap_update_bits(max98925->regmap, + MAX98925_FORMAT, + M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_24); + max98925->ch_size = 24; + break; + case 32: + regmap_update_bits(max98925->regmap, + MAX98925_FORMAT, + M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_32); + max98925->ch_size = 32; + break; + default: + pr_err("%s: format unsupported %d", + __func__, params_format(params)); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: format supported %d", + __func__, params_format(params)); + return max98925_set_clock(max98925, params); +} + +static int max98925_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case 0: + /* use MCLK for Left channel, right channel always BCLK */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE1, + M98925_DAI_CLK_SOURCE_MASK, 0); + break; + case 1: + /* configure dai clock source to BCLK instead of MCLK */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE1, + M98925_DAI_CLK_SOURCE_MASK, + M98925_DAI_CLK_SOURCE_MASK); + break; + default: + return -EINVAL; + } + max98925->sysclk = freq; + return 0; +} + +#define MAX98925_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98925_dai_ops = { + .set_sysclk = max98925_dai_set_sysclk, + .set_fmt = max98925_dai_set_fmt, + .hw_params = max98925_dai_hw_params, +}; + +static struct snd_soc_dai_driver max98925_dai[] = { + { + .name = "max98925-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98925_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98925_FORMATS, + }, + .ops = &max98925_dai_ops, + } +}; + +static int max98925_probe(struct snd_soc_codec *codec) +{ + struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); + + max98925->codec = codec; + codec->control_data = max98925->regmap; + regmap_write(max98925->regmap, MAX98925_GLOBAL_ENABLE, 0x00); + /* It's not the default but we need to set DAI_DLY */ + regmap_write(max98925->regmap, + MAX98925_FORMAT, M98925_DAI_DLY_MASK); + regmap_write(max98925->regmap, MAX98925_TDM_SLOT_SELECT, 0xC8); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG1, 0xFF); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG2, 0xFF); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG3, 0xFF); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG4, 0xF0); + regmap_write(max98925->regmap, MAX98925_FILTERS, 0xD8); + regmap_write(max98925->regmap, MAX98925_ALC_CONFIGURATION, 0xF8); + regmap_write(max98925->regmap, MAX98925_CONFIGURATION, 0xF0); + /* Disable ALC muting */ + regmap_write(max98925->regmap, MAX98925_BOOST_LIMITER, 0xF8); + return 0; +} + +static const struct snd_soc_codec_driver soc_codec_dev_max98925 = { + .probe = max98925_probe, + .controls = max98925_snd_controls, + .num_controls = ARRAY_SIZE(max98925_snd_controls), + .dapm_routes = max98925_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98925_audio_map), + .dapm_widgets = max98925_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98925_dapm_widgets), +}; + +static const struct regmap_config max98925_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX98925_REV_VERSION, + .reg_defaults = max98925_reg, + .num_reg_defaults = ARRAY_SIZE(max98925_reg), + .volatile_reg = max98925_volatile_register, + .readable_reg = max98925_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98925_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret, reg; + u32 value; + struct max98925_priv *max98925; + + max98925 = devm_kzalloc(&i2c->dev, + sizeof(*max98925), GFP_KERNEL); + if (!max98925) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98925); + max98925->regmap = devm_regmap_init_i2c(i2c, &max98925_regmap); + if (IS_ERR(max98925->regmap)) { + ret = PTR_ERR(max98925->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + goto err_out; + } + + if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { + if (value > M98925_DAI_VMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "vmon slot number is wrong:\n"); + return -EINVAL; + } + max98925->v_slot = value; + } + if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) { + if (value > M98925_DAI_IMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "imon slot number is wrong:\n"); + return -EINVAL; + } + max98925->i_slot = value; + } + ret = regmap_read(max98925->regmap, + MAX98925_REV_VERSION, ®); + if ((ret < 0) || + ((reg != MAX98925_VERSION) && + (reg != MAX98925_VERSION1))) { + dev_err(&i2c->dev, + "device initialization error (%d 0x%02X)\n", + ret, reg); + goto err_out; + } + dev_info(&i2c->dev, "device version 0x%02X\n", reg); + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98925, + max98925_dai, ARRAY_SIZE(max98925_dai)); + if (ret < 0) + dev_err(&i2c->dev, + "Failed to register codec: %d\n", ret); +err_out: + return ret; +} + +static int max98925_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id max98925_i2c_id[] = { + { "max98925", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98925_i2c_id); + +static const struct of_device_id max98925_of_match[] = { + { .compatible = "maxim,max98925", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98925_of_match); + +static struct i2c_driver max98925_i2c_driver = { + .driver = { + .name = "max98925", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max98925_of_match), + .pm = NULL, + }, + .probe = max98925_i2c_probe, + .remove = max98925_i2c_remove, + .id_table = max98925_i2c_id, +}; + +module_i2c_driver(max98925_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98925 driver"); +MODULE_AUTHOR("Ralph Birt <rdbirt@gmail.com>, Anish kumar <anish.kumar@maximintegrated.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98925.h b/sound/soc/codecs/max98925.h new file mode 100644 index 000000000000..3783248f2780 --- /dev/null +++ b/sound/soc/codecs/max98925.h @@ -0,0 +1,832 @@ +/* + * max98925.h -- MAX98925 ALSA SoC Audio driver + * + * Copyright 2013-2015 Maxim Integrated Products + * + * 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 _MAX98925_H +#define _MAX98925_H + +#define MAX98925_VERSION 0x51 +#define MAX98925_VERSION1 0x80 +#define MAX98925_VBAT_DATA 0x00 +#define MAX98925_VBST_DATA 0x01 +#define MAX98925_LIVE_STATUS0 0x02 +#define MAX98925_LIVE_STATUS1 0x03 +#define MAX98925_LIVE_STATUS2 0x04 +#define MAX98925_STATE0 0x05 +#define MAX98925_STATE1 0x06 +#define MAX98925_STATE2 0x07 +#define MAX98925_FLAG0 0x08 +#define MAX98925_FLAG1 0x09 +#define MAX98925_FLAG2 0x0A +#define MAX98925_IRQ_ENABLE0 0x0B +#define MAX98925_IRQ_ENABLE1 0x0C +#define MAX98925_IRQ_ENABLE2 0x0D +#define MAX98925_IRQ_CLEAR0 0x0E +#define MAX98925_IRQ_CLEAR1 0x0F +#define MAX98925_IRQ_CLEAR2 0x10 +#define MAX98925_MAP0 0x11 +#define MAX98925_MAP1 0x12 +#define MAX98925_MAP2 0x13 +#define MAX98925_MAP3 0x14 +#define MAX98925_MAP4 0x15 +#define MAX98925_MAP5 0x16 +#define MAX98925_MAP6 0x17 +#define MAX98925_MAP7 0x18 +#define MAX98925_MAP8 0x19 +#define MAX98925_DAI_CLK_MODE1 0x1A +#define MAX98925_DAI_CLK_MODE2 0x1B +#define MAX98925_DAI_CLK_DIV_M_MSBS 0x1C +#define MAX98925_DAI_CLK_DIV_M_LSBS 0x1D +#define MAX98925_DAI_CLK_DIV_N_MSBS 0x1E +#define MAX98925_DAI_CLK_DIV_N_LSBS 0x1F +#define MAX98925_FORMAT 0x20 +#define MAX98925_TDM_SLOT_SELECT 0x21 +#define MAX98925_DOUT_CFG_VMON 0x22 +#define MAX98925_DOUT_CFG_IMON 0x23 +#define MAX98925_DOUT_CFG_VBAT 0x24 +#define MAX98925_DOUT_CFG_VBST 0x25 +#define MAX98925_DOUT_CFG_FLAG 0x26 +#define MAX98925_DOUT_HIZ_CFG1 0x27 +#define MAX98925_DOUT_HIZ_CFG2 0x28 +#define MAX98925_DOUT_HIZ_CFG3 0x29 +#define MAX98925_DOUT_HIZ_CFG4 0x2A +#define MAX98925_DOUT_DRV_STRENGTH 0x2B +#define MAX98925_FILTERS 0x2C +#define MAX98925_GAIN 0x2D +#define MAX98925_GAIN_RAMPING 0x2E +#define MAX98925_SPK_AMP 0x2F +#define MAX98925_THRESHOLD 0x30 +#define MAX98925_ALC_ATTACK 0x31 +#define MAX98925_ALC_ATTEN_RLS 0x32 +#define MAX98925_ALC_HOLD_RLS 0x33 +#define MAX98925_ALC_CONFIGURATION 0x34 +#define MAX98925_BOOST_CONVERTER 0x35 +#define MAX98925_BLOCK_ENABLE 0x36 +#define MAX98925_CONFIGURATION 0x37 +#define MAX98925_GLOBAL_ENABLE 0x38 +#define MAX98925_BOOST_LIMITER 0x3A +#define MAX98925_REV_VERSION 0xFF + +#define MAX98925_REG_CNT (MAX98925_R03A_BOOST_LIMITER+1) + +/* MAX98925 Register Bit Fields */ + +/* MAX98925_R002_LIVE_STATUS0 */ +#define M98925_THERMWARN_STATUS_MASK (1<<3) +#define M98925_THERMWARN_STATUS_SHIFT 3 +#define M98925_THERMWARN_STATUS_WIDTH 1 +#define M98925_THERMSHDN_STATUS_MASK (1<<1) +#define M98925_THERMSHDN_STATUS_SHIFT 1 +#define M98925_THERMSHDN_STATUS_WIDTH 1 + +/* MAX98925_R003_LIVE_STATUS1 */ +#define M98925_SPKCURNT_STATUS_MASK (1<<5) +#define M98925_SPKCURNT_STATUS_SHIFT 5 +#define M98925_SPKCURNT_STATUS_WIDTH 1 +#define M98925_WATCHFAIL_STATUS_MASK (1<<4) +#define M98925_WATCHFAIL_STATUS_SHIFT 4 +#define M98925_WATCHFAIL_STATUS_WIDTH 1 +#define M98925_ALCINFH_STATUS_MASK (1<<3) +#define M98925_ALCINFH_STATUS_SHIFT 3 +#define M98925_ALCINFH_STATUS_WIDTH 1 +#define M98925_ALCACT_STATUS_MASK (1<<2) +#define M98925_ALCACT_STATUS_SHIFT 2 +#define M98925_ALCACT_STATUS_WIDTH 1 +#define M98925_ALCMUT_STATUS_MASK (1<<1) +#define M98925_ALCMUT_STATUS_SHIFT 1 +#define M98925_ALCMUT_STATUS_WIDTH 1 +#define M98925_ACLP_STATUS_MASK (1<<0) +#define M98925_ACLP_STATUS_SHIFT 0 +#define M98925_ACLP_STATUS_WIDTH 1 + +/* MAX98925_R004_LIVE_STATUS2 */ +#define M98925_SLOTOVRN_STATUS_MASK (1<<6) +#define M98925_SLOTOVRN_STATUS_SHIFT 6 +#define M98925_SLOTOVRN_STATUS_WIDTH 1 +#define M98925_INVALSLOT_STATUS_MASK (1<<5) +#define M98925_INVALSLOT_STATUS_SHIFT 5 +#define M98925_INVALSLOT_STATUS_WIDTH 1 +#define M98925_SLOTCNFLT_STATUS_MASK (1<<4) +#define M98925_SLOTCNFLT_STATUS_SHIFT 4 +#define M98925_SLOTCNFLT_STATUS_WIDTH 1 +#define M98925_VBSTOVFL_STATUS_MASK (1<<3) +#define M98925_VBSTOVFL_STATUS_SHIFT 3 +#define M98925_VBSTOVFL_STATUS_WIDTH 1 +#define M98925_VBATOVFL_STATUS_MASK (1<<2) +#define M98925_VBATOVFL_STATUS_SHIFT 2 +#define M98925_VBATOVFL_STATUS_WIDTH 1 +#define M98925_IMONOVFL_STATUS_MASK (1<<1) +#define M98925_IMONOVFL_STATUS_SHIFT 1 +#define M98925_IMONOVFL_STATUS_WIDTH 1 +#define M98925_VMONOVFL_STATUS_MASK (1<<0) +#define M98925_VMONOVFL_STATUS_SHIFT 0 +#define M98925_VMONOVFL_STATUS_WIDTH 1 + +/* MAX98925_R005_STATE0 */ +#define M98925_THERMWARN_END_STATE_MASK (1<<3) +#define M98925_THERMWARN_END_STATE_SHIFT 3 +#define M98925_THERMWARN_END_STATE_WIDTH 1 +#define M98925_THERMWARN_BGN_STATE_MASK (1<<2) +#define M98925_THERMWARN_BGN_STATE_SHIFT 1 +#define M98925_THERMWARN_BGN_STATE_WIDTH 1 +#define M98925_THERMSHDN_END_STATE_MASK (1<<1) +#define M98925_THERMSHDN_END_STATE_SHIFT 1 +#define M98925_THERMSHDN_END_STATE_WIDTH 1 +#define M98925_THERMSHDN_BGN_STATE_MASK (1<<0) +#define M98925_THERMSHDN_BGN_STATE_SHIFT 0 +#define M98925_THERMSHDN_BGN_STATE_WIDTH 1 + +/* MAX98925_R006_STATE1 */ +#define M98925_SPRCURNT_STATE_MASK (1<<5) +#define M98925_SPRCURNT_STATE_SHIFT 5 +#define M98925_SPRCURNT_STATE_WIDTH 1 +#define M98925_WATCHFAIL_STATE_MASK (1<<4) +#define M98925_WATCHFAIL_STATE_SHIFT 4 +#define M98925_WATCHFAIL_STATE_WIDTH 1 +#define M98925_ALCINFH_STATE_MASK (1<<3) +#define M98925_ALCINFH_STATE_SHIFT 3 +#define M98925_ALCINFH_STATE_WIDTH 1 +#define M98925_ALCACT_STATE_MASK (1<<2) +#define M98925_ALCACT_STATE_SHIFT 2 +#define M98925_ALCACT_STATE_WIDTH 1 +#define M98925_ALCMUT_STATE_MASK (1<<1) +#define M98925_ALCMUT_STATE_SHIFT 1 +#define M98925_ALCMUT_STATE_WIDTH 1 +#define M98925_ALCP_STATE_MASK (1<<0) +#define M98925_ALCP_STATE_SHIFT 0 +#define M98925_ALCP_STATE_WIDTH 1 + +/* MAX98925_R007_STATE2 */ +#define M98925_SLOTOVRN_STATE_MASK (1<<6) +#define M98925_SLOTOVRN_STATE_SHIFT 6 +#define M98925_SLOTOVRN_STATE_WIDTH 1 +#define M98925_INVALSLOT_STATE_MASK (1<<5) +#define M98925_INVALSLOT_STATE_SHIFT 5 +#define M98925_INVALSLOT_STATE_WIDTH 1 +#define M98925_SLOTCNFLT_STATE_MASK (1<<4) +#define M98925_SLOTCNFLT_STATE_SHIFT 4 +#define M98925_SLOTCNFLT_STATE_WIDTH 1 +#define M98925_VBSTOVFL_STATE_MASK (1<<3) +#define M98925_VBSTOVFL_STATE_SHIFT 3 +#define M98925_VBSTOVFL_STATE_WIDTH 1 +#define M98925_VBATOVFL_STATE_MASK (1<<2) +#define M98925_VBATOVFL_STATE_SHIFT 2 +#define M98925_VBATOVFL_STATE_WIDTH 1 +#define M98925_IMONOVFL_STATE_MASK (1<<1) +#define M98925_IMONOVFL_STATE_SHIFT 1 +#define M98925_IMONOVFL_STATE_WIDTH 1 +#define M98925_VMONOVFL_STATE_MASK (1<<0) +#define M98925_VMONOVFL_STATE_SHIFT 0 +#define M98925_VMONOVFL_STATE_WIDTH 1 + +/* MAX98925_R008_FLAG0 */ +#define M98925_THERMWARN_END_FLAG_MASK (1<<3) +#define M98925_THERMWARN_END_FLAG_SHIFT 3 +#define M98925_THERMWARN_END_FLAG_WIDTH 1 +#define M98925_THERMWARN_BGN_FLAG_MASK (1<<2) +#define M98925_THERMWARN_BGN_FLAG_SHIFT 2 +#define M98925_THERMWARN_BGN_FLAG_WIDTH 1 +#define M98925_THERMSHDN_END_FLAG_MASK (1<<1) +#define M98925_THERMSHDN_END_FLAG_SHIFT 1 +#define M98925_THERMSHDN_END_FLAG_WIDTH 1 +#define M98925_THERMSHDN_BGN_FLAG_MASK (1<<0) +#define M98925_THERMSHDN_BGN_FLAG_SHIFT 0 +#define M98925_THERMSHDN_BGN_FLAG_WIDTH 1 + +/* MAX98925_R009_FLAG1 */ +#define M98925_SPKCURNT_FLAG_MASK (1<<5) +#define M98925_SPKCURNT_FLAG_SHIFT 5 +#define M98925_SPKCURNT_FLAG_WIDTH 1 +#define M98925_WATCHFAIL_FLAG_MASK (1<<4) +#define M98925_WATCHFAIL_FLAG_SHIFT 4 +#define M98925_WATCHFAIL_FLAG_WIDTH 1 +#define M98925_ALCINFH_FLAG_MASK (1<<3) +#define M98925_ALCINFH_FLAG_SHIFT 3 +#define M98925_ALCINFH_FLAG_WIDTH 1 +#define M98925_ALCACT_FLAG_MASK (1<<2) +#define M98925_ALCACT_FLAG_SHIFT 2 +#define M98925_ALCACT_FLAG_WIDTH 1 +#define M98925_ALCMUT_FLAG_MASK (1<<1) +#define M98925_ALCMUT_FLAG_SHIFT 1 +#define M98925_ALCMUT_FLAG_WIDTH 1 +#define M98925_ALCP_FLAG_MASK (1<<0) +#define M98925_ALCP_FLAG_SHIFT 0 +#define M98925_ALCP_FLAG_WIDTH 1 + +/* MAX98925_R00A_FLAG2 */ +#define M98925_SLOTOVRN_FLAG_MASK (1<<6) +#define M98925_SLOTOVRN_FLAG_SHIFT 6 +#define M98925_SLOTOVRN_FLAG_WIDTH 1 +#define M98925_INVALSLOT_FLAG_MASK (1<<5) +#define M98925_INVALSLOT_FLAG_SHIFT 5 +#define M98925_INVALSLOT_FLAG_WIDTH 1 +#define M98925_SLOTCNFLT_FLAG_MASK (1<<4) +#define M98925_SLOTCNFLT_FLAG_SHIFT 4 +#define M98925_SLOTCNFLT_FLAG_WIDTH 1 +#define M98925_VBSTOVFL_FLAG_MASK (1<<3) +#define M98925_VBSTOVFL_FLAG_SHIFT 3 +#define M98925_VBSTOVFL_FLAG_WIDTH 1 +#define M98925_VBATOVFL_FLAG_MASK (1<<2) +#define M98925_VBATOVFL_FLAG_SHIFT 2 +#define M98925_VBATOVFL_FLAG_WIDTH 1 +#define M98925_IMONOVFL_FLAG_MASK (1<<1) +#define M98925_IMONOVFL_FLAG_SHIFT 1 +#define M98925_IMONOVFL_FLAG_WIDTH 1 +#define M98925_VMONOVFL_FLAG_MASK (1<<0) +#define M98925_VMONOVFL_FLAG_SHIFT 0 +#define M98925_VMONOVFL_FLAG_WIDTH 1 + +/* MAX98925_R00B_IRQ_ENABLE0 */ +#define M98925_THERMWARN_END_EN_MASK (1<<3) +#define M98925_THERMWARN_END_EN_SHIFT 3 +#define M98925_THERMWARN_END_EN_WIDTH 1 +#define M98925_THERMWARN_BGN_EN_MASK (1<<2) +#define M98925_THERMWARN_BGN_EN_SHIFT 2 +#define M98925_THERMWARN_BGN_EN_WIDTH 1 +#define M98925_THERMSHDN_END_EN_MASK (1<<1) +#define M98925_THERMSHDN_END_EN_SHIFT 1 +#define M98925_THERMSHDN_END_EN_WIDTH 1 +#define M98925_THERMSHDN_BGN_EN_MASK (1<<0) +#define M98925_THERMSHDN_BGN_EN_SHIFT 0 +#define M98925_THERMSHDN_BGN_EN_WIDTH 1 + +/* MAX98925_R00C_IRQ_ENABLE1 */ +#define M98925_SPKCURNT_EN_MASK (1<<5) +#define M98925_SPKCURNT_EN_SHIFT 5 +#define M98925_SPKCURNT_EN_WIDTH 1 +#define M98925_WATCHFAIL_EN_MASK (1<<4) +#define M98925_WATCHFAIL_EN_SHIFT 4 +#define M98925_WATCHFAIL_EN_WIDTH 1 +#define M98925_ALCINFH_EN_MASK (1<<3) +#define M98925_ALCINFH_EN_SHIFT 3 +#define M98925_ALCINFH_EN_WIDTH 1 +#define M98925_ALCACT_EN_MASK (1<<2) +#define M98925_ALCACT_EN_SHIFT 2 +#define M98925_ALCACT_EN_WIDTH 1 +#define M98925_ALCMUT_EN_MASK (1<<1) +#define M98925_ALCMUT_EN_SHIFT 1 +#define M98925_ALCMUT_EN_WIDTH 1 +#define M98925_ALCP_EN_MASK (1<<0) +#define M98925_ALCP_EN_SHIFT 0 +#define M98925_ALCP_EN_WIDTH 1 + +/* MAX98925_R00D_IRQ_ENABLE2 */ +#define M98925_SLOTOVRN_EN_MASK (1<<6) +#define M98925_SLOTOVRN_EN_SHIFT 6 +#define M98925_SLOTOVRN_EN_WIDTH 1 +#define M98925_INVALSLOT_EN_MASK (1<<5) +#define M98925_INVALSLOT_EN_SHIFT 5 +#define M98925_INVALSLOT_EN_WIDTH 1 +#define M98925_SLOTCNFLT_EN_MASK (1<<4) +#define M98925_SLOTCNFLT_EN_SHIFT 4 +#define M98925_SLOTCNFLT_EN_WIDTH 1 +#define M98925_VBSTOVFL_EN_MASK (1<<3) +#define M98925_VBSTOVFL_EN_SHIFT 3 +#define M98925_VBSTOVFL_EN_WIDTH 1 +#define M98925_VBATOVFL_EN_MASK (1<<2) +#define M98925_VBATOVFL_EN_SHIFT 2 +#define M98925_VBATOVFL_EN_WIDTH 1 +#define M98925_IMONOVFL_EN_MASK (1<<1) +#define M98925_IMONOVFL_EN_SHIFT 1 +#define M98925_IMONOVFL_EN_WIDTH 1 +#define M98925_VMONOVFL_EN_MASK (1<<0) +#define M98925_VMONOVFL_EN_SHIFT 0 +#define M98925_VMONOVFL_EN_WIDTH 1 + +/* MAX98925_R00E_IRQ_CLEAR0 */ +#define M98925_THERMWARN_END_CLR_MASK (1<<3) +#define M98925_THERMWARN_END_CLR_SHIFT 3 +#define M98925_THERMWARN_END_CLR_WIDTH 1 +#define M98925_THERMWARN_BGN_CLR_MASK (1<<2) +#define M98925_THERMWARN_BGN_CLR_SHIFT 2 +#define M98925_THERMWARN_BGN_CLR_WIDTH 1 +#define M98925_THERMSHDN_END_CLR_MASK (1<<1) +#define M98925_THERMSHDN_END_CLR_SHIFT 1 +#define M98925_THERMSHDN_END_CLR_WIDTH 1 +#define M98925_THERMSHDN_BGN_CLR_MASK (1<<0) +#define M98925_THERMSHDN_BGN_CLR_SHIFT 0 +#define M98925_THERMSHDN_BGN_CLR_WIDTH 1 + +/* MAX98925_R00F_IRQ_CLEAR1 */ +#define M98925_SPKCURNT_CLR_MASK (1<<5) +#define M98925_SPKCURNT_CLR_SHIFT 5 +#define M98925_SPKCURNT_CLR_WIDTH 1 +#define M98925_WATCHFAIL_CLR_MASK (1<<4) +#define M98925_WATCHFAIL_CLR_SHIFT 4 +#define M98925_WATCHFAIL_CLR_WIDTH 1 +#define M98925_ALCINFH_CLR_MASK (1<<3) +#define M98925_ALCINFH_CLR_SHIFT 3 +#define M98925_ALCINFH_CLR_WIDTH 1 +#define M98925_ALCACT_CLR_MASK (1<<2) +#define M98925_ALCACT_CLR_SHIFT 2 +#define M98925_ALCACT_CLR_WIDTH 1 +#define M98925_ALCMUT_CLR_MASK (1<<1) +#define M98925_ALCMUT_CLR_SHIFT 1 +#define M98925_ALCMUT_CLR_WIDTH 1 +#define M98925_ALCP_CLR_MASK (1<<0) +#define M98925_ALCP_CLR_SHIFT 0 +#define M98925_ALCP_CLR_WIDTH 1 + +/* MAX98925_R010_IRQ_CLEAR2 */ +#define M98925_SLOTOVRN_CLR_MASK (1<<6) +#define M98925_SLOTOVRN_CLR_SHIFT 6 +#define M98925_SLOTOVRN_CLR_WIDTH 1 +#define M98925_INVALSLOT_CLR_MASK (1<<5) +#define M98925_INVALSLOT_CLR_SHIFT 5 +#define M98925_INVALSLOT_CLR_WIDTH 1 +#define M98925_SLOTCNFLT_CLR_MASK (1<<4) +#define M98925_SLOTCNFLT_CLR_SHIFT 4 +#define M98925_SLOTCNFLT_CLR_WIDTH 1 +#define M98925_VBSTOVFL_CLR_MASK (1<<3) +#define M98925_VBSTOVFL_CLR_SHIFT 3 +#define M98925_VBSTOVFL_CLR_WIDTH 1 +#define M98925_VBATOVFL_CLR_MASK (1<<2) +#define M98925_VBATOVFL_CLR_SHIFT 2 +#define M98925_VBATOVFL_CLR_WIDTH 1 +#define M98925_IMONOVFL_CLR_MASK (1<<1) +#define M98925_IMONOVFL_CLR_SHIFT 1 +#define M98925_IMONOVFL_CLR_WIDTH 1 +#define M98925_VMONOVFL_CLR_MASK (1<<0) +#define M98925_VMONOVFL_CLR_SHIFT 0 +#define M98925_VMONOVFL_CLR_WIDTH 1 + +/* MAX98925_R011_MAP0 */ +#define M98925_ER_THERMWARN_EN_MASK (1<<7) +#define M98925_ER_THERMWARN_EN_SHIFT 7 +#define M98925_ER_THERMWARN_EN_WIDTH 1 +#define M98925_ER_THERMWARN_MAP_MASK (0x07<<4) +#define M98925_ER_THERMWARN_MAP_SHIFT 4 +#define M98925_ER_THERMWARN_MAP_WIDTH 3 + +/* MAX98925_R012_MAP1 */ +#define M98925_ER_ALCMUT_EN_MASK (1<<7) +#define M98925_ER_ALCMUT_EN_SHIFT 7 +#define M98925_ER_ALCMUT_EN_WIDTH 1 +#define M98925_ER_ALCMUT_MAP_MASK (0x07<<4) +#define M98925_ER_ALCMUT_MAP_SHIFT 4 +#define M98925_ER_ALCMUT_MAP_WIDTH 3 +#define M98925_ER_ALCP_EN_MASK (1<<3) +#define M98925_ER_ALCP_EN_SHIFT 3 +#define M98925_ER_ALCP_EN_WIDTH 1 +#define M98925_ER_ALCP_MAP_MASK (0x07<<0) +#define M98925_ER_ALCP_MAP_SHIFT 0 +#define M98925_ER_ALCP_MAP_WIDTH 3 + +/* MAX98925_R013_MAP2 */ +#define M98925_ER_ALCINFH_EN_MASK (1<<7) +#define M98925_ER_ALCINFH_EN_SHIFT 7 +#define M98925_ER_ALCINFH_EN_WIDTH 1 +#define M98925_ER_ALCINFH_MAP_MASK (0x07<<4) +#define M98925_ER_ALCINFH_MAP_SHIFT 4 +#define M98925_ER_ALCINFH_MAP_WIDTH 3 +#define M98925_ER_ALCACT_EN_MASK (1<<3) +#define M98925_ER_ALCACT_EN_SHIFT 3 +#define M98925_ER_ALCACT_EN_WIDTH 1 +#define M98925_ER_ALCACT_MAP_MASK (0x07<<0) +#define M98925_ER_ALCACT_MAP_SHIFT 0 +#define M98925_ER_ALCACT_MAP_WIDTH 3 + +/* MAX98925_R014_MAP3 */ +#define M98925_ER_SPKCURNT_EN_MASK (1<<7) +#define M98925_ER_SPKCURNT_EN_SHIFT 7 +#define M98925_ER_SPKCURNT_EN_WIDTH 1 +#define M98925_ER_SPKCURNT_MAP_MASK (0x07<<4) +#define M98925_ER_SPKCURNT_MAP_SHIFT 4 +#define M98925_ER_SPKCURNT_MAP_WIDTH 3 + +/* MAX98925_R015_MAP4 */ +/* RESERVED */ + +/* MAX98925_R016_MAP5 */ +#define M98925_ER_IMONOVFL_EN_MASK (1<<7) +#define M98925_ER_IMONOVFL_EN_SHIFT 7 +#define M98925_ER_IMONOVFL_EN_WIDTH 1 +#define M98925_ER_IMONOVFL_MAP_MASK (0x07<<4) +#define M98925_ER_IMONOVFL_MAP_SHIFT 4 +#define M98925_ER_IMONOVFL_MAP_WIDTH 3 +#define M98925_ER_VMONOVFL_EN_MASK (1<<3) +#define M98925_ER_VMONOVFL_EN_SHIFT 3 +#define M98925_ER_VMONOVFL_EN_WIDTH 1 +#define M98925_ER_VMONOVFL_MAP_MASK (0x07<<0) +#define M98925_ER_VMONOVFL_MAP_SHIFT 0 +#define M98925_ER_VMONOVFL_MAP_WIDTH 3 + +/* MAX98925_R017_MAP6 */ +#define M98925_ER_VBSTOVFL_EN_MASK (1<<7) +#define M98925_ER_VBSTOVFL_EN_SHIFT 7 +#define M98925_ER_VBSTOVFL_EN_WIDTH 1 +#define M98925_ER_VBSTOVFL_MAP_MASK (0x07<<4) +#define M98925_ER_VBSTOVFL_MAP_SHIFT 4 +#define M98925_ER_VBSTOVFL_MAP_WIDTH 3 +#define M98925_ER_VBATOVFL_EN_MASK (1<<3) +#define M98925_ER_VBATOVFL_EN_SHIFT 3 +#define M98925_ER_VBATOVFL_EN_WIDTH 1 +#define M98925_ER_VBATOVFL_MAP_MASK (0x07<<0) +#define M98925_ER_VBATOVFL_MAP_SHIFT 0 +#define M98925_ER_VBATOVFL_MAP_WIDTH 3 + +/* MAX98925_R018_MAP7 */ +#define M98925_ER_INVALSLOT_EN_MASK (1<<7) +#define M98925_ER_INVALSLOT_EN_SHIFT 7 +#define M98925_ER_INVALSLOT_EN_WIDTH 1 +#define M98925_ER_INVALSLOT_MAP_MASK (0x07<<4) +#define M98925_ER_INVALSLOT_MAP_SHIFT 4 +#define M98925_ER_INVALSLOT_MAP_WIDTH 3 +#define M98925_ER_SLOTCNFLT_EN_MASK (1<<3) +#define M98925_ER_SLOTCNFLT_EN_SHIFT 3 +#define M98925_ER_SLOTCNFLT_EN_WIDTH 1 +#define M98925_ER_SLOTCNFLT_MAP_MASK (0x07<<0) +#define M98925_ER_SLOTCNFLT_MAP_SHIFT 0 +#define M98925_ER_SLOTCNFLT_MAP_WIDTH 3 + +/* MAX98925_R019_MAP8 */ +#define M98925_ER_SLOTOVRN_EN_MASK (1<<3) +#define M98925_ER_SLOTOVRN_EN_SHIFT 3 +#define M98925_ER_SLOTOVRN_EN_WIDTH 1 +#define M98925_ER_SLOTOVRN_MAP_MASK (0x07<<0) +#define M98925_ER_SLOTOVRN_MAP_SHIFT 0 +#define M98925_ER_SLOTOVRN_MAP_WIDTH 3 + +/* MAX98925_R01A_DAI_CLK_MODE1 */ +#define M98925_DAI_CLK_SOURCE_MASK (1<<6) +#define M98925_DAI_CLK_SOURCE_SHIFT 6 +#define M98925_DAI_CLK_SOURCE_WIDTH 1 +#define M98925_MDLL_MULT_MASK (0x0F<<0) +#define M98925_MDLL_MULT_SHIFT 0 +#define M98925_MDLL_MULT_WIDTH 4 + +#define M98925_MDLL_MULT_MCLKx8 6 +#define M98925_MDLL_MULT_MCLKx16 8 + +/* MAX98925_R01B_DAI_CLK_MODE2 */ +#define M98925_DAI_SR_MASK (0x0F<<4) +#define M98925_DAI_SR_SHIFT 4 +#define M98925_DAI_SR_WIDTH 4 +#define M98925_DAI_MAS_MASK (1<<3) +#define M98925_DAI_MAS_SHIFT 3 +#define M98925_DAI_MAS_WIDTH 1 +#define M98925_DAI_BSEL_MASK (0x07<<0) +#define M98925_DAI_BSEL_SHIFT 0 +#define M98925_DAI_BSEL_WIDTH 3 + +#define M98925_DAI_BSEL_32 (0 << M98925_DAI_BSEL_SHIFT) +#define M98925_DAI_BSEL_48 (1 << M98925_DAI_BSEL_SHIFT) +#define M98925_DAI_BSEL_64 (2 << M98925_DAI_BSEL_SHIFT) +#define M98925_DAI_BSEL_256 (6 << M98925_DAI_BSEL_SHIFT) + +/* MAX98925_R01C_DAI_CLK_DIV_M_MSBS */ +#define M98925_DAI_M_MSBS_MASK (0xFF<<0) +#define M98925_DAI_M_MSBS_SHIFT 0 +#define M98925_DAI_M_MSBS_WIDTH 8 + +/* MAX98925_R01D_DAI_CLK_DIV_M_LSBS */ +#define M98925_DAI_M_LSBS_MASK (0xFF<<0) +#define M98925_DAI_M_LSBS_SHIFT 0 +#define M98925_DAI_M_LSBS_WIDTH 8 + +/* MAX98925_R01E_DAI_CLK_DIV_N_MSBS */ +#define M98925_DAI_N_MSBS_MASK (0x7F<<0) +#define M98925_DAI_N_MSBS_SHIFT 0 +#define M98925_DAI_N_MSBS_WIDTH 7 + +/* MAX98925_R01F_DAI_CLK_DIV_N_LSBS */ +#define M98925_DAI_N_LSBS_MASK (0xFF<<0) +#define M98925_DAI_N_LSBS_SHIFT 0 +#define M98925_DAI_N_LSBS_WIDTH 8 + +/* MAX98925_R020_FORMAT */ +#define M98925_DAI_CHANSZ_MASK (0x03<<6) +#define M98925_DAI_CHANSZ_SHIFT 6 +#define M98925_DAI_CHANSZ_WIDTH 2 +#define M98925_DAI_EXTBCLK_HIZ_MASK (1<<4) +#define M98925_DAI_EXTBCLK_HIZ_SHIFT 4 +#define M98925_DAI_EXTBCLK_HIZ_WIDTH 1 +#define M98925_DAI_WCI_MASK (1<<3) +#define M98925_DAI_WCI_SHIFT 3 +#define M98925_DAI_WCI_WIDTH 1 +#define M98925_DAI_BCI_MASK (1<<2) +#define M98925_DAI_BCI_SHIFT 2 +#define M98925_DAI_BCI_WIDTH 1 +#define M98925_DAI_DLY_MASK (1<<1) +#define M98925_DAI_DLY_SHIFT 1 +#define M98925_DAI_DLY_WIDTH 1 +#define M98925_DAI_TDM_MASK (1<<0) +#define M98925_DAI_TDM_SHIFT 0 +#define M98925_DAI_TDM_WIDTH 1 + +#define M98925_DAI_CHANSZ_16 (1 << M98925_DAI_CHANSZ_SHIFT) +#define M98925_DAI_CHANSZ_24 (2 << M98925_DAI_CHANSZ_SHIFT) +#define M98925_DAI_CHANSZ_32 (3 << M98925_DAI_CHANSZ_SHIFT) + +/* MAX98925_R021_TDM_SLOT_SELECT */ +#define M98925_DAI_DO_EN_MASK (1<<7) +#define M98925_DAI_DO_EN_SHIFT 7 +#define M98925_DAI_DO_EN_WIDTH 1 +#define M98925_DAI_DIN_EN_MASK (1<<6) +#define M98925_DAI_DIN_EN_SHIFT 6 +#define M98925_DAI_DIN_EN_WIDTH 1 +#define M98925_DAI_INR_SOURCE_MASK (0x07<<3) +#define M98925_DAI_INR_SOURCE_SHIFT 3 +#define M98925_DAI_INR_SOURCE_WIDTH 3 +#define M98925_DAI_INL_SOURCE_MASK (0x07<<0) +#define M98925_DAI_INL_SOURCE_SHIFT 0 +#define M98925_DAI_INL_SOURCE_WIDTH 3 + +/* MAX98925_R022_DOUT_CFG_VMON */ +#define M98925_DAI_VMON_EN_MASK (1<<5) +#define M98925_DAI_VMON_EN_SHIFT 5 +#define M98925_DAI_VMON_EN_WIDTH 1 +#define M98925_DAI_VMON_SLOT_MASK (0x1F<<0) +#define M98925_DAI_VMON_SLOT_SHIFT 0 +#define M98925_DAI_VMON_SLOT_WIDTH 5 + +#define M98925_DAI_VMON_SLOT_00_01 (0 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_01_02 (1 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_02_03 (2 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_03_04 (3 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_04_05 (4 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_05_06 (5 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_06_07 (6 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_07_08 (7 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_08_09 (8 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_09_0A (9 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0A_0B (10 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0B_0C (11 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0C_0D (12 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0D_0E (13 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0E_0F (14 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0F_10 (15 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_10_11 (16 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_11_12 (17 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_12_13 (18 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_13_14 (19 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_14_15 (20 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_15_16 (21 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_16_17 (22 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_17_18 (23 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_18_19 (24 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_19_1A (25 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1A_1B (26 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1B_1C (27 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1C_1D (28 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1D_1E (29 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1E_1F (30 << M98925_DAI_VMON_SLOT_SHIFT) + +/* MAX98925_R023_DOUT_CFG_IMON */ +#define M98925_DAI_IMON_EN_MASK (1<<5) +#define M98925_DAI_IMON_EN_SHIFT 5 +#define M98925_DAI_IMON_EN_WIDTH 1 +#define M98925_DAI_IMON_SLOT_MASK (0x1F<<0) +#define M98925_DAI_IMON_SLOT_SHIFT 0 +#define M98925_DAI_IMON_SLOT_WIDTH 5 + +#define M98925_DAI_IMON_SLOT_00_01 (0 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_01_02 (1 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_02_03 (2 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_03_04 (3 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_04_05 (4 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_05_06 (5 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_06_07 (6 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_07_08 (7 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_08_09 (8 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_09_0A (9 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0A_0B (10 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0B_0C (11 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0C_0D (12 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0D_0E (13 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0E_0F (14 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0F_10 (15 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_10_11 (16 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_11_12 (17 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_12_13 (18 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_13_14 (19 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_14_15 (20 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_15_16 (21 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_16_17 (22 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_17_18 (23 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_18_19 (24 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_19_1A (25 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1A_1B (26 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1B_1C (27 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1C_1D (28 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1D_1E (29 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1E_1F (30 << M98925_DAI_IMON_SLOT_SHIFT) + +/* MAX98925_R024_DOUT_CFG_VBAT */ +#define M98925_DAI_VBAT_EN_MASK (1<<5) +#define M98925_DAI_VBAT_EN_SHIFT 5 +#define M98925_DAI_VBAT_EN_WIDTH 1 +#define M98925_DAI_VBAT_SLOT_MASK (0x1F<<0) +#define M98925_DAI_VBAT_SLOT_SHIFT 0 +#define M98925_DAI_VBAT_SLOT_WIDTH 5 + +/* MAX98925_R025_DOUT_CFG_VBST */ +#define M98925_DAI_VBST_EN_MASK (1<<5) +#define M98925_DAI_VBST_EN_SHIFT 5 +#define M98925_DAI_VBST_EN_WIDTH 1 +#define M98925_DAI_VBST_SLOT_MASK (0x1F<<0) +#define M98925_DAI_VBST_SLOT_SHIFT 0 +#define M98925_DAI_VBST_SLOT_WIDTH 5 + +/* MAX98925_R026_DOUT_CFG_FLAG */ +#define M98925_DAI_FLAG_EN_MASK (1<<5) +#define M98925_DAI_FLAG_EN_SHIFT 5 +#define M98925_DAI_FLAG_EN_WIDTH 1 +#define M98925_DAI_FLAG_SLOT_MASK (0x1F<<0) +#define M98925_DAI_FLAG_SLOT_SHIFT 0 +#define M98925_DAI_FLAG_SLOT_WIDTH 5 + +/* MAX98925_R027_DOUT_HIZ_CFG1 */ +#define M98925_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG1_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG1_WIDTH 8 + +/* MAX98925_R028_DOUT_HIZ_CFG2 */ +#define M98925_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG2_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG2_WIDTH 8 + +/* MAX98925_R029_DOUT_HIZ_CFG3 */ +#define M98925_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG3_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG3_WIDTH 8 + +/* MAX98925_R02A_DOUT_HIZ_CFG4 */ +#define M98925_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG4_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG4_WIDTH 8 + +/* MAX98925_R02B_DOUT_DRV_STRENGTH */ +#define M98925_DAI_OUT_DRIVE_MASK (0x03<<0) +#define M98925_DAI_OUT_DRIVE_SHIFT 0 +#define M98925_DAI_OUT_DRIVE_WIDTH 2 + +/* MAX98925_R02C_FILTERS */ +#define M98925_ADC_DITHER_EN_MASK (1<<7) +#define M98925_ADC_DITHER_EN_SHIFT 7 +#define M98925_ADC_DITHER_EN_WIDTH 1 +#define M98925_IV_DCB_EN_MASK (1<<6) +#define M98925_IV_DCB_EN_SHIFT 6 +#define M98925_IV_DCB_EN_WIDTH 1 +#define M98925_DAC_DITHER_EN_MASK (1<<4) +#define M98925_DAC_DITHER_EN_SHIFT 4 +#define M98925_DAC_DITHER_EN_WIDTH 1 +#define M98925_DAC_FILTER_MODE_MASK (1<<3) +#define M98925_DAC_FILTER_MODE_SHIFT 3 +#define M98925_DAC_FILTER_MODE_WIDTH 1 +#define M98925_DAC_HPF_MASK (0x07<<0) +#define M98925_DAC_HPF_SHIFT 0 +#define M98925_DAC_HPF_WIDTH 3 +#define M98925_DAC_HPF_DISABLE (0 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_DC_BLOCK (1 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_100 (2 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_200 (3 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_400 (4 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_800 (5 << M98925_DAC_HPF_SHIFT) + +/* MAX98925_R02D_GAIN */ +#define M98925_DAC_IN_SEL_MASK (0x03<<5) +#define M98925_DAC_IN_SEL_SHIFT 5 +#define M98925_DAC_IN_SEL_WIDTH 2 +#define M98925_SPK_GAIN_MASK (0x1F<<0) +#define M98925_SPK_GAIN_SHIFT 0 +#define M98925_SPK_GAIN_WIDTH 5 + +#define M98925_DAC_IN_SEL_LEFT_DAI (0 << M98925_DAC_IN_SEL_SHIFT) +#define M98925_DAC_IN_SEL_RIGHT_DAI (1 << M98925_DAC_IN_SEL_SHIFT) +#define M98925_DAC_IN_SEL_SUMMED_DAI (2 << M98925_DAC_IN_SEL_SHIFT) +#define M98925_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << M98925_DAC_IN_SEL_SHIFT) + +/* MAX98925_R02E_GAIN_RAMPING */ +#define M98925_SPK_RMP_EN_MASK (1<<1) +#define M98925_SPK_RMP_EN_SHIFT 1 +#define M98925_SPK_RMP_EN_WIDTH 1 +#define M98925_SPK_ZCD_EN_MASK (1<<0) +#define M98925_SPK_ZCD_EN_SHIFT 0 +#define M98925_SPK_ZCD_EN_WIDTH 1 + +/* MAX98925_R02F_SPK_AMP */ +#define M98925_SPK_MODE_MASK (1<<0) +#define M98925_SPK_MODE_SHIFT 0 +#define M98925_SPK_MODE_WIDTH 1 + +/* MAX98925_R030_THRESHOLD */ +#define M98925_ALC_EN_MASK (1<<5) +#define M98925_ALC_EN_SHIFT 5 +#define M98925_ALC_EN_WIDTH 1 +#define M98925_ALC_TH_MASK (0x1F<<0) +#define M98925_ALC_TH_SHIFT 0 +#define M98925_ALC_TH_WIDTH 5 + +/* MAX98925_R031_ALC_ATTACK */ +#define M98925_ALC_ATK_STEP_MASK (0x0F<<4) +#define M98925_ALC_ATK_STEP_SHIFT 4 +#define M98925_ALC_ATK_STEP_WIDTH 4 +#define M98925_ALC_ATK_RATE_MASK (0x7<<0) +#define M98925_ALC_ATK_RATE_SHIFT 0 +#define M98925_ALC_ATK_RATE_WIDTH 3 + +/* MAX98925_R032_ALC_ATTEN_RLS */ +#define M98925_ALC_MAX_ATTEN_MASK (0x0F<<4) +#define M98925_ALC_MAX_ATTEN_SHIFT 4 +#define M98925_ALC_MAX_ATTEN_WIDTH 4 +#define M98925_ALC_RLS_RATE_MASK (0x7<<0) +#define M98925_ALC_RLS_RATE_SHIFT 0 +#define M98925_ALC_RLS_RATE_WIDTH 3 + +/* MAX98925_R033_ALC_HOLD_RLS */ +#define M98925_ALC_RLS_TGR_MASK (1<<0) +#define M98925_ALC_RLS_TGR_SHIFT 0 +#define M98925_ALC_RLS_TGR_WIDTH 1 + +/* MAX98925_R034_ALC_CONFIGURATION */ +#define M98925_ALC_MUTE_EN_MASK (1<<7) +#define M98925_ALC_MUTE_EN_SHIFT 7 +#define M98925_ALC_MUTE_EN_WIDTH 1 +#define M98925_ALC_MUTE_DLY_MASK (0x07<<4) +#define M98925_ALC_MUTE_DLY_SHIFT 4 +#define M98925_ALC_MUTE_DLY_WIDTH 3 +#define M98925_ALC_RLS_DBT_MASK (0x07<<0) +#define M98925_ALC_RLS_DBT_SHIFT 0 +#define M98925_ALC_RLS_DBT_WIDTH 3 + +/* MAX98925_R035_BOOST_CONVERTER */ +#define M98925_BST_SYNC_MASK (1<<7) +#define M98925_BST_SYNC_SHIFT 7 +#define M98925_BST_SYNC_WIDTH 1 +#define M98925_BST_PHASE_MASK (0x03<<4) +#define M98925_BST_PHASE_SHIFT 4 +#define M98925_BST_PHASE_WIDTH 2 +#define M98925_BST_SKIP_MODE_MASK (0x03<<0) +#define M98925_BST_SKIP_MODE_SHIFT 0 +#define M98925_BST_SKIP_MODE_WIDTH 2 + +/* MAX98925_R036_BLOCK_ENABLE */ +#define M98925_BST_EN_MASK (1<<7) +#define M98925_BST_EN_SHIFT 7 +#define M98925_BST_EN_WIDTH 1 +#define M98925_WATCH_EN_MASK (1<<6) +#define M98925_WATCH_EN_SHIFT 6 +#define M98925_WATCH_EN_WIDTH 1 +#define M98925_CLKMON_EN_MASK (1<<5) +#define M98925_CLKMON_EN_SHIFT 5 +#define M98925_CLKMON_EN_WIDTH 1 +#define M98925_SPK_EN_MASK (1<<4) +#define M98925_SPK_EN_SHIFT 4 +#define M98925_SPK_EN_WIDTH 1 +#define M98925_ADC_VBST_EN_MASK (1<<3) +#define M98925_ADC_VBST_EN_SHIFT 3 +#define M98925_ADC_VBST_EN_WIDTH 1 +#define M98925_ADC_VBAT_EN_MASK (1<<2) +#define M98925_ADC_VBAT_EN_SHIFT 2 +#define M98925_ADC_VBAT_EN_WIDTH 1 +#define M98925_ADC_IMON_EN_MASK (1<<1) +#define M98925_ADC_IMON_EN_SHIFT 1 +#define M98925_ADC_IMON_EN_WIDTH 1 +#define M98925_ADC_VMON_EN_MASK (1<<0) +#define M98925_ADC_VMON_EN_SHIFT 0 +#define M98925_ADC_VMON_EN_WIDTH 1 + +/* MAX98925_R037_CONFIGURATION */ +#define M98925_BST_VOUT_MASK (0x0F<<4) +#define M98925_BST_VOUT_SHIFT 4 +#define M98925_BST_VOUT_WIDTH 4 +#define M98925_THERMWARN_LEVEL_MASK (0x03<<2) +#define M98925_THERMWARN_LEVEL_SHIFT 2 +#define M98925_THERMWARN_LEVEL_WIDTH 2 +#define M98925_WATCH_TIME_MASK (0x03<<0) +#define M98925_WATCH_TIME_SHIFT 0 +#define M98925_WATCH_TIME_WIDTH 2 + +/* MAX98925_R038_GLOBAL_ENABLE */ +#define M98925_EN_MASK (1<<7) +#define M98925_EN_SHIFT 7 +#define M98925_EN_WIDTH 1 + +/* MAX98925_R03A_BOOST_LIMITER */ +#define M98925_BST_ILIM_MASK (0x1F<<3) +#define M98925_BST_ILIM_SHIFT 3 +#define M98925_BST_ILIM_WIDTH 5 + +/* MAX98925_R0FF_VERSION */ +#define M98925_REV_ID_MASK (0xFF<<0) +#define M98925_REV_ID_SHIFT 0 +#define M98925_REV_ID_WIDTH 8 + +struct max98925_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct max98925_pdata *pdata; + unsigned int sysclk; + unsigned int v_slot; + unsigned int i_slot; + unsigned int spk_gain; + unsigned int ch_size; +}; +#endif diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 9974f201a08f..e12764d15431 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/clk.h> +#include <linux/kernel.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -31,8 +32,6 @@ #define DIV_ROUND_DOWN_ULL(ll, d) \ ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) -#define DIV_ROUND_CLOSEST_ULL(ll, d) \ - ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) #define PCM512x_NUM_SUPPLIES 3 static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = { @@ -54,6 +53,9 @@ struct pcm512x_priv { int pll_d; int pll_p; unsigned long real_pll; + unsigned long overclock_pll; + unsigned long overclock_dac; + unsigned long overclock_dsp; }; /* @@ -224,6 +226,90 @@ static bool pcm512x_volatile(struct device *dev, unsigned int reg) } } +static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pcm512x->overclock_pll; + return 0; +} + +static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + 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) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x->overclock_pll = ucontrol->value.integer.value[0]; + return 0; +} + +static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pcm512x->overclock_dsp; + return 0; +} + +static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + 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) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x->overclock_dsp = ucontrol->value.integer.value[0]; + return 0; +} + +static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pcm512x->overclock_dac; + return 0; +} + +static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + 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) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x->overclock_dac = ucontrol->value.integer.value[0]; + return 0; +} + static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1); static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0); @@ -304,9 +390,9 @@ static const struct soc_enum pcm512x_veds = static const struct snd_kcontrol_new pcm512x_controls[] = { SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2, PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv), -SOC_DOUBLE_TLV("Playback Volume", PCM512x_ANALOG_GAIN_CTRL, +SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL, PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv), -SOC_DOUBLE_TLV("Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST, +SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST, PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv), SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT, PCM512x_RQMR_SHIFT, 1, 1), @@ -328,6 +414,13 @@ SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf), SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus), SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf), SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds), + +SOC_SINGLE_EXT("Max Overclock PLL", SND_SOC_NOPM, 0, 20, 0, + pcm512x_overclock_pll_get, pcm512x_overclock_pll_put), +SOC_SINGLE_EXT("Max Overclock DSP", SND_SOC_NOPM, 0, 40, 0, + pcm512x_overclock_dsp_get, pcm512x_overclock_dsp_put), +SOC_SINGLE_EXT("Max Overclock DAC", SND_SOC_NOPM, 0, 40, 0, + pcm512x_overclock_dac_get, pcm512x_overclock_dac_put), }; static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = { @@ -346,6 +439,45 @@ static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { { "OUTR", NULL, "DACR" }, }; +static unsigned long pcm512x_pll_max(struct pcm512x_priv *pcm512x) +{ + return 25000000 + 25000000 * pcm512x->overclock_pll / 100; +} + +static unsigned long pcm512x_dsp_max(struct pcm512x_priv *pcm512x) +{ + return 50000000 + 50000000 * pcm512x->overclock_dsp / 100; +} + +static unsigned long pcm512x_dac_max(struct pcm512x_priv *pcm512x, + unsigned long rate) +{ + return rate + rate * pcm512x->overclock_dac / 100; +} + +static unsigned long pcm512x_sck_max(struct pcm512x_priv *pcm512x) +{ + if (!pcm512x->pll_out) + return 25000000; + return pcm512x_pll_max(pcm512x); +} + +static unsigned long pcm512x_ncp_target(struct pcm512x_priv *pcm512x, + unsigned long dac_rate) +{ + /* + * If the DAC is not actually overclocked, use the good old + * NCP target rate... + */ + if (dac_rate <= 6144000) + return 1536000; + /* + * ...but if the DAC is in fact overclocked, bump the NCP target + * rate to get the recommended dividers even when overclocking. + */ + return pcm512x_dac_max(pcm512x, 1536000); +} + static const u32 pcm512x_dai_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, 384000, @@ -359,6 +491,7 @@ static const struct snd_pcm_hw_constraint_list constraints_slave = { static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { + struct pcm512x_priv *pcm512x = rule->private; struct snd_interval ranges[2]; int frame_size; @@ -377,7 +510,7 @@ static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params, */ memset(ranges, 0, sizeof(ranges)); ranges[0].min = 8000; - ranges[0].max = 25000000 / frame_size / 2; + ranges[0].max = pcm512x_sck_max(pcm512x) / frame_size / 2; ranges[1].min = DIV_ROUND_UP(16000000, frame_size); ranges[1].max = 384000; break; @@ -408,7 +541,7 @@ static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream, return snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, pcm512x_hw_rule_rate, - NULL, + pcm512x, SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); @@ -517,6 +650,8 @@ static unsigned long pcm512x_find_sck(struct snd_soc_dai *dai, unsigned long bclk_rate) { struct device *dev = dai->dev; + struct snd_soc_codec *codec = dai->codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); unsigned long sck_rate; int pow2; @@ -527,9 +662,10 @@ static unsigned long pcm512x_find_sck(struct snd_soc_dai *dai, * as many factors of 2 as possible, as that makes it easier * to find a fast DAC rate */ - pow2 = 1 << fls((25000000 - 16000000) / bclk_rate); + pow2 = 1 << fls((pcm512x_pll_max(pcm512x) - 16000000) / bclk_rate); for (; pow2; pow2 >>= 1) { - sck_rate = rounddown(25000000, bclk_rate * pow2); + sck_rate = rounddown(pcm512x_pll_max(pcm512x), + bclk_rate * pow2); if (sck_rate >= 16000000) break; } @@ -576,8 +712,8 @@ static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai, /* pllin_rate / P (or here, den) cannot be greater than 20 MHz */ if (pllin_rate / den > 20000000 && num < 8) { - num *= 20000000 / (pllin_rate / den); - den *= 20000000 / (pllin_rate / den); + num *= DIV_ROUND_UP(pllin_rate / den, 20000000); + den *= DIV_ROUND_UP(pllin_rate / den, 20000000); } dev_dbg(dev, "num / den = %lu / %lu\n", num, den); @@ -678,7 +814,7 @@ static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai, return 0; /* futile, quit early */ /* run DAC no faster than 6144000 Hz */ - for (dac_rate = rounddown(6144000, osr_rate); + for (dac_rate = rounddown(pcm512x_dac_max(pcm512x, 6144000), osr_rate); dac_rate; dac_rate -= osr_rate) { @@ -805,7 +941,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, osr_rate = 16 * sample_rate; /* run DSP no faster than 50 MHz */ - dsp_div = mck_rate > 50000000 ? 2 : 1; + dsp_div = mck_rate > pcm512x_dsp_max(pcm512x) ? 2 : 1; dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate); if (dac_rate) { @@ -836,7 +972,8 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, dacsrc_rate = pllin_rate; } else { /* run DAC no faster than 6144000 Hz */ - unsigned long dac_mul = 6144000 / osr_rate; + unsigned long dac_mul = pcm512x_dac_max(pcm512x, 6144000) + / osr_rate; unsigned long sck_mul = sck_rate / osr_rate; for (; dac_mul; dac_mul--) { @@ -863,28 +1000,30 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, dacsrc_rate = sck_rate; } + osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate); + if (osr_div > 128) { + dev_err(dev, "Failed to find OSR divider\n"); + return -EINVAL; + } + dac_div = DIV_ROUND_CLOSEST(dacsrc_rate, dac_rate); if (dac_div > 128) { dev_err(dev, "Failed to find DAC divider\n"); return -EINVAL; } + dac_rate = dacsrc_rate / dac_div; - ncp_div = DIV_ROUND_CLOSEST(dacsrc_rate / dac_div, 1536000); - if (ncp_div > 128 || dacsrc_rate / dac_div / ncp_div > 2048000) { + ncp_div = DIV_ROUND_CLOSEST(dac_rate, + pcm512x_ncp_target(pcm512x, dac_rate)); + if (ncp_div > 128 || dac_rate / ncp_div > 2048000) { /* run NCP no faster than 2048000 Hz, but why? */ - ncp_div = DIV_ROUND_UP(dacsrc_rate / dac_div, 2048000); + ncp_div = DIV_ROUND_UP(dac_rate, 2048000); if (ncp_div > 128) { dev_err(dev, "Failed to find NCP divider\n"); return -EINVAL; } } - osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate); - if (osr_div > 128) { - dev_err(dev, "Failed to find OSR divider\n"); - return -EINVAL; - } - idac = mck_rate / (dsp_div * sample_rate); ret = regmap_write(pcm512x->regmap, PCM512x_DSP_CLKDIV, dsp_div - 1); @@ -937,11 +1076,11 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, return ret; } - if (sample_rate <= 48000) + if (sample_rate <= pcm512x_dac_max(pcm512x, 48000)) fssp = PCM512x_FSSP_48KHZ; - else if (sample_rate <= 96000) + else if (sample_rate <= pcm512x_dac_max(pcm512x, 96000)) fssp = PCM512x_FSSP_96KHZ; - else if (sample_rate <= 192000) + else if (sample_rate <= pcm512x_dac_max(pcm512x, 192000)) fssp = PCM512x_FSSP_192KHZ; else fssp = PCM512x_FSSP_384KHZ; @@ -1156,25 +1295,6 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, ret, pcm512x->pll_out); return ret; } - - gpio = PCM512x_G1OE << (4 - 1); - ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN, - gpio, gpio); - if (ret != 0) { - dev_err(codec->dev, "Failed to enable gpio %d: %d\n", - 4, ret); - return ret; - } - - gpio = PCM512x_GPIO_OUTPUT_1 + 4 - 1; - ret = regmap_update_bits(pcm512x->regmap, gpio, - PCM512x_GxSL, PCM512x_GxSL_PLLLK); - if (ret != 0) { - dev_err(codec->dev, - "Failed to output pll lock on %d: %d\n", - ret, 4); - return ret; - } } ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE, diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 9b541e52da8c..0fcda35a3a93 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -395,9 +395,20 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) rt286->jack = jack; - /* Send an initial empty report */ - snd_soc_jack_report(rt286->jack, 0, - SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + if (jack) { + /* enable IRQ */ + if (rt286->jack->status & SND_JACK_HEADPHONE) + snd_soc_dapm_force_enable_pin(&codec->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, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + } else { + /* disable IRQ */ + regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x0); + snd_soc_dapm_disable_pin(&codec->dapm, "LDO1"); + } + snd_soc_dapm_sync(&codec->dapm); return 0; } @@ -1037,7 +1048,6 @@ static int rt286_probe(struct snd_soc_codec *codec) struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); rt286->codec = codec; - codec->dapm.bias_level = SND_SOC_BIAS_OFF; if (rt286->i2c->irq) { regmap_update_bits(rt286->regmap, @@ -1209,7 +1219,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c, { struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt286_priv *rt286; - int i, ret; + int i, ret, val; rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286), GFP_KERNEL); @@ -1224,11 +1234,15 @@ static int rt286_i2c_probe(struct i2c_client *i2c, return ret; } - regmap_read(rt286->regmap, - RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret); - if (ret != RT286_VENDOR_ID && ret != RT288_VENDOR_ID) { + ret = regmap_read(rt286->regmap, + RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val); + if (ret != 0) { + dev_err(&i2c->dev, "I2C error %d\n", ret); + return ret; + } + if (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) { dev_err(&i2c->dev, - "Device with ID register %x is not rt286\n", ret); + "Device with ID register %x is not rt286\n", val); return -ENODEV; } @@ -1236,6 +1250,14 @@ static int rt286_i2c_probe(struct i2c_client *i2c, rt286->i2c = i2c; i2c_set_clientdata(i2c, rt286); + /* restore codec default */ + for (i = 0; i < INDEX_CACHE_SIZE; i++) + regmap_write(rt286->regmap, rt286->index_cache[i].reg, + rt286->index_cache[i].def); + for (i = 0; i < ARRAY_SIZE(rt286_reg); i++) + regmap_write(rt286->regmap, rt286_reg[i].reg, + rt286_reg[i].def); + if (pdata) rt286->pdata = *pdata; diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index c61852742ee3..2c10d77727af 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1675,7 +1675,7 @@ static const struct i2c_device_id rt5631_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id); #ifdef CONFIG_OF -static struct of_device_id rt5631_i2c_dt_ids[] = { +static const struct of_device_id rt5631_i2c_dt_ids[] = { { .compatible = "realtek,rt5631"}, { .compatible = "realtek,alc5631"}, { } diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index c9a4c5be083b..69528ae5410c 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -1270,6 +1270,8 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on) 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); } } } @@ -1538,8 +1540,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY_S("adc stereo1 filter", 1, RT5645_PWR_DIG2, RT5645_PWR_ADC_S1F_BIT, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY_S("adc stereo2 filter", 1, RT5645_PWR_DIG2, - RT5645_PWR_ADC_S2F_BIT, 0, NULL, 0), SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, rt5645_sto1_adc_l_mix, ARRAY_SIZE(rt5645_sto1_adc_l_mix), NULL, 0), @@ -1729,7 +1729,6 @@ static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = { static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc }, - { "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc }, { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc }, { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc }, { "dac mono left filter", NULL, "DAC MONO L ASRC", is_using_asrc }, @@ -2052,7 +2051,7 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); - unsigned int val_len = 0, val_clk, mask_clk; + unsigned int val_len = 0, val_clk, mask_clk, dl_sft; int pre_div, bclk_ms, frame_size; rt5645->lrck[dai->id] = params_rate(params); @@ -2066,6 +2065,16 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); return -EINVAL; } + + switch (rt5645->codec_type) { + case CODEC_TYPE_RT5650: + dl_sft = 4; + break; + default: + dl_sft = 2; + break; + } + bclk_ms = frame_size > 32; rt5645->bclk[dai->id] = rt5645->lrck[dai->id] * (32 << bclk_ms); @@ -2078,13 +2087,13 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, case 16: break; case 20: - val_len |= RT5645_I2S_DL_20; + val_len = 0x1; break; case 24: - val_len |= RT5645_I2S_DL_24; + val_len = 0x2; break; case 8: - val_len |= RT5645_I2S_DL_8; + val_len = 0x3; break; default: return -EINVAL; @@ -2096,7 +2105,7 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, val_clk = bclk_ms << RT5645_I2S_BCLK_MS1_SFT | pre_div << RT5645_I2S_PD1_SFT; snd_soc_update_bits(codec, RT5645_I2S1_SDP, - RT5645_I2S_DL_MASK, val_len); + (0x3 << dl_sft), (val_len << dl_sft)); snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk); break; case RT5645_AIF2: @@ -2104,7 +2113,7 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, val_clk = bclk_ms << RT5645_I2S_BCLK_MS2_SFT | pre_div << RT5645_I2S_PD2_SFT; snd_soc_update_bits(codec, RT5645_I2S2_SDP, - RT5645_I2S_DL_MASK, val_len); + (0x3 << dl_sft), (val_len << dl_sft)); snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk); break; default: @@ -2119,7 +2128,16 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); - unsigned int reg_val = 0; + unsigned int reg_val = 0, pol_sft; + + switch (rt5645->codec_type) { + case CODEC_TYPE_RT5650: + pol_sft = 8; + break; + default: + pol_sft = 7; + break; + } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: @@ -2137,7 +2155,7 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_IB_NF: - reg_val |= RT5645_I2S_BP_INV; + reg_val |= (1 << pol_sft); break; default: return -EINVAL; @@ -2161,12 +2179,12 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (dai->id) { case RT5645_AIF1: snd_soc_update_bits(codec, RT5645_I2S1_SDP, - RT5645_I2S_MS_MASK | RT5645_I2S_BP_MASK | + RT5645_I2S_MS_MASK | (1 << pol_sft) | RT5645_I2S_DF_MASK, reg_val); break; case RT5645_AIF2: snd_soc_update_bits(codec, RT5645_I2S2_SDP, - RT5645_I2S_MS_MASK | RT5645_I2S_BP_MASK | + RT5645_I2S_MS_MASK | (1 << pol_sft) | RT5645_I2S_DF_MASK, reg_val); break; default: @@ -2285,23 +2303,42 @@ static int rt5645_set_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; - unsigned int val = 0; + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + unsigned int i_slot_sft, o_slot_sft, i_width_sht, o_width_sht, en_sft; + unsigned int mask, val = 0; + switch (rt5645->codec_type) { + case CODEC_TYPE_RT5650: + en_sft = 15; + i_slot_sft = 10; + o_slot_sft = 8; + i_width_sht = 6; + o_width_sht = 4; + mask = 0x8ff0; + break; + default: + en_sft = 14; + i_slot_sft = o_slot_sft = 12; + i_width_sht = o_width_sht = 10; + mask = 0x7c00; + break; + } if (rx_mask || tx_mask) { - val |= (1 << 14); - snd_soc_update_bits(codec, RT5645_BASS_BACK, - RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB); + val |= (1 << en_sft); + if (rt5645->codec_type == CODEC_TYPE_RT5645) + snd_soc_update_bits(codec, RT5645_BASS_BACK, + RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB); } switch (slots) { case 4: - val |= (1 << 12); + val |= (1 << i_slot_sft) | (1 << o_slot_sft); break; case 6: - val |= (2 << 12); + val |= (2 << i_slot_sft) | (2 << o_slot_sft); break; case 8: - val |= (3 << 12); + val |= (3 << i_slot_sft) | (3 << o_slot_sft); break; case 2: default: @@ -2310,20 +2347,20 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, switch (slot_width) { case 20: - val |= (1 << 10); + val |= (1 << i_width_sht) | (1 << o_width_sht); break; case 24: - val |= (2 << 10); + val |= (2 << i_width_sht) | (2 << o_width_sht); break; case 32: - val |= (3 << 10); + val |= (3 << i_width_sht) | (3 << o_width_sht); break; case 16: default: break; } - snd_soc_update_bits(codec, RT5645_TDM_CTRL_1, 0x7c00, val); + snd_soc_update_bits(codec, RT5645_TDM_CTRL_1, mask, val); return 0; } @@ -2361,7 +2398,8 @@ 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_write(codec, RT5645_GEN_CTRL1, 0x0128); + 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 | @@ -2598,7 +2636,7 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5645 = { static const struct regmap_config rt5645_regmap = { .reg_bits = 8, .val_bits = 16, - + .use_single_rw = true, .max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) * RT5645_PR_SPACING), .volatile_reg = rt5645_volatile_register, diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index dbfd98c22f4d..db78e9462876 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -804,8 +804,6 @@ #define RT5645_PWR_DAC_MF_L_BIT 10 #define RT5645_PWR_DAC_MF_R (0x1 << 9) #define RT5645_PWR_DAC_MF_R_BIT 9 -#define RT5645_PWR_ADC_S2F (0x1 << 8) -#define RT5645_PWR_ADC_S2F_BIT 8 #define RT5645_PWR_PDM1 (0x1 << 7) #define RT5645_PWR_PDM1_BIT 7 #define RT5645_PWR_PDM2 (0x1 << 6) diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index fd102613d20d..cc7f84a150a7 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -403,6 +403,189 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg) } } +/** + * rt5670_headset_detect - Detect headset. + * @codec: SoC audio codec device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ + +static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert) +{ + int val; + 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_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0); + snd_soc_update_bits(codec, RT5670_CJ_CTRL2, + RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD, + RT5670_CBJ_MN_JD); + snd_soc_write(codec, RT5670_GPIO_CTRL2, 0x0004); + snd_soc_update_bits(codec, RT5670_GPIO_CTRL1, + RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); + snd_soc_update_bits(codec, RT5670_CJ_CTRL1, + RT5670_CBJ_BST1_EN, RT5670_CBJ_BST1_EN); + snd_soc_write(codec, RT5670_JD_CTRL3, 0x00f0); + snd_soc_update_bits(codec, RT5670_CJ_CTRL2, + RT5670_CBJ_MN_JD, RT5670_CBJ_MN_JD); + snd_soc_update_bits(codec, RT5670_CJ_CTRL2, + RT5670_CBJ_MN_JD, 0); + msleep(300); + val = snd_soc_read(codec, RT5670_CJ_CTRL3) & 0x7; + if (val == 0x1 || val == 0x2) { + rt5670->jack_type = SND_JACK_HEADSET; + /* for push button */ + snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x8); + snd_soc_update_bits(codec, RT5670_IL_CMD, 0x40, 0x40); + snd_soc_read(codec, RT5670_IL_CMD); + } 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); + } + } 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); + } + + return rt5670->jack_type; +} + +void rt5670_jack_suspend(struct snd_soc_codec *codec) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + rt5670->jack_type_saved = rt5670->jack_type; + rt5670_headset_detect(codec, 0); +} +EXPORT_SYMBOL_GPL(rt5670_jack_suspend); + +void rt5670_jack_resume(struct snd_soc_codec *codec) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + if (rt5670->jack_type_saved) + rt5670_headset_detect(codec, 1); +} +EXPORT_SYMBOL_GPL(rt5670_jack_resume); + +static int rt5670_button_detect(struct snd_soc_codec *codec) +{ + int btn_type, val; + + val = snd_soc_read(codec, RT5670_IL_CMD); + btn_type = val & 0xff80; + snd_soc_write(codec, RT5670_IL_CMD, val); + if (btn_type != 0) { + msleep(20); + val = snd_soc_read(codec, RT5670_IL_CMD); + snd_soc_write(codec, RT5670_IL_CMD, val); + } + + return btn_type; +} + +static int rt5670_irq_detection(void *data) +{ + struct rt5670_priv *rt5670 = (struct rt5670_priv *)data; + struct snd_soc_jack_gpio *gpio = &rt5670->hp_gpio; + struct snd_soc_jack *jack = rt5670->jack; + int val, btn_type, report = jack->status; + + if (rt5670->pdata.jd_mode == 1) /* 2 port */ + val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0070; + else + val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0020; + + switch (val) { + /* jack in */ + case 0x30: /* 2 port */ + case 0x0: /* 1 port or 2 port */ + if (rt5670->jack_type == 0) { + report = rt5670_headset_detect(rt5670->codec, 1); + /* for push button and jack out */ + gpio->debounce_time = 25; + break; + } + btn_type = 0; + if (snd_soc_read(rt5670->codec, RT5670_INT_IRQ_ST) & 0x4) { + /* button pressed */ + report = SND_JACK_HEADSET; + btn_type = rt5670_button_detect(rt5670->codec); + switch (btn_type) { + case 0x2000: /* up */ + report |= SND_JACK_BTN_1; + break; + case 0x0400: /* center */ + report |= SND_JACK_BTN_0; + break; + case 0x0080: /* down */ + report |= SND_JACK_BTN_2; + break; + default: + dev_err(rt5670->codec->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + if (btn_type == 0)/* button release */ + report = rt5670->jack_type; + + break; + /* jack out */ + case 0x70: /* 2 port */ + case 0x10: /* 2 port */ + case 0x20: /* 1 port */ + report = 0; + snd_soc_update_bits(rt5670->codec, RT5670_INT_IRQ_ST, 0x1, 0x0); + rt5670_headset_detect(rt5670->codec, 0); + gpio->debounce_time = 150; /* for jack in */ + break; + default: + break; + } + + return report; +} + +int rt5670_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + int ret; + + rt5670->jack = jack; + rt5670->hp_gpio.gpiod_dev = codec->dev; + rt5670->hp_gpio.name = "headphone detect"; + rt5670->hp_gpio.report = SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2; + rt5670->hp_gpio.debounce_time = 150; + rt5670->hp_gpio.wake = true; + rt5670->hp_gpio.data = (struct rt5670_priv *)rt5670; + rt5670->hp_gpio.jack_status_check = rt5670_irq_detection; + + ret = snd_soc_jack_add_gpios(rt5670->jack, 1, + &rt5670->hp_gpio); + if (ret) { + dev_err(codec->dev, "Adding jack GPIO failed\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5670_set_jack_detect); + 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(in_vol_tlv, -3450, 150, 0); @@ -517,11 +700,9 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); - unsigned int val; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); - val = snd_soc_read(codec, RT5670_GLB_CLK); - val &= RT5670_SCLK_SRC_MASK; - if (val == RT5670_SCLK_SRC_PLL1) + if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1) return 1; else return 0; @@ -2271,16 +2452,6 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); unsigned int reg_val = 0; - if (freq == rt5670->sysclk && clk_id == rt5670->sysclk_src) - return 0; - - if (rt5670->pdata.jd_mode) { - if (clk_id == RT5670_SCLK_S_PLL1) - snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1"); - else - snd_soc_dapm_disable_pin(&codec->dapm, "PLL1"); - snd_soc_dapm_sync(&codec->dapm); - } switch (clk_id) { case RT5670_SCLK_S_MCLK: reg_val |= RT5670_SCLK_SRC_MCLK; @@ -2298,7 +2469,8 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, snd_soc_update_bits(codec, RT5670_GLB_CLK, RT5670_SCLK_SRC_MASK, reg_val); rt5670->sysclk = freq; - rt5670->sysclk_src = clk_id; + if (clk_id != RT5670_SCLK_S_RCCLK) + rt5670->sysclk_src = clk_id; dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); @@ -2517,6 +2689,7 @@ static int rt5670_remove(struct snd_soc_codec *codec) struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); regmap_write(rt5670->regmap, RT5670_RESET, 0); + snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio); return 0; } @@ -2676,6 +2849,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, if (dmi_check_system(dmi_platform_intel_braswell)) { rt5670->pdata.dmic_en = true; rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; + rt5670->pdata.dev_gpio = true; rt5670->pdata.jd_mode = 1; } @@ -2717,12 +2891,17 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5670->regmap, RT5670_IN2, RT5670_IN_DF2, RT5670_IN_DF2); - if (i2c->irq) { + if (rt5670->pdata.dev_gpio) { + /* for push button */ + regmap_write(rt5670->regmap, RT5670_IL_CMD, 0x0000); + regmap_write(rt5670->regmap, RT5670_IL_CMD2, 0x0010); + regmap_write(rt5670->regmap, RT5670_IL_CMD3, 0x0014); + /* for irq */ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2, RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT); - + regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, 0x8, 0x8); } if (rt5670->pdata.jd_mode) { diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index 21f8e18c13c4..dc2b46236c5c 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -1950,17 +1950,20 @@ enum { }; enum { + RT5670_DMIC1_DISABLED, RT5670_DMIC_DATA_GPIO6, RT5670_DMIC_DATA_IN2P, RT5670_DMIC_DATA_GPIO7, }; enum { + RT5670_DMIC2_DISABLED, RT5670_DMIC_DATA_GPIO8, RT5670_DMIC_DATA_IN3N, }; enum { + RT5670_DMIC3_DISABLED, RT5670_DMIC_DATA_GPIO9, RT5670_DMIC_DATA_GPIO10, RT5670_DMIC_DATA_GPIO5, @@ -1985,6 +1988,8 @@ struct rt5670_priv { struct snd_soc_codec *codec; struct rt5670_platform_data pdata; struct regmap *regmap; + struct snd_soc_jack *jack; + struct snd_soc_jack_gpio hp_gpio; int sysclk; int sysclk_src; @@ -1999,6 +2004,11 @@ struct rt5670_priv { int dsp_sw; /* expected parameter setting */ int dsp_rate; int jack_type; + int jack_type_saved; }; +void rt5670_jack_suspend(struct snd_soc_codec *codec); +void rt5670_jack_resume(struct snd_soc_codec *codec); +int rt5670_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); #endif /* __RT5670_H__ */ diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index fb9c20eace3f..af182586712d 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -718,11 +718,24 @@ static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on) RT5677_LDO1_SEL_MASK, 0x0); regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, RT5677_PWR_LDO1, RT5677_PWR_LDO1); - regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, - RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC); - regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2, - RT5677_PLL2_PR_SRC_MASK | RT5677_DSP_CLK_SRC_MASK, - RT5677_PLL2_PR_SRC_MCLK2 | RT5677_DSP_CLK_SRC_BYPASS); + switch (rt5677->type) { + case RT5677: + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, + RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC); + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2, + RT5677_PLL2_PR_SRC_MASK | + RT5677_DSP_CLK_SRC_MASK, + RT5677_PLL2_PR_SRC_MCLK2 | + RT5677_DSP_CLK_SRC_BYPASS); + break; + case RT5676: + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2, + RT5677_DSP_CLK_SRC_MASK, + RT5677_DSP_CLK_SRC_BYPASS); + break; + default: + break; + } regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff); regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd); rt5677_set_dsp_mode(codec, true); @@ -1021,6 +1034,169 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source, return 0; } +/** + * rt5677_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @codec: SoC audio codec device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5677 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the codec driver will turn on ASRC + * for these filters if ASRC is selected as their clock source. + */ +int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec, + unsigned int filter_mask, unsigned int clk_src) +{ + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + unsigned int asrc3_mask = 0, asrc3_value = 0; + unsigned int asrc4_mask = 0, asrc4_value = 0; + unsigned int asrc5_mask = 0, asrc5_value = 0; + unsigned int asrc6_mask = 0, asrc6_value = 0; + unsigned int asrc7_mask = 0, asrc7_value = 0; + + switch (clk_src) { + case RT5677_CLK_SEL_SYS: + case RT5677_CLK_SEL_I2S1_ASRC: + case RT5677_CLK_SEL_I2S2_ASRC: + case RT5677_CLK_SEL_I2S3_ASRC: + case RT5677_CLK_SEL_I2S4_ASRC: + case RT5677_CLK_SEL_I2S5_ASRC: + case RT5677_CLK_SEL_I2S6_ASRC: + case RT5677_CLK_SEL_SYS2: + case RT5677_CLK_SEL_SYS3: + case RT5677_CLK_SEL_SYS4: + case RT5677_CLK_SEL_SYS5: + case RT5677_CLK_SEL_SYS6: + case RT5677_CLK_SEL_SYS7: + break; + + default: + return -EINVAL; + } + + /* ASRC 3 */ + if (filter_mask & RT5677_DA_STEREO_FILTER) { + asrc3_mask |= RT5677_DA_STO_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5677_DA_STO_CLK_SEL_MASK) + | (clk_src << RT5677_DA_STO_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO2_L_FILTER) { + asrc3_mask |= RT5677_DA_MONO2L_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5677_DA_MONO2L_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO2L_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO2_R_FILTER) { + asrc3_mask |= RT5677_DA_MONO2R_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5677_DA_MONO2R_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO2R_CLK_SEL_SFT); + } + + if (asrc3_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_3, asrc3_mask, + asrc3_value); + + /* ASRC 4 */ + if (filter_mask & RT5677_DA_MONO3_L_FILTER) { + asrc4_mask |= RT5677_DA_MONO3L_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO3L_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO3L_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO3_R_FILTER) { + asrc4_mask |= RT5677_DA_MONO3R_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO3R_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO3R_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO4_L_FILTER) { + asrc4_mask |= RT5677_DA_MONO4L_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO4L_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO4L_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO4_R_FILTER) { + asrc4_mask |= RT5677_DA_MONO4R_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO4R_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO4R_CLK_SEL_SFT); + } + + if (asrc4_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_4, asrc4_mask, + asrc4_value); + + /* ASRC 5 */ + if (filter_mask & RT5677_AD_STEREO1_FILTER) { + asrc5_mask |= RT5677_AD_STO1_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO1_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO1_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_STEREO2_FILTER) { + asrc5_mask |= RT5677_AD_STO2_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO2_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO2_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_STEREO3_FILTER) { + asrc5_mask |= RT5677_AD_STO3_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO3_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO3_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_STEREO4_FILTER) { + asrc5_mask |= RT5677_AD_STO4_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO4_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO4_CLK_SEL_SFT); + } + + if (asrc5_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_5, asrc5_mask, + asrc5_value); + + /* ASRC 6 */ + if (filter_mask & RT5677_AD_MONO_L_FILTER) { + asrc6_mask |= RT5677_AD_MONOL_CLK_SEL_MASK; + asrc6_value = (asrc6_value & ~RT5677_AD_MONOL_CLK_SEL_MASK) + | (clk_src << RT5677_AD_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_MONO_R_FILTER) { + asrc6_mask |= RT5677_AD_MONOR_CLK_SEL_MASK; + asrc6_value = (asrc6_value & ~RT5677_AD_MONOR_CLK_SEL_MASK) + | (clk_src << RT5677_AD_MONOR_CLK_SEL_SFT); + } + + if (asrc6_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_6, asrc6_mask, + asrc6_value); + + /* ASRC 7 */ + if (filter_mask & RT5677_DSP_OB_0_3_FILTER) { + asrc7_mask |= RT5677_DSP_OB_0_3_CLK_SEL_MASK; + asrc7_value = (asrc7_value & ~RT5677_DSP_OB_0_3_CLK_SEL_MASK) + | (clk_src << RT5677_DSP_OB_0_3_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DSP_OB_4_7_FILTER) { + asrc7_mask |= RT5677_DSP_OB_4_7_CLK_SEL_MASK; + asrc7_value = (asrc7_value & ~RT5677_DSP_OB_4_7_CLK_SEL_MASK) + | (clk_src << RT5677_DSP_OB_4_7_CLK_SEL_SFT); + } + + if (asrc7_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_7, asrc7_mask, + asrc7_value); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5677_sel_asrc_clk_src); + /* Digital Mixer */ static const struct snd_kcontrol_new rt5677_sto1_adc_l_mix[] = { SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER, @@ -4500,10 +4676,10 @@ static int rt5677_suspend(struct snd_soc_codec *codec) if (!rt5677->dsp_vad_en) { regcache_cache_only(rt5677->regmap, true); regcache_mark_dirty(rt5677->regmap); - } - if (gpio_is_valid(rt5677->pow_ldo2)) - gpio_set_value_cansleep(rt5677->pow_ldo2, 0); + if (gpio_is_valid(rt5677->pow_ldo2)) + gpio_set_value_cansleep(rt5677->pow_ldo2, 0); + } return 0; } @@ -4512,12 +4688,12 @@ static int rt5677_resume(struct snd_soc_codec *codec) { struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); - if (gpio_is_valid(rt5677->pow_ldo2)) { - gpio_set_value_cansleep(rt5677->pow_ldo2, 1); - msleep(10); - } - if (!rt5677->dsp_vad_en) { + if (gpio_is_valid(rt5677->pow_ldo2)) { + gpio_set_value_cansleep(rt5677->pow_ldo2, 1); + msleep(10); + } + regcache_cache_only(rt5677->regmap, false); regcache_sync(rt5677->regmap); } @@ -4733,7 +4909,8 @@ static const struct regmap_config rt5677_regmap = { }; static const struct i2c_device_id rt5677_i2c_id[] = { - { "rt5677", 0 }, + { "rt5677", RT5677 }, + { "rt5676", RT5676 }, { } }; MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id); @@ -4850,6 +5027,8 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, rt5677); + rt5677->type = id->driver_data; + if (pdata) rt5677->pdata = *pdata; diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index c0a625f290cc..9dceb41d18ea 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1406,6 +1406,46 @@ #define RT5677_DSP_CLK_SRC_PLL2 (0x0 << 7) #define RT5677_DSP_CLK_SRC_BYPASS (0x1 << 7) +/* ASRC Control 3 (0x85) */ +#define RT5677_DA_STO_CLK_SEL_MASK (0xf << 12) +#define RT5677_DA_STO_CLK_SEL_SFT 12 +#define RT5677_DA_MONO2L_CLK_SEL_MASK (0xf << 4) +#define RT5677_DA_MONO2L_CLK_SEL_SFT 4 +#define RT5677_DA_MONO2R_CLK_SEL_MASK (0xf << 0) +#define RT5677_DA_MONO2R_CLK_SEL_SFT 0 + +/* ASRC Control 4 (0x86) */ +#define RT5677_DA_MONO3L_CLK_SEL_MASK (0xf << 12) +#define RT5677_DA_MONO3L_CLK_SEL_SFT 12 +#define RT5677_DA_MONO3R_CLK_SEL_MASK (0xf << 8) +#define RT5677_DA_MONO3R_CLK_SEL_SFT 8 +#define RT5677_DA_MONO4L_CLK_SEL_MASK (0xf << 4) +#define RT5677_DA_MONO4L_CLK_SEL_SFT 4 +#define RT5677_DA_MONO4R_CLK_SEL_MASK (0xf << 0) +#define RT5677_DA_MONO4R_CLK_SEL_SFT 0 + +/* ASRC Control 5 (0x87) */ +#define RT5677_AD_STO1_CLK_SEL_MASK (0xf << 12) +#define RT5677_AD_STO1_CLK_SEL_SFT 12 +#define RT5677_AD_STO2_CLK_SEL_MASK (0xf << 8) +#define RT5677_AD_STO2_CLK_SEL_SFT 8 +#define RT5677_AD_STO3_CLK_SEL_MASK (0xf << 4) +#define RT5677_AD_STO3_CLK_SEL_SFT 4 +#define RT5677_AD_STO4_CLK_SEL_MASK (0xf << 0) +#define RT5677_AD_STO4_CLK_SEL_SFT 0 + +/* ASRC Control 6 (0x88) */ +#define RT5677_AD_MONOL_CLK_SEL_MASK (0xf << 12) +#define RT5677_AD_MONOL_CLK_SEL_SFT 12 +#define RT5677_AD_MONOR_CLK_SEL_MASK (0xf << 8) +#define RT5677_AD_MONOR_CLK_SEL_SFT 8 + +/* ASRC Control 7 (0x89) */ +#define RT5677_DSP_OB_0_3_CLK_SEL_MASK (0xf << 12) +#define RT5677_DSP_OB_0_3_CLK_SEL_SFT 12 +#define RT5677_DSP_OB_4_7_CLK_SEL_MASK (0xf << 8) +#define RT5677_DSP_OB_4_7_CLK_SEL_SFT 8 + /* VAD Function Control 4 (0x9f) */ #define RT5677_VAD_SRC_MASK (0x7 << 8) #define RT5677_VAD_SRC_SFT 8 @@ -1665,6 +1705,47 @@ enum { RT5677_IRQ_JD3, }; +enum rt5677_type { + RT5677, + RT5676, +}; + +/* ASRC clock source selection */ +enum { + RT5677_CLK_SEL_SYS, + RT5677_CLK_SEL_I2S1_ASRC, + RT5677_CLK_SEL_I2S2_ASRC, + RT5677_CLK_SEL_I2S3_ASRC, + RT5677_CLK_SEL_I2S4_ASRC, + RT5677_CLK_SEL_I2S5_ASRC, + RT5677_CLK_SEL_I2S6_ASRC, + RT5677_CLK_SEL_SYS2, + RT5677_CLK_SEL_SYS3, + RT5677_CLK_SEL_SYS4, + RT5677_CLK_SEL_SYS5, + RT5677_CLK_SEL_SYS6, + RT5677_CLK_SEL_SYS7, +}; + +/* filter mask */ +enum { + RT5677_DA_STEREO_FILTER = 0x1, + RT5677_DA_MONO2_L_FILTER = (0x1 << 1), + RT5677_DA_MONO2_R_FILTER = (0x1 << 2), + RT5677_DA_MONO3_L_FILTER = (0x1 << 3), + RT5677_DA_MONO3_R_FILTER = (0x1 << 4), + RT5677_DA_MONO4_L_FILTER = (0x1 << 5), + RT5677_DA_MONO4_R_FILTER = (0x1 << 6), + RT5677_AD_STEREO1_FILTER = (0x1 << 7), + RT5677_AD_STEREO2_FILTER = (0x1 << 8), + RT5677_AD_STEREO3_FILTER = (0x1 << 9), + RT5677_AD_STEREO4_FILTER = (0x1 << 10), + RT5677_AD_MONO_L_FILTER = (0x1 << 11), + RT5677_AD_MONO_R_FILTER = (0x1 << 12), + RT5677_DSP_OB_0_3_FILTER = (0x1 << 13), + RT5677_DSP_OB_4_7_FILTER = (0x1 << 14), +}; + struct rt5677_priv { struct snd_soc_codec *codec; struct rt5677_platform_data pdata; @@ -1681,6 +1762,7 @@ struct rt5677_priv { int pll_in; int pll_out; int pow_ldo2; /* POW_LDO2 pin */ + enum rt5677_type type; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio_chip; #endif @@ -1690,4 +1772,7 @@ struct rt5677_priv { bool is_vref_slow; }; +int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec, + unsigned int filter_mask, unsigned int clk_src); + #endif /* __RT5677_H__ */ diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 82095d6cd070..7947c0ebb1ed 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -783,19 +783,21 @@ static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec) snd_soc_write(codec, SN95031_BTNCTRL2, 0x01); } -static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack) +static int sn95031_get_headset_state(struct snd_soc_codec *codec, + struct snd_soc_jack *mfld_jack) { - int micbias = sn95031_get_mic_bias(mfld_jack->codec); + int micbias = sn95031_get_mic_bias(codec); int jack_type = snd_soc_jack_get_type(mfld_jack, micbias); pr_debug("jack type detected = %d\n", jack_type); if (jack_type == SND_JACK_HEADSET) - sn95031_enable_jack_btn(mfld_jack->codec); + sn95031_enable_jack_btn(codec); return jack_type; } -void sn95031_jack_detection(struct mfld_jack_data *jack_data) +void sn95031_jack_detection(struct snd_soc_codec *codec, + struct mfld_jack_data *jack_data) { unsigned int status; unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET; @@ -809,11 +811,11 @@ void sn95031_jack_detection(struct mfld_jack_data *jack_data) status = SND_JACK_HEADSET | SND_JACK_BTN_1; } else if (jack_data->intr_id & 0x4) { pr_debug("headset or headphones inserted\n"); - status = sn95031_get_headset_state(jack_data->mfld_jack); + status = sn95031_get_headset_state(codec, jack_data->mfld_jack); } else if (jack_data->intr_id & 0x8) { pr_debug("headset or headphones removed\n"); status = 0; - sn95031_disable_jack_btn(jack_data->mfld_jack->codec); + sn95031_disable_jack_btn(codec); } else { pr_err("unidentified interrupt\n"); return; diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h index 20376d234fb8..7651fe4e6a45 100644 --- a/sound/soc/codecs/sn95031.h +++ b/sound/soc/codecs/sn95031.h @@ -127,6 +127,7 @@ struct mfld_jack_data { struct snd_soc_jack *mfld_jack; }; -extern void sn95031_jack_detection(struct mfld_jack_data *jack_data); +extern void sn95031_jack_detection(struct snd_soc_codec *codec, + struct mfld_jack_data *jack_data); #endif diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index bda2ee18769e..669e3228241e 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1213,27 +1213,15 @@ static int sta350_i2c_probe(struct i2c_client *i2c, #endif /* GPIOs */ - sta350->gpiod_nreset = devm_gpiod_get(dev, "reset"); - if (IS_ERR(sta350->gpiod_nreset)) { - ret = PTR_ERR(sta350->gpiod_nreset); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - sta350->gpiod_nreset = NULL; - } else { - gpiod_direction_output(sta350->gpiod_nreset, 0); - } - - sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down"); - if (IS_ERR(sta350->gpiod_power_down)) { - ret = PTR_ERR(sta350->gpiod_power_down); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - sta350->gpiod_power_down = NULL; - } else { - gpiod_direction_output(sta350->gpiod_power_down, 0); - } + sta350->gpiod_nreset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + 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); + if (IS_ERR(sta350->gpiod_power_down)) + return PTR_ERR(sta350->gpiod_power_down); /* regulators */ for (i = 0; i < ARRAY_SIZE(sta350->supplies); i++) diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index ae23acdd2708..dfb4ff5cc9ea 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -485,16 +485,9 @@ static int tas2552_probe(struct i2c_client *client, if (data == NULL) return -ENOMEM; - data->enable_gpio = devm_gpiod_get(dev, "enable"); - if (IS_ERR(data->enable_gpio)) { - ret = PTR_ERR(data->enable_gpio); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - data->enable_gpio = NULL; - } else { - gpiod_direction_output(data->enable_gpio, 0); - } + data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(data->enable_gpio)) + return PTR_ERR(data->enable_gpio); data->tas2552_client = client; data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c index f13701995482..78a94af65518 100644 --- a/sound/soc/codecs/tlv320aic23-i2c.c +++ b/sound/soc/codecs/tlv320aic23-i2c.c @@ -31,7 +31,7 @@ static int tlv320aic23_i2c_probe(struct i2c_client *i2c, return tlv320aic23_probe(&i2c->dev, regmap); } -static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c) +static int tlv320aic23_i2c_remove(struct i2c_client *i2c) { snd_soc_unregister_codec(&i2c->dev); return 0; @@ -56,7 +56,7 @@ static struct i2c_driver tlv320aic23_i2c_driver = { .of_match_table = of_match_ptr(tlv320aic23_of_match), }, .probe = tlv320aic23_i2c_probe, - .remove = __exit_p(tlv320aic23_i2c_remove), + .remove = tlv320aic23_i2c_remove, .id_table = tlv320aic23_id, }; diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 15599845a660..5a9da28f4f33 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1554,7 +1554,6 @@ static int wm2200_probe(struct snd_soc_codec *codec) int ret; wm2200->codec = codec; - codec->dapm.bias_level = SND_SOC_BIAS_OFF; ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2); if (ret != 0) @@ -1942,6 +1941,7 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source, struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec); struct _fll_div factors; int ret, i, timeout; + unsigned long time_left; if (!Fout) { dev_dbg(codec->dev, "FLL disabled"); @@ -2021,9 +2021,10 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source, /* Poll for the lock; will use the interrupt to exit quickly */ for (i = 0; i < timeout; i++) { if (i2c->irq) { - ret = wait_for_completion_timeout(&wm2200->fll_lock, - msecs_to_jiffies(25)); - if (ret > 0) + time_left = wait_for_completion_timeout( + &wm2200->fll_lock, + msecs_to_jiffies(25)); + if (time_left > 0) break; } else { msleep(1); diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index ea09db585aa1..96740379b711 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1762,6 +1762,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, struct _fll_div factors; struct wm5100_fll *fll; int ret, base, lock, i, timeout; + unsigned long time_left; switch (fll_id) { case WM5100_FLL1: @@ -1842,9 +1843,9 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, /* Poll for the lock; will use interrupt when we can test */ for (i = 0; i < timeout; i++) { if (i2c->irq) { - ret = wait_for_completion_timeout(&fll->lock, - msecs_to_jiffies(25)); - if (ret > 0) + time_left = wait_for_completion_timeout(&fll->lock, + msecs_to_jiffies(25)); + if (time_left > 0) break; } else { msleep(1); diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 6d0fe0ac95a3..0c6d1bc0526e 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1861,7 +1861,6 @@ static unsigned int wm5102_digital_vu[] = { ARIZONA_DAC_DIGITAL_VOLUME_2L, ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_DAC_DIGITAL_VOLUME_3L, - ARIZONA_DAC_DIGITAL_VOLUME_3R, ARIZONA_DAC_DIGITAL_VOLUME_4L, ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_DAC_DIGITAL_VOLUME_5L, diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index c81a9eab3e3e..c65e5a75fc1a 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -69,14 +69,14 @@ struct wm8350_data { struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; int fll_freq_out; int fll_freq_in; + struct delayed_work pga_work; }; /* * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown. */ -static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec) +static inline int wm8350_out1_ramp_step(struct wm8350_data *wm8350_data) { - struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out1 = &wm8350_data->out1; struct wm8350 *wm8350 = wm8350_data->wm8350; int left_complete = 0, right_complete = 0; @@ -140,9 +140,8 @@ static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec) /* * Ramp OUT2 PGA volume to minimise pops at stream startup and shutdown. */ -static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec) +static inline int wm8350_out2_ramp_step(struct wm8350_data *wm8350_data) { - struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out2 = &wm8350_data->out2; struct wm8350 *wm8350 = wm8350_data->wm8350; int left_complete = 0, right_complete = 0; @@ -210,10 +209,8 @@ static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec) */ static void wm8350_pga_work(struct work_struct *work) { - struct snd_soc_dapm_context *dapm = - container_of(work, struct snd_soc_dapm_context, delayed_work.work); - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); - struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); + struct wm8350_data *wm8350_data = + container_of(work, struct wm8350_data, pga_work.work); struct wm8350_output *out1 = &wm8350_data->out1, *out2 = &wm8350_data->out2; int i, out1_complete, out2_complete; @@ -226,9 +223,9 @@ static void wm8350_pga_work(struct work_struct *work) for (i = 0; i <= 63; i++) { out1_complete = 1, out2_complete = 1; if (out1->ramp != WM8350_RAMP_NONE) - out1_complete = wm8350_out1_ramp_step(codec); + out1_complete = wm8350_out1_ramp_step(wm8350_data); if (out2->ramp != WM8350_RAMP_NONE) - out2_complete = wm8350_out2_ramp_step(codec); + out2_complete = wm8350_out2_ramp_step(wm8350_data); /* ramp finished ? */ if (out1_complete && out2_complete) @@ -283,7 +280,7 @@ static int pga_event(struct snd_soc_dapm_widget *w, out->ramp = WM8350_RAMP_UP; out->active = 1; - schedule_delayed_work(&codec->dapm.delayed_work, + schedule_delayed_work(&wm8350_data->pga_work, msecs_to_jiffies(1)); break; @@ -291,7 +288,7 @@ static int pga_event(struct snd_soc_dapm_widget *w, out->ramp = WM8350_RAMP_DOWN; out->active = 0; - schedule_delayed_work(&codec->dapm.delayed_work, + schedule_delayed_work(&wm8350_data->pga_work, msecs_to_jiffies(1)); break; } @@ -1492,7 +1489,7 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) /* Put the codec into reset if it wasn't already */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); - INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8350_pga_work); + INIT_DELAYED_WORK(&priv->pga_work, wm8350_pga_work); INIT_DELAYED_WORK(&priv->hpl.work, wm8350_hpl_work); INIT_DELAYED_WORK(&priv->hpr.work, wm8350_hpr_work); @@ -1578,7 +1575,7 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) /* if there was any work waiting then we run it now and * wait for its completion */ - flush_delayed_work(&codec->dapm.delayed_work); + flush_delayed_work(&priv->pga_work); wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index c6d10533e2bd..2245b6a32f3d 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -25,6 +25,7 @@ #include <linux/spi/spi.h> #include <linux/of_device.h> #include <linux/mutex.h> +#include <linux/clk.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -45,6 +46,7 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { /* codec private data */ struct wm8731_priv { struct regmap *regmap; + struct clk *mclk; struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; const struct snd_pcm_hw_constraint_list *constraints; unsigned int sysclk; @@ -390,6 +392,8 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, switch (clk_id) { case WM8731_SYSCLK_XTAL: case WM8731_SYSCLK_MCLK: + if (wm8731->mclk && clk_set_rate(wm8731->mclk, freq)) + return -EINVAL; wm8731->sysclk_type = clk_id; break; default: @@ -491,6 +495,8 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: + if (wm8731->mclk) + clk_prepare_enable(wm8731->mclk); break; case SND_SOC_BIAS_PREPARE: break; @@ -509,6 +515,8 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8731_PWR, reg | 0x0040); break; case SND_SOC_BIAS_OFF: + if (wm8731->mclk) + clk_disable_unprepare(wm8731->mclk); snd_soc_write(codec, WM8731_PWR, 0xffff); regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); @@ -667,6 +675,19 @@ static int wm8731_spi_probe(struct spi_device *spi) if (wm8731 == NULL) return -ENOMEM; + wm8731->mclk = devm_clk_get(&spi->dev, "mclk"); + if (IS_ERR(wm8731->mclk)) { + ret = PTR_ERR(wm8731->mclk); + if (ret == -ENOENT) { + wm8731->mclk = NULL; + dev_warn(&spi->dev, "Assuming static MCLK\n"); + } else { + dev_err(&spi->dev, "Failed to get MCLK: %d\n", + ret); + return ret; + } + } + mutex_init(&wm8731->lock); wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); @@ -718,6 +739,19 @@ static int wm8731_i2c_probe(struct i2c_client *i2c, if (wm8731 == NULL) return -ENOMEM; + wm8731->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (IS_ERR(wm8731->mclk)) { + ret = PTR_ERR(wm8731->mclk); + if (ret == -ENOENT) { + wm8731->mclk = NULL; + dev_warn(&i2c->dev, "Assuming static MCLK\n"); + } else { + dev_err(&i2c->dev, "Failed to get MCLK: %d\n", + ret); + return ret; + } + } + mutex_init(&wm8731->lock); wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 31bb4801a005..9e71c768966f 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -123,7 +123,7 @@ static struct { }; static const unsigned int rates_11289[] = { - 44100, 88235, + 44100, 88200, }; static const struct snd_pcm_hw_constraint_list constraints_11289 = { @@ -150,7 +150,7 @@ static const struct snd_pcm_hw_constraint_list constraints_16384 = { }; static const unsigned int rates_16934[] = { - 44100, 88235, + 44100, 88200, }; static const struct snd_pcm_hw_constraint_list constraints_16934 = { @@ -168,7 +168,7 @@ static const struct snd_pcm_hw_constraint_list constraints_18432 = { }; static const unsigned int rates_22579[] = { - 44100, 88235, 1764000 + 44100, 88200, 176400 }; static const struct snd_pcm_hw_constraint_list constraints_22579 = { @@ -186,7 +186,7 @@ static const struct snd_pcm_hw_constraint_list constraints_24576 = { }; static const unsigned int rates_36864[] = { - 48000, 96000, 19200 + 48000, 96000, 192000 }; static const struct snd_pcm_hw_constraint_list constraints_36864 = { diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 21ca3a94fc96..c50a5959345f 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -153,6 +153,7 @@ struct wm8753_priv { unsigned int hifi_fmt; int dai_func; + struct delayed_work charge_work; }; #define wm8753_reset(c) snd_soc_write(c, WM8753_RESET, 0) @@ -1326,9 +1327,19 @@ static int wm8753_mute(struct snd_soc_dai *dai, int mute) return 0; } +static void wm8753_charge_work(struct work_struct *work) +{ + struct wm8753_priv *wm8753 = + container_of(work, struct wm8753_priv, charge_work.work); + + /* Set to 500k */ + regmap_update_bits(wm8753->regmap, WM8753_PWR1, 0x0180, 0x0100); +} + static int wm8753_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); u16 pwr_reg = snd_soc_read(codec, WM8753_PWR1) & 0xfe3e; switch (level) { @@ -1337,14 +1348,22 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x00c0); break; case SND_SOC_BIAS_PREPARE: - /* set vmid to 5k for quick power up */ - snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1); + /* Wait until fully charged */ + flush_delayed_work(&wm8753->charge_work); break; case SND_SOC_BIAS_STANDBY: - /* mute dac and set vmid to 500k, enable VREF */ - snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x0141); + if (codec->dapm.bias_level == 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, + msecs_to_jiffies(caps_charge)); + } else { + /* mute dac and set vmid to 500k, enable VREF */ + snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x0141); + } break; case SND_SOC_BIAS_OFF: + cancel_delayed_work_sync(&wm8753->charge_work); snd_soc_write(codec, WM8753_PWR1, 0x0001); break; } @@ -1428,38 +1447,12 @@ static struct snd_soc_dai_driver wm8753_dai[] = { }, }; -static void wm8753_work(struct work_struct *work) -{ - struct snd_soc_dapm_context *dapm = - container_of(work, struct snd_soc_dapm_context, - delayed_work.work); - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); - wm8753_set_bias_level(codec, dapm->bias_level); -} - -static int wm8753_suspend(struct snd_soc_codec *codec) -{ - wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int wm8753_resume(struct snd_soc_codec *codec) { struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); regcache_sync(wm8753->regmap); - wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - /* charge wm8753 caps */ - if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { - wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); - codec->dapm.bias_level = SND_SOC_BIAS_ON; - queue_delayed_work(system_power_efficient_wq, - &codec->dapm.delayed_work, - msecs_to_jiffies(caps_charge)); - } - return 0; } @@ -1468,7 +1461,7 @@ static int wm8753_probe(struct snd_soc_codec *codec) struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); int ret; - INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8753_work); + INIT_DELAYED_WORK(&wm8753->charge_work, wm8753_charge_work); ret = wm8753_reset(codec); if (ret < 0) { @@ -1476,14 +1469,8 @@ static int wm8753_probe(struct snd_soc_codec *codec) return ret; } - wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY); wm8753->dai_func = 0; - /* charge output caps */ - wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); - schedule_delayed_work(&codec->dapm.delayed_work, - msecs_to_jiffies(caps_charge)); - /* set the update bits */ snd_soc_update_bits(codec, WM8753_LDAC, 0x0100, 0x0100); snd_soc_update_bits(codec, WM8753_RDAC, 0x0100, 0x0100); @@ -1499,21 +1486,11 @@ static int wm8753_probe(struct snd_soc_codec *codec) return 0; } -/* power down chip */ -static int wm8753_remove(struct snd_soc_codec *codec) -{ - flush_delayed_work(&codec->dapm.delayed_work); - wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8753 = { .probe = wm8753_probe, - .remove = wm8753_remove, - .suspend = wm8753_suspend, .resume = wm8753_resume, .set_bias_level = wm8753_set_bias_level, + .suspend_bias_off = true, .controls = wm8753_snd_controls, .num_controls = ARRAY_SIZE(wm8753_snd_controls), diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c new file mode 100644 index 000000000000..6596f5f3a0c3 --- /dev/null +++ b/sound/soc/codecs/wm8804-i2c.c @@ -0,0 +1,65 @@ +/* + * wm8804-i2c.c -- WM8804 S/PDIF transceiver driver - I2C + * + * Copyright 2015 Cirrus Logic Inc + * + * Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.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/init.h> +#include <linux/module.h> +#include <linux/i2c.h> + +#include "wm8804.h" + +static int wm8804_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &wm8804_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return wm8804_probe(&i2c->dev, regmap); +} + +static int wm8804_i2c_remove(struct i2c_client *i2c) +{ + wm8804_remove(&i2c->dev); + return 0; +} + +static const struct i2c_device_id wm8804_i2c_id[] = { + { "wm8804", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id); + +static const struct of_device_id wm8804_of_match[] = { + { .compatible = "wlf,wm8804", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8804_of_match); + +static struct i2c_driver wm8804_i2c_driver = { + .driver = { + .name = "wm8804", + .owner = THIS_MODULE, + .pm = &wm8804_pm, + .of_match_table = wm8804_of_match, + }, + .probe = wm8804_i2c_probe, + .remove = wm8804_i2c_remove, + .id_table = wm8804_i2c_id +}; + +module_i2c_driver(wm8804_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8804 driver - I2C"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8804-spi.c b/sound/soc/codecs/wm8804-spi.c new file mode 100644 index 000000000000..407a3cf391e5 --- /dev/null +++ b/sound/soc/codecs/wm8804-spi.c @@ -0,0 +1,57 @@ +/* + * wm8804-spi.c -- WM8804 S/PDIF transceiver driver - SPI + * + * Copyright 2015 Cirrus Logic Inc + * + * Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.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/init.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include "wm8804.h" + +static int wm8804_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &wm8804_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return wm8804_probe(&spi->dev, regmap); +} + +static int wm8804_spi_remove(struct spi_device *spi) +{ + wm8804_remove(&spi->dev); + return 0; +} + +static const struct of_device_id wm8804_of_match[] = { + { .compatible = "wlf,wm8804", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8804_of_match); + +static struct spi_driver wm8804_spi_driver = { + .driver = { + .name = "wm8804", + .owner = THIS_MODULE, + .pm = &wm8804_pm, + .of_match_table = wm8804_of_match, + }, + .probe = wm8804_spi_probe, + .remove = wm8804_spi_remove +}; + +module_spi_driver(wm8804_spi_driver); + +MODULE_DESCRIPTION("ASoC WM8804 driver - SPI"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index b2b0e68f707e..1e403f67cf16 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -13,12 +13,11 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/i2c.h> +#include <linux/pm_runtime.h> #include <linux/of_device.h> -#include <linux/spi/spi.h> -#include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <sound/core.h> @@ -27,6 +26,7 @@ #include <sound/soc.h> #include <sound/initval.h> #include <sound/tlv.h> +#include <sound/soc-dapm.h> #include "wm8804.h" @@ -60,18 +60,23 @@ static const struct reg_default wm8804_reg_defaults[] = { }; struct wm8804_priv { + struct device *dev; struct regmap *regmap; struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES]; struct notifier_block disable_nb[WM8804_NUM_SUPPLIES]; int mclk_div; -}; -static int txsrc_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); + struct gpio_desc *reset; + + int aif_pwr; +}; static int txsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +static int wm8804_aif_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + /* * We can't use the same notifier block for more than one supply and * there's no way I can see to get from a callback to the caller @@ -93,26 +98,62 @@ WM8804_REGULATOR_EVENT(0) WM8804_REGULATOR_EVENT(1) static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; -static SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); +static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text); -static const struct snd_kcontrol_new wm8804_snd_controls[] = { - SOC_ENUM_EXT("Input Source", txsrc, txsrc_get, txsrc_put), - SOC_SINGLE("TX Playback Switch", WM8804_PWRDN, 2, 1, 1), - SOC_SINGLE("AIF Playback Switch", WM8804_PWRDN, 4, 1, 1) +static const struct snd_kcontrol_new wm8804_tx_source_mux[] = { + SOC_DAPM_ENUM_EXT("Input Source", txsrc, + snd_soc_dapm_get_enum_double, txsrc_put), }; -static int txsrc_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec; - unsigned int src; +static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("SPDIF Out"), +SND_SOC_DAPM_INPUT("SPDIF In"), + +SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0), +SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0), + +SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux), + +SND_SOC_DAPM_AIF_OUT_E("AIFTX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_AIF_IN_E("AIFRX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route wm8804_dapm_routes[] = { + { "AIFRX", NULL, "Playback" }, + { "Tx Source", "AIF", "AIFRX" }, - codec = snd_soc_kcontrol_codec(kcontrol); - src = snd_soc_read(codec, WM8804_SPDTX4); - if (src & 0x40) - ucontrol->value.integer.value[0] = 1; - else - ucontrol->value.integer.value[0] = 0; + { "SPDIFRX", NULL, "SPDIF In" }, + { "Tx Source", "S/PDIF RX", "SPDIFRX" }, + + { "SPDIFTX", NULL, "Tx Source" }, + { "SPDIF Out", NULL, "SPDIFTX" }, + + { "AIFTX", NULL, "SPDIFRX" }, + { "Capture", NULL, "AIFTX" }, +}; + +static int wm8804_aif_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); + struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* power up the aif */ + if (!wm8804->aif_pwr) + snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x0); + wm8804->aif_pwr++; + break; + case SND_SOC_DAPM_POST_PMD: + /* power down only both paths are disabled */ + wm8804->aif_pwr--; + if (!wm8804->aif_pwr) + snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x10); + break; + } return 0; } @@ -120,48 +161,33 @@ static int txsrc_get(struct snd_kcontrol *kcontrol, static int txsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec; - unsigned int src, txpwr; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = &codec->dapm; + 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; + unsigned int txpwr; + + if (val != 0 && val != mask) + return -EINVAL; - codec = snd_soc_kcontrol_codec(kcontrol); + snd_soc_dapm_mutex_lock(dapm); - if (ucontrol->value.integer.value[0] != 0 - && ucontrol->value.integer.value[0] != 1) - return -EINVAL; + if (snd_soc_test_bits(codec, e->reg, mask, val)) { + /* save the current power state of the transmitter */ + txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4; - src = snd_soc_read(codec, WM8804_SPDTX4); - switch ((src & 0x40) >> 6) { - case 0: - if (!ucontrol->value.integer.value[0]) - return 0; - break; - case 1: - if (ucontrol->value.integer.value[1]) - return 0; - break; - } + /* power down the transmitter */ + snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4); - /* save the current power state of the transmitter */ - txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4; - /* power down the transmitter */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4); - /* set the tx source */ - snd_soc_update_bits(codec, WM8804_SPDTX4, 0x40, - ucontrol->value.integer.value[0] << 6); - - if (ucontrol->value.integer.value[0]) { - /* power down the receiver */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0x2); - /* power up the AIF */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0); - } else { - /* don't power down the AIF -- may be used as an output */ - /* power up the receiver */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0); + /* set the tx source */ + snd_soc_update_bits(codec, e->reg, mask, val); + + /* restore the transmitter's configuration */ + snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr); } - /* restore the transmitter's configuration */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr); + snd_soc_dapm_mutex_unlock(dapm); return 0; } @@ -185,9 +211,9 @@ static bool wm8804_volatile(struct device *dev, unsigned int reg) } } -static int wm8804_reset(struct snd_soc_codec *codec) +static int wm8804_soft_reset(struct wm8804_priv *wm8804) { - return snd_soc_write(codec, WM8804_RST_DEVID1, 0x0); + return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0); } static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) @@ -379,19 +405,19 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { - struct snd_soc_codec *codec; + struct snd_soc_codec *codec = dai->codec; + struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec); + bool change; - codec = dai->codec; if (!freq_in || !freq_out) { /* disable the PLL */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); - return 0; + regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN, + 0x1, 0x1, &change); + if (change) + pm_runtime_put(wm8804->dev); } else { int ret; struct pll_div pll_div; - struct wm8804_priv *wm8804; - - wm8804 = snd_soc_codec_get_drvdata(codec); ret = pll_factors(&pll_div, freq_out, freq_in, wm8804->mclk_div); @@ -399,7 +425,10 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id, return ret; /* power down the PLL before reprogramming it */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); + regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN, + 0x1, 0x1, &change); + if (!change) + pm_runtime_get_sync(wm8804->dev); /* set PLLN and PRESCALE */ snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10, @@ -477,141 +506,6 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai, return 0; } -static int wm8804_set_bias_level(struct snd_soc_codec *codec, - enum snd_soc_bias_level level) -{ - int ret; - struct wm8804_priv *wm8804; - - wm8804 = snd_soc_codec_get_drvdata(codec); - switch (level) { - case SND_SOC_BIAS_ON: - break; - case SND_SOC_BIAS_PREPARE: - /* power up the OSC and the PLL */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0); - break; - case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), - wm8804->supplies); - if (ret) { - dev_err(codec->dev, - "Failed to enable supplies: %d\n", - ret); - return ret; - } - regcache_sync(wm8804->regmap); - } - /* power down the OSC and the PLL */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9); - break; - case SND_SOC_BIAS_OFF: - /* power down the OSC and the PLL */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9); - regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), - wm8804->supplies); - break; - } - - codec->dapm.bias_level = level; - return 0; -} - -static int wm8804_remove(struct snd_soc_codec *codec) -{ - struct wm8804_priv *wm8804; - int i; - - wm8804 = snd_soc_codec_get_drvdata(codec); - - for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i) - regulator_unregister_notifier(wm8804->supplies[i].consumer, - &wm8804->disable_nb[i]); - return 0; -} - -static int wm8804_probe(struct snd_soc_codec *codec) -{ - struct wm8804_priv *wm8804; - int i, id1, id2, ret; - - wm8804 = snd_soc_codec_get_drvdata(codec); - - for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) - wm8804->supplies[i].supply = wm8804_supply_names[i]; - - ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8804->supplies), - wm8804->supplies); - if (ret) { - dev_err(codec->dev, "Failed to request supplies: %d\n", ret); - return ret; - } - - wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0; - wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1; - - /* This should really be moved into the regulator core */ - for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { - ret = regulator_register_notifier(wm8804->supplies[i].consumer, - &wm8804->disable_nb[i]); - if (ret != 0) { - dev_err(codec->dev, - "Failed to register regulator notifier: %d\n", - ret); - } - } - - ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), - wm8804->supplies); - if (ret) { - dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); - return ret; - } - - id1 = snd_soc_read(codec, WM8804_RST_DEVID1); - if (id1 < 0) { - dev_err(codec->dev, "Failed to read device ID: %d\n", id1); - ret = id1; - goto err_reg_enable; - } - - id2 = snd_soc_read(codec, WM8804_DEVID2); - if (id2 < 0) { - dev_err(codec->dev, "Failed to read device ID: %d\n", id2); - ret = id2; - goto err_reg_enable; - } - - id2 = (id2 << 8) | id1; - - if (id2 != 0x8805) { - dev_err(codec->dev, "Invalid device ID: %#x\n", id2); - ret = -EINVAL; - goto err_reg_enable; - } - - ret = snd_soc_read(codec, WM8804_DEVREV); - if (ret < 0) { - dev_err(codec->dev, "Failed to read device revision: %d\n", - ret); - goto err_reg_enable; - } - dev_info(codec->dev, "revision %c\n", ret + 'A'); - - ret = wm8804_reset(codec); - if (ret < 0) { - dev_err(codec->dev, "Failed to issue reset: %d\n", ret); - goto err_reg_enable; - } - - return 0; - -err_reg_enable: - regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); - return ret; -} - static const struct snd_soc_dai_ops wm8804_dai_ops = { .hw_params = wm8804_hw_params, .set_fmt = wm8804_set_fmt, @@ -649,22 +543,15 @@ static struct snd_soc_dai_driver wm8804_dai = { }; static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { - .probe = wm8804_probe, - .remove = wm8804_remove, - .set_bias_level = wm8804_set_bias_level, .idle_bias_off = true, - .controls = wm8804_snd_controls, - .num_controls = ARRAY_SIZE(wm8804_snd_controls), -}; - -static const struct of_device_id wm8804_of_match[] = { - { .compatible = "wlf,wm8804", }, - { } + .dapm_widgets = wm8804_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets), + .dapm_routes = wm8804_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes), }; -MODULE_DEVICE_TABLE(of, wm8804_of_match); -static const struct regmap_config wm8804_regmap_config = { +const struct regmap_config wm8804_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -675,128 +562,169 @@ static const struct regmap_config wm8804_regmap_config = { .reg_defaults = wm8804_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults), }; +EXPORT_SYMBOL_GPL(wm8804_regmap_config); -#if defined(CONFIG_SPI_MASTER) -static int wm8804_spi_probe(struct spi_device *spi) +int wm8804_probe(struct device *dev, struct regmap *regmap) { struct wm8804_priv *wm8804; - int ret; + unsigned int id1, id2; + int i, ret; - wm8804 = devm_kzalloc(&spi->dev, sizeof *wm8804, GFP_KERNEL); + wm8804 = devm_kzalloc(dev, sizeof(*wm8804), GFP_KERNEL); if (!wm8804) return -ENOMEM; - wm8804->regmap = devm_regmap_init_spi(spi, &wm8804_regmap_config); - if (IS_ERR(wm8804->regmap)) { - ret = PTR_ERR(wm8804->regmap); + dev_set_drvdata(dev, wm8804); + + wm8804->dev = dev; + wm8804->regmap = regmap; + + wm8804->reset = devm_gpiod_get_optional(dev, "wlf,reset", + GPIOD_OUT_LOW); + if (IS_ERR(wm8804->reset)) { + ret = PTR_ERR(wm8804->reset); + dev_err(dev, "Failed to get reset line: %d\n", ret); return ret; } - spi_set_drvdata(spi, wm8804); + for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) + wm8804->supplies[i].supply = wm8804_supply_names[i]; - ret = snd_soc_register_codec(&spi->dev, - &soc_codec_dev_wm8804, &wm8804_dai, 1); + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); + if (ret) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } - return ret; -} + wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0; + wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1; -static int wm8804_spi_remove(struct spi_device *spi) -{ - snd_soc_unregister_codec(&spi->dev); - return 0; -} + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { + struct regulator *regulator = wm8804->supplies[i].consumer; -static struct spi_driver wm8804_spi_driver = { - .driver = { - .name = "wm8804", - .owner = THIS_MODULE, - .of_match_table = wm8804_of_match, - }, - .probe = wm8804_spi_probe, - .remove = wm8804_spi_remove -}; -#endif + ret = devm_regulator_register_notifier(regulator, + &wm8804->disable_nb[i]); + if (ret != 0) { + dev_err(dev, + "Failed to register regulator notifier: %d\n", + ret); + return ret; + } + } -#if IS_ENABLED(CONFIG_I2C) -static int wm8804_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct wm8804_priv *wm8804; - int ret; + ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); + if (ret) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } - wm8804 = devm_kzalloc(&i2c->dev, sizeof *wm8804, GFP_KERNEL); - if (!wm8804) - return -ENOMEM; + if (wm8804->reset) + gpiod_set_value_cansleep(wm8804->reset, 1); - wm8804->regmap = devm_regmap_init_i2c(i2c, &wm8804_regmap_config); - if (IS_ERR(wm8804->regmap)) { - ret = PTR_ERR(wm8804->regmap); - return ret; + ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1); + if (ret < 0) { + dev_err(dev, "Failed to read device ID: %d\n", ret); + goto err_reg_enable; + } + + ret = regmap_read(regmap, WM8804_DEVID2, &id2); + if (ret < 0) { + dev_err(dev, "Failed to read device ID: %d\n", ret); + goto err_reg_enable; + } + + id2 = (id2 << 8) | id1; + + if (id2 != 0x8805) { + dev_err(dev, "Invalid device ID: %#x\n", id2); + ret = -EINVAL; + goto err_reg_enable; + } + + ret = regmap_read(regmap, WM8804_DEVREV, &id1); + if (ret < 0) { + dev_err(dev, "Failed to read device revision: %d\n", + ret); + goto err_reg_enable; } + dev_info(dev, "revision %c\n", id1 + 'A'); - i2c_set_clientdata(i2c, wm8804); + if (!wm8804->reset) { + ret = wm8804_soft_reset(wm8804); + if (ret < 0) { + dev_err(dev, "Failed to issue reset: %d\n", ret); + goto err_reg_enable; + } + } + + ret = snd_soc_register_codec(dev, &soc_codec_dev_wm8804, + &wm8804_dai, 1); + if (ret < 0) { + dev_err(dev, "Failed to register CODEC: %d\n", ret); + goto err_reg_enable; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); - ret = snd_soc_register_codec(&i2c->dev, - &soc_codec_dev_wm8804, &wm8804_dai, 1); + return 0; + +err_reg_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); return ret; } +EXPORT_SYMBOL_GPL(wm8804_probe); -static int wm8804_i2c_remove(struct i2c_client *i2c) +void wm8804_remove(struct device *dev) { - snd_soc_unregister_codec(&i2c->dev); - return 0; + pm_runtime_disable(dev); + snd_soc_unregister_codec(dev); } +EXPORT_SYMBOL_GPL(wm8804_remove); -static const struct i2c_device_id wm8804_i2c_id[] = { - { "wm8804", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id); - -static struct i2c_driver wm8804_i2c_driver = { - .driver = { - .name = "wm8804", - .owner = THIS_MODULE, - .of_match_table = wm8804_of_match, - }, - .probe = wm8804_i2c_probe, - .remove = wm8804_i2c_remove, - .id_table = wm8804_i2c_id -}; -#endif - -static int __init wm8804_modinit(void) +#if IS_ENABLED(CONFIG_PM) +static int wm8804_runtime_resume(struct device *dev) { - int ret = 0; + struct wm8804_priv *wm8804 = dev_get_drvdata(dev); + int ret; -#if IS_ENABLED(CONFIG_I2C) - ret = i2c_add_driver(&wm8804_i2c_driver); + ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); if (ret) { - printk(KERN_ERR "Failed to register wm8804 I2C driver: %d\n", - ret); - } -#endif -#if defined(CONFIG_SPI_MASTER) - ret = spi_register_driver(&wm8804_spi_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register wm8804 SPI driver: %d\n", - ret); + dev_err(wm8804->dev, "Failed to enable supplies: %d\n", ret); + return ret; } -#endif - return ret; + + regcache_sync(wm8804->regmap); + + /* Power up OSCCLK */ + regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x0); + + return 0; } -module_init(wm8804_modinit); -static void __exit wm8804_exit(void) +static int wm8804_runtime_suspend(struct device *dev) { -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&wm8804_i2c_driver); -#endif -#if defined(CONFIG_SPI_MASTER) - spi_unregister_driver(&wm8804_spi_driver); -#endif + struct wm8804_priv *wm8804 = dev_get_drvdata(dev); + + /* Power down OSCCLK */ + regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x8); + + regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); + + return 0; } -module_exit(wm8804_exit); +#endif + +const struct dev_pm_ops wm8804_pm = { + SET_RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(wm8804_pm); MODULE_DESCRIPTION("ASoC WM8804 driver"); MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); diff --git a/sound/soc/codecs/wm8804.h b/sound/soc/codecs/wm8804.h index e72d4f4ba6b1..aa72fa66c932 100644 --- a/sound/soc/codecs/wm8804.h +++ b/sound/soc/codecs/wm8804.h @@ -13,6 +13,8 @@ #ifndef _WM8804_H #define _WM8804_H +#include <linux/regmap.h> + /* * Register values. */ @@ -62,4 +64,10 @@ #define WM8804_MCLKDIV_256FS 0 #define WM8804_MCLKDIV_128FS 1 +extern const struct regmap_config wm8804_regmap_config; +extern const struct dev_pm_ops wm8804_pm; + +int wm8804_probe(struct device *dev, struct regmap *regmap); +void wm8804_remove(struct device *dev); + #endif /* _WM8804_H */ diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 39ddb9b8834c..f9cbabdc6238 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -31,11 +31,11 @@ #define WM8971_REG_COUNT 43 -static struct workqueue_struct *wm8971_workq = NULL; - /* codec private data */ struct wm8971_priv { unsigned int sysclk; + struct delayed_work charge_work; + struct regmap *regmap; }; /* @@ -552,9 +552,19 @@ static int wm8971_mute(struct snd_soc_dai *dai, int mute) return 0; } +static void wm8971_charge_work(struct work_struct *work) +{ + struct wm8971_priv *wm8971 = + container_of(work, struct wm8971_priv, charge_work.work); + + /* Set to 500k */ + regmap_update_bits(wm8971->regmap, WM8971_PWR1, 0x0180, 0x0100); +} + static int wm8971_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); u16 pwr_reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; switch (level) { @@ -563,15 +573,24 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1); break; case SND_SOC_BIAS_PREPARE: + /* Wait until fully charged */ + flush_delayed_work(&wm8971->charge_work); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (codec->dapm.bias_level == 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); + queue_delayed_work(system_power_efficient_wq, + &wm8971->charge_work, msecs_to_jiffies(1000)); + } else { + /* mute dac and set vmid to 500k, enable VREF */ + snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140); + } - /* mute dac and set vmid to 500k, enable VREF */ - snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140); break; case SND_SOC_BIAS_OFF: + cancel_delayed_work_sync(&wm8971->charge_work); snd_soc_write(codec, WM8971_PWR1, 0x0001); break; } @@ -610,58 +629,14 @@ static struct snd_soc_dai_driver wm8971_dai = { .ops = &wm8971_dai_ops, }; -static void wm8971_work(struct work_struct *work) -{ - struct snd_soc_dapm_context *dapm = - container_of(work, struct snd_soc_dapm_context, - delayed_work.work); - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); - wm8971_set_bias_level(codec, codec->dapm.bias_level); -} - -static int wm8971_suspend(struct snd_soc_codec *codec) -{ - wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8971_resume(struct snd_soc_codec *codec) -{ - u16 reg; - - wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - /* charge wm8971 caps */ - if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { - reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; - snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0); - codec->dapm.bias_level = SND_SOC_BIAS_ON; - queue_delayed_work(wm8971_workq, &codec->dapm.delayed_work, - msecs_to_jiffies(1000)); - } - - return 0; -} - static int wm8971_probe(struct snd_soc_codec *codec) { - int ret = 0; - u16 reg; + struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); - INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8971_work); - wm8971_workq = create_workqueue("wm8971"); - if (wm8971_workq == NULL) - return -ENOMEM; + INIT_DELAYED_WORK(&wm8971->charge_work, wm8971_charge_work); wm8971_reset(codec); - /* charge output caps - set vmid to 5k for quick power up */ - reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; - snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0); - codec->dapm.bias_level = SND_SOC_BIAS_STANDBY; - queue_delayed_work(wm8971_workq, &codec->dapm.delayed_work, - msecs_to_jiffies(1000)); - /* set the update bits */ snd_soc_update_bits(codec, WM8971_LDAC, 0x0100, 0x0100); snd_soc_update_bits(codec, WM8971_RDAC, 0x0100, 0x0100); @@ -672,26 +647,13 @@ static int wm8971_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8971_LINVOL, 0x0100, 0x0100); snd_soc_update_bits(codec, WM8971_RINVOL, 0x0100, 0x0100); - return ret; -} - - -/* power down chip */ -static int wm8971_remove(struct snd_soc_codec *codec) -{ - wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); - - if (wm8971_workq) - destroy_workqueue(wm8971_workq); return 0; } static struct snd_soc_codec_driver soc_codec_dev_wm8971 = { .probe = wm8971_probe, - .remove = wm8971_remove, - .suspend = wm8971_suspend, - .resume = wm8971_resume, .set_bias_level = wm8971_set_bias_level, + .suspend_bias_off = true, .controls = wm8971_snd_controls, .num_controls = ARRAY_SIZE(wm8971_snd_controls), @@ -715,7 +677,6 @@ static int wm8971_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8971_priv *wm8971; - struct regmap *regmap; int ret; wm8971 = devm_kzalloc(&i2c->dev, sizeof(struct wm8971_priv), @@ -723,9 +684,9 @@ static int wm8971_i2c_probe(struct i2c_client *i2c, if (wm8971 == NULL) return -ENOMEM; - regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + wm8971->regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap); + if (IS_ERR(wm8971->regmap)) + return PTR_ERR(wm8971->regmap); i2c_set_clientdata(i2c, wm8971); diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index dc92d5e4e942..308748a022c5 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2009,7 +2009,7 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source, struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); struct i2c_client *i2c = to_i2c_client(codec->dev); struct _fll_div fll_div; - unsigned long timeout; + unsigned long timeout, time_left; int ret, reg, retry; /* Any change? */ @@ -2110,13 +2110,15 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source, if (i2c->irq) timeout *= 10; else - timeout /= 2; + /* ensure timeout of atleast 1 jiffies */ + timeout = timeout/2 ? : 1; for (retry = 0; retry < 10; retry++) { - ret = wait_for_completion_timeout(&wm8996->fll_lock, - timeout); - if (ret != 0) { + time_left = wait_for_completion_timeout(&wm8996->fll_lock, + timeout); + if (time_left != 0) { WARN_ON(!i2c->irq); + ret = 1; break; } diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index ff67b334065b..d01c2095452f 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -420,10 +420,9 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol, memcpy(ctl->cache, p, ctl->len); - if (!ctl->enabled) { - ctl->set = 1; + ctl->set = 1; + if (!ctl->enabled) return 0; - } return wm_coeff_write_control(kcontrol, p, ctl->len); } @@ -1185,7 +1184,6 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) int ret, pos, blocks, type, offset, reg; char *file; struct wm_adsp_buf *buf; - int tmp; file = kzalloc(PAGE_SIZE, GFP_KERNEL); if (file == NULL) @@ -1335,12 +1333,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) } } - tmp = le32_to_cpu(blk->len) % 4; - if (tmp) - pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk); - else - pos += le32_to_cpu(blk->len) + sizeof(*blk); - + pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; blocks++; } diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 2b81ca418d2a..3736d9aabc56 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -1,14 +1,16 @@ config SND_DAVINCI_SOC - tristate "SoC Audio for TI DAVINCI" + tristate depends on ARCH_DAVINCI + select SND_EDMA_SOC config SND_EDMA_SOC - tristate "SoC Audio for Texas Instruments chips using eDMA (AM33XX/43XX)" - depends on SOC_AM33XX || SOC_AM43XX + tristate "SoC Audio for Texas Instruments chips using eDMA" + depends on SOC_AM33XX || SOC_AM43XX || ARCH_DAVINCI select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M here if you want audio support for TI SoC which uses eDMA. The following line of SoCs are supported by this platform driver: + - daVinci devices - AM335x - AM437x/AM438x @@ -17,7 +19,7 @@ config SND_DAVINCI_SOC_I2S config SND_DAVINCI_SOC_MCASP tristate "Multichannel Audio Serial Port (McASP) support" - depends on SND_DAVINCI_SOC || SND_OMAP_SOC || SND_EDMA_SOC + depends on SND_OMAP_SOC || SND_EDMA_SOC help Say Y or M here if you want to have support for McASP IP found in various Texas Instruments SoCs like: @@ -45,7 +47,7 @@ config SND_AM33XX_SOC_EVM config SND_DAVINCI_SOC_EVM tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM" - depends on SND_DAVINCI_SOC && I2C + depends on SND_EDMA_SOC && I2C depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM select SND_DAVINCI_SOC_GENERIC_EVM help @@ -73,7 +75,7 @@ endchoice config SND_DM6467_SOC_EVM tristate "SoC Audio support for DaVinci DM6467 EVM" - depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM && I2C + depends on SND_EDMA_SOC && MACH_DAVINCI_DM6467_EVM && I2C select SND_DAVINCI_SOC_GENERIC_EVM select SND_SOC_SPDIF @@ -82,7 +84,7 @@ config SND_DM6467_SOC_EVM config SND_DA830_SOC_EVM tristate "SoC Audio support for DA830/OMAP-L137 EVM" - depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM && I2C + depends on SND_EDMA_SOC && MACH_DAVINCI_DA830_EVM && I2C select SND_DAVINCI_SOC_GENERIC_EVM help @@ -91,7 +93,7 @@ config SND_DA830_SOC_EVM config SND_DA850_SOC_EVM tristate "SoC Audio support for DA850/OMAP-L138 EVM" - depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM && I2C + depends on SND_EDMA_SOC && MACH_DAVINCI_DA850_EVM && I2C select SND_DAVINCI_SOC_GENERIC_EVM help Say Y if you want to add support for SoC audio on TI diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile index 09bf2ba92d38..f883933c1a19 100644 --- a/sound/soc/davinci/Makefile +++ b/sound/soc/davinci/Makefile @@ -1,11 +1,9 @@ # DAVINCI Platform Support -snd-soc-davinci-objs := davinci-pcm.o snd-soc-edma-objs := edma-pcm.o snd-soc-davinci-i2s-objs := davinci-i2s.o snd-soc-davinci-mcasp-objs:= davinci-mcasp.o snd-soc-davinci-vcif-objs:= davinci-vcif.o -obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o obj-$(CONFIG_SND_EDMA_SOC) += snd-soc-edma.o obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index b6bb5947a8a8..731fb0d86c6a 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -117,7 +117,6 @@ static const struct snd_soc_dapm_route audio_map[] = { static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; - struct snd_soc_codec *codec = rtd->codec; struct device_node *np = card->dev->of_node; int ret; @@ -136,9 +135,9 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) } /* not connected */ - snd_soc_dapm_nc_pin(&codec->dapm, "MONO_LOUT"); - snd_soc_dapm_nc_pin(&codec->dapm, "HPLCOM"); - snd_soc_dapm_nc_pin(&codec->dapm, "HPRCOM"); + snd_soc_dapm_nc_pin(&card->dapm, "MONO_LOUT"); + snd_soc_dapm_nc_pin(&card->dapm, "HPLCOM"); + snd_soc_dapm_nc_pin(&card->dapm, "HPRCOM"); return 0; } @@ -425,18 +424,8 @@ static int davinci_evm_probe(struct platform_device *pdev) return ret; } -static int davinci_evm_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static struct platform_driver davinci_evm_driver = { .probe = davinci_evm_probe, - .remove = davinci_evm_remove, .driver = { .name = "davinci_evm", .pm = &snd_soc_pm_ops, diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 15fb28fc8e1b..56cb4d95637d 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -23,8 +23,9 @@ #include <sound/pcm_params.h> #include <sound/initval.h> #include <sound/soc.h> +#include <sound/dmaengine_pcm.h> -#include "davinci-pcm.h" +#include "edma-pcm.h" #include "davinci-i2s.h" @@ -122,7 +123,8 @@ static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = { struct davinci_mcbsp_dev { struct device *dev; - struct davinci_pcm_dma_params dma_params[2]; + struct snd_dmaengine_dai_dma_data dma_data[2]; + int dma_request[2]; void __iomem *base; #define MOD_DSP_A 0 #define MOD_DSP_B 1 @@ -419,8 +421,6 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); - struct davinci_pcm_dma_params *dma_params = - &dev->dma_params[substream->stream]; struct snd_interval *i = NULL; int mcbsp_word_length, master; unsigned int rcr, xcr, srgr, clk_div, freq, framesize; @@ -532,8 +532,6 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } } - dma_params->acnt = dma_params->data_type = data_type[fmt]; - dma_params->fifo_level = 0; mcbsp_word_length = asp_word_length[fmt]; switch (master) { @@ -600,15 +598,6 @@ static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } -static int davinci_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); - - snd_soc_dai_set_dma_data(dai, substream, dev->dma_params); - return 0; -} - static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -620,7 +609,6 @@ static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, #define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { - .startup = davinci_i2s_startup, .shutdown = davinci_i2s_shutdown, .prepare = davinci_i2s_prepare, .trigger = davinci_i2s_trigger, @@ -630,7 +618,18 @@ static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { }; +static int davinci_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + + return 0; +} + static struct snd_soc_dai_driver davinci_i2s_dai = { + .probe = davinci_i2s_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, @@ -651,11 +650,9 @@ static const struct snd_soc_component_driver davinci_i2s_component = { static int davinci_i2s_probe(struct platform_device *pdev) { - struct snd_platform_data *pdata = pdev->dev.platform_data; struct davinci_mcbsp_dev *dev; struct resource *mem, *ioarea, *res; - enum dma_event_q asp_chan_q = EVENTQ_0; - enum dma_event_q ram_chan_q = EVENTQ_1; + int *dma; int ret; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -676,22 +673,6 @@ static int davinci_i2s_probe(struct platform_device *pdev) GFP_KERNEL); if (!dev) return -ENOMEM; - if (pdata) { - dev->enable_channel_combine = pdata->enable_channel_combine; - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size = - pdata->sram_size_playback; - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size = - pdata->sram_size_capture; - dev->clk_input_pin = pdata->clk_input_pin; - dev->i2s_accurate_sck = pdata->i2s_accurate_sck; - asp_chan_q = pdata->asp_chan_q; - ram_chan_q = pdata->ram_chan_q; - } - - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].asp_chan_q = asp_chan_q; - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].ram_chan_q = ram_chan_q; - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].asp_chan_q = asp_chan_q; - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].ram_chan_q = ram_chan_q; dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) @@ -705,10 +686,10 @@ static int davinci_i2s_probe(struct platform_device *pdev) goto err_release_clk; } - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr = + dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr = + dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG); /* first TX, then RX */ @@ -718,7 +699,9 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENXIO; goto err_release_clk; } - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start; + dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK]; + *dma = res->start; + dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = dma; res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { @@ -726,9 +709,11 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENXIO; goto err_release_clk; } - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; - dev->dev = &pdev->dev; + dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE]; + *dma = res->start; + dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = dma; + dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &davinci_i2s_component, @@ -736,7 +721,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) if (ret != 0) goto err_release_clk; - ret = davinci_soc_platform_register(&pdev->dev); + ret = edma_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "register PCM failed: %d\n", ret); goto err_unregister_component; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index de3b155a5011..bb4b78eada58 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -26,6 +26,8 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/of_device.h> +#include <linux/platform_data/davinci_asp.h> +#include <linux/math64.h> #include <sound/asoundef.h> #include <sound/core.h> @@ -36,7 +38,6 @@ #include <sound/dmaengine_pcm.h> #include <sound/omap-pcm.h> -#include "davinci-pcm.h" #include "edma-pcm.h" #include "davinci-mcasp.h" @@ -62,10 +63,15 @@ struct davinci_mcasp_context { u32 config_regs[ARRAY_SIZE(context_regs)]; u32 afifo_regs[2]; /* for read/write fifo control registers */ u32 *xrsr_regs; /* for serializer configuration */ + bool pm_state; +}; + +struct davinci_mcasp_ruledata { + struct davinci_mcasp *mcasp; + int serializers; }; struct davinci_mcasp { - struct davinci_pcm_dma_params dma_params[2]; struct snd_dmaengine_dai_dma_data dma_data[2]; void __iomem *base; u32 fifo_base; @@ -82,6 +88,7 @@ struct davinci_mcasp { u16 bclk_lrclk_ratio; int streams; u32 irq_request[2]; + int dma_request[2]; int sysclk_freq; bool bclk_master; @@ -98,6 +105,8 @@ struct davinci_mcasp { #ifdef CONFIG_PM_SLEEP struct davinci_mcasp_context context; #endif + + struct davinci_mcasp_ruledata ruledata[2]; }; static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset, @@ -441,6 +450,18 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); mcasp->bclk_master = 1; break; + case SND_SOC_DAIFMT_CBS_CFM: + /* codec is clock slave and frame master */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); + mcasp->bclk_master = 1; + break; case SND_SOC_DAIFMT_CBM_CFS: /* codec is clock master and frame slave */ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); @@ -507,7 +528,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); } out: - pm_runtime_put_sync(mcasp->dev); + pm_runtime_put(mcasp->dev); return ret; } @@ -516,6 +537,7 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + pm_runtime_get_sync(mcasp->dev); switch (div_id) { case 0: /* MCLK divider */ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, @@ -541,6 +563,7 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, return -EINVAL; } + pm_runtime_put(mcasp->dev); return 0; } @@ -555,6 +578,7 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + pm_runtime_get_sync(mcasp->dev); if (dir == SND_SOC_CLOCK_OUT) { mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); @@ -567,6 +591,7 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, mcasp->sysclk_freq = freq; + pm_runtime_put(mcasp->dev); return 0; } @@ -631,7 +656,6 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, int period_words, int channels) { - struct davinci_pcm_dma_params *dma_params = &mcasp->dma_params[stream]; struct snd_dmaengine_dai_dma_data *dma_data = &mcasp->dma_data[stream]; int i; u8 tx_ser = 0; @@ -699,10 +723,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, * For example if three serializers are enabled the DMA * need to transfer three words per DMA request. */ - dma_params->fifo_level = active_serializers; dma_data->maxburst = active_serializers; } else { - dma_params->fifo_level = 0; dma_data->maxburst = 0; } return 0; @@ -734,7 +756,6 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, /* Configure the burst size for platform drivers */ if (numevt == 1) numevt = 0; - dma_params->fifo_level = numevt; dma_data->maxburst = numevt; return 0; @@ -855,13 +876,35 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp, return 0; } +static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, + unsigned int bclk_freq, + int *error_ppm) +{ + int div = mcasp->sysclk_freq / bclk_freq; + int rem = mcasp->sysclk_freq % bclk_freq; + + if (rem != 0) { + if (div == 0 || + ((mcasp->sysclk_freq / div) - bclk_freq) > + (bclk_freq - (mcasp->sysclk_freq / (div+1)))) { + div++; + rem = rem - bclk_freq; + } + } + if (error_ppm) + *error_ppm = + (div*1000000 + (int)div64_long(1000000LL*rem, + (int)bclk_freq)) + /div - 1000000; + + return div; +} + static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); - struct davinci_pcm_dma_params *dma_params = - &mcasp->dma_params[substream->stream]; int word_length; int channels = params_channels(params); int period_size = params_period_size(params); @@ -872,16 +915,20 @@ 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) { - unsigned int bclk_freq = snd_soc_params_to_bclk(params); - unsigned int div = mcasp->sysclk_freq / bclk_freq; - if (mcasp->sysclk_freq % bclk_freq != 0) { - if (((mcasp->sysclk_freq / div) - bclk_freq) > - (bclk_freq - (mcasp->sysclk_freq / (div+1)))) - div++; - dev_warn(mcasp->dev, - "Inaccurate BCLK: %u Hz / %u != %u Hz\n", - mcasp->sysclk_freq, div, bclk_freq); - } + int channels = params_channels(params); + 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, + &ppm); + if (ppm) + dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", + ppm); + __davinci_mcasp_set_clkdiv(cpu_dai, 1, div, 0); } @@ -902,31 +949,26 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_U8: case SNDRV_PCM_FORMAT_S8: - dma_params->data_type = 1; word_length = 8; break; case SNDRV_PCM_FORMAT_U16_LE: case SNDRV_PCM_FORMAT_S16_LE: - dma_params->data_type = 2; word_length = 16; break; case SNDRV_PCM_FORMAT_U24_3LE: case SNDRV_PCM_FORMAT_S24_3LE: - dma_params->data_type = 3; word_length = 24; break; case SNDRV_PCM_FORMAT_U24_LE: case SNDRV_PCM_FORMAT_S24_LE: - dma_params->data_type = 4; word_length = 24; break; case SNDRV_PCM_FORMAT_U32_LE: case SNDRV_PCM_FORMAT_S32_LE: - dma_params->data_type = 4; word_length = 32; break; @@ -935,11 +977,6 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (mcasp->version == MCASP_VERSION_2 && !dma_params->fifo_level) - dma_params->acnt = 4; - else - dma_params->acnt = dma_params->data_type; - davinci_config_channel_size(mcasp, word_length); if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) @@ -973,10 +1010,126 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, return ret; } +static const unsigned int davinci_mcasp_dai_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000, +}; + +#define DAVINCI_MAX_RATE_ERROR_PPM 1000 + +static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct davinci_mcasp_ruledata *rd = rule->private; + 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; + + if (channels > rd->mcasp->tdm_slots) + channels = rd->mcasp->tdm_slots; + + 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* + 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]; + } + } + dev_dbg(rd->mcasp->dev, + "%d frequencies (%d-%d) for %d sbits and %d channels\n", + count, ri->min, ri->max, sbits, channels); + + return snd_interval_list(hw_param_interval(params, rule->var), + count, list, 0); +} + +static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct davinci_mcasp_ruledata *rd = rule->private; + 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 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; + int ppm; + + davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); + if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { + snd_mask_set(&nfmt, i); + count++; + } + } + } + dev_dbg(rd->mcasp->dev, + "%d possible sample format for %d Hz and %d channels\n", + count, rate, channels); + + 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) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + struct davinci_mcasp_ruledata *ruledata = + &mcasp->ruledata[substream->stream]; u32 max_channels = 0; int i, dir; @@ -998,6 +1151,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, if (mcasp->serial_dir[i] == dir) max_channels++; } + ruledata->serializers = max_channels; max_channels *= mcasp->tdm_slots; /* * If the already active stream has less channels than the calculated @@ -1012,6 +1166,42 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, max_channels); + + /* + * If we rely on implicit BCLK divider setting we should + * set constraints based on what we can provide. + */ + if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { + int ret; + + ruledata->mcasp = mcasp; + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + davinci_mcasp_hw_rule_rate, + ruledata, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, -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); + if (ret) + return ret; + } + return 0; } @@ -1043,17 +1233,8 @@ static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); - if (mcasp->version >= MCASP_VERSION_3) { - /* Using dmaengine PCM */ - dai->playback_dma_data = - &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = - &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; - } else { - /* Using davinci-pcm */ - dai->playback_dma_data = mcasp->dma_params; - dai->capture_dma_data = mcasp->dma_params; - } + dai->playback_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; return 0; } @@ -1066,6 +1247,10 @@ static int davinci_mcasp_suspend(struct snd_soc_dai *dai) u32 reg; int i; + context->pm_state = pm_runtime_enabled(mcasp->dev); + if (!context->pm_state) + pm_runtime_get_sync(mcasp->dev); + for (i = 0; i < ARRAY_SIZE(context_regs); i++) context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]); @@ -1082,6 +1267,8 @@ static int davinci_mcasp_suspend(struct snd_soc_dai *dai) context->xrsr_regs[i] = mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i)); + pm_runtime_put_sync(mcasp->dev); + return 0; } @@ -1092,6 +1279,8 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai) u32 reg; int i; + pm_runtime_get_sync(mcasp->dev); + for (i = 0; i < ARRAY_SIZE(context_regs); i++) mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]); @@ -1108,6 +1297,9 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai) mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), context->xrsr_regs[i]); + if (!context->pm_state) + pm_runtime_put_sync(mcasp->dev); + return 0; } #else @@ -1172,28 +1364,24 @@ static const struct snd_soc_component_driver davinci_mcasp_component = { static struct davinci_mcasp_pdata dm646x_mcasp_pdata = { .tx_dma_offset = 0x400, .rx_dma_offset = 0x400, - .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_1, }; static struct davinci_mcasp_pdata da830_mcasp_pdata = { .tx_dma_offset = 0x2000, .rx_dma_offset = 0x2000, - .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_2, }; static struct davinci_mcasp_pdata am33xx_mcasp_pdata = { .tx_dma_offset = 0, .rx_dma_offset = 0, - .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_3, }; static struct davinci_mcasp_pdata dra7_mcasp_pdata = { .tx_dma_offset = 0x200, .rx_dma_offset = 0x284, - .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_4, }; @@ -1370,12 +1558,12 @@ nodata: static int davinci_mcasp_probe(struct platform_device *pdev) { - struct davinci_pcm_dma_params *dma_params; struct snd_dmaengine_dai_dma_data *dma_data; struct resource *mem, *ioarea, *res, *dat; struct davinci_mcasp_pdata *pdata; struct davinci_mcasp *mcasp; char *irq_name; + int *dma; int irq; int ret; @@ -1415,13 +1603,6 @@ static int davinci_mcasp_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); - if (IS_ERR_VALUE(ret)) { - dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); - pm_runtime_disable(&pdev->dev); - return ret; - } - mcasp->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); if (!mcasp->base) { dev_err(&pdev->dev, "ioremap failed\n"); @@ -1509,59 +1690,45 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (dat) mcasp->dat_port = true; - dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dma_params->asp_chan_q = pdata->asp_chan_q; - dma_params->ram_chan_q = pdata->ram_chan_q; - dma_params->sram_pool = pdata->sram_pool; - dma_params->sram_size = pdata->sram_size_playback; if (dat) - dma_params->dma_addr = dat->start; + dma_data->addr = dat->start; else - dma_params->dma_addr = mem->start + pdata->tx_dma_offset; - - /* Unconditional dmaengine stuff */ - dma_data->addr = dma_params->dma_addr; + dma_data->addr = mem->start + pdata->tx_dma_offset; + dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK]; res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (res) - dma_params->channel = res->start; + *dma = res->start; else - dma_params->channel = pdata->tx_dma_channel; + *dma = pdata->tx_dma_channel; /* dmaengine filter data for DT and non-DT boot */ if (pdev->dev.of_node) dma_data->filter_data = "tx"; else - dma_data->filter_data = &dma_params->channel; + dma_data->filter_data = dma; /* RX is not valid in DIT mode */ if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { - dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_CAPTURE]; dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; - dma_params->asp_chan_q = pdata->asp_chan_q; - dma_params->ram_chan_q = pdata->ram_chan_q; - dma_params->sram_pool = pdata->sram_pool; - dma_params->sram_size = pdata->sram_size_capture; if (dat) - dma_params->dma_addr = dat->start; + dma_data->addr = dat->start; else - dma_params->dma_addr = mem->start + pdata->rx_dma_offset; - - /* Unconditional dmaengine stuff */ - dma_data->addr = dma_params->dma_addr; + dma_data->addr = mem->start + pdata->rx_dma_offset; + dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE]; res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (res) - dma_params->channel = res->start; + *dma = res->start; else - dma_params->channel = pdata->rx_dma_channel; + *dma = pdata->rx_dma_channel; /* dmaengine filter data for DT and non-DT boot */ if (pdev->dev.of_node) dma_data->filter_data = "rx"; else - dma_data->filter_data = &dma_params->channel; + dma_data->filter_data = dma; } if (mcasp->version < MCASP_VERSION_3) { @@ -1584,17 +1751,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) goto err; switch (mcasp->version) { -#if IS_BUILTIN(CONFIG_SND_DAVINCI_SOC) || \ - (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ - IS_MODULE(CONFIG_SND_DAVINCI_SOC)) - case MCASP_VERSION_1: - case MCASP_VERSION_2: - ret = davinci_soc_platform_register(&pdev->dev); - break; -#endif #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; @@ -1621,14 +1782,12 @@ static int davinci_mcasp_probe(struct platform_device *pdev) return 0; err: - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return ret; } static int davinci_mcasp_remove(struct platform_device *pdev) { - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c deleted file mode 100644 index 7809e9d935fc..000000000000 --- a/sound/soc/davinci/davinci-pcm.c +++ /dev/null @@ -1,861 +0,0 @@ -/* - * ALSA PCM interface for the TI DAVINCI processor - * - * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> - * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> - * added SRAM ping/pong (C) 2008 Troy Kisky <troy.kisky@boundarydevices.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/init.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/dma-mapping.h> -#include <linux/kernel.h> -#include <linux/genalloc.h> -#include <linux/platform_data/edma.h> - -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> - -#include <asm/dma.h> - -#include "davinci-pcm.h" - -#ifdef DEBUG -static void print_buf_info(int slot, char *name) -{ - struct edmacc_param p; - if (slot < 0) - return; - edma_read_slot(slot, &p); - printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n", - name, slot, p.opt, p.src, p.a_b_cnt, p.dst); - printk(KERN_DEBUG " src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n", - p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt); -} -#else -static void print_buf_info(int slot, char *name) -{ -} -#endif - -static struct snd_pcm_hardware pcm_hardware_playback = { - .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME| - SNDRV_PCM_INFO_BATCH), - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 8 * 1024, - .periods_min = 16, - .periods_max = 255, - .fifo_size = 0, -}; - -static struct snd_pcm_hardware pcm_hardware_capture = { - .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH), - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 8 * 1024, - .periods_min = 16, - .periods_max = 255, - .fifo_size = 0, -}; - -/* - * How ping/pong works.... - * - * Playback: - * ram_params - copys 2*ping_size from start of SDRAM to iram, - * links to ram_link2 - * ram_link2 - copys rest of SDRAM to iram in ping_size units, - * links to ram_link - * ram_link - copys entire SDRAM to iram in ping_size uints, - * links to self - * - * asp_params - same as asp_link[0] - * asp_link[0] - copys from lower half of iram to asp port - * links to asp_link[1], triggers iram copy event on completion - * asp_link[1] - copys from upper half of iram to asp port - * links to asp_link[0], triggers iram copy event on completion - * triggers interrupt only needed to let upper SOC levels update position - * in stream on completion - * - * When playback is started: - * ram_params started - * asp_params started - * - * Capture: - * ram_params - same as ram_link, - * links to ram_link - * ram_link - same as playback - * links to self - * - * asp_params - same as playback - * asp_link[0] - same as playback - * asp_link[1] - same as playback - * - * When capture is started: - * asp_params started - */ -struct davinci_runtime_data { - spinlock_t lock; - int period; /* current DMA period */ - int asp_channel; /* Master DMA channel */ - int asp_link[2]; /* asp parameter link channel, ping/pong */ - struct davinci_pcm_dma_params *params; /* DMA params */ - int ram_channel; - int ram_link; - int ram_link2; - struct edmacc_param asp_params; - struct edmacc_param ram_params; -}; - -static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - - prtd->period++; - if (unlikely(prtd->period >= runtime->periods)) - prtd->period = 0; -} - -static void davinci_pcm_period_reset(struct snd_pcm_substream *substream) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - - prtd->period = 0; -} -/* - * Not used with ping/pong - */ -static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int period_size; - unsigned int dma_offset; - dma_addr_t dma_pos; - dma_addr_t src, dst; - unsigned short src_bidx, dst_bidx; - unsigned short src_cidx, dst_cidx; - unsigned int data_type; - unsigned short acnt; - unsigned int count; - unsigned int fifo_level; - - period_size = snd_pcm_lib_period_bytes(substream); - dma_offset = prtd->period * period_size; - dma_pos = runtime->dma_addr + dma_offset; - fifo_level = prtd->params->fifo_level; - - pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d " - "dma_ptr = %x period_size=%x\n", prtd->asp_link[0], dma_pos, - period_size); - - data_type = prtd->params->data_type; - count = period_size / data_type; - if (fifo_level) - count /= fifo_level; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - src = dma_pos; - dst = prtd->params->dma_addr; - src_bidx = data_type; - dst_bidx = 4; - src_cidx = data_type * fifo_level; - dst_cidx = 0; - } else { - src = prtd->params->dma_addr; - dst = dma_pos; - src_bidx = 0; - dst_bidx = data_type; - src_cidx = 0; - dst_cidx = data_type * fifo_level; - } - - acnt = prtd->params->acnt; - edma_set_src(prtd->asp_link[0], src, INCR, W8BIT); - edma_set_dest(prtd->asp_link[0], dst, INCR, W8BIT); - - edma_set_src_index(prtd->asp_link[0], src_bidx, src_cidx); - edma_set_dest_index(prtd->asp_link[0], dst_bidx, dst_cidx); - - if (!fifo_level) - edma_set_transfer_params(prtd->asp_link[0], acnt, count, 1, 0, - ASYNC); - else - edma_set_transfer_params(prtd->asp_link[0], acnt, - fifo_level, - count, fifo_level, - ABSYNC); -} - -static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) -{ - struct snd_pcm_substream *substream = data; - struct davinci_runtime_data *prtd = substream->runtime->private_data; - - print_buf_info(prtd->ram_channel, "i ram_channel"); - pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status); - - if (unlikely(ch_status != EDMA_DMA_COMPLETE)) - return; - - if (snd_pcm_running(substream)) { - spin_lock(&prtd->lock); - if (prtd->ram_channel < 0) { - /* No ping/pong must fix up link dma data*/ - davinci_pcm_enqueue_dma(substream); - } - davinci_pcm_period_elapsed(substream); - spin_unlock(&prtd->lock); - snd_pcm_period_elapsed(substream); - } -} - -#ifdef CONFIG_GENERIC_ALLOCATOR -static int allocate_sram(struct snd_pcm_substream *substream, - struct gen_pool *sram_pool, unsigned size, - struct snd_pcm_hardware *ppcm) -{ - struct snd_dma_buffer *buf = &substream->dma_buffer; - struct snd_dma_buffer *iram_dma = NULL; - dma_addr_t iram_phys = 0; - void *iram_virt = NULL; - - if (buf->private_data || !size) - return 0; - - ppcm->period_bytes_max = size; - iram_virt = gen_pool_dma_alloc(sram_pool, size, &iram_phys); - if (!iram_virt) - goto exit1; - iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL); - if (!iram_dma) - goto exit2; - iram_dma->area = iram_virt; - iram_dma->addr = iram_phys; - memset(iram_dma->area, 0, size); - iram_dma->bytes = size; - buf->private_data = iram_dma; - return 0; -exit2: - if (iram_virt) - gen_pool_free(sram_pool, (unsigned)iram_virt, size); -exit1: - return -ENOMEM; -} - -static void davinci_free_sram(struct snd_pcm_substream *substream, - struct snd_dma_buffer *iram_dma) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct gen_pool *sram_pool = prtd->params->sram_pool; - - gen_pool_free(sram_pool, (unsigned) iram_dma->area, iram_dma->bytes); -} -#else -static int allocate_sram(struct snd_pcm_substream *substream, - struct gen_pool *sram_pool, unsigned size, - struct snd_pcm_hardware *ppcm) -{ - return 0; -} - -static void davinci_free_sram(struct snd_pcm_substream *substream, - struct snd_dma_buffer *iram_dma) -{ -} -#endif - -/* - * Only used with ping/pong. - * This is called after runtime->dma_addr, period_bytes and data_type are valid - */ -static int ping_pong_dma_setup(struct snd_pcm_substream *substream) -{ - unsigned short ram_src_cidx, ram_dst_cidx; - struct snd_pcm_runtime *runtime = substream->runtime; - struct davinci_runtime_data *prtd = runtime->private_data; - struct snd_dma_buffer *iram_dma = - (struct snd_dma_buffer *)substream->dma_buffer.private_data; - struct davinci_pcm_dma_params *params = prtd->params; - unsigned int data_type = params->data_type; - unsigned int acnt = params->acnt; - /* divide by 2 for ping/pong */ - unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1; - unsigned int fifo_level = prtd->params->fifo_level; - unsigned int count; - if ((data_type == 0) || (data_type > 4)) { - printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type); - return -EINVAL; - } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dma_addr_t asp_src_pong = iram_dma->addr + ping_size; - ram_src_cidx = ping_size; - ram_dst_cidx = -ping_size; - edma_set_src(prtd->asp_link[1], asp_src_pong, INCR, W8BIT); - - edma_set_src_index(prtd->asp_link[0], data_type, - data_type * fifo_level); - edma_set_src_index(prtd->asp_link[1], data_type, - data_type * fifo_level); - - edma_set_src(prtd->ram_link, runtime->dma_addr, INCR, W32BIT); - } else { - dma_addr_t asp_dst_pong = iram_dma->addr + ping_size; - ram_src_cidx = -ping_size; - ram_dst_cidx = ping_size; - edma_set_dest(prtd->asp_link[1], asp_dst_pong, INCR, W8BIT); - - edma_set_dest_index(prtd->asp_link[0], data_type, - data_type * fifo_level); - edma_set_dest_index(prtd->asp_link[1], data_type, - data_type * fifo_level); - - edma_set_dest(prtd->ram_link, runtime->dma_addr, INCR, W32BIT); - } - - if (!fifo_level) { - count = ping_size / data_type; - edma_set_transfer_params(prtd->asp_link[0], acnt, count, - 1, 0, ASYNC); - edma_set_transfer_params(prtd->asp_link[1], acnt, count, - 1, 0, ASYNC); - } else { - count = ping_size / (data_type * fifo_level); - edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level, - count, fifo_level, ABSYNC); - edma_set_transfer_params(prtd->asp_link[1], acnt, fifo_level, - count, fifo_level, ABSYNC); - } - - edma_set_src_index(prtd->ram_link, ping_size, ram_src_cidx); - edma_set_dest_index(prtd->ram_link, ping_size, ram_dst_cidx); - edma_set_transfer_params(prtd->ram_link, ping_size, 2, - runtime->periods, 2, ASYNC); - - /* init master params */ - edma_read_slot(prtd->asp_link[0], &prtd->asp_params); - edma_read_slot(prtd->ram_link, &prtd->ram_params); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - struct edmacc_param p_ram; - /* Copy entire iram buffer before playback started */ - prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1); - /* 0 dst_bidx */ - prtd->ram_params.src_dst_bidx = (ping_size << 1); - /* 0 dst_cidx */ - prtd->ram_params.src_dst_cidx = (ping_size << 1); - prtd->ram_params.ccnt = 1; - - /* Skip 1st period */ - edma_read_slot(prtd->ram_link, &p_ram); - p_ram.src += (ping_size << 1); - p_ram.ccnt -= 1; - edma_write_slot(prtd->ram_link2, &p_ram); - /* - * When 1st started, ram -> iram dma channel will fill the - * entire iram. Then, whenever a ping/pong asp buffer finishes, - * 1/2 iram will be filled. - */ - prtd->ram_params.link_bcntrld = - EDMA_CHAN_SLOT(prtd->ram_link2) << 5; - } - return 0; -} - -/* 1 asp tx or rx channel using 2 parameter channels - * 1 ram to/from iram channel using 1 parameter channel - * - * Playback - * ram copy channel kicks off first, - * 1st ram copy of entire iram buffer completion kicks off asp channel - * asp tcc always kicks off ram copy of 1/2 iram buffer - * - * Record - * asp channel starts, tcc kicks off ram copy - */ -static int request_ping_pong(struct snd_pcm_substream *substream, - struct davinci_runtime_data *prtd, - struct snd_dma_buffer *iram_dma) -{ - dma_addr_t asp_src_ping; - dma_addr_t asp_dst_ping; - int ret; - struct davinci_pcm_dma_params *params = prtd->params; - - /* Request ram master channel */ - ret = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY, - davinci_pcm_dma_irq, substream, - prtd->params->ram_chan_q); - if (ret < 0) - goto exit1; - - /* Request ram link channel */ - ret = prtd->ram_link = edma_alloc_slot( - EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY); - if (ret < 0) - goto exit2; - - ret = prtd->asp_link[1] = edma_alloc_slot( - EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); - if (ret < 0) - goto exit3; - - prtd->ram_link2 = -1; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ret = prtd->ram_link2 = edma_alloc_slot( - EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY); - if (ret < 0) - goto exit4; - } - /* circle ping-pong buffers */ - edma_link(prtd->asp_link[0], prtd->asp_link[1]); - edma_link(prtd->asp_link[1], prtd->asp_link[0]); - /* circle ram buffers */ - edma_link(prtd->ram_link, prtd->ram_link); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - asp_src_ping = iram_dma->addr; - asp_dst_ping = params->dma_addr; /* fifo */ - } else { - asp_src_ping = params->dma_addr; /* fifo */ - asp_dst_ping = iram_dma->addr; - } - /* ping */ - edma_set_src(prtd->asp_link[0], asp_src_ping, INCR, W16BIT); - edma_set_dest(prtd->asp_link[0], asp_dst_ping, INCR, W16BIT); - edma_set_src_index(prtd->asp_link[0], 0, 0); - edma_set_dest_index(prtd->asp_link[0], 0, 0); - - edma_read_slot(prtd->asp_link[0], &prtd->asp_params); - prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN); - prtd->asp_params.opt |= TCCHEN | - EDMA_TCC(prtd->ram_channel & 0x3f); - edma_write_slot(prtd->asp_link[0], &prtd->asp_params); - - /* pong */ - edma_set_src(prtd->asp_link[1], asp_src_ping, INCR, W16BIT); - edma_set_dest(prtd->asp_link[1], asp_dst_ping, INCR, W16BIT); - edma_set_src_index(prtd->asp_link[1], 0, 0); - edma_set_dest_index(prtd->asp_link[1], 0, 0); - - edma_read_slot(prtd->asp_link[1], &prtd->asp_params); - prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f)); - /* interrupt after every pong completion */ - prtd->asp_params.opt |= TCINTEN | TCCHEN | - EDMA_TCC(prtd->ram_channel & 0x3f); - edma_write_slot(prtd->asp_link[1], &prtd->asp_params); - - /* ram */ - edma_set_src(prtd->ram_link, iram_dma->addr, INCR, W32BIT); - edma_set_dest(prtd->ram_link, iram_dma->addr, INCR, W32BIT); - pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u," - "for asp:%u %u %u\n", __func__, - prtd->ram_channel, prtd->ram_link, prtd->ram_link2, - prtd->asp_channel, prtd->asp_link[0], - prtd->asp_link[1]); - return 0; -exit4: - edma_free_channel(prtd->asp_link[1]); - prtd->asp_link[1] = -1; -exit3: - edma_free_channel(prtd->ram_link); - prtd->ram_link = -1; -exit2: - edma_free_channel(prtd->ram_channel); - prtd->ram_channel = -1; -exit1: - return ret; -} - -static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) -{ - struct snd_dma_buffer *iram_dma; - struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct davinci_pcm_dma_params *params = prtd->params; - int ret; - - if (!params) - return -ENODEV; - - /* Request asp master DMA channel */ - ret = prtd->asp_channel = edma_alloc_channel(params->channel, - davinci_pcm_dma_irq, substream, - prtd->params->asp_chan_q); - if (ret < 0) - goto exit1; - - /* Request asp link channels */ - ret = prtd->asp_link[0] = edma_alloc_slot( - EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); - if (ret < 0) - goto exit2; - - iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data; - if (iram_dma) { - if (request_ping_pong(substream, prtd, iram_dma) == 0) - return 0; - printk(KERN_WARNING "%s: dma channel allocation failed," - "not using sram\n", __func__); - } - - /* Issue transfer completion IRQ when the channel completes a - * transfer, then always reload from the same slot (by a kind - * of loopback link). The completion IRQ handler will update - * the reload slot with a new buffer. - * - * REVISIT save p_ram here after setting up everything except - * the buffer and its length (ccnt) ... use it as a template - * so davinci_pcm_enqueue_dma() takes less time in IRQ. - */ - edma_read_slot(prtd->asp_link[0], &prtd->asp_params); - prtd->asp_params.opt |= TCINTEN | - EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel)); - prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(prtd->asp_link[0]) << 5; - edma_write_slot(prtd->asp_link[0], &prtd->asp_params); - return 0; -exit2: - edma_free_channel(prtd->asp_channel); - prtd->asp_channel = -1; -exit1: - return ret; -} - -static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - int ret = 0; - - spin_lock(&prtd->lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - edma_start(prtd->asp_channel); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - prtd->ram_channel >= 0) { - /* copy 1st iram buffer */ - edma_start(prtd->ram_channel); - } - break; - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - edma_resume(prtd->asp_channel); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - edma_pause(prtd->asp_channel); - break; - default: - ret = -EINVAL; - break; - } - - spin_unlock(&prtd->lock); - - return ret; -} - -static int davinci_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - - davinci_pcm_period_reset(substream); - if (prtd->ram_channel >= 0) { - int ret = ping_pong_dma_setup(substream); - if (ret < 0) - return ret; - - edma_write_slot(prtd->ram_channel, &prtd->ram_params); - edma_write_slot(prtd->asp_channel, &prtd->asp_params); - - print_buf_info(prtd->ram_channel, "ram_channel"); - print_buf_info(prtd->ram_link, "ram_link"); - print_buf_info(prtd->ram_link2, "ram_link2"); - print_buf_info(prtd->asp_channel, "asp_channel"); - print_buf_info(prtd->asp_link[0], "asp_link[0]"); - print_buf_info(prtd->asp_link[1], "asp_link[1]"); - - /* - * There is a phase offset of 2 periods between the position - * used by dma setup and the position reported in the pointer - * function. - * - * The phase offset, when not using ping-pong buffers, is due to - * the two consecutive calls to davinci_pcm_enqueue_dma() below. - * - * Whereas here, with ping-pong buffers, the phase is due to - * there being an entire buffer transfer complete before the - * first dma completion event triggers davinci_pcm_dma_irq(). - */ - davinci_pcm_period_elapsed(substream); - davinci_pcm_period_elapsed(substream); - - return 0; - } - davinci_pcm_enqueue_dma(substream); - davinci_pcm_period_elapsed(substream); - - /* Copy self-linked parameter RAM entry into master channel */ - edma_read_slot(prtd->asp_link[0], &prtd->asp_params); - edma_write_slot(prtd->asp_channel, &prtd->asp_params); - davinci_pcm_enqueue_dma(substream); - davinci_pcm_period_elapsed(substream); - - return 0; -} - -static snd_pcm_uframes_t -davinci_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct davinci_runtime_data *prtd = runtime->private_data; - unsigned int offset; - int asp_count; - unsigned int period_size = snd_pcm_lib_period_bytes(substream); - - /* - * There is a phase offset of 2 periods between the position used by dma - * setup and the position reported in the pointer function. Either +2 in - * the dma setup or -2 here in the pointer function (with wrapping, - * both) accounts for this offset -- choose the latter since it makes - * the first-time setup clearer. - */ - spin_lock(&prtd->lock); - asp_count = prtd->period - 2; - spin_unlock(&prtd->lock); - - if (asp_count < 0) - asp_count += runtime->periods; - asp_count *= period_size; - - offset = bytes_to_frames(runtime, asp_count); - if (offset >= runtime->buffer_size) - offset = 0; - - return offset; -} - -static int davinci_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct davinci_runtime_data *prtd; - struct snd_pcm_hardware *ppcm; - int ret = 0; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct davinci_pcm_dma_params *pa; - struct davinci_pcm_dma_params *params; - - pa = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - if (!pa) - return -ENODEV; - params = &pa[substream->stream]; - - ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - &pcm_hardware_playback : &pcm_hardware_capture; - allocate_sram(substream, params->sram_pool, params->sram_size, ppcm); - snd_soc_set_runtime_hwparams(substream, ppcm); - /* ensure that buffer size is a multiple of period size */ - ret = snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) - return ret; - - prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL); - if (prtd == NULL) - return -ENOMEM; - - spin_lock_init(&prtd->lock); - prtd->params = params; - prtd->asp_channel = -1; - prtd->asp_link[0] = prtd->asp_link[1] = -1; - prtd->ram_channel = -1; - prtd->ram_link = -1; - prtd->ram_link2 = -1; - - runtime->private_data = prtd; - - ret = davinci_pcm_dma_request(substream); - if (ret) { - printk(KERN_ERR "davinci_pcm: Failed to get dma channels\n"); - kfree(prtd); - } - - return ret; -} - -static int davinci_pcm_close(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct davinci_runtime_data *prtd = runtime->private_data; - - if (prtd->ram_channel >= 0) - edma_stop(prtd->ram_channel); - if (prtd->asp_channel >= 0) - edma_stop(prtd->asp_channel); - if (prtd->asp_link[0] >= 0) - edma_unlink(prtd->asp_link[0]); - if (prtd->asp_link[1] >= 0) - edma_unlink(prtd->asp_link[1]); - if (prtd->ram_link >= 0) - edma_unlink(prtd->ram_link); - - if (prtd->asp_link[0] >= 0) - edma_free_slot(prtd->asp_link[0]); - if (prtd->asp_link[1] >= 0) - edma_free_slot(prtd->asp_link[1]); - if (prtd->asp_channel >= 0) - edma_free_channel(prtd->asp_channel); - if (prtd->ram_link >= 0) - edma_free_slot(prtd->ram_link); - if (prtd->ram_link2 >= 0) - edma_free_slot(prtd->ram_link2); - if (prtd->ram_channel >= 0) - edma_free_channel(prtd->ram_channel); - - kfree(prtd); - - return 0; -} - -static int davinci_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); -} - -static int davinci_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - -static int davinci_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); -} - -static struct snd_pcm_ops davinci_pcm_ops = { - .open = davinci_pcm_open, - .close = davinci_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = davinci_pcm_hw_params, - .hw_free = davinci_pcm_hw_free, - .prepare = davinci_pcm_prepare, - .trigger = davinci_pcm_trigger, - .pointer = davinci_pcm_pointer, - .mmap = davinci_pcm_mmap, -}; - -static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, - size_t size) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - - pr_debug("davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, " - "size=%d\n", (void *) buf->area, (void *) buf->addr, size); - - if (!buf->area) - return -ENOMEM; - - buf->bytes = size; - return 0; -} - -static void davinci_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - struct snd_dma_buffer *iram_dma; - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - iram_dma = buf->private_data; - if (iram_dma) { - davinci_free_sram(substream, iram_dma); - kfree(iram_dma); - } - } -} - -static int davinci_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret; - - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = davinci_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK, - pcm_hardware_playback.buffer_bytes_max); - if (ret) - return ret; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = davinci_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE, - pcm_hardware_capture.buffer_bytes_max); - if (ret) - return ret; - } - - return 0; -} - -static struct snd_soc_platform_driver davinci_soc_platform = { - .ops = &davinci_pcm_ops, - .pcm_new = davinci_pcm_new, - .pcm_free = davinci_pcm_free, -}; - -int davinci_soc_platform_register(struct device *dev) -{ - return devm_snd_soc_register_platform(dev, &davinci_soc_platform); -} -EXPORT_SYMBOL_GPL(davinci_soc_platform_register); - -MODULE_AUTHOR("Vladimir Barinov"); -MODULE_DESCRIPTION("TI DAVINCI PCM DMA module"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h deleted file mode 100644 index 0fe2346a9aa2..000000000000 --- a/sound/soc/davinci/davinci-pcm.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * ALSA PCM interface for the TI DAVINCI processor - * - * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> - * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.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 _DAVINCI_PCM_H -#define _DAVINCI_PCM_H - -#include <linux/genalloc.h> -#include <linux/platform_data/davinci_asp.h> -#include <linux/platform_data/edma.h> - -struct davinci_pcm_dma_params { - int channel; /* sync dma channel ID */ - unsigned short acnt; - dma_addr_t dma_addr; /* device physical address for DMA */ - unsigned sram_size; - struct gen_pool *sram_pool; /* SRAM gen_pool for ping pong */ - enum dma_event_q asp_chan_q; /* event queue number for ASP channel */ - enum dma_event_q ram_chan_q; /* event queue number for RAM channel */ - unsigned char data_type; /* xfer data type */ - unsigned char convert_mono_stereo; - unsigned int fifo_level; -}; - -#if IS_ENABLED(CONFIG_SND_DAVINCI_SOC) -int davinci_soc_platform_register(struct device *dev); -#else -static inline int davinci_soc_platform_register(struct device *dev) -{ - return 0; -} -#endif /* CONFIG_SND_DAVINCI_SOC */ - -#endif diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index 5bee04279ebe..fabd05f24aeb 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -33,8 +33,9 @@ #include <sound/pcm_params.h> #include <sound/initval.h> #include <sound/soc.h> +#include <sound/dmaengine_pcm.h> -#include "davinci-pcm.h" +#include "edma-pcm.h" #include "davinci-i2s.h" #define MOD_REG_BIT(val, mask, set) do { \ @@ -47,7 +48,8 @@ struct davinci_vcif_dev { struct davinci_vc *davinci_vc; - struct davinci_pcm_dma_params dma_params[2]; + struct snd_dmaengine_dai_dma_data dma_data[2]; + int dma_request[2]; }; static void davinci_vcif_start(struct snd_pcm_substream *substream) @@ -93,8 +95,6 @@ static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, { struct davinci_vcif_dev *davinci_vcif_dev = snd_soc_dai_get_drvdata(dai); struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; - struct davinci_pcm_dma_params *dma_params = - &davinci_vcif_dev->dma_params[substream->stream]; u32 w; /* Restart the codec before setup */ @@ -113,16 +113,12 @@ static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, /* Determine xfer data type */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_U8: - dma_params->data_type = 0; - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | DAVINCI_VC_CTRL_RD_UNSIGNED | DAVINCI_VC_CTRL_WD_BITS_8 | DAVINCI_VC_CTRL_WD_UNSIGNED, 1); break; case SNDRV_PCM_FORMAT_S8: - dma_params->data_type = 1; - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | DAVINCI_VC_CTRL_WD_BITS_8, 1); @@ -130,8 +126,6 @@ static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, DAVINCI_VC_CTRL_WD_UNSIGNED, 0); break; case SNDRV_PCM_FORMAT_S16_LE: - dma_params->data_type = 2; - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | DAVINCI_VC_CTRL_RD_UNSIGNED | DAVINCI_VC_CTRL_WD_BITS_8 | @@ -142,8 +136,6 @@ static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - dma_params->acnt = dma_params->data_type; - writel(w, davinci_vc->base + DAVINCI_VC_CTRL); return 0; @@ -172,24 +164,25 @@ static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } -static int davinci_vcif_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai); - - snd_soc_dai_set_dma_data(dai, substream, dev->dma_params); - return 0; -} - #define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000 static const struct snd_soc_dai_ops davinci_vcif_dai_ops = { - .startup = davinci_vcif_startup, .trigger = davinci_vcif_trigger, .hw_params = davinci_vcif_hw_params, }; +static int davinci_vcif_dai_probe(struct snd_soc_dai *dai) +{ + struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + + return 0; +} + static struct snd_soc_dai_driver davinci_vcif_dai = { + .probe = davinci_vcif_dai_probe, .playback = { .channels_min = 1, .channels_max = 2, @@ -225,16 +218,16 @@ static int davinci_vcif_probe(struct platform_device *pdev) /* DMA tx params */ davinci_vcif_dev->davinci_vc = davinci_vc; - davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = - davinci_vc->davinci_vcif.dma_tx_channel; - davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr = - davinci_vc->davinci_vcif.dma_tx_addr; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = + &davinci_vc->davinci_vcif.dma_tx_channel; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = + davinci_vc->davinci_vcif.dma_tx_addr; /* DMA rx params */ - davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = - davinci_vc->davinci_vcif.dma_rx_channel; - davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr = - davinci_vc->davinci_vcif.dma_rx_addr; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = + &davinci_vc->davinci_vcif.dma_rx_channel; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = + davinci_vc->davinci_vcif.dma_rx_addr; dev_set_drvdata(&pdev->dev, davinci_vcif_dev); @@ -245,7 +238,7 @@ static int davinci_vcif_probe(struct platform_device *pdev) return ret; } - ret = davinci_soc_platform_register(&pdev->dev); + ret = edma_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "register PCM failed: %d\n", ret); snd_soc_unregister_component(&pdev->dev); diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 081e406b3713..19c302b0d763 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -24,7 +24,7 @@ config SND_SOC_FSL_SAI in-tree drivers select it automatically. config SND_SOC_FSL_SSI - tristate "Synchronous Serial Interface module support" + tristate "Synchronous Serial Interface module (SSI) support" select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC) select REGMAP_MMIO @@ -35,7 +35,7 @@ config SND_SOC_FSL_SSI in-tree drivers select it automatically. config SND_SOC_FSL_SPDIF - tristate "Sony/Philips Digital Interface module support" + tristate "Sony/Philips Digital Interface (S/PDIF) module support" select REGMAP_MMIO select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 3f6959c8e2f7..de438871040b 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -512,6 +512,12 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) memcpy(priv->dai_link, fsl_asoc_card_dai, sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); + ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing"); + if (ret) { + dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); + goto asrc_fail; + } + /* Normal DAI Link */ priv->dai_link[0].cpu_of_node = cpu_np; priv->dai_link[0].codec_of_node = codec_np; diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 6b0c8f717ec2..e8bb8eef1d16 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1288,7 +1288,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) const struct of_device_id *of_id; const char *p, *sprop; const uint32_t *iprop; - struct resource res; + struct resource *res; void __iomem *iomem; char name[64]; @@ -1335,19 +1335,11 @@ static int fsl_ssi_probe(struct platform_device *pdev) } ssi_private->cpu_dai_drv.name = dev_name(&pdev->dev); - /* Get the addresses and IRQ */ - ret = of_address_to_resource(np, 0, &res); - if (ret) { - dev_err(&pdev->dev, "could not determine device resources\n"); - return ret; - } - ssi_private->ssi_phys = res.start; - - iomem = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); - if (!iomem) { - dev_err(&pdev->dev, "could not map device resources\n"); - return -ENOMEM; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iomem = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(iomem)) + return PTR_ERR(iomem); + ssi_private->ssi_phys = res->start; ret = of_property_match_string(np, "clock-names", "ipg"); if (ret < 0) { @@ -1393,8 +1385,8 @@ static int fsl_ssi_probe(struct platform_device *pdev) return ret; } - ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component, - &ssi_private->cpu_dai_drv, 1); + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_ssi_component, + &ssi_private->cpu_dai_drv, 1); if (ret) { dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); goto error_asoc_register; @@ -1407,13 +1399,13 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq); - goto error_irq; + goto error_asoc_register; } } ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev); if (ret) - goto error_irq; + goto error_asoc_register; /* * If codec-handle property is missing from SSI node, we assume @@ -1454,9 +1446,6 @@ done: error_sound_card: fsl_ssi_debugfs_remove(&ssi_private->dbg_stats); -error_irq: - snd_soc_unregister_component(&pdev->dev); - error_asoc_register: if (ssi_private->soc->imx) fsl_ssi_imx_clean(pdev, ssi_private); @@ -1472,7 +1461,6 @@ static int fsl_ssi_remove(struct platform_device *pdev) if (ssi_private->pdev) platform_device_unregister(ssi_private->pdev); - snd_soc_unregister_component(&pdev->dev); if (ssi_private->soc->imx) fsl_ssi_imx_clean(pdev, ssi_private); diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c index f8cf10e16ce9..20e7400e2611 100644 --- a/sound/soc/fsl/imx-es8328.c +++ b/sound/soc/fsl/imx-es8328.c @@ -53,9 +53,9 @@ static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) /* Headphone jack detection */ if (gpio_is_valid(data->jack_gpio)) { - ret = snd_soc_jack_new(rtd->codec, "Headphone", - SND_JACK_HEADPHONE | SND_JACK_BTN_0, - &headset_jack); + ret = snd_soc_card_jack_new(rtd->card, "Headphone", + SND_JACK_HEADPHONE | SND_JACK_BTN_0, + &headset_jack, NULL, 0); if (ret) return ret; diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 08d2a8069b0a..0bab76051fd8 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -326,7 +326,7 @@ static int psc_ac97_of_remove(struct platform_device *op) } /* Match table for of_platform binding */ -static struct of_device_id psc_ac97_match[] = { +static const struct of_device_id psc_ac97_match[] = { { .compatible = "fsl,mpc5200-psc-ac97", }, { .compatible = "fsl,mpc5200b-psc-ac97", }, {} diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 51fb0c00fe73..d8232943ccb6 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -217,7 +217,7 @@ static int psc_i2s_of_remove(struct platform_device *op) } /* Match table for of_platform binding */ -static struct of_device_id psc_i2s_match[] = { +static const struct of_device_id psc_i2s_match[] = { { .compatible = "fsl,mpc5200-psc-i2s", }, { .compatible = "fsl,mpc5200b-psc-i2s", }, {} diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c index c44459d24c50..ec731223cab3 100644 --- a/sound/soc/fsl/pcm030-audio-fabric.c +++ b/sound/soc/fsl/pcm030-audio-fabric.c @@ -113,7 +113,7 @@ static int pcm030_fabric_remove(struct platform_device *op) return ret; } -static struct of_device_id pcm030_audio_match[] = { +static const struct of_device_id pcm030_audio_match[] = { { .compatible = "phytec,pcm030-audio-fabric", }, {} }; diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c index a958937ab405..b454972dce35 100644 --- a/sound/soc/fsl/wm1133-ev1.c +++ b/sound/soc/fsl/wm1133-ev1.c @@ -202,23 +202,20 @@ static struct snd_soc_jack_pin mic_jack_pins[] = { static int wm1133_ev1_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; /* Headphone jack detection */ - snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, &hp_jack); - snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), - hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone", SND_JACK_HEADPHONE, + &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); wm8350_hp_jack_detect(codec, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE); /* Microphone jack detection */ - snd_soc_jack_new(codec, "Microphone", - SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack); - snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), - mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Microphone", + SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack, + mic_jack_pins, ARRAY_SIZE(mic_jack_pins)); wm8350_mic_jack_detect(codec, &mic_jack, SND_JACK_MICROPHONE, SND_JACK_BTN_0); - snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "Mic Bias"); return 0; } diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index fb550b5869d2..33feee9ca8c3 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -125,14 +125,6 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, { int ret; - if (set->fmt) { - ret = snd_soc_dai_set_fmt(dai, set->fmt); - if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "simple-card: set_fmt error\n"); - goto err; - } - } - if (set->sysclk) { ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); if (ret && ret != -ENOTSUPP) { @@ -176,11 +168,11 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) return ret; if (gpio_is_valid(priv->gpio_hp_det)) { - snd_soc_jack_new(codec->codec, "Headphones", SND_JACK_HEADPHONE, - &simple_card_hp_jack); - snd_soc_jack_add_pins(&simple_card_hp_jack, - ARRAY_SIZE(simple_card_hp_jack_pins), - simple_card_hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphones", + SND_JACK_HEADPHONE, + &simple_card_hp_jack, + simple_card_hp_jack_pins, + ARRAY_SIZE(simple_card_hp_jack_pins)); simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det; simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert; @@ -189,11 +181,11 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) } if (gpio_is_valid(priv->gpio_mic_det)) { - snd_soc_jack_new(codec->codec, "Mic Jack", SND_JACK_MICROPHONE, - &simple_card_mic_jack); - snd_soc_jack_add_pins(&simple_card_mic_jack, - ARRAY_SIZE(simple_card_mic_jack_pins), - simple_card_mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, + &simple_card_mic_jack, + simple_card_mic_jack_pins, + ARRAY_SIZE(simple_card_mic_jack_pins)); simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det; simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert; snd_soc_jack_add_gpios(&simple_card_mic_jack, 1, @@ -269,12 +261,10 @@ static int asoc_simple_card_parse_daifmt(struct device_node *node, struct device_node *codec, char *prefix, int idx) { + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); struct device *dev = simple_priv_to_dev(priv); struct device_node *bitclkmaster = NULL; struct device_node *framemaster = NULL; - struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); - struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; - struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; unsigned int daifmt; daifmt = snd_soc_of_parse_daifmt(node, prefix, @@ -289,8 +279,7 @@ static int asoc_simple_card_parse_daifmt(struct device_node *node, */ dev_dbg(dev, "Revert to legacy daifmt parsing\n"); - cpu_dai->fmt = codec_dai->fmt = - snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) | + daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) | (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK); } else { if (codec == bitclkmaster) @@ -299,11 +288,10 @@ static int asoc_simple_card_parse_daifmt(struct device_node *node, else daifmt |= (codec == framemaster) ? SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; - - cpu_dai->fmt = daifmt; - codec_dai->fmt = daifmt; } + dai_link->dai_fmt = daifmt; + of_node_put(bitclkmaster); of_node_put(framemaster); @@ -384,13 +372,12 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, dai_link->init = asoc_simple_card_dai_init; dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); - dev_dbg(dev, "\tcpu : %s / %04x / %d\n", + dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); + dev_dbg(dev, "\tcpu : %s / %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", + dev_dbg(dev, "\tcodec : %s / %d\n", dai_link->codec_dai_name, - dai_props->codec_dai.fmt, dai_props->codec_dai.sysclk); /* @@ -577,14 +564,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_link->codec_name = cinfo->codec; dai_link->cpu_dai_name = cinfo->cpu_dai.name; dai_link->codec_dai_name = cinfo->codec_dai.name; + dai_link->dai_fmt = cinfo->daifmt; dai_link->init = asoc_simple_card_dai_init; memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, sizeof(priv->dai_props->cpu_dai)); memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, sizeof(priv->dai_props->codec_dai)); - priv->dai_props->cpu_dai.fmt |= cinfo->daifmt; - priv->dai_props->codec_dai.fmt |= cinfo->daifmt; } snd_soc_card_set_drvdata(&priv->snd_card, priv); diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index a8e53c45c6b6..cd9aee9871a3 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -1,42 +1,10 @@ # Core support -snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o -snd-soc-sst-acpi-objs := sst-acpi.o - -snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \ - sst-mfld-platform-compress.o sst-atom-controls.o -snd-soc-mfld-machine-objs := mfld_machine.o - -obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o -obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o - -obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o -obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o +obj-$(CONFIG_SND_SOC_INTEL_SST) += common/ # Platform Support -snd-soc-sst-haswell-pcm-objs := \ - sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o -snd-soc-sst-baytrail-pcm-objs := \ - sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o - -obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o -obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o +obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/ +obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/ +obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += atom/ # Machine support -snd-soc-sst-haswell-objs := haswell.o -snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o -snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o -snd-soc-sst-broadwell-objs := broadwell.o -snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o -snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o -snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.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 -obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o -obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o -obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-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 - -# DSP driver -obj-$(CONFIG_SND_SST_IPC) += sst/ +obj-$(CONFIG_SND_SOC_INTEL_SST) += boards/ diff --git a/sound/soc/intel/atom/Makefile b/sound/soc/intel/atom/Makefile new file mode 100644 index 000000000000..ce8074fa6d66 --- /dev/null +++ b/sound/soc/intel/atom/Makefile @@ -0,0 +1,7 @@ +snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \ + sst-mfld-platform-compress.o sst-atom-controls.o + +obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o + +# DSP driver +obj-$(CONFIG_SND_SST_IPC) += sst/ diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 90aa5c0476f3..90aa5c0476f3 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/atom/sst-atom-controls.h index daecc58f28af..daecc58f28af 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/atom/sst-atom-controls.h diff --git a/sound/soc/intel/sst-mfld-dsp.h b/sound/soc/intel/atom/sst-mfld-dsp.h index 4257263157cd..4257263157cd 100644 --- a/sound/soc/intel/sst-mfld-dsp.h +++ b/sound/soc/intel/atom/sst-mfld-dsp.h diff --git a/sound/soc/intel/sst-mfld-platform-compress.c b/sound/soc/intel/atom/sst-mfld-platform-compress.c index 395168986462..395168986462 100644 --- a/sound/soc/intel/sst-mfld-platform-compress.c +++ b/sound/soc/intel/atom/sst-mfld-platform-compress.c diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 7523cbef8780..2fbaf2c75d17 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -594,11 +594,13 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, ret_val = stream->ops->stream_drop(sst->dev, str_id); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: dev_dbg(rtd->dev, "sst: in pause\n"); status = SST_PLATFORM_PAUSED; ret_val = stream->ops->stream_pause(sst->dev, str_id); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: dev_dbg(rtd->dev, "sst: in pause release\n"); status = SST_PLATFORM_RUNNING; ret_val = stream->ops->stream_pause_release(sst->dev, str_id); @@ -665,6 +667,9 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) static int sst_soc_probe(struct snd_soc_platform *platform) { + struct sst_data *drv = dev_get_drvdata(platform->dev); + + drv->soc_card = platform->component.card; return sst_dsp_init_v2_dpcm(platform); } @@ -727,9 +732,64 @@ static int sst_platform_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP + +static int sst_soc_prepare(struct device *dev) +{ + struct sst_data *drv = dev_get_drvdata(dev); + int i; + + /* suspend all pcms first */ + snd_soc_suspend(drv->soc_card->dev); + snd_soc_poweroff(drv->soc_card->dev); + + /* set the SSPs to idle */ + for (i = 0; i < drv->soc_card->num_rtd; i++) { + struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; + + if (dai->active) { + send_ssp_cmd(dai, dai->name, 0); + sst_handle_vb_timer(dai, false); + } + } + + return 0; +} + +static void sst_soc_complete(struct device *dev) +{ + struct sst_data *drv = dev_get_drvdata(dev); + int i; + + /* restart SSPs */ + for (i = 0; i < drv->soc_card->num_rtd; i++) { + struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; + + if (dai->active) { + sst_handle_vb_timer(dai, true); + send_ssp_cmd(dai, dai->name, 1); + } + } + snd_soc_resume(drv->soc_card->dev); +} + +#else + +#define sst_soc_prepare NULL +#define sst_soc_complete NULL + +#endif + + +static const struct dev_pm_ops sst_platform_pm = { + .prepare = sst_soc_prepare, + .complete = sst_soc_complete, +}; + static struct platform_driver sst_platform_driver = { .driver = { .name = "sst-mfld-platform", + .pm = &sst_platform_pm, }, .probe = sst_platform_probe, .remove = sst_platform_remove, diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h index 79c8d1246a8f..9094314be2b0 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/atom/sst-mfld-platform.h @@ -174,6 +174,7 @@ struct sst_data { struct sst_platform_data *pdata; struct snd_sst_bytes_v2 *byte_stream; struct mutex lock; + struct snd_soc_card *soc_card; }; int sst_register_dsp(struct sst_device *sst); int sst_unregister_dsp(struct sst_device *sst); diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/atom/sst/Makefile index fd21726361b5..fd21726361b5 100644 --- a/sound/soc/intel/sst/Makefile +++ b/sound/soc/intel/atom/sst/Makefile diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index 11c578651c1c..96c2e420cce6 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -32,7 +32,7 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); @@ -423,23 +423,135 @@ static int intel_sst_runtime_suspend(struct device *dev) return ret; } -static int intel_sst_runtime_resume(struct device *dev) +static int intel_sst_suspend(struct device *dev) { - int ret = 0; struct intel_sst_drv *ctx = dev_get_drvdata(dev); + struct sst_fw_save *fw_save; + int i, ret = 0; - if (ctx->sst_state == SST_RESET) { - ret = sst_load_fw(ctx); - if (ret) { - dev_err(dev, "FW download fail %d\n", ret); - sst_set_fw_state_locked(ctx, SST_RESET); + /* check first if we are already in SW reset */ + if (ctx->sst_state == SST_RESET) + return 0; + + /* + * check if any stream is active and running + * they should already by suspend by soc_suspend + */ + for (i = 1; i <= ctx->info.max_streams; i++) { + struct stream_info *stream = &ctx->streams[i]; + + if (stream->status == STREAM_RUNNING) { + dev_err(dev, "stream %d is running, cant susupend, abort\n", i); + return -EBUSY; } } + synchronize_irq(ctx->irq_num); + flush_workqueue(ctx->post_msg_wq); + + /* Move the SST state to Reset */ + sst_set_fw_state_locked(ctx, SST_RESET); + + /* tell DSP we are suspending */ + if (ctx->ops->save_dsp_context(ctx)) + return -EBUSY; + + /* save the memories */ + fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL); + if (!fw_save) + return -ENOMEM; + fw_save->iram = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL); + if (!fw_save->iram) { + ret = -ENOMEM; + goto iram; + } + fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL); + if (!fw_save->dram) { + ret = -ENOMEM; + goto dram; + } + fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); + if (!fw_save->sram) { + ret = -ENOMEM; + goto sram; + } + + fw_save->ddr = kzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL); + if (!fw_save->ddr) { + ret = -ENOMEM; + goto ddr; + } + + memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base); + memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base); + memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE); + memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base); + + ctx->fw_save = fw_save; + ctx->ops->reset(ctx); + return 0; +ddr: + kfree(fw_save->sram); +sram: + kfree(fw_save->dram); +dram: + kfree(fw_save->iram); +iram: + kfree(fw_save); + return ret; +} + +static int intel_sst_resume(struct device *dev) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + struct sst_fw_save *fw_save = ctx->fw_save; + int ret = 0; + struct sst_block *block; + + if (!fw_save) + return 0; + + sst_set_fw_state_locked(ctx, SST_FW_LOADING); + + /* we have to restore the memory saved */ + ctx->ops->reset(ctx); + + ctx->fw_save = NULL; + + memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base); + memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base); + memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE); + memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base); + + kfree(fw_save->sram); + kfree(fw_save->dram); + kfree(fw_save->iram); + kfree(fw_save->ddr); + kfree(fw_save); + + block = sst_create_block(ctx, 0, FW_DWNL_ID); + if (block == NULL) + return -ENOMEM; + + + /* start and wait for ack */ + ctx->ops->start(ctx); + ret = sst_wait_timeout(ctx, block); + if (ret) { + dev_err(ctx->dev, "fw download failed %d\n", ret); + /* FW download failed due to timeout */ + ret = -EBUSY; + + } else { + sst_set_fw_state_locked(ctx, SST_FW_RUNNING); + } + + sst_free_block(ctx, block); return ret; } const struct dev_pm_ops intel_sst_pm = { + .suspend = intel_sst_suspend, + .resume = intel_sst_resume, .runtime_suspend = intel_sst_runtime_suspend, - .runtime_resume = intel_sst_runtime_resume, }; EXPORT_SYMBOL_GPL(intel_sst_pm); diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/atom/sst/sst.h index 562bc483d6b7..3f493862e98d 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/atom/sst/sst.h @@ -337,6 +337,13 @@ struct sst_shim_regs64 { u64 csr2; }; +struct sst_fw_save { + void *iram; + void *dram; + void *sram; + void *ddr; +}; + /** * struct intel_sst_drv - driver ops * @@ -428,6 +435,8 @@ struct intel_sst_drv { * persistent till worker thread gets called */ char firmware_name[FW_NAME_SIZE]; + + struct sst_fw_save *fw_save; }; /* misc definitions */ @@ -544,4 +553,7 @@ int sst_alloc_drv_context(struct intel_sst_drv **ctx, int sst_context_init(struct intel_sst_drv *ctx); void sst_context_cleanup(struct intel_sst_drv *ctx); void sst_configure_runtime_pm(struct intel_sst_drv *ctx); +void memcpy32_toio(void __iomem *dst, const void *src, int count); +void memcpy32_fromio(void *dst, const void __iomem *src, int count); + #endif diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index b782dfdcdbba..05f693083911 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -39,7 +39,7 @@ #include <acpi/actypes.h> #include <acpi/acpi_bus.h> #include "../sst-mfld-platform.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" #include "sst.h" struct sst_machines { @@ -309,7 +309,7 @@ static int sst_acpi_probe(struct platform_device *pdev) ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64), GFP_KERNEL); if (!ctx->shim_regs64) { - return -ENOMEM; + ret = -ENOMEM; goto do_sst_cleanup; } diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index 5f75ef3cdd22..7b50a9d17ec1 100644 --- a/sound/soc/intel/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -32,7 +32,7 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" @@ -138,12 +138,36 @@ int sst_get_stream(struct intel_sst_drv *ctx, static int sst_power_control(struct device *dev, bool state) { struct intel_sst_drv *ctx = dev_get_drvdata(dev); - - dev_dbg(ctx->dev, "state:%d", state); - if (state == true) - return pm_runtime_get_sync(dev); - else + int ret = 0; + int usage_count = 0; + +#ifdef CONFIG_PM + usage_count = atomic_read(&dev->power.usage_count); +#else + usage_count = 1; +#endif + + if (state == true) { + ret = pm_runtime_get_sync(dev); + + dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count); + if (ret < 0) { + dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret); + return ret; + } + if ((ctx->sst_state == SST_RESET) && (usage_count == 1)) { + ret = sst_load_fw(ctx); + if (ret) { + dev_err(dev, "FW download fail %d\n", ret); + sst_set_fw_state_locked(ctx, SST_RESET); + ret = sst_pm_runtime_put(ctx); + } + } + } else { + dev_dbg(ctx->dev, "Disable: pm usage count: %d\n", usage_count); return sst_pm_runtime_put(ctx); + } + return ret; } /* @@ -357,7 +381,7 @@ static int sst_cdev_tstamp(struct device *dev, unsigned int str_id, tstamp->copied_total = fw_tstamp.ring_buffer_counter; tstamp->pcm_frames = fw_tstamp.frames_decoded; tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter, - (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24))); + (u64)stream->num_ch * SST_GET_BYTES_PER_SAMPLE(24)); tstamp->sampling_rate = fw_tstamp.sampling_frequency; dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames); @@ -572,6 +596,35 @@ static int sst_stream_drop(struct device *dev, int str_id) return sst_drop_stream(ctx, str_id); } +static int sst_stream_pause(struct device *dev, int str_id) +{ + struct stream_info *str_info; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (ctx->sst_state != SST_FW_RUNNING) + return 0; + + str_info = get_stream_info(ctx, str_id); + if (!str_info) + return -EINVAL; + + return sst_pause_stream(ctx, str_id); +} + +static int sst_stream_resume(struct device *dev, int str_id) +{ + struct stream_info *str_info; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (ctx->sst_state != SST_FW_RUNNING) + return 0; + + str_info = get_stream_info(ctx, str_id); + if (!str_info) + return -EINVAL; + return sst_resume_stream(ctx, str_id); +} + static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info) { int str_id = 0; @@ -633,6 +686,8 @@ static struct sst_ops pcm_ops = { .stream_init = sst_stream_init, .stream_start = sst_stream_start, .stream_drop = sst_stream_drop, + .stream_pause = sst_stream_pause, + .stream_pause_release = sst_stream_resume, .stream_read_tstamp = sst_read_timestamp, .send_byte_stream = sst_send_byte_stream, .close = sst_close_pcm_stream, diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 484e60978477..5a278618466c 100644 --- a/sound/soc/intel/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -32,7 +32,7 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" struct sst_block *sst_create_block(struct intel_sst_drv *ctx, u32 msg_id, u32 drv_id) diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index 7888cd707853..33917146d9c4 100644 --- a/sound/soc/intel/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -37,9 +37,17 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" -static inline void memcpy32_toio(void __iomem *dst, const void *src, int count) +void memcpy32_toio(void __iomem *dst, const void *src, int count) +{ + /* __iowrite32_copy uses 32-bit count values so divide by 4 for + * right count in words + */ + __iowrite32_copy(dst, src, count/4); +} + +void memcpy32_fromio(void *dst, const void __iomem *src, int count) { /* __iowrite32_copy uses 32-bit count values so divide by 4 for * right count in words diff --git a/sound/soc/intel/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c index 3a0b3bf0af97..3a0b3bf0af97 100644 --- a/sound/soc/intel/sst/sst_pci.c +++ b/sound/soc/intel/atom/sst/sst_pci.c diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index 4b7720864492..adb32fefd693 100644 --- a/sound/soc/intel/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -34,7 +34,7 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" int sst_shim_write(void __iomem *addr, int offset, int value) { @@ -111,30 +111,6 @@ int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, } -unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr) -{ - unsigned long long val = 0; - - switch (sst->dev_id) { - case SST_MRFLD_PCI_ID: - case SST_BYT_ACPI_ID: - val = sst_shim_read64(sst->shim, addr); - break; - } - return val; -} - -void write_shim_data(struct intel_sst_drv *sst, int addr, - unsigned long long data) -{ - switch (sst->dev_id) { - case SST_MRFLD_PCI_ID: - case SST_BYT_ACPI_ID: - sst_shim_write64(sst->shim, addr, (u64) data); - break; - } -} - /* * sst_wait_timeout - wait on event for timeout * diff --git a/sound/soc/intel/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index dae2a41997aa..a74c64c7053c 100644 --- a/sound/soc/intel/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -31,7 +31,7 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) { diff --git a/sound/soc/intel/baytrail/Makefile b/sound/soc/intel/baytrail/Makefile new file mode 100644 index 000000000000..488408cadf6d --- /dev/null +++ b/sound/soc/intel/baytrail/Makefile @@ -0,0 +1,4 @@ +snd-soc-sst-baytrail-pcm-objs := \ + sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o + +obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o diff --git a/sound/soc/intel/sst-baytrail-dsp.c b/sound/soc/intel/baytrail/sst-baytrail-dsp.c index 5a9e56700f31..01d023cc05dd 100644 --- a/sound/soc/intel/sst-baytrail-dsp.c +++ b/sound/soc/intel/baytrail/sst-baytrail-dsp.c @@ -22,8 +22,8 @@ #include <linux/platform_device.h> #include <linux/firmware.h> -#include "sst-dsp.h" -#include "sst-dsp-priv.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" #include "sst-baytrail-ipc.h" #define SST_BYT_FW_SIGNATURE_SIZE 4 diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c index b4ad98c43e5c..1efb33b36303 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c @@ -29,8 +29,9 @@ #include <asm/div64.h> #include "sst-baytrail-ipc.h" -#include "sst-dsp.h" -#include "sst-dsp-priv.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "../common/sst-ipc.h" /* IPC message timeout */ #define IPC_TIMEOUT_MSECS 300 @@ -142,23 +143,6 @@ struct sst_byt_fw_init { u8 debug_info; } __packed; -/* driver internal IPC message structure */ -struct ipc_message { - struct list_head list; - u64 header; - - /* direction wrt host CPU */ - char tx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE]; - size_t tx_size; - char rx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE]; - size_t rx_size; - - wait_queue_head_t waitq; - bool complete; - bool wait; - int errno; -}; - struct sst_byt_stream; struct sst_byt; @@ -195,14 +179,7 @@ struct sst_byt { struct sst_fw *fw; /* IPC messaging */ - struct list_head tx_list; - struct list_head rx_list; - struct list_head empty_list; - wait_queue_head_t wait_txq; - struct task_struct *tx_thread; - struct kthread_worker kworker; - struct kthread_work kwork; - struct ipc_message *msg; + struct sst_generic_ipc ipc; }; static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id) @@ -246,209 +223,6 @@ static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt, return NULL; } -static void sst_byt_ipc_shim_dbg(struct sst_byt *byt, const char *text) -{ - struct sst_dsp *sst = byt->dsp; - u64 isr, ipcd, imrx, ipcx; - - ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX); - isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX); - ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); - imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX); - - dev_err(byt->dev, - "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n", - text, ipcx, isr, ipcd, imrx); -} - -/* locks held by caller */ -static struct ipc_message *sst_byt_msg_get_empty(struct sst_byt *byt) -{ - struct ipc_message *msg = NULL; - - if (!list_empty(&byt->empty_list)) { - msg = list_first_entry(&byt->empty_list, - struct ipc_message, list); - list_del(&msg->list); - } - - return msg; -} - -static void sst_byt_ipc_tx_msgs(struct kthread_work *work) -{ - struct sst_byt *byt = - container_of(work, struct sst_byt, kwork); - struct ipc_message *msg; - u64 ipcx; - unsigned long flags; - - spin_lock_irqsave(&byt->dsp->spinlock, flags); - if (list_empty(&byt->tx_list)) { - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); - return; - } - - /* if the DSP is busy we will TX messages after IRQ */ - ipcx = sst_dsp_shim_read64_unlocked(byt->dsp, SST_IPCX); - if (ipcx & SST_BYT_IPCX_BUSY) { - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); - return; - } - - msg = list_first_entry(&byt->tx_list, struct ipc_message, list); - - list_move(&msg->list, &byt->rx_list); - - /* send the message */ - if (msg->header & IPC_HEADER_LARGE(true)) - sst_dsp_outbox_write(byt->dsp, msg->tx_data, msg->tx_size); - sst_dsp_shim_write64_unlocked(byt->dsp, SST_IPCX, msg->header); - - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); -} - -static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt, - struct ipc_message *msg) -{ - msg->complete = true; - - if (!msg->wait) - list_add_tail(&msg->list, &byt->empty_list); - else - wake_up(&msg->waitq); -} - -static void sst_byt_drop_all(struct sst_byt *byt) -{ - struct ipc_message *msg, *tmp; - unsigned long flags; - - /* drop all TX and Rx messages before we stall + reset DSP */ - spin_lock_irqsave(&byt->dsp->spinlock, flags); - list_for_each_entry_safe(msg, tmp, &byt->tx_list, list) { - list_move(&msg->list, &byt->empty_list); - } - - list_for_each_entry_safe(msg, tmp, &byt->rx_list, list) { - list_move(&msg->list, &byt->empty_list); - } - - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); -} - -static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg, - void *rx_data) -{ - unsigned long flags; - int ret; - - /* wait for DSP completion */ - ret = wait_event_timeout(msg->waitq, msg->complete, - msecs_to_jiffies(IPC_TIMEOUT_MSECS)); - - spin_lock_irqsave(&byt->dsp->spinlock, flags); - if (ret == 0) { - list_del(&msg->list); - sst_byt_ipc_shim_dbg(byt, "message timeout"); - - ret = -ETIMEDOUT; - } else { - - /* copy the data returned from DSP */ - if (msg->rx_size) - memcpy(rx_data, msg->rx_data, msg->rx_size); - ret = msg->errno; - } - - list_add_tail(&msg->list, &byt->empty_list); - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); - return ret; -} - -static int sst_byt_ipc_tx_message(struct sst_byt *byt, u64 header, - void *tx_data, size_t tx_bytes, - void *rx_data, size_t rx_bytes, int wait) -{ - unsigned long flags; - struct ipc_message *msg; - - spin_lock_irqsave(&byt->dsp->spinlock, flags); - - msg = sst_byt_msg_get_empty(byt); - if (msg == NULL) { - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); - return -EBUSY; - } - - msg->header = header; - msg->tx_size = tx_bytes; - msg->rx_size = rx_bytes; - msg->wait = wait; - msg->errno = 0; - msg->complete = false; - - if (tx_bytes) { - /* msg content = lower 32-bit of the header + data */ - *(u32 *)msg->tx_data = (u32)(header & (u32)-1); - memcpy(msg->tx_data + sizeof(u32), tx_data, tx_bytes); - msg->tx_size += sizeof(u32); - } - - list_add_tail(&msg->list, &byt->tx_list); - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); - - queue_kthread_work(&byt->kworker, &byt->kwork); - - if (wait) - return sst_byt_tx_wait_done(byt, msg, rx_data); - else - return 0; -} - -static inline int sst_byt_ipc_tx_msg_wait(struct sst_byt *byt, u64 header, - void *tx_data, size_t tx_bytes, - void *rx_data, size_t rx_bytes) -{ - return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes, - rx_data, rx_bytes, 1); -} - -static inline int sst_byt_ipc_tx_msg_nowait(struct sst_byt *byt, u64 header, - void *tx_data, size_t tx_bytes) -{ - return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes, - NULL, 0, 0); -} - -static struct ipc_message *sst_byt_reply_find_msg(struct sst_byt *byt, - u64 header) -{ - struct ipc_message *msg = NULL, *_msg; - u64 mask; - - /* match reply to message sent based on msg and stream IDs */ - mask = IPC_HEADER_MSG_ID_MASK | - IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT; - header &= mask; - - if (list_empty(&byt->rx_list)) { - dev_err(byt->dev, - "ipc: rx list is empty but received 0x%llx\n", header); - goto out; - } - - list_for_each_entry(_msg, &byt->rx_list, list) { - if ((_msg->header & mask) == header) { - msg = _msg; - break; - } - } - -out: - return msg; -} - static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg) { struct sst_byt_stream *stream; @@ -477,7 +251,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header) { struct ipc_message *msg; - msg = sst_byt_reply_find_msg(byt, header); + msg = sst_ipc_reply_find_msg(&byt->ipc, header); if (msg == NULL) return 1; @@ -491,7 +265,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header) list_del(&msg->list); /* wake up */ - sst_byt_tx_msg_reply_complete(byt, msg); + sst_ipc_tx_msg_reply_complete(&byt->ipc, msg); return 1; } @@ -538,6 +312,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context) { struct sst_dsp *sst = (struct sst_dsp *) context; struct sst_byt *byt = sst_dsp_get_thread_context(sst); + struct sst_generic_ipc *ipc = &byt->ipc; u64 header; unsigned long flags; @@ -569,7 +344,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context) spin_unlock_irqrestore(&sst->spinlock, flags); /* continue to send any remaining messages... */ - queue_kthread_work(&byt->kworker, &byt->kwork); + queue_kthread_work(&ipc->kworker, &ipc->kwork); return IRQ_HANDLED; } @@ -656,7 +431,8 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream) header = sst_byt_header(IPC_IA_ALLOC_STREAM, sizeof(*str_req) + sizeof(u32), true, stream->str_id); - ret = sst_byt_ipc_tx_msg_wait(byt, header, str_req, sizeof(*str_req), + ret = sst_ipc_tx_message_wait(&byt->ipc, header, str_req, + sizeof(*str_req), reply, sizeof(*reply)); if (ret < 0) { dev_err(byt->dev, "ipc: error stream commit failed\n"); @@ -679,7 +455,7 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) goto out; header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id); - ret = sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, 0); if (ret < 0) { dev_err(byt->dev, "ipc: free stream %d failed\n", stream->str_id); @@ -703,9 +479,11 @@ static int sst_byt_stream_operations(struct sst_byt *byt, int type, header = sst_byt_header(type, 0, false, stream_id); if (wait) - return sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0); + return sst_ipc_tx_message_wait(&byt->ipc, header, NULL, + 0, NULL, 0); else - return sst_byt_ipc_tx_msg_nowait(byt, header, NULL, 0); + return sst_ipc_tx_message_nowait(&byt->ipc, header, + NULL, 0); } /* stream ALSA trigger operations */ @@ -725,7 +503,7 @@ int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream, tx_msg = &start_stream; size = sizeof(start_stream); - ret = sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size); + ret = sst_ipc_tx_message_nowait(&byt->ipc, header, tx_msg, size); if (ret < 0) dev_err(byt->dev, "ipc: error failed to start stream %d\n", stream->str_id); @@ -790,23 +568,6 @@ int sst_byt_get_dsp_position(struct sst_byt *byt, return do_div(fw_tstamp.ring_buffer_counter, buffer_size); } -static int msg_empty_list_init(struct sst_byt *byt) -{ - struct ipc_message *msg; - int i; - - byt->msg = kzalloc(sizeof(*msg) * IPC_EMPTY_LIST_SIZE, GFP_KERNEL); - if (byt->msg == NULL) - return -ENOMEM; - - for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { - init_waitqueue_head(&byt->msg[i].waitq); - list_add(&byt->msg[i].list, &byt->empty_list); - } - - return 0; -} - struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt) { return byt->dsp; @@ -823,7 +584,7 @@ int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) dev_dbg(byt->dev, "dsp reset\n"); sst_dsp_reset(byt->dsp); - sst_byt_drop_all(byt); + sst_ipc_drop_all(&byt->ipc); dev_dbg(byt->dev, "dsp in reset\n"); dev_dbg(byt->dev, "free all blocks and unload fw\n"); @@ -876,9 +637,52 @@ int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata) } EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready); +static void byt_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) +{ + if (msg->header & IPC_HEADER_LARGE(true)) + sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size); + + sst_dsp_shim_write64_unlocked(ipc->dsp, SST_IPCX, msg->header); +} + +static void byt_shim_dbg(struct sst_generic_ipc *ipc, const char *text) +{ + struct sst_dsp *sst = ipc->dsp; + u64 isr, ipcd, imrx, ipcx; + + ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX); + isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX); + ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); + imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX); + + dev_err(ipc->dev, + "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n", + text, ipcx, isr, ipcd, imrx); +} + +static void byt_tx_data_copy(struct ipc_message *msg, char *tx_data, + size_t tx_size) +{ + /* msg content = lower 32-bit of the header + data */ + *(u32 *)msg->tx_data = (u32)(msg->header & (u32)-1); + memcpy(msg->tx_data + sizeof(u32), tx_data, tx_size); + msg->tx_size += sizeof(u32); +} + +static u64 byt_reply_msg_match(u64 header, u64 *mask) +{ + /* match reply to message sent based on msg and stream IDs */ + *mask = IPC_HEADER_MSG_ID_MASK | + IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT; + header &= *mask; + + return header; +} + int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) { struct sst_byt *byt; + struct sst_generic_ipc *ipc; struct sst_fw *byt_sst_fw; struct sst_byt_fw_init init; int err; @@ -889,39 +693,30 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) if (byt == NULL) return -ENOMEM; - byt->dev = dev; - INIT_LIST_HEAD(&byt->stream_list); - INIT_LIST_HEAD(&byt->tx_list); - INIT_LIST_HEAD(&byt->rx_list); - INIT_LIST_HEAD(&byt->empty_list); - init_waitqueue_head(&byt->boot_wait); - init_waitqueue_head(&byt->wait_txq); + 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; - err = msg_empty_list_init(byt); - if (err < 0) - return -ENOMEM; - - /* start the IPC message thread */ - init_kthread_worker(&byt->kworker); - byt->tx_thread = kthread_run(kthread_worker_fn, - &byt->kworker, "%s", - dev_name(byt->dev)); - if (IS_ERR(byt->tx_thread)) { - err = PTR_ERR(byt->tx_thread); - dev_err(byt->dev, "error failed to create message TX task\n"); - goto err_free_msg; - } - init_kthread_work(&byt->kwork, sst_byt_ipc_tx_msgs); + err = sst_ipc_init(ipc); + if (err != 0) + goto ipc_init_err; + INIT_LIST_HEAD(&byt->stream_list); + init_waitqueue_head(&byt->boot_wait); byt_dev.thread_context = byt; /* init SST shim */ byt->dsp = sst_dsp_new(dev, &byt_dev, pdata); if (byt->dsp == NULL) { err = -ENODEV; - goto dsp_err; + goto dsp_new_err; } + ipc->dsp = byt->dsp; + /* keep the DSP in reset state for base FW loading */ sst_dsp_reset(byt->dsp); @@ -961,10 +756,10 @@ boot_err: sst_fw_free(byt_sst_fw); fw_err: sst_dsp_free(byt->dsp); -dsp_err: - kthread_stop(byt->tx_thread); -err_free_msg: - kfree(byt->msg); +dsp_new_err: + sst_ipc_fini(ipc); +ipc_init_err: + kfree(byt); return err; } @@ -977,7 +772,6 @@ void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata) sst_dsp_reset(byt->dsp); sst_fw_free_all(byt->dsp); sst_dsp_free(byt->dsp); - kthread_stop(byt->tx_thread); - kfree(byt->msg); + sst_ipc_fini(&byt->ipc); } EXPORT_SYMBOL_GPL(sst_byt_dsp_free); diff --git a/sound/soc/intel/sst-baytrail-ipc.h b/sound/soc/intel/baytrail/sst-baytrail-ipc.h index 8faff6dcf25d..8faff6dcf25d 100644 --- a/sound/soc/intel/sst-baytrail-ipc.h +++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.h diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/baytrail/sst-baytrail-pcm.c index 224c49c9f135..79547bec558b 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/baytrail/sst-baytrail-pcm.c @@ -20,8 +20,8 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include "sst-baytrail-ipc.h" -#include "sst-dsp-priv.h" -#include "sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "../common/sst-dsp.h" #define BYT_PCM_COUNT 2 diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile new file mode 100644 index 000000000000..f8237f0044eb --- /dev/null +++ b/sound/soc/intel/boards/Makefile @@ -0,0 +1,15 @@ +snd-soc-sst-haswell-objs := haswell.o +snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o +snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o +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 + +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 +obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o +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 diff --git a/sound/soc/intel/broadwell.c b/sound/soc/intel/boards/broadwell.c index 9cf7d01479ad..8bafaf6ceab1 100644 --- a/sound/soc/intel/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -22,10 +22,10 @@ #include <sound/jack.h> #include <sound/pcm_params.h> -#include "sst-dsp.h" -#include "sst-haswell-ipc.h" +#include "../common/sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" -#include "../codecs/rt286.h" +#include "../../codecs/rt286.h" static struct snd_soc_jack broadwell_headset; /* Headset jack detection DAPM pins */ @@ -80,15 +80,9 @@ static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; int ret = 0; - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset); - - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&broadwell_headset, - ARRAY_SIZE(broadwell_headset_pins), - broadwell_headset_pins); + ret = snd_soc_card_jack_new(rtd->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset, + broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins)); if (ret) return ret; @@ -110,9 +104,7 @@ static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP0 to 16 bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S16_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); return 0; } @@ -227,6 +219,32 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { }, }; +static int broadwell_suspend(struct snd_soc_card *card){ + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &card->codec_dev_list, card_list) { + if (!strcmp(codec->component.name, "i2c-INT343A:00")) { + dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n"); + rt286_mic_detect(codec, NULL); + break; + } + } + return 0; +} + +static int broadwell_resume(struct snd_soc_card *card){ + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &card->codec_dev_list, card_list) { + if (!strcmp(codec->component.name, "i2c-INT343A:00")) { + dev_dbg(codec->dev, "enabling jack detect for resume.\n"); + rt286_mic_detect(codec, &broadwell_headset); + break; + } + } + return 0; +} + /* broadwell audio machine driver for WPT + RT286S */ static struct snd_soc_card broadwell_rt286 = { .name = "broadwell-rt286", @@ -240,6 +258,8 @@ static struct snd_soc_card broadwell_rt286 = { .dapm_routes = broadwell_rt286_map, .num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map), .fully_routed = true, + .suspend_pre = broadwell_suspend, + .resume_post = broadwell_resume, }; static int broadwell_audio_probe(struct platform_device *pdev) diff --git a/sound/soc/intel/byt-max98090.c b/sound/soc/intel/boards/byt-max98090.c index 9832afe7d22c..7ab8cc9fbfd5 100644 --- a/sound/soc/intel/byt-max98090.c +++ b/sound/soc/intel/boards/byt-max98090.c @@ -24,7 +24,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> -#include "../codecs/max98090.h" +#include "../../codecs/max98090.h" struct byt_max98090_private { struct snd_soc_jack jack; @@ -84,7 +84,6 @@ static struct snd_soc_jack_gpio hs_jack_gpios[] = { static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; struct byt_max98090_private *drv = snd_soc_card_get_drvdata(card); struct snd_soc_jack *jack = &drv->jack; @@ -100,13 +99,9 @@ static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime) } /* Enable jack detection */ - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_LINEOUT | SND_JACK_HEADSET, jack); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_LINEOUT | SND_JACK_HEADSET, jack, + hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); if (ret) return ret; diff --git a/sound/soc/intel/byt-rt5640.c b/sound/soc/intel/boards/byt-rt5640.c index 354eaad886e1..ae89b9b966d9 100644 --- a/sound/soc/intel/byt-rt5640.c +++ b/sound/soc/intel/boards/byt-rt5640.c @@ -23,9 +23,9 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> -#include "../codecs/rt5640.h" +#include "../../codecs/rt5640.h" -#include "sst-dsp.h" +#include "../common/sst-dsp.h" static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 59308629043e..7f55d59024a8 100644 --- a/sound/soc/intel/bytcr_dpcm_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -26,8 +26,8 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include "../codecs/rt5640.h" -#include "sst-atom-controls.h" +#include "../../codecs/rt5640.h" +#include "../atom/sst-atom-controls.h" static const struct snd_soc_dapm_widget byt_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), @@ -113,9 +113,7 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP2 to 24-bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S24_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index bd29617a9ab9..20a28b22e30f 100644 --- a/sound/soc/intel/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -27,8 +27,8 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> -#include "../codecs/rt5645.h" -#include "sst-atom-controls.h" +#include "../../codecs/rt5645.h" +#include "../atom/sst-atom-controls.h" #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI "rt5645-aif1" @@ -169,17 +169,17 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) return ret; } - ret = snd_soc_jack_new(codec, "Headphone Jack", - SND_JACK_HEADPHONE, - &ctx->hp_jack); + 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; } - ret = snd_soc_jack_new(codec, "Mic Jack", - SND_JACK_MICROPHONE, - &ctx->mic_jack); + ret = snd_soc_card_jack_new(runtime->card, "Mic Jack", + SND_JACK_MICROPHONE, &ctx->mic_jack, + NULL, 0); if (ret) { dev_err(runtime->dev, "Mic jack creation failed %d\n", ret); return ret; @@ -203,9 +203,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP2 to 24-bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S24_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index ff016621583a..2c9cc5be439e 100644 --- a/sound/soc/intel/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -22,13 +22,28 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include "../codecs/rt5670.h" -#include "sst-atom-controls.h" +#include <sound/jack.h> +#include "../../codecs/rt5670.h" +#include "../atom/sst-atom-controls.h" /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI "rt5670-aif1" +static struct snd_soc_jack cht_bsw_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin cht_bsw_headset_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, +}; + static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) { int i; @@ -50,6 +65,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct snd_soc_dai *codec_dai; + int ret; codec_dai = cht_get_codec_dai(card); if (!codec_dai) { @@ -57,17 +73,31 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return -EIO; } - if (!SND_SOC_DAPM_EVENT_OFF(event)) - return 0; - - /* Set codec sysclk source to its internal clock because codec PLL will - * be off when idle and MCLK will also be off by ACPI when codec is - * runtime suspended. Codec needs clock for jack detection and button - * press. - */ - snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK, - 0, SND_SOC_CLOCK_IN); - + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* set codec PLL source to the 19.2MHz platform clock (MCLK) */ + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK, + CHT_PLAT_CLK_3_HZ, 48000 * 512); + if (ret < 0) { + dev_err(card->dev, "can't set codec pll: %d\n", ret); + return ret; + } + + /* set codec sysclk source to PLL */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1, + 48000 * 512, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + } else { + /* Set codec sysclk source to its internal clock because codec + * PLL will be off when idle and MCLK will also be off by ACPI + * when codec is runtime suspended. Codec needs clock for jack + * detection and button press. + */ + snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK, + 48000 * 512, SND_SOC_CLOCK_IN); + } return 0; } @@ -77,7 +107,8 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_SPK("Ext Spk", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, - platform_clock_control, SND_SOC_DAPM_POST_PMD), + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route cht_audio_map[] = { @@ -162,6 +193,15 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) | RT5670_AD_MONO_L_FILTER | RT5670_AD_MONO_R_FILTER, RT5670_CLK_SEL_I2S1_ASRC); + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2, &cht_bsw_headset, + cht_bsw_headset_pins, ARRAY_SIZE(cht_bsw_headset_pins)); + if (ret) + return ret; + + rt5670_set_jack_detect(codec, &cht_bsw_headset); return 0; } @@ -178,9 +218,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP2 to 24-bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S24_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; } @@ -217,7 +255,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, @@ -240,19 +278,48 @@ static struct snd_soc_dai_link cht_dailink[] = { .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, + .nonatomic = true, .codec_dai_name = "rt5670-aif1", .codec_name = "i2c-10EC5670:00", .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS, .init = cht_codec_init, .be_hw_params_fixup = cht_codec_fixup, - .ignore_suspend = 1, .dpcm_playback = 1, .dpcm_capture = 1, .ops = &cht_be_ssp2_ops, }, }; +static int cht_suspend_pre(struct snd_soc_card *card) +{ + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &card->codec_dev_list, card_list) { + if (!strcmp(codec->component.name, "i2c-10EC5670:00")) { + dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n"); + rt5670_jack_suspend(codec); + break; + } + } + return 0; +} + +static int cht_resume_post(struct snd_soc_card *card) +{ + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &card->codec_dev_list, card_list) { + if (!strcmp(codec->component.name, "i2c-10EC5670:00")) { + dev_dbg(codec->dev, "enabling jack detect for resume.\n"); + rt5670_jack_resume(codec); + break; + } + } + + return 0; +} + /* SoC card */ static struct snd_soc_card snd_soc_card_cht = { .name = "cherrytrailcraudio", @@ -264,6 +331,8 @@ static struct snd_soc_card snd_soc_card_cht = { .num_dapm_routes = ARRAY_SIZE(cht_audio_map), .controls = cht_mc_controls, .num_controls = ARRAY_SIZE(cht_mc_controls), + .suspend_pre = cht_suspend_pre, + .resume_post = cht_resume_post, }; static int snd_cht_mc_probe(struct platform_device *pdev) @@ -285,7 +354,6 @@ static int snd_cht_mc_probe(struct platform_device *pdev) static struct platform_driver snd_cht_mc_driver = { .driver = { .name = "cht-bsw-rt5672", - .pm = &snd_soc_pm_ops, }, .probe = snd_cht_mc_probe, }; diff --git a/sound/soc/intel/haswell.c b/sound/soc/intel/boards/haswell.c index 35edf51a52aa..22558572cb9c 100644 --- a/sound/soc/intel/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -21,10 +21,10 @@ #include <sound/soc.h> #include <sound/pcm_params.h> -#include "sst-dsp.h" -#include "sst-haswell-ipc.h" +#include "../common/sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" -#include "../codecs/rt5640.h" +#include "../../codecs/rt5640.h" /* Haswell ULT platforms have a Headphone and Mic jack */ static const struct snd_soc_dapm_widget haswell_widgets[] = { @@ -56,9 +56,7 @@ static int haswell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP0 to 16 bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S16_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); return 0; } diff --git a/sound/soc/intel/mfld_machine.c b/sound/soc/intel/boards/mfld_machine.c index 90b7a57713a0..49c09a0add79 100644 --- a/sound/soc/intel/mfld_machine.c +++ b/sound/soc/intel/boards/mfld_machine.c @@ -228,10 +228,13 @@ static void mfld_jack_check(unsigned int intr_status) { struct mfld_jack_data jack_data; + if (!mfld_codec) + return; + jack_data.mfld_jack = &mfld_jack; jack_data.intr_id = intr_status; - sn95031_jack_detection(&jack_data); + sn95031_jack_detection(mfld_codec, &jack_data); /* TODO: add american headset detection post gpiolib support */ } @@ -240,8 +243,6 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_dapm_context *dapm = &runtime->card->dapm; int ret_val; - mfld_codec = runtime->codec; - /* default is earpiece pin, userspace sets it explcitly */ snd_soc_dapm_disable_pin(dapm, "Headphones"); /* default is lineout NC, userspace sets it explcitly */ @@ -254,20 +255,15 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_disable_pin(dapm, "LINEINR"); /* Headset and button jack detection */ - ret_val = snd_soc_jack_new(mfld_codec, "Intel(R) MID Audio Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1, &mfld_jack); + ret_val = snd_soc_card_jack_new(runtime->card, + "Intel(R) MID Audio Jack", SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1, &mfld_jack, + mfld_jack_pins, ARRAY_SIZE(mfld_jack_pins)); if (ret_val) { pr_err("jack creation failed\n"); return ret_val; } - ret_val = snd_soc_jack_add_pins(&mfld_jack, - ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins); - if (ret_val) { - pr_err("adding jack pins failed\n"); - return ret_val; - } ret_val = snd_soc_jack_add_zones(&mfld_jack, ARRAY_SIZE(mfld_zones), mfld_zones); if (ret_val) { @@ -275,6 +271,8 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) return ret_val; } + mfld_codec = runtime->codec; + /* we want to check if anything is inserted at boot, * so send a fake event to codec and it will read adc * to find if anything is there or not */ @@ -359,8 +357,6 @@ static irqreturn_t snd_mfld_jack_detection(int irq, void *data) { struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data; - if (mfld_jack.codec == NULL) - return IRQ_HANDLED; mfld_jack_check(mc_drv_ctx->interrupt_status); return IRQ_HANDLED; diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile new file mode 100644 index 000000000000..f24154ca4e98 --- /dev/null +++ b/sound/soc/intel/common/Makefile @@ -0,0 +1,7 @@ +snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o +snd-soc-sst-acpi-objs := sst-acpi.o +snd-soc-sst-ipc-objs := sst-ipc.o + +obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o +obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o + diff --git a/sound/soc/intel/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index b3d84560fbb5..42f293f9c6e2 100644 --- a/sound/soc/intel/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -142,6 +142,7 @@ static int sst_acpi_probe(struct platform_device *pdev) sst_acpi->desc = desc; sst_acpi->mach = mach; + sst_pdata->resindex_dma_base = desc->resindex_dma_base; if (desc->resindex_dma_base >= 0) { sst_pdata->dma_engine = desc->dma_engine; sst_pdata->dma_base = desc->resindex_dma_base; diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index b9da030e312d..396d54510350 100644 --- a/sound/soc/intel/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -173,6 +173,16 @@ struct sst_module_runtime_context { }; /* + * Audio DSP Module State + */ +enum sst_module_state { + SST_MODULE_STATE_UNLOADED = 0, /* default state */ + SST_MODULE_STATE_LOADED, + SST_MODULE_STATE_INITIALIZED, /* and inactive */ + SST_MODULE_STATE_ACTIVE, +}; + +/* * Audio DSP Generic Module. * * Each Firmware file can consist of 1..N modules. A module can span multiple @@ -203,6 +213,9 @@ struct sst_module { struct list_head list; /* DSP list of modules */ struct list_head list_fw; /* FW list of modules */ struct list_head runtime_list; /* list of runtime module objects*/ + + /* state */ + enum sst_module_state state; }; /* diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index 64e94212d2d2..64e94212d2d2 100644 --- a/sound/soc/intel/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index f291e32f0077..96aeb2556ad4 100644 --- a/sound/soc/intel/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -28,7 +28,6 @@ /* Supported SST DMA Devices */ #define SST_DMA_TYPE_DW 1 -#define SST_DMA_TYPE_MID 2 /* autosuspend delay 5s*/ #define SST_RUNTIME_SUSPEND_DELAY (5 * 1000) @@ -206,6 +205,7 @@ struct sst_pdata { const struct firmware *fw; /* DMA */ + int resindex_dma_base; /* other fields invalid if equals to -1 */ u32 dma_base; u32 dma_size; int dma_engine; diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index 5f71ef607a57..ebcca6dc48d1 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -221,8 +221,6 @@ int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id) dma_cap_mask_t mask; int ret; - /* The Intel MID DMA engine driver needs the slave config set but - * Synopsis DMA engine driver safely ignores the slave config */ dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); dma_cap_set(DMA_MEMCPY, mask); @@ -271,15 +269,16 @@ int sst_dma_new(struct sst_dsp *sst) const char *dma_dev_name; int ret = 0; + if (sst->pdata->resindex_dma_base == -1) + /* DMA is not used, return and squelsh error messages */ + return 0; + /* configure the correct platform data for whatever DMA engine * is attached to the ADSP IP. */ switch (sst->pdata->dma_engine) { case SST_DMA_TYPE_DW: dma_dev_name = "dw_dmac"; break; - case SST_DMA_TYPE_MID: - dma_dev_name = "Intel MID DMA"; - break; default: dev_err(sst->dev, "error: invalid DMA engine %d\n", sst->pdata->dma_engine); @@ -498,6 +497,7 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw, sst_module->scratch_size = template->scratch_size; sst_module->persistent_size = template->persistent_size; sst_module->entry = template->entry; + sst_module->state = SST_MODULE_STATE_UNLOADED; INIT_LIST_HEAD(&sst_module->block_list); INIT_LIST_HEAD(&sst_module->runtime_list); diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c new file mode 100644 index 000000000000..4b62a553823c --- /dev/null +++ b/sound/soc/intel/common/sst-ipc.c @@ -0,0 +1,294 @@ +/* + * Intel SST generic IPC Support + * + * Copyright (C) 2015, Intel Corporation. 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 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/types.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/wait.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/kthread.h> +#include <sound/asound.h> + +#include "sst-dsp.h" +#include "sst-dsp-priv.h" +#include "sst-ipc.h" + +/* IPC message timeout (msecs) */ +#define IPC_TIMEOUT_MSECS 300 + +#define IPC_EMPTY_LIST_SIZE 8 + +/* locks held by caller */ +static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc) +{ + struct ipc_message *msg = NULL; + + if (!list_empty(&ipc->empty_list)) { + msg = list_first_entry(&ipc->empty_list, struct ipc_message, + list); + list_del(&msg->list); + } + + return msg; +} + +static int tx_wait_done(struct sst_generic_ipc *ipc, + struct ipc_message *msg, void *rx_data) +{ + unsigned long flags; + int ret; + + /* wait for DSP completion (in all cases atm inc pending) */ + ret = wait_event_timeout(msg->waitq, msg->complete, + msecs_to_jiffies(IPC_TIMEOUT_MSECS)); + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + if (ret == 0) { + if (ipc->ops.shim_dbg != NULL) + ipc->ops.shim_dbg(ipc, "message timeout"); + + list_del(&msg->list); + ret = -ETIMEDOUT; + } else { + + /* copy the data returned from DSP */ + if (msg->rx_size) + memcpy(rx_data, msg->rx_data, msg->rx_size); + ret = msg->errno; + } + + list_add_tail(&msg->list, &ipc->empty_list); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return ret; +} + +static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, + size_t rx_bytes, int wait) +{ + struct ipc_message *msg; + unsigned long flags; + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + msg = msg_get_empty(ipc); + if (msg == NULL) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return -EBUSY; + } + + msg->header = header; + msg->tx_size = tx_bytes; + msg->rx_size = rx_bytes; + msg->wait = wait; + msg->errno = 0; + msg->pending = false; + msg->complete = false; + + if ((tx_bytes) && (ipc->ops.tx_data_copy != NULL)) + ipc->ops.tx_data_copy(msg, tx_data, tx_bytes); + + list_add_tail(&msg->list, &ipc->tx_list); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + + queue_kthread_work(&ipc->kworker, &ipc->kwork); + + if (wait) + return tx_wait_done(ipc, msg, rx_data); + else + return 0; +} + +static int msg_empty_list_init(struct sst_generic_ipc *ipc) +{ + int i; + + ipc->msg = kzalloc(sizeof(struct ipc_message) * + IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (ipc->msg == NULL) + return -ENOMEM; + + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + init_waitqueue_head(&ipc->msg[i].waitq); + list_add(&ipc->msg[i].list, &ipc->empty_list); + } + + return 0; +} + +static void ipc_tx_msgs(struct kthread_work *work) +{ + struct sst_generic_ipc *ipc = + container_of(work, struct sst_generic_ipc, kwork); + struct ipc_message *msg; + unsigned long flags; + u64 ipcx; + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + if (list_empty(&ipc->tx_list) || ipc->pending) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return; + } + + /* 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)) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return; + } + + msg = list_first_entry(&ipc->tx_list, struct ipc_message, list); + list_move(&msg->list, &ipc->rx_list); + + if (ipc->ops.tx_msg != NULL) + ipc->ops.tx_msg(ipc, msg); + + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); +} + +int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) +{ + return ipc_tx_message(ipc, header, tx_data, tx_bytes, + rx_data, rx_bytes, 1); +} +EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait); + +int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes) +{ + return ipc_tx_message(ipc, header, tx_data, tx_bytes, + NULL, 0, 0); +} +EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait); + +struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, + u64 header) +{ + struct ipc_message *msg; + u64 mask; + + if (ipc->ops.reply_msg_match != NULL) + header = ipc->ops.reply_msg_match(header, &mask); + + if (list_empty(&ipc->rx_list)) { + dev_err(ipc->dev, "error: rx list empty but received 0x%llx\n", + header); + return NULL; + } + + list_for_each_entry(msg, &ipc->rx_list, list) { + if ((msg->header & mask) == header) + return msg; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(sst_ipc_reply_find_msg); + +/* locks held by caller */ +void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc, + struct ipc_message *msg) +{ + msg->complete = true; + + if (!msg->wait) + list_add_tail(&msg->list, &ipc->empty_list); + else + wake_up(&msg->waitq); +} +EXPORT_SYMBOL_GPL(sst_ipc_tx_msg_reply_complete); + +void sst_ipc_drop_all(struct sst_generic_ipc *ipc) +{ + struct ipc_message *msg, *tmp; + unsigned long flags; + int tx_drop_cnt = 0, rx_drop_cnt = 0; + + /* drop all TX and Rx messages before we stall + reset DSP */ + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) { + list_move(&msg->list, &ipc->empty_list); + tx_drop_cnt++; + } + + list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) { + list_move(&msg->list, &ipc->empty_list); + rx_drop_cnt++; + } + + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + + if (tx_drop_cnt || rx_drop_cnt) + dev_err(ipc->dev, "dropped IPC msg RX=%d, TX=%d\n", + tx_drop_cnt, rx_drop_cnt); +} +EXPORT_SYMBOL_GPL(sst_ipc_drop_all); + +int sst_ipc_init(struct sst_generic_ipc *ipc) +{ + int ret; + + INIT_LIST_HEAD(&ipc->tx_list); + INIT_LIST_HEAD(&ipc->rx_list); + INIT_LIST_HEAD(&ipc->empty_list); + init_waitqueue_head(&ipc->wait_txq); + + ret = msg_empty_list_init(ipc); + if (ret < 0) + return -ENOMEM; + + /* start the IPC message thread */ + init_kthread_worker(&ipc->kworker); + ipc->tx_thread = kthread_run(kthread_worker_fn, + &ipc->kworker, "%s", + dev_name(ipc->dev)); + if (IS_ERR(ipc->tx_thread)) { + dev_err(ipc->dev, "error: failed to create message TX task\n"); + ret = PTR_ERR(ipc->tx_thread); + kfree(ipc->msg); + return ret; + } + + init_kthread_work(&ipc->kwork, ipc_tx_msgs); + return 0; +} +EXPORT_SYMBOL_GPL(sst_ipc_init); + +void sst_ipc_fini(struct sst_generic_ipc *ipc) +{ + if (ipc->tx_thread) + kthread_stop(ipc->tx_thread); + + if (ipc->msg) + kfree(ipc->msg); +} +EXPORT_SYMBOL_GPL(sst_ipc_fini); + +/* Module information */ +MODULE_AUTHOR("Jin Yao"); +MODULE_DESCRIPTION("Intel SST IPC generic"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h new file mode 100644 index 000000000000..125ea451a373 --- /dev/null +++ b/sound/soc/intel/common/sst-ipc.h @@ -0,0 +1,91 @@ +/* + * Intel SST generic IPC Support + * + * Copyright (C) 2015, Intel Corporation. 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 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 __SST_GENERIC_IPC_H +#define __SST_GENERIC_IPC_H + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/list.h> +#include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/kthread.h> + +#define IPC_MAX_MAILBOX_BYTES 256 + +struct ipc_message { + struct list_head list; + u64 header; + + /* direction wrt host CPU */ + char tx_data[IPC_MAX_MAILBOX_BYTES]; + size_t tx_size; + char rx_data[IPC_MAX_MAILBOX_BYTES]; + size_t rx_size; + + wait_queue_head_t waitq; + bool pending; + bool complete; + bool wait; + int errno; +}; + +struct sst_generic_ipc; + +struct sst_plat_ipc_ops { + void (*tx_msg)(struct sst_generic_ipc *, struct ipc_message *); + 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); +}; + +/* SST generic IPC data */ +struct sst_generic_ipc { + struct device *dev; + struct sst_dsp *dsp; + + /* IPC messaging */ + struct list_head tx_list; + struct list_head rx_list; + struct list_head empty_list; + wait_queue_head_t wait_txq; + struct task_struct *tx_thread; + struct kthread_worker kworker; + struct kthread_work kwork; + bool pending; + struct ipc_message *msg; + + struct sst_plat_ipc_ops ops; +}; + +int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes); + +int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes); + +struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, + u64 header); + +void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc, + struct ipc_message *msg); + +void sst_ipc_drop_all(struct sst_generic_ipc *ipc); +int sst_ipc_init(struct sst_generic_ipc *ipc); +void sst_ipc_fini(struct sst_generic_ipc *ipc); + +#endif diff --git a/sound/soc/intel/haswell/Makefile b/sound/soc/intel/haswell/Makefile new file mode 100644 index 000000000000..9c1723112d22 --- /dev/null +++ b/sound/soc/intel/haswell/Makefile @@ -0,0 +1,4 @@ +snd-soc-sst-haswell-pcm-objs := \ + sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o + +obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/haswell/sst-haswell-dsp.c index 402b728c0a06..7f94920c8a4d 100644 --- a/sound/soc/intel/sst-haswell-dsp.c +++ b/sound/soc/intel/haswell/sst-haswell-dsp.c @@ -28,9 +28,9 @@ #include <linux/firmware.h> #include <linux/pm_runtime.h> -#include "sst-dsp.h" -#include "sst-dsp-priv.h" -#include "sst-haswell-ipc.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "../haswell/sst-haswell-ipc.h" #include <trace/events/hswadsp.h> @@ -100,6 +100,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, && module->type != SST_HSW_MODULE_PCM && module->type != SST_HSW_MODULE_PCM_REFERENCE && module->type != SST_HSW_MODULE_PCM_CAPTURE + && module->type != SST_HSW_MODULE_WAVES && module->type != SST_HSW_MODULE_LPAL) return 0; @@ -139,6 +140,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, mod->type = SST_MEM_IRAM; break; case SST_HSW_DRAM: + case SST_HSW_REGS: ram = dsp->addr.lpe; mod->offset = block->ram_offset; mod->type = SST_MEM_DRAM; @@ -169,6 +171,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, block = (void *)block + sizeof(*block) + block->size; } + mod->state = SST_MODULE_STATE_LOADED; return 0; } diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index 863a9ca34b8e..344a1e9bbce5 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -34,8 +34,9 @@ #include <sound/asound.h> #include "sst-haswell-ipc.h" -#include "sst-dsp.h" -#include "sst-dsp-priv.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "../common/sst-ipc.h" /* Global Message - Generic */ #define IPC_GLB_TYPE_SHIFT 24 @@ -79,6 +80,15 @@ #define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT) #define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT) +/* Module Message */ +#define IPC_MODULE_OPERATION_SHIFT 20 +#define IPC_MODULE_OPERATION_MASK (0xf << IPC_MODULE_OPERATION_SHIFT) +#define IPC_MODULE_OPERATION(x) (x << IPC_MODULE_OPERATION_SHIFT) + +#define IPC_MODULE_ID_SHIFT 16 +#define IPC_MODULE_ID_MASK (0xf << IPC_MODULE_ID_SHIFT) +#define IPC_MODULE_ID(x) (x << IPC_MODULE_ID_SHIFT) + /* IPC message timeout (msecs) */ #define IPC_TIMEOUT_MSECS 300 #define IPC_BOOT_MSECS 200 @@ -115,6 +125,7 @@ enum ipc_glb_type { IPC_GLB_ENTER_DX_STATE = 12, IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */ IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */ + IPC_GLB_MODULE_OPERATION = 15, /* Message to loadable fw module */ IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */ IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */ }; @@ -133,6 +144,16 @@ enum ipc_glb_reply { IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */ }; +enum ipc_module_operation { + IPC_MODULE_NOTIFICATION = 0, + IPC_MODULE_ENABLE = 1, + IPC_MODULE_DISABLE = 2, + IPC_MODULE_GET_PARAMETER = 3, + IPC_MODULE_SET_PARAMETER = 4, + IPC_MODULE_GET_INFO = 5, + IPC_MODULE_MAX_MESSAGE +}; + /* Stream Message - Types */ enum ipc_str_operation { IPC_STR_RESET = 0, @@ -190,23 +211,6 @@ struct sst_hsw_ipc_fw_ready { u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)]; } __attribute__((packed)); -struct ipc_message { - struct list_head list; - u32 header; - - /* direction wrt host CPU */ - char tx_data[IPC_MAX_MAILBOX_BYTES]; - size_t tx_size; - char rx_data[IPC_MAX_MAILBOX_BYTES]; - size_t rx_size; - - wait_queue_head_t waitq; - bool pending; - bool complete; - bool wait; - int errno; -}; - struct sst_hsw_stream; struct sst_hsw; @@ -305,18 +309,19 @@ struct sst_hsw { bool shutdown; /* IPC messaging */ - struct list_head tx_list; - struct list_head rx_list; - struct list_head empty_list; - wait_queue_head_t wait_txq; - struct task_struct *tx_thread; - struct kthread_worker kworker; - struct kthread_work kwork; - bool pending; - struct ipc_message *msg; + struct sst_generic_ipc ipc; /* FW log stream */ struct sst_hsw_log_stream log_stream; + + /* flags bit field to track module state when resume from RTD3, + * each bit represent state (enabled/disabled) of single module */ + u32 enabled_modules_rtd3; + + /* buffer to store parameter lines */ + u32 param_idx_w; /* write index */ + u32 param_idx_r; /* read index */ + u8 param_buf[WAVES_PARAM_LINES][WAVES_PARAM_COUNT]; }; #define CREATE_TRACE_POINTS @@ -352,6 +357,16 @@ static inline u32 msg_get_notify_reason(u32 msg) return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT; } +static inline u32 msg_get_module_operation(u32 msg) +{ + return (msg & IPC_MODULE_OPERATION_MASK) >> IPC_MODULE_OPERATION_SHIFT; +} + +static inline u32 msg_get_module_id(u32 msg) +{ + return (msg & IPC_MODULE_ID_MASK) >> IPC_MODULE_ID_SHIFT; +} + u32 create_channel_map(enum sst_hsw_channel_config config) { switch (config) { @@ -417,159 +432,6 @@ static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw, return NULL; } -static void ipc_shim_dbg(struct sst_hsw *hsw, const char *text) -{ - struct sst_dsp *sst = hsw->dsp; - u32 isr, ipcd, imrx, ipcx; - - ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX); - isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX); - ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD); - imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX); - - dev_err(hsw->dev, "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n", - text, ipcx, isr, ipcd, imrx); -} - -/* locks held by caller */ -static struct ipc_message *msg_get_empty(struct sst_hsw *hsw) -{ - struct ipc_message *msg = NULL; - - if (!list_empty(&hsw->empty_list)) { - msg = list_first_entry(&hsw->empty_list, struct ipc_message, - list); - list_del(&msg->list); - } - - return msg; -} - -static void ipc_tx_msgs(struct kthread_work *work) -{ - struct sst_hsw *hsw = - container_of(work, struct sst_hsw, kwork); - struct ipc_message *msg; - unsigned long flags; - u32 ipcx; - - spin_lock_irqsave(&hsw->dsp->spinlock, flags); - - if (list_empty(&hsw->tx_list) || hsw->pending) { - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - return; - } - - /* 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(hsw->dsp, SST_IPCX); - if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) { - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - return; - } - - msg = list_first_entry(&hsw->tx_list, struct ipc_message, list); - - list_move(&msg->list, &hsw->rx_list); - - /* send the message */ - sst_dsp_outbox_write(hsw->dsp, msg->tx_data, msg->tx_size); - sst_dsp_ipc_msg_tx(hsw->dsp, msg->header | SST_IPCX_BUSY); - - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); -} - -/* locks held by caller */ -static void tx_msg_reply_complete(struct sst_hsw *hsw, struct ipc_message *msg) -{ - msg->complete = true; - trace_ipc_reply("completed", msg->header); - - if (!msg->wait) - list_add_tail(&msg->list, &hsw->empty_list); - else - wake_up(&msg->waitq); -} - -static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg, - void *rx_data) -{ - unsigned long flags; - int ret; - - /* wait for DSP completion (in all cases atm inc pending) */ - ret = wait_event_timeout(msg->waitq, msg->complete, - msecs_to_jiffies(IPC_TIMEOUT_MSECS)); - - spin_lock_irqsave(&hsw->dsp->spinlock, flags); - if (ret == 0) { - ipc_shim_dbg(hsw, "message timeout"); - - trace_ipc_error("error message timeout for", msg->header); - list_del(&msg->list); - ret = -ETIMEDOUT; - } else { - - /* copy the data returned from DSP */ - if (msg->rx_size) - memcpy(rx_data, msg->rx_data, msg->rx_size); - ret = msg->errno; - } - - list_add_tail(&msg->list, &hsw->empty_list); - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - return ret; -} - -static int ipc_tx_message(struct sst_hsw *hsw, u32 header, void *tx_data, - size_t tx_bytes, void *rx_data, size_t rx_bytes, int wait) -{ - struct ipc_message *msg; - unsigned long flags; - - spin_lock_irqsave(&hsw->dsp->spinlock, flags); - - msg = msg_get_empty(hsw); - if (msg == NULL) { - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - return -EBUSY; - } - - if (tx_bytes) - memcpy(msg->tx_data, tx_data, tx_bytes); - - msg->header = header; - msg->tx_size = tx_bytes; - msg->rx_size = rx_bytes; - msg->wait = wait; - msg->errno = 0; - msg->pending = false; - msg->complete = false; - - list_add_tail(&msg->list, &hsw->tx_list); - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - - queue_kthread_work(&hsw->kworker, &hsw->kwork); - - if (wait) - return tx_wait_done(hsw, msg, rx_data); - else - return 0; -} - -static inline int ipc_tx_message_wait(struct sst_hsw *hsw, u32 header, - void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) -{ - return ipc_tx_message(hsw, header, tx_data, tx_bytes, rx_data, - rx_bytes, 1); -} - -static inline int ipc_tx_message_nowait(struct sst_hsw *hsw, u32 header, - void *tx_data, size_t tx_bytes) -{ - return ipc_tx_message(hsw, header, tx_data, tx_bytes, NULL, 0, 0); -} - static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) { struct sst_hsw_ipc_fw_ready fw_ready; @@ -604,7 +466,7 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) /* log the FW version info got from the mailbox here. */ memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size); pinfo = &fw_info[0]; - for (i = 0; i < sizeof(tmp) / sizeof(char *); i++) + for (i = 0; i < ARRAY_SIZE(tmp); i++) tmp[i] = strsep(&pinfo, " "); dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - " "version: %s.%s, build %s, source commit id: %s\n", @@ -657,27 +519,6 @@ static void hsw_notification_work(struct work_struct *work) sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0); } -static struct ipc_message *reply_find_msg(struct sst_hsw *hsw, u32 header) -{ - struct ipc_message *msg; - - /* clear reply bits & status bits */ - header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK); - - if (list_empty(&hsw->rx_list)) { - dev_err(hsw->dev, "error: rx list empty but received 0x%x\n", - header); - return NULL; - } - - list_for_each_entry(msg, &hsw->rx_list, list) { - if (msg->header == header) - return msg; - } - - return NULL; -} - static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg) { struct sst_hsw_stream *stream; @@ -716,7 +557,7 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) trace_ipc_reply("processing -->", header); - msg = reply_find_msg(hsw, header); + msg = sst_ipc_reply_find_msg(&hsw->ipc, header); if (msg == NULL) { trace_ipc_error("error: can't find message header", header); return -EIO; @@ -727,14 +568,14 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) case IPC_GLB_REPLY_PENDING: trace_ipc_pending_reply("received", header); msg->pending = true; - hsw->pending = true; + hsw->ipc.pending = true; return 1; case IPC_GLB_REPLY_SUCCESS: if (msg->pending) { trace_ipc_pending_reply("completed", header); sst_dsp_inbox_read(hsw->dsp, msg->rx_data, msg->rx_size); - hsw->pending = false; + hsw->ipc.pending = false; } else { /* copy data from the DSP */ sst_dsp_outbox_read(hsw->dsp, msg->rx_data, @@ -790,11 +631,36 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) /* wake up and return the error if we have waiters on this message ? */ list_del(&msg->list); - tx_msg_reply_complete(hsw, msg); + sst_ipc_tx_msg_reply_complete(&hsw->ipc, msg); return 1; } +static int hsw_module_message(struct sst_hsw *hsw, u32 header) +{ + u32 operation, module_id; + int handled = 0; + + operation = msg_get_module_operation(header); + module_id = msg_get_module_id(header); + dev_dbg(hsw->dev, "received module message header: 0x%8.8x\n", + header); + dev_dbg(hsw->dev, "operation: 0x%8.8x module_id: 0x%8.8x\n", + operation, module_id); + + switch (operation) { + case IPC_MODULE_NOTIFICATION: + dev_dbg(hsw->dev, "module notification received"); + handled = 1; + break; + default: + handled = hsw_process_reply(hsw, header); + break; + } + + return handled; +} + static int hsw_stream_message(struct sst_hsw *hsw, u32 header) { u32 stream_msg, stream_id, stage_type; @@ -890,6 +756,9 @@ static int hsw_process_notification(struct sst_hsw *hsw) case IPC_GLB_DEBUG_LOG_MESSAGE: handled = hsw_log_message(hsw, header); break; + case IPC_GLB_MODULE_OPERATION: + handled = hsw_module_message(hsw, header); + break; default: dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n", type, header); @@ -903,6 +772,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context) { struct sst_dsp *sst = (struct sst_dsp *) context; struct sst_hsw *hsw = sst_dsp_get_thread_context(sst); + struct sst_generic_ipc *ipc = &hsw->ipc; u32 ipcx, ipcd; int handled; unsigned long flags; @@ -949,7 +819,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context) spin_unlock_irqrestore(&sst->spinlock, flags); /* continue to send any remaining messages... */ - queue_kthread_work(&hsw->kworker, &hsw->kwork); + queue_kthread_work(&ipc->kworker, &ipc->kwork); return IRQ_HANDLED; } @@ -959,7 +829,8 @@ int sst_hsw_fw_get_version(struct sst_hsw *hsw, { int ret; - ret = ipc_tx_message_wait(hsw, IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION), + ret = sst_ipc_tx_message_wait(&hsw->ipc, + IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION), NULL, 0, version, sizeof(*version)); if (ret < 0) dev_err(hsw->dev, "error: get version failed\n"); @@ -1023,7 +894,8 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw, req->channel = channel; } - ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, req, + sizeof(*req), NULL, 0); if (ret < 0) { dev_err(hsw->dev, "error: set stream volume failed\n"); return ret; @@ -1088,7 +960,8 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, req.curve_type = hsw->curve_type; req.target_volume = volume; - ret = ipc_tx_message_wait(hsw, header, &req, sizeof(req), NULL, 0); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &req, + sizeof(req), NULL, 0); if (ret < 0) { dev_err(hsw->dev, "error: set mixer volume failed\n"); return ret; @@ -1146,7 +1019,7 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) stream->free_req.stream_id = stream->reply.stream_hw_id; header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM); - ret = ipc_tx_message_wait(hsw, header, &stream->free_req, + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &stream->free_req, sizeof(stream->free_req), NULL, 0); if (ret < 0) { dev_err(hsw->dev, "error: free stream %d failed\n", @@ -1338,8 +1211,8 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM); - ret = ipc_tx_message_wait(hsw, header, str_req, sizeof(*str_req), - reply, sizeof(*reply)); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, str_req, + sizeof(*str_req), reply, sizeof(*reply)); if (ret < 0) { dev_err(hsw->dev, "error: stream commit failed\n"); return ret; @@ -1388,7 +1261,8 @@ int sst_hsw_mixer_get_info(struct sst_hsw *hsw) trace_ipc_request("get global mixer info", 0); - ret = ipc_tx_message_wait(hsw, header, NULL, 0, reply, sizeof(*reply)); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, + reply, sizeof(*reply)); if (ret < 0) { dev_err(hsw->dev, "error: get stream info failed\n"); return ret; @@ -1409,9 +1283,10 @@ static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type, header |= (stream_id << IPC_STR_ID_SHIFT); if (wait) - return ipc_tx_message_wait(hsw, header, NULL, 0, NULL, 0); + return sst_ipc_tx_message_wait(&hsw->ipc, header, + NULL, 0, NULL, 0); else - return ipc_tx_message_nowait(hsw, header, NULL, 0); + return sst_ipc_tx_message_nowait(&hsw->ipc, header, NULL, 0); } /* Stream ALSA trigger operations */ @@ -1538,8 +1413,8 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw, header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS); - ret = ipc_tx_message_wait(hsw, header, &config, sizeof(config), - NULL, 0); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &config, + sizeof(config), NULL, 0); if (ret < 0) dev_err(hsw->dev, "error: set device formats failed\n"); @@ -1559,8 +1434,8 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw, trace_ipc_request("PM enter Dx state", state); - ret = ipc_tx_message_wait(hsw, header, &state_, sizeof(state_), - dx, sizeof(*dx)); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &state_, + sizeof(state_), dx, sizeof(*dx)); if (ret < 0) { dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state); return ret; @@ -1703,32 +1578,6 @@ static int sst_hsw_dx_state_restore(struct sst_hsw *hsw) return 0; } -static void sst_hsw_drop_all(struct sst_hsw *hsw) -{ - struct ipc_message *msg, *tmp; - unsigned long flags; - int tx_drop_cnt = 0, rx_drop_cnt = 0; - - /* drop all TX and Rx messages before we stall + reset DSP */ - spin_lock_irqsave(&hsw->dsp->spinlock, flags); - - list_for_each_entry_safe(msg, tmp, &hsw->tx_list, list) { - list_move(&msg->list, &hsw->empty_list); - tx_drop_cnt++; - } - - list_for_each_entry_safe(msg, tmp, &hsw->rx_list, list) { - list_move(&msg->list, &hsw->empty_list); - rx_drop_cnt++; - } - - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - - if (tx_drop_cnt || rx_drop_cnt) - dev_err(hsw->dev, "dropped IPC msg RX=%d, TX=%d\n", - tx_drop_cnt, rx_drop_cnt); -} - int sst_hsw_dsp_load(struct sst_hsw *hsw) { struct sst_dsp *dsp = hsw->dsp; @@ -1808,7 +1657,7 @@ int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw) if (ret < 0) return ret; - sst_hsw_drop_all(hsw); + sst_ipc_drop_all(&hsw->ipc); return 0; } @@ -1844,6 +1693,8 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) if (ret < 0) dev_err(dev, "error: audio DSP boot failure\n"); + sst_hsw_init_module_state(hsw); + ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, msecs_to_jiffies(IPC_BOOT_MSECS)); if (ret == 0) { @@ -1864,26 +1715,345 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) } #endif -static int msg_empty_list_init(struct sst_hsw *hsw) +struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw) { - int i; + return hsw->dsp; +} - hsw->msg = kzalloc(sizeof(struct ipc_message) * - IPC_EMPTY_LIST_SIZE, GFP_KERNEL); - if (hsw->msg == NULL) - return -ENOMEM; +void sst_hsw_init_module_state(struct sst_hsw *hsw) +{ + struct sst_module *module; + enum sst_hsw_module_id id; + + /* the base fw contains several modules */ + for (id = SST_HSW_MODULE_BASE_FW; id < SST_HSW_MAX_MODULE_ID; id++) { + module = sst_module_get_from_id(hsw->dsp, id); + if (module) { + /* module waves is active only after being enabled */ + if (id == SST_HSW_MODULE_WAVES) + module->state = SST_MODULE_STATE_INITIALIZED; + else + module->state = SST_MODULE_STATE_ACTIVE; + } + } +} + +bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id) +{ + struct sst_module *module; + + module = sst_module_get_from_id(hsw->dsp, module_id); + if (module == NULL || module->state == SST_MODULE_STATE_UNLOADED) + return false; + else + return true; +} + +bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id) +{ + struct sst_module *module; + + module = sst_module_get_from_id(hsw->dsp, module_id); + if (module != NULL && module->state == SST_MODULE_STATE_ACTIVE) + return true; + else + return false; +} - for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { - init_waitqueue_head(&hsw->msg[i].waitq); - list_add(&hsw->msg[i].list, &hsw->empty_list); +void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id) +{ + hsw->enabled_modules_rtd3 |= (1 << module_id); +} + +void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id) +{ + hsw->enabled_modules_rtd3 &= ~(1 << module_id); +} + +bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id) +{ + return hsw->enabled_modules_rtd3 & (1 << module_id); +} + +void sst_hsw_reset_param_buf(struct sst_hsw *hsw) +{ + hsw->param_idx_w = 0; + hsw->param_idx_r = 0; + memset((void *)hsw->param_buf, 0, sizeof(hsw->param_buf)); +} + +int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf) +{ + /* save line to the first available position of param buffer */ + if (hsw->param_idx_w > WAVES_PARAM_LINES - 1) { + dev_warn(hsw->dev, "warning: param buffer overflow!\n"); + return -EPERM; + } + memcpy(hsw->param_buf[hsw->param_idx_w], buf, WAVES_PARAM_COUNT); + hsw->param_idx_w++; + return 0; +} + +int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf) +{ + u8 id = 0; + + /* read the first matching line from param buffer */ + while (hsw->param_idx_r < WAVES_PARAM_LINES) { + id = hsw->param_buf[hsw->param_idx_r][0]; + hsw->param_idx_r++; + if (buf[0] == id) { + memcpy(buf, hsw->param_buf[hsw->param_idx_r], + WAVES_PARAM_COUNT); + break; + } } + if (hsw->param_idx_r > WAVES_PARAM_LINES - 1) { + dev_dbg(hsw->dev, "end of buffer, roll to the beginning\n"); + hsw->param_idx_r = 0; + return 0; + } + return 0; +} + +int sst_hsw_launch_param_buf(struct sst_hsw *hsw) +{ + int ret, idx; + if (!sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) { + dev_dbg(hsw->dev, "module waves is not active\n"); + return 0; + } + + /* put all param lines to DSP through ipc */ + for (idx = 0; idx < hsw->param_idx_w; idx++) { + ret = sst_hsw_module_set_param(hsw, + SST_HSW_MODULE_WAVES, 0, hsw->param_buf[idx][0], + WAVES_PARAM_COUNT, hsw->param_buf[idx]); + if (ret < 0) + return ret; + } return 0; } -struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw) +int sst_hsw_module_load(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, char *name) { - return hsw->dsp; + int ret = 0; + const struct firmware *fw = NULL; + struct sst_fw *hsw_sst_fw; + struct sst_module *module; + struct device *dev = hsw->dev; + struct sst_dsp *dsp = hsw->dsp; + + dev_dbg(dev, "sst_hsw_module_load id=%d, name='%s'", module_id, name); + + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + /* loading for the first time */ + if (module_id == SST_HSW_MODULE_BASE_FW) { + /* for base module: use fw requested in acpi probe */ + fw = dsp->pdata->fw; + if (!fw) { + dev_err(dev, "request Base fw failed\n"); + return -ENODEV; + } + } else { + /* try and load any other optional modules if they are + * available. Use dev_info instead of dev_err in case + * request firmware failed */ + ret = request_firmware(&fw, name, dev); + if (ret) { + dev_info(dev, "fw image %s not available(%d)\n", + name, ret); + return ret; + } + } + hsw_sst_fw = sst_fw_new(dsp, fw, hsw); + if (hsw_sst_fw == NULL) { + dev_err(dev, "error: failed to load firmware\n"); + ret = -ENOMEM; + goto out; + } + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + dev_err(dev, "error: no module %d in firmware %s\n", + module_id, name); + } + } else + dev_info(dev, "module %d (%s) already loaded\n", + module_id, name); +out: + /* release fw, but base fw should be released by acpi driver */ + if (fw && module_id != SST_HSW_MODULE_BASE_FW) + release_firmware(fw); + + return ret; +} + +int sst_hsw_module_enable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id) +{ + int ret; + u32 header = 0; + struct sst_hsw_ipc_module_config config; + struct sst_module *module; + struct sst_module_runtime *runtime; + struct device *dev = hsw->dev; + struct sst_dsp *dsp = hsw->dsp; + + if (!sst_hsw_is_module_loaded(hsw, module_id)) { + dev_dbg(dev, "module %d not loaded\n", module_id); + return 0; + } + + if (sst_hsw_is_module_active(hsw, module_id)) { + dev_info(dev, "module %d already enabled\n", module_id); + return 0; + } + + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + dev_err(dev, "module %d not valid\n", module_id); + return -ENXIO; + } + + runtime = sst_module_runtime_get_from_id(module, module_id); + if (runtime == NULL) { + dev_err(dev, "runtime %d not valid", module_id); + return -ENXIO; + } + + header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | + IPC_MODULE_OPERATION(IPC_MODULE_ENABLE) | + IPC_MODULE_ID(module_id); + dev_dbg(dev, "module enable header: %x\n", header); + + config.map.module_entries_count = 1; + config.map.module_entries[0].module_id = module->id; + config.map.module_entries[0].entry_point = module->entry; + + config.persistent_mem.offset = + sst_dsp_get_offset(dsp, + runtime->persistent_offset, SST_MEM_DRAM); + config.persistent_mem.size = module->persistent_size; + + config.scratch_mem.offset = + sst_dsp_get_offset(dsp, + dsp->scratch_offset, SST_MEM_DRAM); + config.scratch_mem.size = module->scratch_size; + dev_dbg(dev, "mod %d enable p:%d @ %x, s:%d @ %x, ep: %x", + config.map.module_entries[0].module_id, + config.persistent_mem.size, + config.persistent_mem.offset, + config.scratch_mem.size, config.scratch_mem.offset, + config.map.module_entries[0].entry_point); + + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, + &config, sizeof(config), NULL, 0); + if (ret < 0) + dev_err(dev, "ipc: module enable failed - %d\n", ret); + else + module->state = SST_MODULE_STATE_ACTIVE; + + return ret; +} + +int sst_hsw_module_disable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id) +{ + int ret; + u32 header; + struct sst_module *module; + struct device *dev = hsw->dev; + struct sst_dsp *dsp = hsw->dsp; + + if (!sst_hsw_is_module_loaded(hsw, module_id)) { + dev_dbg(dev, "module %d not loaded\n", module_id); + return 0; + } + + if (!sst_hsw_is_module_active(hsw, module_id)) { + dev_info(dev, "module %d already disabled\n", module_id); + return 0; + } + + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + dev_err(dev, "module %d not valid\n", module_id); + return -ENXIO; + } + + header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | + IPC_MODULE_OPERATION(IPC_MODULE_DISABLE) | + IPC_MODULE_ID(module_id); + + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, NULL, 0); + if (ret < 0) + dev_err(dev, "module disable failed - %d\n", ret); + else + module->state = SST_MODULE_STATE_INITIALIZED; + + return ret; +} + +int sst_hsw_module_set_param(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, u32 parameter_id, + u32 param_size, char *param) +{ + int ret; + unsigned char *data = NULL; + u32 header = 0; + u32 payload_size = 0, transfer_parameter_size = 0; + dma_addr_t dma_addr = 0; + struct sst_hsw_transfer_parameter *parameter; + struct device *dev = hsw->dev; + + header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | + IPC_MODULE_OPERATION(IPC_MODULE_SET_PARAMETER) | + IPC_MODULE_ID(module_id); + dev_dbg(dev, "sst_hsw_module_set_param header=%x\n", header); + + payload_size = param_size + + sizeof(struct sst_hsw_transfer_parameter) - + sizeof(struct sst_hsw_transfer_list); + dev_dbg(dev, "parameter size : %d\n", param_size); + dev_dbg(dev, "payload size : %d\n", payload_size); + + if (payload_size <= SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE) { + /* short parameter, mailbox can contain data */ + dev_dbg(dev, "transfer parameter size : %d\n", + transfer_parameter_size); + + transfer_parameter_size = ALIGN(payload_size, 4); + dev_dbg(dev, "transfer parameter aligned size : %d\n", + transfer_parameter_size); + + parameter = kzalloc(transfer_parameter_size, GFP_KERNEL); + if (parameter == NULL) + return -ENOMEM; + + memcpy(parameter->data, param, param_size); + } else { + dev_warn(dev, "transfer parameter size too large!"); + return 0; + } + + parameter->parameter_id = parameter_id; + parameter->data_size = param_size; + + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, + parameter, transfer_parameter_size , NULL, 0); + if (ret < 0) + dev_err(dev, "ipc: module set parameter failed - %d\n", ret); + + kfree(parameter); + + if (data) + dma_free_coherent(hsw->dsp->dma_dev, + param_size, (void *)data, dma_addr); + + return ret; } static struct sst_dsp_device hsw_dev = { @@ -1891,10 +2061,48 @@ static struct sst_dsp_device hsw_dev = { .ops = &haswell_ops, }; +static void hsw_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) +{ + /* send the message */ + sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size); + sst_dsp_ipc_msg_tx(ipc->dsp, msg->header); +} + +static void hsw_shim_dbg(struct sst_generic_ipc *ipc, const char *text) +{ + struct sst_dsp *sst = ipc->dsp; + u32 isr, ipcd, imrx, ipcx; + + ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX); + isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX); + ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD); + imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX); + + dev_err(ipc->dev, + "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n", + text, ipcx, isr, ipcd, imrx); +} + +static void hsw_tx_data_copy(struct ipc_message *msg, char *tx_data, + size_t tx_size) +{ + memcpy(msg->tx_data, tx_data, tx_size); +} + +static u64 hsw_reply_msg_match(u64 header, u64 *mask) +{ + /* clear reply bits & status bits */ + header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK); + *mask = (u64)-1; + + return header; +} + int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) { struct sst_hsw_ipc_fw_version version; struct sst_hsw *hsw; + struct sst_generic_ipc *ipc; int ret; dev_dbg(dev, "initialising Audio DSP IPC\n"); @@ -1903,39 +2111,30 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) if (hsw == NULL) return -ENOMEM; - hsw->dev = dev; - INIT_LIST_HEAD(&hsw->stream_list); - INIT_LIST_HEAD(&hsw->tx_list); - INIT_LIST_HEAD(&hsw->rx_list); - INIT_LIST_HEAD(&hsw->empty_list); - init_waitqueue_head(&hsw->boot_wait); - init_waitqueue_head(&hsw->wait_txq); - - ret = msg_empty_list_init(hsw); - if (ret < 0) - return -ENOMEM; + 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; - /* start the IPC message thread */ - init_kthread_worker(&hsw->kworker); - hsw->tx_thread = kthread_run(kthread_worker_fn, - &hsw->kworker, "%s", - dev_name(hsw->dev)); - if (IS_ERR(hsw->tx_thread)) { - ret = PTR_ERR(hsw->tx_thread); - dev_err(hsw->dev, "error: failed to create message TX task\n"); - goto err_free_msg; - } - init_kthread_work(&hsw->kwork, ipc_tx_msgs); + ret = sst_ipc_init(ipc); + if (ret != 0) + goto ipc_init_err; + INIT_LIST_HEAD(&hsw->stream_list); + init_waitqueue_head(&hsw->boot_wait); hsw_dev.thread_context = hsw; /* init SST shim */ hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata); if (hsw->dsp == NULL) { ret = -ENODEV; - goto dsp_err; + goto dsp_new_err; } + ipc->dsp = hsw->dsp; + /* allocate DMA buffer for context storage */ hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL); @@ -1947,18 +2146,22 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) /* keep the DSP in reset state for base FW loading */ sst_dsp_reset(hsw->dsp); - hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw); - if (hsw->sst_fw == NULL) { - ret = -ENODEV; - dev_err(dev, "error: failed to load firmware\n"); + /* load base module and other modules in base firmware image */ + ret = sst_hsw_module_load(hsw, SST_HSW_MODULE_BASE_FW, 0, "Base"); + if (ret < 0) goto fw_err; - } + + /* try to load module waves */ + sst_hsw_module_load(hsw, SST_HSW_MODULE_WAVES, 0, "intel/IntcPP01.bin"); /* allocate scratch mem regions */ ret = sst_block_alloc_scratch(hsw->dsp); if (ret < 0) goto boot_err; + /* init param buffer */ + sst_hsw_reset_param_buf(hsw); + /* wait for DSP boot completion */ sst_dsp_boot(hsw->dsp); ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, @@ -1971,6 +2174,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) goto boot_err; } + /* init module state after boot */ + sst_hsw_init_module_state(hsw); + /* get the FW version */ sst_hsw_fw_get_version(hsw, &version); @@ -1986,17 +2192,16 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) boot_err: sst_dsp_reset(hsw->dsp); - sst_fw_free(hsw->sst_fw); + sst_fw_free_all(hsw->dsp); fw_err: dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, hsw->dx_context, hsw->dx_context_paddr); dma_err: sst_dsp_free(hsw->dsp); -dsp_err: - kthread_stop(hsw->tx_thread); -err_free_msg: - kfree(hsw->msg); - +dsp_new_err: + sst_ipc_fini(ipc); +ipc_init_err: + kfree(hsw); return ret; } EXPORT_SYMBOL_GPL(sst_hsw_dsp_init); @@ -2010,7 +2215,6 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata) dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, hsw->dx_context, hsw->dx_context_paddr); sst_dsp_free(hsw->dsp); - kthread_stop(hsw->tx_thread); - kfree(hsw->msg); + sst_ipc_fini(&hsw->ipc); } EXPORT_SYMBOL_GPL(sst_hsw_dsp_free); diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/haswell/sst-haswell-ipc.h index 858096041cb1..06d71aefa1fe 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/haswell/sst-haswell-ipc.h @@ -37,6 +37,9 @@ #define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400 #define SST_HSW_MAX_INFO_SIZE 64 #define SST_HSW_BUILD_HASH_LENGTH 40 +#define SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE 500 +#define WAVES_PARAM_COUNT 128 +#define WAVES_PARAM_LINES 160 struct sst_hsw; struct sst_hsw_stream; @@ -187,6 +190,28 @@ enum sst_hsw_performance_action { SST_HSW_PERF_STOP = 1, }; +struct sst_hsw_transfer_info { + uint32_t destination; /* destination address */ + uint32_t reverse:1; /* if 1 data flows from destination */ + uint32_t size:31; /* transfer size in bytes.*/ + uint16_t first_page_offset; /* offset to data in the first page. */ + uint8_t packed_pages; /* page addresses. Each occupies 20 bits */ +} __attribute__((packed)); + +struct sst_hsw_transfer_list { + uint32_t transfers_count; + struct sst_hsw_transfer_info transfers; +} __attribute__((packed)); + +struct sst_hsw_transfer_parameter { + uint32_t parameter_id; + uint32_t data_size; + union { + uint8_t data[1]; + struct sst_hsw_transfer_list transfer_list; + }; +} __attribute__((packed)); + /* SST firmware module info */ struct sst_hsw_module_info { u8 name[SST_HSW_MAX_INFO_SIZE]; @@ -215,6 +240,12 @@ struct sst_hsw_fx_enable { struct sst_hsw_memory_info persistent_mem; } __attribute__((packed)); +struct sst_hsw_ipc_module_config { + struct sst_hsw_module_map map; + struct sst_hsw_memory_info persistent_mem; + struct sst_hsw_memory_info scratch_mem; +} __attribute__((packed)); + struct sst_hsw_get_fx_param { u32 parameter_id; u32 param_size; @@ -467,6 +498,28 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata); void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata); struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw); +/* fw module function */ +void sst_hsw_init_module_state(struct sst_hsw *hsw); +bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id); +bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id); +void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id); +void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id); +bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id); +void sst_hsw_reset_param_buf(struct sst_hsw *hsw); +int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf); +int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf); +int sst_hsw_launch_param_buf(struct sst_hsw *hsw); + +int sst_hsw_module_load(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, char *name); +int sst_hsw_module_enable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id); +int sst_hsw_module_disable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id); +int sst_hsw_module_set_param(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, u32 parameter_id, + u32 param_size, char *param); + /* runtime module management */ struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw, int mod_id, int offset); diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index 7e21e8f85885..23ae0400d6db 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -29,9 +29,9 @@ #include <sound/tlv.h> #include <sound/compress_driver.h> -#include "sst-haswell-ipc.h" -#include "sst-dsp-priv.h" -#include "sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" +#include "../common/sst-dsp-priv.h" +#include "../common/sst-dsp.h" #define HSW_PCM_COUNT 6 #define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */ @@ -137,6 +137,7 @@ struct hsw_priv_data { struct device *dev; enum hsw_pm_state pm_state; struct snd_soc_card *soc_card; + struct sst_module_runtime *runtime_waves; /* sound effect module */ /* page tables */ struct snd_dma_buffer dmab[HSW_PCM_COUNT][2]; @@ -318,6 +319,93 @@ static int hsw_volume_get(struct snd_kcontrol *kcontrol, return 0; } +static int hsw_waves_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; + + ucontrol->value.integer.value[0] = + (sst_hsw_is_module_active(hsw, id) || + sst_hsw_is_module_enabled_rtd3(hsw, id)); + return 0; +} + +static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + int ret = 0; + enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; + bool switch_on = (bool)ucontrol->value.integer.value[0]; + + /* if module is in RAM on the DSP, apply user settings to module through + * ipc. If module is not in RAM on the DSP, store user setting for + * track */ + if (sst_hsw_is_module_loaded(hsw, id)) { + if (switch_on == sst_hsw_is_module_active(hsw, id)) + return 0; + + if (switch_on) + ret = sst_hsw_module_enable(hsw, id, 0); + else + ret = sst_hsw_module_disable(hsw, id, 0); + } else { + if (switch_on == sst_hsw_is_module_enabled_rtd3(hsw, id)) + return 0; + + if (switch_on) + sst_hsw_set_module_enabled_rtd3(hsw, id); + else + sst_hsw_set_module_disabled_rtd3(hsw, id); + } + + return ret; +} + +static int hsw_waves_param_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + + /* return a matching line from param buffer */ + return sst_hsw_load_param_line(hsw, ucontrol->value.bytes.data); +} + +static int hsw_waves_param_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + int ret; + enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; + int param_id = ucontrol->value.bytes.data[0]; + int param_size = WAVES_PARAM_COUNT; + + /* clear param buffer and reset buffer index */ + if (param_id == 0xFF) { + sst_hsw_reset_param_buf(hsw); + return 0; + } + + /* store params into buffer */ + ret = sst_hsw_store_param_line(hsw, ucontrol->value.bytes.data); + if (ret < 0) + return ret; + + if (sst_hsw_is_module_active(hsw, id)) + ret = sst_hsw_module_set_param(hsw, id, 0, param_id, + param_size, ucontrol->value.bytes.data); + return ret; +} + /* TLV used by both global and stream volumes */ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1); @@ -339,6 +427,12 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = { SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, ARRAY_SIZE(volume_map) - 1, 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), + /* enable/disable module waves */ + SOC_SINGLE_BOOL_EXT("Waves Switch", 0, + hsw_waves_switch_get, hsw_waves_switch_put), + /* set parameters to module waves */ + SND_SOC_BYTES_EXT("Waves Set Param", WAVES_PARAM_COUNT, + hsw_waves_param_get, hsw_waves_param_put), }; /* Create DMA buffer page table for DSP */ @@ -807,6 +901,14 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) pcm_data->runtime->persistent_offset; } + /* create runtime blocks for module waves */ + if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) { + pdata->runtime_waves = sst_hsw_runtime_module_create(hsw, + SST_HSW_MODULE_WAVES, 0); + if (pdata->runtime_waves == NULL) + goto err; + } + return 0; err: @@ -820,14 +922,17 @@ err: static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) { + struct sst_hsw *hsw = pdata->hsw; struct hsw_pcm_data *pcm_data; int i; 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 (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) { + sst_hsw_runtime_module_free(pdata->runtime_waves); + } } static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) @@ -984,7 +1089,9 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) } /* allocate runtime modules */ - hsw_pcm_create_modules(priv_data); + ret = hsw_pcm_create_modules(priv_data); + if (ret < 0) + goto err; /* enable runtime PM with auto suspend */ pm_runtime_set_autosuspend_delay(platform->dev, @@ -996,7 +1103,7 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) return 0; err: - for (;i >= 0; i--) { + for (--i; i >= 0; i--) { if (hsw_dais[i].playback.channels_min) snd_dma_free_pages(&priv_data->dmab[i][0]); if (hsw_dais[i].capture.channels_min) @@ -1101,10 +1208,18 @@ static int hsw_pcm_runtime_suspend(struct device *dev) { struct hsw_priv_data *pdata = dev_get_drvdata(dev); struct sst_hsw *hsw = pdata->hsw; + int ret; if (pdata->pm_state >= HSW_PM_STATE_RTD3) return 0; + /* fw modules will be unloaded on RTD3, set flag to track */ + if (sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) { + ret = sst_hsw_module_disable(hsw, SST_HSW_MODULE_WAVES, 0); + if (ret < 0) + 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); pdata->pm_state = HSW_PM_STATE_RTD3; @@ -1139,6 +1254,19 @@ static int hsw_pcm_runtime_resume(struct device *dev) else if (ret == 1) /* no action required */ return 0; + /* check flag when resume */ + if (sst_hsw_is_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES)) { + ret = sst_hsw_module_enable(hsw, SST_HSW_MODULE_WAVES, 0); + if (ret < 0) + return ret; + /* put parameters from buffer to dsp */ + ret = sst_hsw_launch_param_buf(hsw); + if (ret < 0) + return ret; + /* unset flag */ + sst_hsw_set_module_disabled_rtd3(hsw, SST_HSW_MODULE_WAVES); + } + pdata->pm_state = HSW_PM_STATE_D0; return ret; } diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index 07f77815a586..b05fb1c1a848 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -58,6 +58,12 @@ #define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12 #define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8 +#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24 +#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16 +#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_MASK \ + (0xf << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) +#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_MASK \ + (0x1f << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) #define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19) #define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16) @@ -79,6 +85,7 @@ #define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16 #define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12) +#define JZ_AIC_I2S_FMT_DISABLE_BIT_ICLK BIT(13) #define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4) #define JZ_AIC_I2S_FMT_MSB BIT(0) @@ -87,6 +94,13 @@ #define JZ_AIC_CLK_DIV_MASK 0xf #define I2SDIV_DV_SHIFT 8 #define I2SDIV_DV_MASK (0xf << I2SDIV_DV_SHIFT) +#define I2SDIV_IDV_SHIFT 8 +#define I2SDIV_IDV_MASK (0xf << I2SDIV_IDV_SHIFT) + +enum jz47xx_i2s_version { + JZ_I2S_JZ4740, + JZ_I2S_JZ4780, +}; struct jz4740_i2s { struct resource *mem; @@ -98,6 +112,8 @@ struct jz4740_i2s { struct snd_dmaengine_dai_dma_data playback_dma_data; struct snd_dmaengine_dai_dma_data capture_dma_data; + + enum jz47xx_i2s_version version; }; static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, @@ -267,13 +283,22 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO; else ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO; + + div_reg &= ~I2SDIV_DV_MASK; + div_reg |= (div - 1) << I2SDIV_DV_SHIFT; } else { ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK; ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET; + + if (i2s->version >= JZ_I2S_JZ4780) { + div_reg &= ~I2SDIV_IDV_MASK; + div_reg |= (div - 1) << I2SDIV_IDV_SHIFT; + } else { + div_reg &= ~I2SDIV_DV_MASK; + div_reg |= (div - 1) << I2SDIV_DV_SHIFT; + } } - div_reg &= ~I2SDIV_DV_MASK; - div_reg |= (div - 1) << I2SDIV_DV_SHIFT; jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div_reg); @@ -369,11 +394,19 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, &i2s->capture_dma_data); - conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | - (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | - JZ_AIC_CONF_OVERFLOW_PLAY_LAST | - JZ_AIC_CONF_I2S | - JZ_AIC_CONF_INTERNAL_CODEC; + if (i2s->version >= JZ_I2S_JZ4780) { + conf = (7 << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | + (8 << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | + JZ_AIC_CONF_OVERFLOW_PLAY_LAST | + JZ_AIC_CONF_I2S | + JZ_AIC_CONF_INTERNAL_CODEC; + } else { + conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | + (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | + JZ_AIC_CONF_OVERFLOW_PLAY_LAST | + JZ_AIC_CONF_I2S | + JZ_AIC_CONF_INTERNAL_CODEC; + } jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET); jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); @@ -422,13 +455,34 @@ static struct snd_soc_dai_driver jz4740_i2s_dai = { .resume = jz4740_i2s_resume, }; +static struct snd_soc_dai_driver jz4780_i2s_dai = { + .probe = jz4740_i2s_dai_probe, + .remove = jz4740_i2s_dai_remove, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = JZ4740_I2S_FMTS, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = JZ4740_I2S_FMTS, + }, + .ops = &jz4740_i2s_dai_ops, + .suspend = jz4740_i2s_suspend, + .resume = jz4740_i2s_resume, +}; + static const struct snd_soc_component_driver jz4740_i2s_component = { .name = "jz4740-i2s", }; #ifdef CONFIG_OF static const struct of_device_id jz4740_of_matches[] = { - { .compatible = "ingenic,jz4740-i2s" }, + { .compatible = "ingenic,jz4740-i2s", .data = (void *)JZ_I2S_JZ4740 }, + { .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 }, { /* sentinel */ } }; #endif @@ -438,11 +492,16 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev) struct jz4740_i2s *i2s; struct resource *mem; int ret; + const struct of_device_id *match; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); if (!i2s) return -ENOMEM; + match = of_match_device(jz4740_of_matches, &pdev->dev); + if (match) + i2s->version = (enum jz47xx_i2s_version)match->data; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2s->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(i2s->base)) @@ -460,8 +519,13 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2s); - ret = devm_snd_soc_register_component(&pdev->dev, - &jz4740_i2s_component, &jz4740_i2s_dai, 1); + if (i2s->version == JZ_I2S_JZ4780) + ret = devm_snd_soc_register_component(&pdev->dev, + &jz4740_i2s_component, &jz4780_i2s_dai, 1); + else + ret = devm_snd_soc_register_component(&pdev->dev, + &jz4740_i2s_component, &jz4740_i2s_dai, 1); + if (ret) return ret; diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index d19483081f9b..3a36d60e1785 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -643,7 +643,7 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev) } #ifdef CONFIG_OF -static struct of_device_id mvebu_audio_of_match[] = { +static const struct of_device_id mvebu_audio_of_match[] = { { .compatible = "marvell,kirkwood-audio" }, { .compatible = "marvell,dove-audio" }, { .compatible = "marvell,armada370-audio" }, diff --git a/sound/soc/nuc900/nuc900-audio.h b/sound/soc/nuc900/nuc900-audio.h index 59f7e8ed1a68..d0b725705914 100644 --- a/sound/soc/nuc900/nuc900-audio.h +++ b/sound/soc/nuc900/nuc900-audio.h @@ -100,10 +100,7 @@ struct nuc900_audio { void __iomem *mmio; spinlock_t lock; - dma_addr_t dma_addr[2]; - unsigned long buffersize[2]; unsigned long irq_num; - struct snd_pcm_substream *substream; struct resource *res; struct clk *clk; struct device *dev; diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c index b809fa909e4d..5ae5ca15b6d6 100644 --- a/sound/soc/nuc900/nuc900-pcm.c +++ b/sound/soc/nuc900/nuc900-pcm.c @@ -42,29 +42,10 @@ static const struct snd_pcm_hardware nuc900_pcm_hardware = { static int nuc900_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct nuc900_audio *nuc900_audio = runtime->private_data; - unsigned long flags; - int ret = 0; - - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - if (ret < 0) - return ret; - - spin_lock_irqsave(&nuc900_audio->lock, flags); - - nuc900_audio->substream = substream; - nuc900_audio->dma_addr[substream->stream] = runtime->dma_addr; - nuc900_audio->buffersize[substream->stream] = - params_buffer_bytes(params); - - spin_unlock_irqrestore(&nuc900_audio->lock, flags); - - return ret; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); } -static void nuc900_update_dma_register(struct snd_pcm_substream *substream, - dma_addr_t dma_addr, size_t count) +static void nuc900_update_dma_register(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct nuc900_audio *nuc900_audio = runtime->private_data; @@ -78,8 +59,8 @@ static void nuc900_update_dma_register(struct snd_pcm_substream *substream, mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH; } - AUDIO_WRITE(mmio_addr, dma_addr); - AUDIO_WRITE(mmio_len, count); + AUDIO_WRITE(mmio_addr, runtime->dma_addr); + AUDIO_WRITE(mmio_len, runtime->dma_bytes); } static void nuc900_dma_start(struct snd_pcm_substream *substream) @@ -170,9 +151,7 @@ static int nuc900_dma_prepare(struct snd_pcm_substream *substream) spin_lock_irqsave(&nuc900_audio->lock, flags); - nuc900_update_dma_register(substream, - nuc900_audio->dma_addr[substream->stream], - nuc900_audio->buffersize[substream->stream]); + nuc900_update_dma_register(substream); val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET); diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index a2cd3486ac55..6768e4f7d7d0 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -100,17 +100,19 @@ config SND_OMAP_SOC_OMAP_TWL4030 config SND_OMAP_SOC_OMAP_ABE_TWL6040 tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" - depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || COMPILE_TEST) + depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST) select SND_OMAP_SOC_DMIC select SND_OMAP_SOC_MCPDM select SND_SOC_TWL6040 select SND_SOC_DMIC + select COMMON_CLK_PALMAS if MFD_PALMAS help Say Y if you want to add support for SoC audio on OMAP boards using ABE and twl6040 codec. This driver currently supports: - SDP4430/Blaze boards - PandaBoard (4430) - PandaBoardES (4460) + - omap5-uevm (5432) config SND_OMAP_SOC_OMAP3_PANDORA tristate "SoC Audio support for OMAP3 Pandora" diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 706613077c15..16cc95fa4573 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -479,8 +479,8 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) /* Add hook switch - can be used to control the codec from userspace * even if line discipline fails */ - ret = snd_soc_jack_new(rtd->codec, "hook_switch", - SND_JACK_HEADSET, &ams_delta_hook_switch); + ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET, + &ams_delta_hook_switch, NULL, 0); if (ret) dev_warn(card->dev, "Failed to allocate resources for hook switch, " diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 5d7f9cebe041..dcb5336b5698 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -98,12 +98,11 @@ static int n810_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); - n810_ext_control(&codec->dapm); + n810_ext_control(&rtd->card->dapm); return clk_prepare_enable(sys_clkout2); } @@ -255,24 +254,6 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = { n810_get_input, n810_set_input), }; -static int n810_aic33_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - /* Not connected */ - snd_soc_dapm_nc_pin(dapm, "MONO_LOUT"); - snd_soc_dapm_nc_pin(dapm, "HPLCOM"); - snd_soc_dapm_nc_pin(dapm, "HPRCOM"); - snd_soc_dapm_nc_pin(dapm, "MIC3L"); - snd_soc_dapm_nc_pin(dapm, "MIC3R"); - snd_soc_dapm_nc_pin(dapm, "LINE1R"); - snd_soc_dapm_nc_pin(dapm, "LINE2L"); - snd_soc_dapm_nc_pin(dapm, "LINE2R"); - - return 0; -} - /* Digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link n810_dai = { .name = "TLV320AIC33", @@ -283,7 +264,6 @@ static struct snd_soc_dai_link n810_dai = { .codec_dai_name = "tlv320aic3x-hifi", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, - .init = n810_aic33_init, .ops = &n810_ops, }; @@ -300,6 +280,7 @@ static struct snd_soc_card snd_soc_n810 = { .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets), .dapm_routes = audio_map, .num_dapm_routes = ARRAY_SIZE(audio_map), + .fully_routed = true, }; static struct platform_device *n810_snd_device; diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index b9c65f1ad5a8..0843a68f277c 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -182,17 +182,17 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) /* Headset jack detection only if it is supported */ if (priv->jack_detection) { - ret = snd_soc_jack_new(codec, "Headset Jack", - SND_JACK_HEADSET, &hs_jack); + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET, &hs_jack, + hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret) return ret; - ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET); } - return ret; + return 0; } static const struct snd_soc_dapm_route dmic_audio_map[] = { diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index f7eb42aa3f38..4775da4c4db5 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -142,8 +142,6 @@ static int hdmi_dai_hw_params(struct snd_pcm_substream *substream, iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; - iec->status[0] |= IEC958_AES1_PRO_MODE_NOTID; - iec->status[1] = IEC958_AES1_CON_GENERAL; iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC; diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 1343ecbf0bd5..6bb623a2a4df 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -39,7 +39,7 @@ #define pcm_omap1510() 0 #endif -static const struct snd_pcm_hardware omap_pcm_hardware = { +static struct snd_pcm_hardware omap_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | @@ -53,6 +53,24 @@ static const struct snd_pcm_hardware omap_pcm_hardware = { .buffer_bytes_max = 128 * 1024, }; +/* sDMA supports only 1, 2, and 4 byte transfer elements. */ +static void omap_pcm_limit_supported_formats(void) +{ + int i; + + for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { + switch (snd_pcm_format_physical_width(i)) { + case 8: + case 16: + case 32: + omap_pcm_hardware.formats |= (1LL << i); + break; + default: + break; + } + } +} + /* this may get called several times by oss emulation */ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -235,6 +253,7 @@ static struct snd_soc_platform_driver omap_soc_platform = { int omap_pcm_platform_register(struct device *dev) { + omap_pcm_limit_supported_formats(); return devm_snd_soc_register_platform(dev, &omap_soc_platform); } EXPORT_SYMBOL_GPL(omap_pcm_platform_register); diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c index fb1f6bb87cd4..3673ada43bfb 100644 --- a/sound/soc/omap/omap-twl4030.c +++ b/sound/soc/omap/omap-twl4030.c @@ -170,14 +170,10 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) if (priv->jack_detect > 0) { hs_jack_gpios[0].gpio = priv->jack_detect; - ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, - &priv->hs_jack); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&priv->hs_jack, - ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET, &priv->hs_jack, + hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret) return ret; diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 7f299357c2d2..c2ddf0fbfa28 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -311,9 +311,9 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) } /* AV jack detection */ - err = snd_soc_jack_new(codec, "AV Jack", - SND_JACK_HEADSET | SND_JACK_VIDEOOUT, - &rx51_av_jack); + err = snd_soc_card_jack_new(rtd->card, "AV Jack", + SND_JACK_HEADSET | SND_JACK_VIDEOOUT, + &rx51_av_jack, NULL, 0); if (err) { dev_err(card->dev, "Failed to add AV Jack\n"); return err; diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c index 73eb5ddf9753..9f8be7cd567e 100644 --- a/sound/soc/pxa/hx4700.c +++ b/sound/soc/pxa/hx4700.c @@ -126,17 +126,12 @@ static const struct snd_soc_dapm_route hx4700_audio_map[] = { */ static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; int err; /* Jack detection API stuff */ - err = snd_soc_jack_new(codec, "Headphone Jack", - SND_JACK_HEADPHONE, &hs_jack); - if (err) - return err; - - err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pin), - hs_jack_pin); + err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &hs_jack, hs_jack_pin, + ARRAY_SIZE(hs_jack_pin)); if (err) return err; diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 910336c5ebeb..c20bbc042425 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -75,17 +75,12 @@ static struct snd_soc_card palm27x_asoc; static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; int err; /* Jack detection API stuff */ - err = snd_soc_jack_new(codec, "Headphone Jack", - SND_JACK_HEADPHONE, &hs_jack); - if (err) - return err; - - err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); + err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (err) return err; diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index 5001dbb9b257..1753c7d9e760 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -78,15 +78,12 @@ static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_codec *codec = rtd->codec; /* Headset jack detection */ - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE - | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, - &hs_jack); - snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); - snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE, - &mic_jack); - snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), - mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, + &hs_jack, hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); + snd_soc_card_jack_new(rtd->card, "Microphone Jack", SND_JACK_MICROPHONE, + &mic_jack, mic_jack_pins, + ARRAY_SIZE(mic_jack_pins)); /* headphone, microphone detection & headset short detection */ pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE, diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index 76ccb172d0a7..bcbfbe8303f7 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -143,13 +143,9 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_disable_pin(dapm, "MONO1"); /* Jack detection API stuff */ - ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, - &hs_jack); - if (ret) - goto err; - - ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET, + &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret) goto err; diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig new file mode 100644 index 000000000000..5f58e4f1bca9 --- /dev/null +++ b/sound/soc/qcom/Kconfig @@ -0,0 +1,25 @@ +config SND_SOC_QCOM + tristate "ASoC support for QCOM platforms" + help + Say Y or M if you want to add support to use audio devices + in Qualcomm Technologies SOC-based platforms. + +config SND_SOC_LPASS_CPU + tristate + depends on SND_SOC_QCOM + select REGMAP_MMIO + +config SND_SOC_LPASS_PLATFORM + tristate + depends on SND_SOC_QCOM + select REGMAP_MMIO + +config SND_SOC_STORM + tristate "ASoC I2S support for Storm boards" + depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST + select SND_SOC_LPASS_CPU + select SND_SOC_LPASS_PLATFORM + 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. diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile new file mode 100644 index 000000000000..c5ce96c761c4 --- /dev/null +++ b/sound/soc/qcom/Makefile @@ -0,0 +1,11 @@ +# Platform +snd-soc-lpass-cpu-objs := lpass-cpu.o +snd-soc-lpass-platform-objs := lpass-platform.o + +obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o +obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o + +# Machine +snd-soc-storm-objs := storm.o + +obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c new file mode 100644 index 000000000000..6698d058de29 --- /dev/null +++ b/sound/soc/qcom/lpass-cpu.c @@ -0,0 +1,491 @@ +/* + * 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-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS + */ + +#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/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.h" + +static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = clk_set_rate(drvdata->mi2s_osr_clk, freq); + if (ret) + dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n", + __func__, freq, ret); + + return ret; +} + +static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + 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; + } + + ret = clk_prepare_enable(drvdata->mi2s_bit_clk); + if (ret) { + dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n", + __func__, ret); + clk_disable_unprepare(drvdata->mi2s_osr_clk); + return ret; + } + + return 0; +} + +static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(drvdata->mi2s_bit_clk); + clk_disable_unprepare(drvdata->mi2s_osr_clk); +} + +static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + snd_pcm_format_t format = params_format(params); + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + unsigned int regval; + int bitwidth, ret; + + bitwidth = snd_pcm_format_width(format); + if (bitwidth < 0) { + dev_err(dai->dev, "%s() invalid bit width given: %d\n", + __func__, bitwidth); + return bitwidth; + } + + regval = LPAIF_I2SCTL_LOOPBACK_DISABLE | + LPAIF_I2SCTL_WSSRC_INTERNAL; + + switch (bitwidth) { + case 16: + regval |= LPAIF_I2SCTL_BITWIDTH_16; + break; + case 24: + regval |= LPAIF_I2SCTL_BITWIDTH_24; + break; + case 32: + regval |= LPAIF_I2SCTL_BITWIDTH_32; + break; + default: + dev_err(dai->dev, "%s() invalid bitwidth given: %d\n", + __func__, bitwidth); + return -EINVAL; + } + + switch (channels) { + case 1: + regval |= LPAIF_I2SCTL_SPKMODE_SD0; + regval |= LPAIF_I2SCTL_SPKMONO_MONO; + break; + case 2: + regval |= LPAIF_I2SCTL_SPKMODE_SD0; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 4: + regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 6: + regval |= LPAIF_I2SCTL_SPKMODE_6CH; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 8: + regval |= LPAIF_I2SCTL_SPKMODE_8CH; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + default: + dev_err(dai->dev, "%s() invalid channels given: %u\n", + __func__, channels); + return -EINVAL; + } + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 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); + if (ret) { + dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n", + __func__, rate * bitwidth * 2, ret); + return ret; + } + + return 0; +} + +static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); + if (ret) + dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", + __func__, ret); + + return ret; +} + +static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), + LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); + if (ret) + dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", + __func__, ret); + + return ret; +} + +static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + 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_SPKEN_MASK, + LPAIF_I2SCTL_SPKEN_ENABLE); + if (ret) + dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", + __func__, ret); + break; + case SNDRV_PCM_TRIGGER_STOP: + 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_SPKEN_MASK, + LPAIF_I2SCTL_SPKEN_DISABLE); + if (ret) + dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", + __func__, ret); + break; + } + + return ret; +} + +static struct snd_soc_dai_ops lpass_cpu_dai_ops = { + .set_sysclk = lpass_cpu_daiops_set_sysclk, + .startup = lpass_cpu_daiops_startup, + .shutdown = lpass_cpu_daiops_shutdown, + .hw_params = lpass_cpu_daiops_hw_params, + .hw_free = lpass_cpu_daiops_hw_free, + .prepare = lpass_cpu_daiops_prepare, + .trigger = lpass_cpu_daiops_trigger, +}; + +static int 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); + 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, +}; + +static const struct snd_soc_component_driver lpass_cpu_comp_driver = { + .name = "lpass-cpu", +}; + +static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) + if (reg == LPAIF_I2SCTL_REG(i)) + return true; + + for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { + if (reg == LPAIF_IRQEN_REG(i)) + return true; + if (reg == LPAIF_IRQCLEAR_REG(i)) + return true; + } + + for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { + if (reg == LPAIF_RDMACTL_REG(i)) + return true; + if (reg == LPAIF_RDMABASE_REG(i)) + return true; + if (reg == LPAIF_RDMABUFF_REG(i)) + return true; + if (reg == LPAIF_RDMAPER_REG(i)) + return true; + } + + return false; +} + +static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) + if (reg == LPAIF_I2SCTL_REG(i)) + return true; + + for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { + if (reg == LPAIF_IRQEN_REG(i)) + return true; + if (reg == LPAIF_IRQSTAT_REG(i)) + return true; + } + + for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { + if (reg == LPAIF_RDMACTL_REG(i)) + return true; + if (reg == LPAIF_RDMABASE_REG(i)) + return true; + if (reg == LPAIF_RDMABUFF_REG(i)) + return true; + if (reg == LPAIF_RDMACURR_REG(i)) + return true; + if (reg == LPAIF_RDMAPER_REG(i)) + return true; + } + + return false; +} + +static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) + if (reg == LPAIF_IRQSTAT_REG(i)) + return true; + + for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) + if (reg == LPAIF_RDMACURR_REG(i)) + return true; + + return false; +} + +static const 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) +{ + struct lpass_data *drvdata; + struct device_node *dsp_of_node; + struct resource *res; + int ret; + + dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0); + if (dsp_of_node) { + dev_err(&pdev->dev, "%s() DSP exists and holds audio resources\n", + __func__); + return -EBUSY; + } + + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data), + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + platform_set_drvdata(pdev, drvdata); + + 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)) { + dev_err(&pdev->dev, "%s() error mapping reg resource: %ld\n", + __func__, + PTR_ERR((void const __force *)drvdata->lpaif)); + return PTR_ERR((void const __force *)drvdata->lpaif); + } + + drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, + &lpass_cpu_regmap_config); + if (IS_ERR(drvdata->lpaif_map)) { + dev_err(&pdev->dev, "%s() error initializing regmap: %ld\n", + __func__, PTR_ERR(drvdata->lpaif_map)); + 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); + } + + drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk"); + if (IS_ERR(drvdata->ahbix_clk)) { + dev_err(&pdev->dev, "%s() error getting ahbix-clk: %ld\n", + __func__, PTR_ERR(drvdata->ahbix_clk)); + return PTR_ERR(drvdata->ahbix_clk); + } + + ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); + if (ret) { + dev_err(&pdev->dev, "%s() error setting rate on ahbix_clk: %d\n", + __func__, ret); + return ret; + } + dev_dbg(&pdev->dev, "%s() set ahbix_clk rate to %lu\n", __func__, + clk_get_rate(drvdata->ahbix_clk)); + + ret = clk_prepare_enable(drvdata->ahbix_clk); + if (ret) { + dev_err(&pdev->dev, "%s() error enabling ahbix_clk: %d\n", + __func__, ret); + return ret; + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1); + if (ret) { + dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n", + __func__, ret); + goto err_clk; + } + + ret = asoc_qcom_lpass_platform_register(pdev); + if (ret) { + dev_err(&pdev->dev, "%s() error registering platform driver: %d\n", + __func__, ret); + goto err_clk; + } + + return 0; + +err_clk: + clk_disable_unprepare(drvdata->ahbix_clk); + return ret; +} + +static int lpass_cpu_platform_remove(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(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"); diff --git a/sound/soc/qcom/lpass-lpaif-ipq806x.h b/sound/soc/qcom/lpass-lpaif-ipq806x.h new file mode 100644 index 000000000000..dc423b888842 --- /dev/null +++ b/sound/soc/qcom/lpass-lpaif-ipq806x.h @@ -0,0 +1,172 @@ +/* + * 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-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 + +/* 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_LOOPBACK_MASK 0x8000 +#define LPAIF_I2SCTL_LOOPBACK_SHIFT 15 +#define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT) +#define LPAIF_I2SCTL_LOOPBACK_ENABLE (1 << LPAIF_I2SCTL_LOOPBACK_SHIFT) + +#define LPAIF_I2SCTL_SPKEN_MASK 0x4000 +#define LPAIF_I2SCTL_SPKEN_SHIFT 14 +#define LPAIF_I2SCTL_SPKEN_DISABLE (0 << LPAIF_I2SCTL_SPKEN_SHIFT) +#define LPAIF_I2SCTL_SPKEN_ENABLE (1 << LPAIF_I2SCTL_SPKEN_SHIFT) + +#define LPAIF_I2SCTL_SPKMODE_MASK 0x3C00 +#define LPAIF_I2SCTL_SPKMODE_SHIFT 10 +#define LPAIF_I2SCTL_SPKMODE_NONE (0 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_SD0 (1 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_SD1 (2 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_SD2 (3 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_SD3 (4 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_QUAD01 (5 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_QUAD23 (6 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_6CH (7 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_8CH (8 << LPAIF_I2SCTL_SPKMODE_SHIFT) + +#define LPAIF_I2SCTL_SPKMONO_MASK 0x0200 +#define LPAIF_I2SCTL_SPKMONO_SHIFT 9 +#define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT) +#define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT) + +#define LPAIF_I2SCTL_WSSRC_MASK 0x0004 +#define LPAIF_I2SCTL_WSSRC_SHIFT 2 +#define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT) +#define LPAIF_I2SCTL_WSSRC_EXTERNAL (1 << LPAIF_I2SCTL_WSSRC_SHIFT) + +#define LPAIF_I2SCTL_BITWIDTH_MASK 0x0003 +#define LPAIF_I2SCTL_BITWIDTH_SHIFT 0 +#define LPAIF_I2SCTL_BITWIDTH_16 (0 << LPAIF_I2SCTL_BITWIDTH_SHIFT) +#define LPAIF_I2SCTL_BITWIDTH_24 (1 << LPAIF_I2SCTL_BITWIDTH_SHIFT) +#define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT) + +/* LPAIF IRQ */ + +#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, + + 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_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, + + LPAIF_RDMA_CHAN_MAX = 4, + LPAIF_RDMA_CHAN_NUM = 5, +}; + +#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_BURSTEN_MASK 0x800 +#define LPAIF_RDMACTL_BURSTEN_SHIFT 11 +#define LPAIF_RDMACTL_BURSTEN_SINGLE (0 << LPAIF_RDMACTL_BURSTEN_SHIFT) +#define LPAIF_RDMACTL_BURSTEN_INCR4 (1 << LPAIF_RDMACTL_BURSTEN_SHIFT) + +#define LPAIF_RDMACTL_WPSCNT_MASK 0x700 +#define LPAIF_RDMACTL_WPSCNT_SHIFT 8 +#define LPAIF_RDMACTL_WPSCNT_ONE (0 << LPAIF_RDMACTL_WPSCNT_SHIFT) +#define LPAIF_RDMACTL_WPSCNT_TWO (1 << LPAIF_RDMACTL_WPSCNT_SHIFT) +#define LPAIF_RDMACTL_WPSCNT_THREE (2 << LPAIF_RDMACTL_WPSCNT_SHIFT) +#define LPAIF_RDMACTL_WPSCNT_FOUR (3 << LPAIF_RDMACTL_WPSCNT_SHIFT) +#define LPAIF_RDMACTL_WPSCNT_SIX (5 << LPAIF_RDMACTL_WPSCNT_SHIFT) +#define LPAIF_RDMACTL_WPSCNT_EIGHT (7 << LPAIF_RDMACTL_WPSCNT_SHIFT) + +#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 +#define LPAIF_RDMACTL_FIFOWM_1 (0 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_2 (1 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_3 (2 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_4 (3 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_5 (4 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_6 (5 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_7 (6 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_8 (7 << LPAIF_RDMACTL_FIFOWM_SHIFT) + +#define LPAIF_RDMACTL_ENABLE_MASK 0x1 +#define LPAIF_RDMACTL_ENABLE_SHIFT 0 +#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT) +#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT) + +#endif /* __LPASS_LPAIF_H__ */ diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c new file mode 100644 index 000000000000..2fa6280dfb23 --- /dev/null +++ b/sound/soc/qcom/lpass-platform.c @@ -0,0 +1,526 @@ +/* + * 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-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.h" + +#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024) +#define LPASS_PLATFORM_PERIODS 2 + +static struct snd_pcm_hardware lpass_platform_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = LPASS_PLATFORM_BUFFER_SIZE, + .period_bytes_max = LPASS_PLATFORM_BUFFER_SIZE / + LPASS_PLATFORM_PERIODS, + .period_bytes_min = LPASS_PLATFORM_BUFFER_SIZE / + LPASS_PLATFORM_PERIODS, + .periods_min = LPASS_PLATFORM_PERIODS, + .periods_max = LPASS_PLATFORM_PERIODS, + .fifo_size = 0, +}; + +static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + int ret; + + snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware); + + runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max; + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n", + __func__, ret); + return -EINVAL; + } + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +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_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + snd_pcm_format_t format = params_format(params); + unsigned int channels = params_channels(params); + unsigned int regval; + int bitwidth; + int ret; + + bitwidth = snd_pcm_format_width(format); + if (bitwidth < 0) { + dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n", + __func__, bitwidth); + return bitwidth; + } + + regval = LPAIF_RDMACTL_BURSTEN_INCR4 | + LPAIF_RDMACTL_AUDINTF_MI2S | + LPAIF_RDMACTL_FIFOWM_8; + + switch (bitwidth) { + case 16: + switch (channels) { + case 1: + case 2: + regval |= LPAIF_RDMACTL_WPSCNT_ONE; + break; + case 4: + regval |= LPAIF_RDMACTL_WPSCNT_TWO; + break; + case 6: + regval |= LPAIF_RDMACTL_WPSCNT_THREE; + break; + case 8: + regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + break; + default: + dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", + __func__, bitwidth, channels); + return -EINVAL; + } + break; + case 24: + case 32: + switch (channels) { + case 1: + regval |= LPAIF_RDMACTL_WPSCNT_ONE; + break; + case 2: + regval |= LPAIF_RDMACTL_WPSCNT_TWO; + break; + case 4: + regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + break; + case 6: + regval |= LPAIF_RDMACTL_WPSCNT_SIX; + break; + case 8: + regval |= LPAIF_RDMACTL_WPSCNT_EIGHT; + break; + default: + dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", + __func__, bitwidth, channels); + return -EINVAL; + } + break; + default: + dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", + __func__, bitwidth, channels); + return -EINVAL; + } + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + int ret; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0); + if (ret) + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + + return ret; +} + +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_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + int ret; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), + runtime->dma_addr); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S), + (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S), + (snd_pcm_lib_period_bytes(substream) >> 2) - 1); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +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_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + 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)); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", + __func__, ret); + return ret; + } + + 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)); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMACTL_ENABLE_MASK, + LPAIF_RDMACTL_ENABLE_ON); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + 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_ENABLE_MASK, + LPAIF_RDMACTL_ENABLE_OFF); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", + __func__, ret); + return ret; + } + break; + } + + return 0; +} + +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_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + unsigned int base_addr, curr_addr; + int ret; + + ret = regmap_read(drvdata->lpaif_map, + LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr); + if (ret) { + dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_read(drvdata->lpaif_map, + LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr); + if (ret) { + dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", + __func__, ret); + return ret; + } + + return bytes_to_frames(substream->runtime, curr_addr - base_addr); +} + +static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_coherent(substream->pcm->card->dev, vma, + runtime->dma_area, runtime->dma_addr, + runtime->dma_bytes); +} + +static struct snd_pcm_ops lpass_platform_pcm_ops = { + .open = lpass_platform_pcmops_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = lpass_platform_pcmops_hw_params, + .hw_free = lpass_platform_pcmops_hw_free, + .prepare = lpass_platform_pcmops_prepare, + .trigger = lpass_platform_pcmops_trigger, + .pointer = lpass_platform_pcmops_pointer, + .mmap = lpass_platform_pcmops_mmap, +}; + +static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) +{ + 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; + 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)) { + rv = regmap_write(drvdata->lpaif_map, + LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)); + if (rv) { + dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", + __func__, rv); + return IRQ_NONE; + } + snd_pcm_period_elapsed(substream); + ret = IRQ_HANDLED; + } + + if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) { + rv = regmap_write(drvdata->lpaif_map, + LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)); + if (rv) { + dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", + __func__, rv); + return IRQ_NONE; + } + dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + ret = IRQ_HANDLED; + } + + if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) { + rv = regmap_write(drvdata->lpaif_map, + LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)); + if (rv) { + dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", + __func__, rv); + return IRQ_NONE; + } + dev_err(soc_runtime->dev, "%s() bus access error\n", __func__); + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, + struct snd_soc_pcm_runtime *soc_runtime) +{ + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = soc_runtime->dev; + buf->private_data = NULL; + buf->area = dma_alloc_coherent(soc_runtime->dev, size, &buf->addr, + GFP_KERNEL); + if (!buf->area) { + dev_err(soc_runtime->dev, "%s: Could not allocate DMA buffer\n", + __func__); + return -ENOMEM; + } + buf->bytes = size; + + return 0; +} + +static void lpass_platform_free_buffer(struct snd_pcm_substream *substream, + struct snd_soc_pcm_runtime *soc_runtime) +{ + struct snd_dma_buffer *buf = &substream->dma_buffer; + + if (buf->area) { + dma_free_coherent(soc_runtime->dev, buf->bytes, buf->area, + buf->addr); + } + buf->area = NULL; +} + +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 lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + int ret; + + soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32); + soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask; + + ret = lpass_platform_alloc_buffer(substream, 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); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + + return 0; + +err_buf: + lpass_platform_free_buffer(substream, soc_runtime); + return ret; +} + +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; + + lpass_platform_free_buffer(substream, soc_runtime); +} + +static struct snd_soc_platform_driver lpass_platform_driver = { + .pcm_new = lpass_platform_pcm_new, + .pcm_free = lpass_platform_pcm_free, + .ops = &lpass_platform_pcm_ops, +}; + +int asoc_qcom_lpass_platform_register(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + + drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif"); + if (drvdata->lpaif_irq < 0) { + dev_err(&pdev->dev, "%s() error getting irq handle: %d\n", + __func__, drvdata->lpaif_irq); + return -ENODEV; + } + + return devm_snd_soc_register_platform(&pdev->dev, + &lpass_platform_driver); +} +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register); + +MODULE_DESCRIPTION("QTi LPASS Platform Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h new file mode 100644 index 000000000000..5c99b3dace86 --- /dev/null +++ b/sound/soc/qcom/lpass.h @@ -0,0 +1,51 @@ +/* + * 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.h - Definitions for the QTi LPASS + */ + +#ifndef __LPASS_H__ +#define __LPASS_H__ + +#include <linux/clk.h> +#include <linux/compiler.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 + +/* Both the CPU DAI and platform drivers will access this data */ +struct lpass_data { + + /* AHB-I/X bus clocks inside the low-power audio subsystem (LPASS) */ + struct clk *ahbix_clk; + + /* MI2S system clock */ + struct clk *mi2s_osr_clk; + + /* MI2S bit clock (derived from system clock by a divider */ + struct clk *mi2s_bit_clk; + + /* low-power audio interface (LPAIF) registers */ + void __iomem *lpaif; + + /* regmap backed by the low-power audio interface (LPAIF) registers */ + struct regmap *lpaif_map; + + /* interrupts from the low-power audio interface (LPAIF) */ + int lpaif_irq; +}; + +/* register the platform driver from the CPU DAI driver */ +int asoc_qcom_lpass_platform_register(struct platform_device *); + +#endif /* __LPASS_H__ */ diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c new file mode 100644 index 000000000000..b8bd296190ad --- /dev/null +++ b/sound/soc/qcom/storm.c @@ -0,0 +1,162 @@ +/* + * 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. + * + * storm.c -- ALSA SoC machine driver for QTi ipq806x-based Storm board + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define STORM_SYSCLK_MULT 4 + +static int storm_ops_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct snd_soc_card *card = soc_runtime->card; + snd_pcm_format_t format = params_format(params); + unsigned int rate = params_rate(params); + unsigned int sysclk_freq; + int bitwidth, ret; + + bitwidth = snd_pcm_format_width(format); + if (bitwidth < 0) { + dev_err(card->dev, "%s() invalid bit width given: %d\n", + __func__, bitwidth); + return bitwidth; + } + + /* + * as the CPU DAI is the I2S bus master and no system clock is needed by + * the MAX98357a DAC, simply set the system clock to be a constant + * multiple of the bit clock for the clock divider + */ + sysclk_freq = rate * bitwidth * 2 * STORM_SYSCLK_MULT; + + ret = snd_soc_dai_set_sysclk(soc_runtime->cpu_dai, 0, sysclk_freq, 0); + if (ret) { + dev_err(card->dev, "%s() error setting sysclk to %u: %d\n", + __func__, sysclk_freq, ret); + return ret; + } + + return 0; +} + +static struct snd_soc_ops storm_soc_ops = { + .hw_params = storm_ops_hw_params, +}; + +static struct snd_soc_dai_link storm_dai_link = { + .name = "Primary", + .stream_name = "Primary", + .codec_dai_name = "HiFi", + .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; + struct device_node *np = card->dev->of_node; + + dai_link->cpu_of_node = of_parse_phandle(np, "cpu", 0); + if (!dai_link->cpu_of_node) { + dev_err(card->dev, "%s() error getting cpu phandle\n", + __func__); + return -EINVAL; + } + dai_link->platform_of_node = dai_link->cpu_of_node; + + dai_link->codec_of_node = of_parse_phandle(np, "codec", 0); + if (!dai_link->codec_of_node) { + dev_err(card->dev, "%s() error getting codec phandle\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int storm_platform_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &storm_soc_card; + int ret; + + if (card->dev) { + dev_err(&pdev->dev, "%s() error, existing soundcard\n", + __func__); + return -ENODEV; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "%s() error parsing card name: %d\n", + __func__, ret); + return ret; + } + + card->dai_link = &storm_dai_link; + card->num_links = 1; + + ret = storm_parse_of(card); + if (ret) { + dev_err(&pdev->dev, "%s() error resolving dai links: %d\n", + __func__, ret); + return ret; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + card->dev = NULL; + return ret; + } else if (ret) { + dev_err(&pdev->dev, "%s() error registering soundcard: %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id storm_device_id[] = { + { .compatible = "google,storm-audio" }, + {}, +}; +MODULE_DEVICE_TABLE(of, storm_device_id); +#endif + +static struct platform_driver storm_platform_driver = { + .driver = { + .name = "storm-audio", + .of_match_table = + of_match_ptr(storm_device_id), + }, + .probe = storm_platform_probe, +}; +module_platform_driver(storm_platform_driver); + +MODULE_DESCRIPTION("QTi IPQ806x-based Storm Machine Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index 59b044255b78..c72e9fb26658 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -162,13 +162,8 @@ static struct platform_device *s3c24xx_snd_device; static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, - &hp_jack); - - snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), - hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, + &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), hp_jack_gpios); diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 141519c21e21..31a820eb0ac3 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -260,12 +260,12 @@ static int littlemill_late_probe(struct snd_soc_card *card) if (ret < 0) return ret; - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_HEADSET | SND_JACK_MECHANICAL | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3 | - SND_JACK_BTN_4 | SND_JACK_BTN_5, - &littlemill_headset); + ret = snd_soc_card_jack_new(card, "Headset", + SND_JACK_HEADSET | SND_JACK_MECHANICAL | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4 | SND_JACK_BTN_5, + &littlemill_headset, NULL, 0); if (ret) return ret; diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 243dea7ba38f..5f156093101e 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -56,16 +56,10 @@ static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_LINEOUT | SND_JACK_HEADSET | - SND_JACK_BTN_0, - &lowland_headset); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&lowland_headset, - ARRAY_SIZE(lowland_headset_pins), - lowland_headset_pins); + ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT | + SND_JACK_HEADSET | SND_JACK_BTN_0, + &lowland_headset, lowland_headset_pins, + ARRAY_SIZE(lowland_headset_pins)); if (ret) return ret; diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 873f2cb4bebe..35e37c457f1f 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -211,13 +211,8 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream, static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, - &hp_jack); - - snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), - hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, + &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), hp_jack_gpios); diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index 8291d2a5f152..dfbe2db1c407 100644 --- a/sound/soc/samsung/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -151,13 +151,10 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); /* Headphone jack detection */ - err = snd_soc_jack_new(codec, "Headphone Jack", - SND_JACK_HEADPHONE, &smartq_jack); - if (err) - return err; - - err = snd_soc_jack_add_pins(&smartq_jack, ARRAY_SIZE(smartq_jack_pins), - smartq_jack_pins); + err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &smartq_jack, + smartq_jack_pins, + ARRAY_SIZE(smartq_jack_pins)); if (err) return err; diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 5ec7c52282f2..2dcb988bdff2 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -153,16 +153,10 @@ static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd) pr_err("Failed to request HP_SEL GPIO: %d\n", ret); gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity); - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_LINEOUT | SND_JACK_HEADSET | - SND_JACK_BTN_0, - &speyside_headset); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&speyside_headset, - ARRAY_SIZE(speyside_headset_pins), - speyside_headset_pins); + ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT | + SND_JACK_HEADSET | SND_JACK_BTN_0, + &speyside_headset, speyside_headset_pins, + ARRAY_SIZE(speyside_headset_pins)); if (ret) return ret; diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index 9c80506527c4..85ccfb7188cb 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -179,15 +179,10 @@ static int tobermory_late_probe(struct snd_soc_card *card) if (ret < 0) return ret; - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0, - &tobermory_headset); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&tobermory_headset, - ARRAY_SIZE(tobermory_headset_pins), - tobermory_headset_pins); + ret = snd_soc_card_jack_new(card, "Headset", SND_JACK_HEADSET | + SND_JACK_BTN_0, &tobermory_headset, + tobermory_headset_pins, + ARRAY_SIZE(tobermory_headset_pins)); if (ret) return ret; diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 80245b6eebd6..07114b0b0dc1 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -36,11 +36,17 @@ config SND_SOC_SH4_SIU config SND_SOC_RCAR tristate "R-Car series SRU/SCU/SSIU/SSI support" + depends on DMA_OF select SND_SIMPLE_CARD select REGMAP_MMIO help This option enables R-Car SUR/SCU/SSIU/SSI sound support +config SND_SOC_RSRC_CARD + tristate "Renesas Sampling Rate Convert Sound Card" + help + This option enables simple sound if you need sampling rate convert + ## ## Boards ## diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index b87b22e88e43..0c2af21b0b82 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1876,7 +1876,40 @@ static void fsi_handler_init(struct fsi_priv *fsi, } } -static struct of_device_id fsi_of_match[]; +static const struct fsi_core fsi1_core = { + .ver = 1, + + /* Interrupt */ + .int_st = INT_ST, + .iemsk = IEMSK, + .imsk = IMSK, +}; + +static const struct fsi_core fsi2_core = { + .ver = 2, + + /* Interrupt */ + .int_st = CPU_INT_ST, + .iemsk = CPU_IEMSK, + .imsk = CPU_IMSK, + .a_mclk = A_MST_CTLR, + .b_mclk = B_MST_CTLR, +}; + +static const struct of_device_id fsi_of_match[] = { + { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, + { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, + {}, +}; +MODULE_DEVICE_TABLE(of, fsi_of_match); + +static const struct platform_device_id fsi_id_table[] = { + { "sh_fsi", (kernel_ulong_t)&fsi1_core }, + { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, + {}, +}; +MODULE_DEVICE_TABLE(platform, fsi_id_table); + static int fsi_probe(struct platform_device *pdev) { struct fsi_master *master; @@ -2072,40 +2105,6 @@ static struct dev_pm_ops fsi_pm_ops = { .resume = fsi_resume, }; -static struct fsi_core fsi1_core = { - .ver = 1, - - /* Interrupt */ - .int_st = INT_ST, - .iemsk = IEMSK, - .imsk = IMSK, -}; - -static struct fsi_core fsi2_core = { - .ver = 2, - - /* Interrupt */ - .int_st = CPU_INT_ST, - .iemsk = CPU_IEMSK, - .imsk = CPU_IMSK, - .a_mclk = A_MST_CTLR, - .b_mclk = B_MST_CTLR, -}; - -static struct of_device_id fsi_of_match[] = { - { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, - { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, - {}, -}; -MODULE_DEVICE_TABLE(of, fsi_of_match); - -static struct platform_device_id fsi_id_table[] = { - { "sh_fsi", (kernel_ulong_t)&fsi1_core }, - { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, - {}, -}; -MODULE_DEVICE_TABLE(platform, fsi_id_table); - static struct platform_driver fsi_driver = { .driver = { .name = "fsi-pcm-audio", @@ -2119,7 +2118,7 @@ static struct platform_driver fsi_driver = { module_platform_driver(fsi_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SuperH onchip FSI audio driver"); MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); MODULE_ALIAS("platform:fsi-pcm-audio"); diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 9ac536429800..f1b445173fba 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,5 @@ -snd-soc-rcar-objs := core.o gen.o src.o adg.o ssi.o dvc.o -obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
\ No newline at end of file +snd-soc-rcar-objs := core.o gen.o dma.o src.o adg.o ssi.o dvc.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o + +snd-soc-rsrc-card-objs := rsrc-card.o +obj-$(CONFIG_SND_SOC_RSRC_CARD) += snd-soc-rsrc-card.o diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 7ac35c9d1cb8..fefc881dbac2 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -183,6 +183,8 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, rsnd_mod_bset(mod, DIV_EN, en, en); + dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); + return 0; } @@ -432,7 +434,5 @@ int rsnd_adg_probe(struct platform_device *pdev, priv->adg = adg; - dev_dbg(dev, "adg probed\n"); - return 0; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 110577c52317..9f48d75fa992 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -94,21 +94,20 @@ * */ #include <linux/pm_runtime.h> -#include <linux/shdma-base.h> #include "rsnd.h" #define RSND_RATES SNDRV_PCM_RATE_8000_96000 #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) -static struct rsnd_of_data rsnd_of_data_gen1 = { +static const struct rsnd_of_data rsnd_of_data_gen1 = { .flags = RSND_GEN1, }; -static struct rsnd_of_data rsnd_of_data_gen2 = { +static const struct rsnd_of_data rsnd_of_data_gen2 = { .flags = RSND_GEN2, }; -static struct of_device_id rsnd_of_match[] = { +static const struct of_device_id rsnd_of_match[] = { { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, {}, @@ -138,249 +137,37 @@ char *rsnd_mod_name(struct rsnd_mod *mod) return mod->ops->name; } -char *rsnd_mod_dma_name(struct rsnd_mod *mod) +struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod) { - if (!mod || !mod->ops) - return "unknown"; - - if (!mod->ops->dma_name) - return mod->ops->name; + if (!mod || !mod->ops || !mod->ops->dma_req) + return NULL; - return mod->ops->dma_name(mod); + return mod->ops->dma_req(mod); } -void rsnd_mod_init(struct rsnd_mod *mod, +int rsnd_mod_init(struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, enum rsnd_mod_type type, int id) { + int ret = clk_prepare(clk); + + if (ret) + return ret; + mod->id = id; mod->ops = ops; mod->type = type; mod->clk = clk; -} - -/* - * rsnd_dma functions - */ -void rsnd_dma_stop(struct rsnd_dma *dma) -{ - dmaengine_terminate_all(dma->chan); -} - -static void rsnd_dma_complete(void *data) -{ - 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); - - /* - * Renesas sound Gen1 needs 1 DMAC, - * Gen2 needs 2 DMAC. - * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. - * But, Audio-DMAC-peri-peri doesn't have interrupt, - * and this driver is assuming that here. - * - * If Audio-DMAC-peri-peri has interrpt, - * rsnd_dai_pointer_update() will be called twice, - * ant it will breaks io->byte_pos - */ - - rsnd_dai_pointer_update(io, io->byte_per_period); -} - -void rsnd_dma_start(struct rsnd_dma *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; - - desc = dmaengine_prep_dma_cyclic(dma->chan, - (dma->addr) ? dma->addr : - substream->runtime->dma_addr, - snd_pcm_lib_buffer_bytes(substream), - snd_pcm_lib_period_bytes(substream), - dma->dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - - if (!desc) { - dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); - return; - } - - desc->callback = rsnd_dma_complete; - desc->callback_param = dma; - - if (dmaengine_submit(desc) < 0) { - dev_err(dev, "dmaengine_submit() fail\n"); - return; - } - - dma_async_issue_pending(dma->chan); -} - -int rsnd_dma_available(struct rsnd_dma *dma) -{ - return !!dma->chan; -} - -#define DMA_NAME_SIZE 16 -#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ -static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod) -{ - if (mod) - return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d", - rsnd_mod_dma_name(mod), rsnd_mod_id(mod)); - else - return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem"); - -} - -static void rsnd_dma_of_name(struct rsnd_mod *mod_from, - struct rsnd_mod *mod_to, - char *dma_name) -{ - int index = 0; - - index = _rsnd_dma_of_name(dma_name + index, mod_from); - *(dma_name + index++) = '_'; - index = _rsnd_dma_of_name(dma_name + index, mod_to); -} - -static void rsnd_dma_of_path(struct rsnd_dma *dma, - 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); - struct rsnd_mod *mod[MOD_MAX]; - int i, index; - - for (i = 0; i < MOD_MAX; i++) - mod[i] = NULL; - - /* - * in play case... - * - * src -> dst - * - * mem -> SSI - * mem -> SRC -> SSI - * mem -> SRC -> DVC -> SSI - */ - mod[0] = NULL; /* for "mem" */ - index = 1; - for (i = 1; i < MOD_MAX; i++) { - if (!src) { - mod[i] = ssi; - } else if (!dvc) { - mod[i] = src; - src = NULL; - } else { - if ((!is_play) && (this == src)) - this = dvc; - - mod[i] = (is_play) ? src : dvc; - i++; - mod[i] = (is_play) ? dvc : src; - src = NULL; - dvc = NULL; - } - - if (mod[i] == this) - index = i; - - if (mod[i] == ssi) - break; - } - - if (is_play) { - *mod_from = mod[index - 1]; - *mod_to = mod[index]; - } else { - *mod_from = mod[index]; - *mod_to = mod[index - 1]; - } -} - -int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, - int is_play, int id) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct dma_slave_config cfg; - struct rsnd_mod *mod_from; - struct rsnd_mod *mod_to; - char dma_name[DMA_NAME_SIZE]; - dma_cap_mask_t mask; - int ret; - - if (dma->chan) { - dev_err(dev, "it already has dma channel\n"); - return -EIO; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to); - rsnd_dma_of_name(mod_from, mod_to, dma_name); - - cfg.slave_id = id; - cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; - cfg.src_addr = rsnd_gen_dma_addr(priv, mod_from, is_play, 1); - cfg.dst_addr = rsnd_gen_dma_addr(priv, mod_to, is_play, 0); - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - - dev_dbg(dev, "dma : %s %pad -> %pad\n", - dma_name, &cfg.src_addr, &cfg.dst_addr); - - dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, - (void *)id, dev, - dma_name); - if (!dma->chan) { - dev_err(dev, "can't get dma channel\n"); - goto rsnd_dma_channel_err; - } - - ret = dmaengine_slave_config(dma->chan, &cfg); - if (ret < 0) - goto rsnd_dma_init_err; - - dma->addr = is_play ? cfg.src_addr : cfg.dst_addr; - dma->dir = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; - - return 0; - -rsnd_dma_init_err: - rsnd_dma_quit(priv, dma); -rsnd_dma_channel_err: - - /* - * DMA failed. try to PIO mode - * see - * rsnd_ssi_fallback() - * rsnd_rdai_continuance_probe() - */ - return -EAGAIN; + return ret; } -void rsnd_dma_quit(struct rsnd_priv *priv, - struct rsnd_dma *dma) +void rsnd_mod_quit(struct rsnd_mod *mod) { - if (dma->chan) - dma_release_channel(dma->chan); - - dma->chan = NULL; + if (mod->clk) + clk_unprepare(mod->clk); } /* @@ -416,7 +203,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ - u32 mask = 1 << __rsnd_mod_shift_##func; \ + u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \ u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ int ret = 0; \ if ((mod->status & mask) == call) { \ @@ -458,7 +245,7 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); - dev_err(dev, "%s%d is not empty\n", + dev_err(dev, "%s[%d] is not empty\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); return -EIO; @@ -874,20 +661,28 @@ static int rsnd_dai_probe(struct platform_device *pdev, drv[i].name = rdai[i].name; drv[i].ops = &rsnd_soc_dai_ops; if (pmod) { + snprintf(rdai[i].playback.name, RSND_DAI_NAME_SIZE, + "DAI%d Playback", i); + drv[i].playback.rates = RSND_RATES; drv[i].playback.formats = RSND_FMTS; drv[i].playback.channels_min = 2; drv[i].playback.channels_max = 2; + drv[i].playback.stream_name = rdai[i].playback.name; rdai[i].playback.info = &info->dai_info[i].playback; rdai[i].playback.rdai = rdai + i; rsnd_path_init(priv, &rdai[i], &rdai[i].playback); } if (cmod) { + snprintf(rdai[i].capture.name, RSND_DAI_NAME_SIZE, + "DAI%d Capture", i); + drv[i].capture.rates = RSND_RATES; drv[i].capture.formats = RSND_FMTS; drv[i].capture.channels_min = 2; drv[i].capture.channels_max = 2; + drv[i].capture.stream_name = rdai[i].capture.name; rdai[i].capture.info = &info->dai_info[i].capture; rdai[i].capture.rdai = rdai + i; @@ -933,6 +728,15 @@ static int rsnd_pcm_open(struct snd_pcm_substream *substream) static int rsnd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { + struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + int ret; + + ret = rsnd_dai_call(hw_params, io, substream, hw_params); + if (ret) + return ret; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } @@ -1197,6 +1001,7 @@ static int rsnd_probe(struct platform_device *pdev) const struct rsnd_of_data *of_data, struct rsnd_priv *priv) = { rsnd_gen_probe, + rsnd_dma_probe, rsnd_ssi_probe, rsnd_src_probe, rsnd_dvc_probe, @@ -1290,6 +1095,12 @@ static int rsnd_remove(struct platform_device *pdev) { struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); struct rsnd_dai *rdai; + void (*remove_func[])(struct platform_device *pdev, + struct rsnd_priv *priv) = { + rsnd_ssi_remove, + rsnd_src_remove, + rsnd_dvc_remove, + }; int ret = 0, i; pm_runtime_disable(&pdev->dev); @@ -1299,6 +1110,9 @@ static int rsnd_remove(struct platform_device *pdev) ret |= rsnd_dai_call(remove, &rdai->capture, priv); } + for (i = 0; i < ARRAY_SIZE(remove_func); i++) + remove_func[i](pdev, priv); + snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_platform(&pdev->dev); diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c new file mode 100644 index 000000000000..ac3756f6af60 --- /dev/null +++ b/sound/soc/sh/rcar/dma.c @@ -0,0 +1,616 @@ +/* + * Renesas R-Car Audio DMAC support + * + * Copyright (C) 2015 Renesas Electronics Corp. + * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.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/delay.h> +#include <linux/of_dma.h> +#include "rsnd.h" + +/* + * Audio DMAC peri peri register + */ +#define PDMASAR 0x00 +#define PDMADAR 0x04 +#define PDMACHCR 0x0c + +/* PDMACHCR */ +#define PDMACHCR_DE (1 << 0) + +struct rsnd_dma_ctrl { + void __iomem *base; + int dmapp_num; +}; + +#define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) + +/* + * Audio DMAC + */ +static void rsnd_dmaen_complete(void *data) +{ + 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); + + /* + * Renesas sound Gen1 needs 1 DMAC, + * Gen2 needs 2 DMAC. + * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. + * But, Audio-DMAC-peri-peri doesn't have interrupt, + * and this driver is assuming that here. + * + * If Audio-DMAC-peri-peri has interrpt, + * rsnd_dai_pointer_update() will be called twice, + * ant it will breaks io->byte_pos + */ + + rsnd_dai_pointer_update(io, io->byte_per_period); +} + +static void rsnd_dmaen_stop(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) +{ + 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; + int is_play = rsnd_io_is_play(io); + + desc = dmaengine_prep_dma_cyclic(dmaen->chan, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), + is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (!desc) { + dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); + return; + } + + desc->callback = rsnd_dmaen_complete; + desc->callback_param = dma; + + if (dmaengine_submit(desc) < 0) { + dev_err(dev, "dmaengine_submit() fail\n"); + return; + } + + dma_async_issue_pending(dmaen->chan); +} + +struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, + struct rsnd_mod *mod, char *name) +{ + struct dma_chan *chan; + struct device_node *np; + int i = 0; + + for_each_child_of_node(of_node, np) { + if (i == rsnd_mod_id(mod)) + break; + i++; + } + + chan = of_dma_request_slave_channel(np, name); + + of_node_put(np); + of_node_put(of_node); + + return chan; +} + +static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from, + struct rsnd_mod *mod_to) +{ + if ((!mod_from && !mod_to) || + (mod_from && mod_to)) + return NULL; + + if (mod_from) + return rsnd_mod_dma_req(mod_from); + else + return rsnd_mod_dma_req(mod_to); +} + +static int rsnd_dmaen_init(struct rsnd_priv *priv, 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 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; + + if (dmaen->chan) { + dev_err(dev, "it already has dma channel\n"); + return -EIO; + } + + if (dev->of_node) { + dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to); + } else { + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dmaen->chan = dma_request_channel(mask, shdma_chan_filter, + (void *)id); + } + if (IS_ERR_OR_NULL(dmaen->chan)) { + dev_err(dev, "can't get dma channel\n"); + goto rsnd_dma_channel_err; + } + + cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + cfg.src_addr = dma->src_addr; + cfg.dst_addr = dma->dst_addr; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + dev_dbg(dev, "dma : %pad -> %pad\n", + &cfg.src_addr, &cfg.dst_addr); + + ret = dmaengine_slave_config(dmaen->chan, &cfg); + if (ret < 0) + goto rsnd_dma_init_err; + + return 0; + +rsnd_dma_init_err: + rsnd_dma_quit(dma); +rsnd_dma_channel_err: + + /* + * DMA failed. try to PIO mode + * see + * rsnd_ssi_fallback() + * rsnd_rdai_continuance_probe() + */ + return -EAGAIN; +} + +static void rsnd_dmaen_quit(struct rsnd_dma *dma) +{ + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + + if (dmaen->chan) + dma_release_channel(dmaen->chan); + + dmaen->chan = NULL; +} + +static struct rsnd_dma_ops rsnd_dmaen_ops = { + .start = rsnd_dmaen_start, + .stop = rsnd_dmaen_stop, + .init = rsnd_dmaen_init, + .quit = rsnd_dmaen_quit, +}; + +/* + * Audio DMAC peri peri + */ +static const u8 gen2_id_table_ssiu[] = { + 0x00, /* SSI00 */ + 0x04, /* SSI10 */ + 0x08, /* SSI20 */ + 0x0c, /* SSI3 */ + 0x0d, /* SSI4 */ + 0x0e, /* SSI5 */ + 0x0f, /* SSI6 */ + 0x10, /* SSI7 */ + 0x11, /* SSI8 */ + 0x12, /* SSI90 */ +}; +static const u8 gen2_id_table_scu[] = { + 0x2d, /* SCU_SRCI0 */ + 0x2e, /* SCU_SRCI1 */ + 0x2f, /* SCU_SRCI2 */ + 0x30, /* SCU_SRCI3 */ + 0x31, /* SCU_SRCI4 */ + 0x32, /* SCU_SRCI5 */ + 0x33, /* SCU_SRCI6 */ + 0x34, /* SCU_SRCI7 */ + 0x35, /* SCU_SRCI8 */ + 0x36, /* SCU_SRCI9 */ +}; +static const u8 gen2_id_table_cmd[] = { + 0x37, /* SCU_CMD0 */ + 0x38, /* SCU_CMD1 */ +}; + +static u32 rsnd_dmapp_get_id(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); + const u8 *entry = NULL; + int id = rsnd_mod_id(mod); + int size = 0; + + if (mod == ssi) { + entry = gen2_id_table_ssiu; + size = ARRAY_SIZE(gen2_id_table_ssiu); + } else if (mod == src) { + entry = gen2_id_table_scu; + size = ARRAY_SIZE(gen2_id_table_scu); + } else if (mod == dvc) { + entry = gen2_id_table_cmd; + size = ARRAY_SIZE(gen2_id_table_cmd); + } + + if (!entry) + return 0xFF; + + if (size <= id) + return 0xFF; + + return entry[id]; +} + +static u32 rsnd_dmapp_get_chcr(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); +} + +#define rsnd_dmapp_addr(dmac, dma, reg) \ + (dmac->base + 0x20 + reg + \ + (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id)) +static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) +{ + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "w %p : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data); + + iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg)); +} + +static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) +{ + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + + return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); +} + +static void rsnd_dmapp_stop(struct rsnd_dma *dma) +{ + int i; + + rsnd_dmapp_write(dma, 0, PDMACHCR); + + for (i = 0; i < 1024; i++) { + if (0 == rsnd_dmapp_read(dma, PDMACHCR)) + return; + udelay(1); + } +} + +static void rsnd_dmapp_start(struct rsnd_dma *dma) +{ + struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); + + rsnd_dmapp_write(dma, dma->src_addr, PDMASAR); + rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR); + rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); +} + +static int rsnd_dmapp_init(struct rsnd_priv *priv, 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_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; + + dmac->dmapp_num++; + + rsnd_dmapp_stop(dma); + + dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", + dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); + + return 0; +} + +static struct rsnd_dma_ops rsnd_dmapp_ops = { + .start = rsnd_dmapp_start, + .stop = rsnd_dmapp_stop, + .init = rsnd_dmapp_init, + .quit = rsnd_dmapp_stop, +}; + +/* + * Common DMAC Interface + */ + +/* + * DMA read/write register offset + * + * RSND_xxx_I_N for Audio DMAC input + * RSND_xxx_O_N for Audio DMAC output + * RSND_xxx_I_P for Audio DMAC peri peri input + * RSND_xxx_O_P for Audio DMAC peri peri output + * + * ex) R-Car H2 case + * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out + * SSI : 0xec541000 / 0xec241008 / 0xec24100c + * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 + * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 + * CMD : 0xec500000 / / 0xec008000 0xec308000 + */ +#define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) +#define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) + +#define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) +#define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) + +#define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) +#define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) + +#define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) +#define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) + +#define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) +#define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) + +#define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) +#define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) + +static dma_addr_t +rsnd_gen2_dma_addr(struct rsnd_priv *priv, + struct rsnd_mod *mod, + int is_play, int is_from) +{ + 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); + int use_src = !!rsnd_io_to_mod_src(io); + int use_dvc = !!rsnd_io_to_mod_dvc(io); + int id = rsnd_mod_id(mod); + struct dma_addr { + dma_addr_t out_addr; + dma_addr_t in_addr; + } dma_addrs[3][2][3] = { + /* SRC */ + {{{ 0, 0 }, + /* Capture */ + { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, + { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, + /* Playback */ + {{ 0, 0, }, + { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, + { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } + }, + /* SSI */ + /* Capture */ + {{{ RDMA_SSI_O_N(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id), 0 } }, + /* Playback */ + {{ 0, RDMA_SSI_I_N(ssi, id) }, + { 0, RDMA_SSIU_I_P(ssi, id) }, + { 0, RDMA_SSIU_I_P(ssi, id) } } + }, + /* SSIU */ + /* Capture */ + {{{ RDMA_SSIU_O_N(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id), 0 } }, + /* Playback */ + {{ 0, RDMA_SSIU_I_N(ssi, id) }, + { 0, RDMA_SSIU_I_P(ssi, id) }, + { 0, RDMA_SSIU_I_P(ssi, id) } } }, + }; + + /* it shouldn't happen */ + if (use_dvc && !use_src) + dev_err(dev, "DVC is selected without SRC\n"); + + /* use SSIU or SSI ? */ + if (is_ssi && rsnd_ssi_use_busif(mod)) + is_ssi++; + + return (is_from) ? + dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr : + dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; +} + +static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv, + struct rsnd_mod *mod, + int is_play, int is_from) +{ + /* + * gen1 uses default DMA addr + */ + if (rsnd_is_gen1(priv)) + return 0; + + if (!mod) + return 0; + + return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); +} + +#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ +static void rsnd_dma_of_path(struct rsnd_dma *dma, + 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); + struct rsnd_mod *mod[MOD_MAX]; + int i, index; + + + for (i = 0; i < MOD_MAX; i++) + mod[i] = NULL; + + /* + * in play case... + * + * src -> dst + * + * mem -> SSI + * mem -> SRC -> SSI + * mem -> SRC -> DVC -> SSI + */ + mod[0] = NULL; /* for "mem" */ + index = 1; + for (i = 1; i < MOD_MAX; i++) { + if (!src) { + mod[i] = ssi; + } else if (!dvc) { + mod[i] = src; + src = NULL; + } else { + if ((!is_play) && (this == src)) + this = dvc; + + mod[i] = (is_play) ? src : dvc; + i++; + mod[i] = (is_play) ? dvc : src; + src = NULL; + dvc = NULL; + } + + if (mod[i] == this) + index = i; + + if (mod[i] == ssi) + break; + } + + if (is_play) { + *mod_from = mod[index - 1]; + *mod_to = mod[index]; + } else { + *mod_from = mod[index]; + *mod_to = mod[index - 1]; + } +} + +void rsnd_dma_stop(struct rsnd_dma *dma) +{ + dma->ops->stop(dma); +} + +void rsnd_dma_start(struct rsnd_dma *dma) +{ + dma->ops->start(dma); +} + +void rsnd_dma_quit(struct rsnd_dma *dma) +{ + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + + if (!dmac) + return; + + dma->ops->quit(dma); +} + +int rsnd_dma_init(struct rsnd_priv *priv, 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_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + int is_play = rsnd_io_is_play(io); + + /* + * DMA failed. try to PIO mode + * see + * rsnd_ssi_fallback() + * rsnd_rdai_continuance_probe() + */ + if (!dmac) + return -EAGAIN; + + rsnd_dma_of_path(dma, 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); + + /* for Gen2 */ + if (mod_from && mod_to) + dma->ops = &rsnd_dmapp_ops; + else + dma->ops = &rsnd_dmaen_ops; + + /* for Gen1, overwrite */ + if (rsnd_is_gen1(priv)) + dma->ops = &rsnd_dmaen_ops; + + return dma->ops->init(priv, dma, id, mod_from, mod_to); +} + +int rsnd_dma_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dma_ctrl *dmac; + struct resource *res; + + /* + * for Gen1 + */ + if (rsnd_is_gen1(priv)) + return 0; + + /* + * for Gen2 + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp"); + dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL); + if (!dmac || !res) { + dev_err(dev, "dma allocate failed\n"); + return 0; /* it will be PIO mode */ + } + + dmac->dmapp_num = 0; + dmac->base = devm_ioremap_resource(dev, res); + if (IS_ERR(dmac->base)) + return PTR_ERR(dmac->base); + + priv->dma = dmac; + + return 0; +} diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index d7f9ed959c4e..e5fcb062ad77 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -24,6 +24,9 @@ struct rsnd_dvc { struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ }; +#define rsnd_dvc_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") + #define rsnd_mod_to_dvc(_mod) \ container_of((_mod), struct rsnd_dvc, mod) @@ -33,7 +36,7 @@ struct rsnd_dvc { ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ i++) -static const char const *dvc_ramp_rate[] = { +static const char * const dvc_ramp_rate[] = { "128 dB/1 step", /* 00000 */ "64 dB/1 step", /* 00001 */ "32 dB/1 step", /* 00010 */ @@ -116,17 +119,6 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) rsnd_mod_write(mod, DVC_DVUER, 1); } -static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod, - struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s[%d] (Gen2) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv) { @@ -269,9 +261,17 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return 0; } +static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + + return rsnd_dma_request_channel(rsnd_dvc_of_node(priv), + mod, "tx"); +} + static struct rsnd_mod_ops rsnd_dvc_ops = { .name = DVC_NAME, - .probe = rsnd_dvc_probe_gen2, + .dma_req = rsnd_dvc_dma_req, .remove = rsnd_dvc_remove_gen2, .init = rsnd_dvc_init, .quit = rsnd_dvc_quit, @@ -333,7 +333,7 @@ int rsnd_dvc_probe(struct platform_device *pdev, struct rsnd_dvc *dvc; struct clk *clk; char name[RSND_DVC_NAME_SIZE]; - int i, nr; + int i, nr, ret; rsnd_of_parse_dvc(pdev, of_data, priv); @@ -366,11 +366,22 @@ int rsnd_dvc_probe(struct platform_device *pdev, dvc->info = &info->dvc_info[i]; - rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops, + ret = rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops, clk, RSND_MOD_DVC, i); - - dev_dbg(dev, "CMD%d probed\n", i); + if (ret) + return ret; } return 0; } + +void rsnd_dvc_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_dvc *dvc; + int i; + + for_each_rsnd_dvc(dvc, priv, i) { + rsnd_mod_quit(&dvc->mod); + } +} diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index de0685f2abae..8c7dc51b1c4f 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -28,6 +28,7 @@ struct rsnd_gen { struct regmap *regmap[RSND_BASE_MAX]; struct regmap_field *regs[RSND_REG_MAX]; + phys_addr_t res[RSND_REG_MAX]; }; #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) @@ -118,11 +119,19 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, mask, data); } -#define rsnd_gen_regmap_init(priv, id_size, reg_id, conf) \ - _rsnd_gen_regmap_init(priv, id_size, reg_id, conf, ARRAY_SIZE(conf)) +phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->res[reg_id]; +} + +#define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf) \ + _rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf)) static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, int id_size, int reg_id, + const char *name, struct rsnd_regmap_field_conf *conf, int conf_size) { @@ -141,8 +150,11 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, regc.reg_bits = 32; regc.val_bits = 32; regc.reg_stride = 4; + regc.name = name; - res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id); if (!res) return -ENODEV; @@ -156,6 +168,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, gen->base[reg_id] = base; gen->regmap[reg_id] = regmap; + gen->res[reg_id] = res->start; for (i = 0; i < conf_size; i++) { @@ -176,125 +189,11 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, } /* - * DMA read/write register offset - * - * RSND_xxx_I_N for Audio DMAC input - * RSND_xxx_O_N for Audio DMAC output - * RSND_xxx_I_P for Audio DMAC peri peri input - * RSND_xxx_O_P for Audio DMAC peri peri output - * - * ex) R-Car H2 case - * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out - * SSI : 0xec541000 / 0xec241008 / 0xec24100c - * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 - * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 - * CMD : 0xec500000 / / 0xec008000 0xec308000 - */ -#define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) -#define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) - -#define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) -#define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) - -#define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) -#define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) - -#define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) -#define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) - -#define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) -#define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) - -#define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) -#define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) - -static dma_addr_t -rsnd_gen2_dma_addr(struct rsnd_priv *priv, - struct rsnd_mod *mod, - int is_play, int is_from) -{ - struct platform_device *pdev = rsnd_priv_to_pdev(priv); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - dma_addr_t ssi_reg = platform_get_resource(pdev, - IORESOURCE_MEM, RSND_GEN2_SSI)->start; - dma_addr_t src_reg = platform_get_resource(pdev, - IORESOURCE_MEM, RSND_GEN2_SCU)->start; - int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); - int use_src = !!rsnd_io_to_mod_src(io); - int use_dvc = !!rsnd_io_to_mod_dvc(io); - int id = rsnd_mod_id(mod); - struct dma_addr { - dma_addr_t out_addr; - dma_addr_t in_addr; - } dma_addrs[3][2][3] = { - /* SRC */ - {{{ 0, 0 }, - /* Capture */ - { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, - { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, - /* Playback */ - {{ 0, 0, }, - { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, - { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } - }, - /* SSI */ - /* Capture */ - {{{ RDMA_SSI_O_N(ssi, id), 0 }, - { RDMA_SSIU_O_P(ssi, id), 0 }, - { RDMA_SSIU_O_P(ssi, id), 0 } }, - /* Playback */ - {{ 0, RDMA_SSI_I_N(ssi, id) }, - { 0, RDMA_SSIU_I_P(ssi, id) }, - { 0, RDMA_SSIU_I_P(ssi, id) } } - }, - /* SSIU */ - /* Capture */ - {{{ RDMA_SSIU_O_N(ssi, id), 0 }, - { RDMA_SSIU_O_P(ssi, id), 0 }, - { RDMA_SSIU_O_P(ssi, id), 0 } }, - /* Playback */ - {{ 0, RDMA_SSIU_I_N(ssi, id) }, - { 0, RDMA_SSIU_I_P(ssi, id) }, - { 0, RDMA_SSIU_I_P(ssi, id) } } }, - }; - - /* it shouldn't happen */ - if (use_dvc && !use_src) - dev_err(dev, "DVC is selected without SRC\n"); - - /* use SSIU or SSI ? */ - if (is_ssi && (0 == strcmp(rsnd_mod_dma_name(mod), "ssiu"))) - is_ssi++; - - return (is_from) ? - dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr : - dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; -} - -dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv, - struct rsnd_mod *mod, - int is_play, int is_from) -{ - /* - * gen1 uses default DMA addr - */ - if (rsnd_is_gen1(priv)) - return 0; - - if (!mod) - return 0; - - return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); -} - -/* * Gen2 */ static int rsnd_gen2_probe(struct platform_device *pdev, struct rsnd_priv *priv) { - struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_regmap_field_conf conf_ssiu[] = { RSND_GEN_S_REG(SSI_MODE0, 0x800), RSND_GEN_S_REG(SSI_MODE1, 0x804), @@ -368,18 +267,16 @@ static int rsnd_gen2_probe(struct platform_device *pdev, int ret_adg; int ret_ssi; - ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, conf_ssiu); - ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU, conf_scu); - ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG, conf_adg); - ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI, conf_ssi); + ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, "ssiu", conf_ssiu); + ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU, "scu", conf_scu); + ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG, "adg", conf_adg); + ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI, "ssi", conf_ssi); if (ret_ssiu < 0 || ret_scu < 0 || ret_adg < 0 || ret_ssi < 0) return ret_ssiu | ret_scu | ret_adg | ret_ssi; - dev_dbg(dev, "Gen2 is probed\n"); - return 0; } @@ -390,7 +287,6 @@ static int rsnd_gen2_probe(struct platform_device *pdev, static int rsnd_gen1_probe(struct platform_device *pdev, struct rsnd_priv *priv) { - struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_regmap_field_conf conf_sru[] = { RSND_GEN_S_REG(SRC_ROUTE_SEL, 0x00), RSND_GEN_S_REG(SRC_TMG_SEL0, 0x08), @@ -440,16 +336,14 @@ static int rsnd_gen1_probe(struct platform_device *pdev, int ret_adg; int ret_ssi; - ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, conf_sru); - ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, conf_adg); - ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, conf_ssi); + ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, "sru", conf_sru); + ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg); + ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi); if (ret_sru < 0 || ret_adg < 0 || ret_ssi < 0) return ret_sru | ret_adg | ret_ssi; - dev_dbg(dev, "Gen1 is probed\n"); - return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index e7914bd610e2..4e6de6804cfb 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -170,21 +170,47 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod); /* * R-Car DMA */ -struct rsnd_dma { - struct sh_dmae_slave slave; +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, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); + void (*quit)(struct rsnd_dma *dma); +}; + +struct rsnd_dmaen { struct dma_chan *chan; - enum dma_transfer_direction dir; - dma_addr_t addr; }; +struct rsnd_dmapp { + int dmapp_id; + u32 chcr; +}; + +struct rsnd_dma { + struct rsnd_dma_ops *ops; + dma_addr_t src_addr; + dma_addr_t dst_addr; + union { + struct rsnd_dmaen en; + struct rsnd_dmapp pp; + } 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_available(struct rsnd_dma *dma); -int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, - int is_play, int id); -void rsnd_dma_quit(struct rsnd_priv *priv, - 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); +int rsnd_dma_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv); +struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, + struct rsnd_mod *mod, char *name); +#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) /* * R-Car sound mod @@ -198,7 +224,7 @@ enum rsnd_mod_type { struct rsnd_mod_ops { char *name; - char* (*dma_name)(struct rsnd_mod *mod); + struct dma_chan* (*dma_req)(struct rsnd_mod *mod); int (*probe)(struct rsnd_mod *mod, struct rsnd_priv *priv); int (*remove)(struct rsnd_mod *mod, @@ -213,6 +239,9 @@ struct rsnd_mod_ops { struct rsnd_priv *priv); int (*pcm_new)(struct rsnd_mod *mod, struct snd_soc_pcm_runtime *rtd); + int (*hw_params)(struct rsnd_mod *mod, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params); int (*fallback)(struct rsnd_mod *mod, struct rsnd_priv *priv); }; @@ -236,6 +265,9 @@ struct rsnd_mod { * 2 0: start 1: stop * 3 0: pcm_new * 4 0: fallback + * + * 31 bit is always called (see __rsnd_mod_call) + * 31 0: hw_params */ #define __rsnd_mod_shift_probe 0 #define __rsnd_mod_shift_remove 0 @@ -245,6 +277,7 @@ struct rsnd_mod { #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_call_probe 0 #define __rsnd_mod_call_remove 1 @@ -254,28 +287,30 @@ struct rsnd_mod { #define __rsnd_mod_call_stop 1 #define __rsnd_mod_call_pcm_new 0 #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_dma(mod) (&(mod)->dma) -#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) #define rsnd_mod_to_io(mod) ((mod)->io) #define rsnd_mod_id(mod) ((mod)->id) -#define rsnd_mod_hw_start(mod) clk_prepare_enable((mod)->clk) -#define rsnd_mod_hw_stop(mod) clk_disable_unprepare((mod)->clk) +#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) +#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) -void rsnd_mod_init(struct rsnd_mod *mod, +int rsnd_mod_init(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); -char *rsnd_mod_dma_name(struct rsnd_mod *mod); +struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod); /* * R-Car sound DAI */ #define RSND_DAI_NAME_SIZE 16 struct rsnd_dai_stream { + char name[RSND_DAI_NAME_SIZE]; struct snd_pcm_substream *substream; struct rsnd_mod *mod[RSND_MOD_MAX]; struct rsnd_dai_path_info *info; /* rcar_snd.h */ @@ -331,9 +366,7 @@ int rsnd_gen_probe(struct platform_device *pdev, void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); -dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv, - struct rsnd_mod *mod, - int is_play, int is_from); +phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); #define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1) #define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2) @@ -389,6 +422,11 @@ struct rsnd_priv { void *adg; /* + * below value will be filled on rsnd_dma_probe() + */ + void *dma; + + /* * below value will be filled on rsnd_ssi_probe() */ void *ssi; @@ -414,19 +452,6 @@ struct rsnd_priv { #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) -#define rsnd_info_is_playback(priv, type) \ -({ \ - struct rcar_snd_info *info = rsnd_priv_to_info(priv); \ - int i, is_play = 0; \ - for (i = 0; i < info->dai_info_nr; i++) { \ - if (info->dai_info[i].playback.type == (type)->info) { \ - is_play = 1; \ - break; \ - } \ - } \ - is_play; \ -}) - /* * rsnd_kctrl */ @@ -480,6 +505,8 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod, int rsnd_src_probe(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv); +void rsnd_src_remove(struct platform_device *pdev, + struct rsnd_priv *priv); struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, @@ -498,9 +525,12 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod); int rsnd_ssi_probe(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv); +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv); 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); /* * R-Car DVC diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c new file mode 100644 index 000000000000..a68517afe615 --- /dev/null +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -0,0 +1,512 @@ +/* + * Renesas Sampling Rate Convert Sound Card for DPCM + * + * Copyright (C) 2015 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * based on ${LINUX}/sound/soc/generic/simple-card.c + * + * 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/clk.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/string.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +struct rsrc_card_of_data { + const char *prefix; + const struct snd_soc_dapm_route *routes; + int num_routes; +}; + +static const struct snd_soc_dapm_route routes_ssi0_ak4642[] = { + {"ak4642 Playback", NULL, "DAI0 Playback"}, + {"DAI0 Capture", NULL, "ak4642 Capture"}, +}; + +static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = { + .prefix = "ak4642", + .routes = routes_ssi0_ak4642, + .num_routes = ARRAY_SIZE(routes_ssi0_ak4642), +}; + +static const struct of_device_id rsrc_card_of_match[] = { + { .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 }, + { .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 }, + {}, +}; +MODULE_DEVICE_TABLE(of, rsrc_card_of_match); + +struct rsrc_card_dai { + const char *name; + unsigned int fmt; + unsigned int sysclk; + struct clk *clk; +}; + +#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]; + 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_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; + + ret = clk_prepare_enable(dai_props->codec_dai.clk); + if (ret) + clk_disable_unprepare(dai_props->cpu_dai.clk); + + return ret; +} + +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); + + clk_disable_unprepare(dai_props->codec_dai.clk); +} + +static struct snd_soc_ops rsrc_card_ops = { + .startup = rsrc_card_startup, + .shutdown = rsrc_card_shutdown, +}; + +static int __rsrc_card_dai_init(struct snd_soc_dai *dai, + struct rsrc_card_dai *set) +{ + int ret; + + if (set->fmt) { + ret = snd_soc_dai_set_fmt(dai, set->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 (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "set_sysclk error\n"); + goto err; + } + } + + ret = 0; + +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) +{ + struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + if (!priv->convert_rate) + return 0; + + rate->min = rate->max = priv->convert_rate; + + 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) +{ + 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; + + 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; + } + + if (!np) { + /* use snd-soc-dummy */ + *p_node = NULL; + *dai_name = "snd-soc-dummy-dai"; + *name = "snd-soc-dummy"; + return 0; + } + + /* + * Get node via "sound-dai = <&phandle port>" + * it will be used as xxx_of_node on soc_bind_dai_link() + */ + ret = of_parse_phandle_with_args(np, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) + return ret; + + *p_node = args.np; + + /* Get dai->name */ + ret = snd_soc_of_get_dai_name(np, dai_name); + if (ret < 0) + return ret; + + /* + * FIXME + * + * rsrc assumes DPCM playback/capture + */ + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + + 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; + } + + /* + * Parse dai->sysclk come from "clocks = <&xxx>" + * (if system has common clock) + * or "system-clock-frequency = <xxx>" + * or device's module clock. + */ + if (of_property_read_bool(np, "clocks")) { + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + return ret; + } + + dai->sysclk = clk_get_rate(clk); + dai->clk = clk; + } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { + dai->sysclk = val; + } else { + clk = of_clk_get(args.np, 0); + if (!IS_ERR(clk)) + dai->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 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; + } + + ret = rsrc_card_parse_daifmt(node, priv, codec, idx); + if (ret < 0) + goto dai_link_of_err; + + ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL, + &dai_props->cpu_dai, + dai_link, + &cpu_args); + if (ret < 0) + goto dai_link_of_err; + + ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL, + &dai_props->codec_dai, + dai_link, + NULL); + 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); + + /* + * 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; + +dai_link_of_err: + of_node_put(cpu); + of_node_put(codec); + + return ret; +} + +static int rsrc_card_parse_of(struct device_node *node, + struct rsrc_card_priv *priv) +{ + struct device *dev = rsrc_priv_to_dev(priv); + const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); + int ret; + int i; + + if (!node) + return -EINVAL; + + /* Parse the card name from DT */ + snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); + + /* DAPM routes */ + priv->snd_card.of_dapm_routes = of_data->routes; + priv->snd_card.num_of_dapm_routes = of_data->num_routes; + + /* sampling rate convert */ + of_property_read_u32(node, "convert-rate", &priv->convert_rate); + + dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n", + 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); + if (ret < 0) + return ret; + } + + if (!priv->snd_card.name) + priv->snd_card.name = priv->snd_card.dai_link->name; + + return 0; +} + +/* Decrease the reference count of the device nodes */ +static int rsrc_card_unref(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + int num_links; + + for (num_links = 0, dai_link = card->dai_link; + num_links < card->num_links; + num_links++, dai_link++) { + of_node_put(dai_link->cpu_of_node); + of_node_put(dai_link->codec_of_node); + } + return 0; +} + +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; + + /* Allocate the private data */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + 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); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + goto err; + } + + snd_soc_card_set_drvdata(&priv->snd_card, priv); + + ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); + if (ret >= 0) + return ret; +err: + rsrc_card_unref(&priv->snd_card); + + return ret; +} + +static int rsrc_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + return rsrc_card_unref(card); +} + +static struct platform_driver rsrc_card = { + .driver = { + .name = "renesas-src-audio-card", + .of_match_table = rsrc_card_of_match, + }, + .probe = rsrc_card_probe, + .remove = rsrc_card_remove, +}; + +module_platform_driver(rsrc_card); + +MODULE_ALIAS("platform:renesas-src-audio-card"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas Sampling Rate Convert Sound Card"); +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 81c182b4bad5..3beb32eb412a 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -22,16 +22,20 @@ struct rsnd_src { struct rsnd_src_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; + struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ + struct rsnd_kctrl_cfg_s sync; /* sync convert */ + u32 convert_rate; /* sampling rate convert */ int err; }; #define RSND_SRC_NAME_SIZE 16 -#define rsnd_src_convert_rate(p) ((p)->info->convert_rate) +#define rsnd_enable_sync_convert(src) ((src)->sen.val) +#define rsnd_src_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") + #define rsnd_mod_to_src(_mod) \ container_of((_mod), struct rsnd_src, mod) -#define rsnd_src_dma_available(src) \ - rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod)) #define for_each_rsnd_src(pos, priv, i) \ for ((i) = 0; \ @@ -113,6 +117,17 @@ struct rsnd_src { /* * Gen1/Gen2 common functions */ +static struct dma_chan *rsnd_src_dma_req(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), + mod, + is_play ? "rx" : "tx"); +} + int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, int use_busif) { @@ -220,6 +235,30 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod) return 0; } +static u32 rsnd_src_convert_rate(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; + + if (!runtime) + return 0; + + if (!rsnd_enable_sync_convert(src)) + return src->convert_rate; + + convert_rate = src->sync.val; + + if (!convert_rate) + convert_rate = src->convert_rate; + + if (!convert_rate) + convert_rate = runtime->rate; + + return convert_rate; +} + unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct snd_pcm_runtime *runtime) @@ -276,7 +315,43 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) return 0; } -static int rsnd_src_init(struct rsnd_mod *mod) +static int rsnd_src_hw_params(struct rsnd_mod *mod, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *fe_params) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + struct snd_soc_pcm_runtime *fe = substream->private_data; + + /* default value (mainly for non-DT) */ + src->convert_rate = src->info->convert_rate; + + /* + * SRC assumes that it is used under DPCM if user want to use + * sampling rate convert. Then, SRC should be FE. + * And then, this function will be called *after* BE settings. + * this means, each BE already has fixuped hw_params. + * see + * dpcm_fe_dai_hw_params() + * dpcm_be_dai_hw_params() + */ + if (fe->dai_link->dynamic) { + int stream = substream->stream; + struct snd_soc_dpcm *dpcm; + struct snd_pcm_hw_params *be_params; + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + be_params = &dpcm->hw_params; + + if (params_rate(fe_params) != params_rate(be_params)) + src->convert_rate = params_rate(be_params); + } + } + + return 0; +} + +static int rsnd_src_init(struct rsnd_mod *mod, + struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); @@ -284,6 +359,9 @@ static int rsnd_src_init(struct rsnd_mod *mod) src->err = 0; + /* reset sync convert_rate */ + src->sync.val = 0; + /* * Initialize the operation of the SRC internal circuits * see rsnd_src_start() @@ -305,6 +383,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod, dev_warn(dev, "%s[%d] under/over flow err = %d\n", rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); + src->convert_rate = 0; + + /* reset sync convert_rate */ + src->sync.val = 0; + return 0; } @@ -448,23 +531,12 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) return 0; } -static int rsnd_src_probe_gen1(struct rsnd_mod *mod, - struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s[%d] (Gen1) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - static int rsnd_src_init_gen1(struct rsnd_mod *mod, struct rsnd_priv *priv) { int ret; - ret = rsnd_src_init(mod); + ret = rsnd_src_init(mod, priv); if (ret < 0) return ret; @@ -505,11 +577,12 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod, static struct rsnd_mod_ops rsnd_src_gen1_ops = { .name = SRC_NAME, - .probe = rsnd_src_probe_gen1, + .dma_req = rsnd_src_dma_req, .init = rsnd_src_init_gen1, .quit = rsnd_src_quit, .start = rsnd_src_start_gen1, .stop = rsnd_src_stop_gen1, + .hw_params = rsnd_src_hw_params, }; /* @@ -607,13 +680,17 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) if (rsnd_src_error_record_gen2(mod)) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); - _rsnd_src_stop_gen2(mod); - _rsnd_src_start_gen2(mod); - dev_dbg(dev, "%s[%d] restart\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + _rsnd_src_stop_gen2(mod); + if (src->err < 1024) + _rsnd_src_start_gen2(mod); + else + dev_warn(dev, "no more SRC restart\n"); } return IRQ_HANDLED; @@ -627,6 +704,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *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 cr, route; uint ratio; int ret; @@ -647,13 +725,21 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) if (ret < 0) return ret; - rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); - + cr = 0x00011110; + route = 0x0; if (convert_rate) { - /* Gen1/Gen2 are not compatible */ - rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + route = 0x1; + + if (rsnd_enable_sync_convert(src)) { + cr |= 0x1; + route |= rsnd_io_is_play(io) ? + (0x1 << 24) : (0x1 << 25); + } } + rsnd_mod_write(mod, SRC_SRCCR, cr); + rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); + switch (rsnd_mod_id(mod)) { case 5: case 6: @@ -708,24 +794,12 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, IRQF_SHARED, dev_name(dev), mod); if (ret) - goto rsnd_src_probe_gen2_fail; + return ret; } ret = rsnd_dma_init(priv, rsnd_mod_to_dma(mod), - rsnd_info_is_playback(priv, src), src->info->dma_id); - if (ret) - goto rsnd_src_probe_gen2_fail; - - dev_dbg(dev, "%s[%d] (Gen2) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return ret; - -rsnd_src_probe_gen2_fail: - dev_err(dev, "%s[%d] (Gen2) failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } @@ -733,7 +807,7 @@ rsnd_src_probe_gen2_fail: static int rsnd_src_remove_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv) { - rsnd_dma_quit(priv, rsnd_mod_to_dma(mod)); + rsnd_dma_quit(rsnd_mod_to_dma(mod)); return 0; } @@ -743,7 +817,7 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, { int ret; - ret = rsnd_src_init(mod); + ret = rsnd_src_init(mod, priv); if (ret < 0) return ret; @@ -778,14 +852,91 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod, return ret; } +static void rsnd_src_reconvert_update(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 fsrate; + + if (!runtime) + return; + + if (!convert_rate) + convert_rate = runtime->rate; + + fsrate = 0x0400000 / convert_rate * runtime->rate; + + /* update IFS */ + rsnd_mod_write(mod, SRC_IFSVR, fsrate); +} + +static int rsnd_src_pcm_new(struct rsnd_mod *mod, + 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; + + /* + * enable SRC sync convert if possible + */ + + /* + * Gen1 is not supported + */ + if (rsnd_is_gen1(priv)) + return 0; + + /* + * SRC sync convert needs clock master + */ + if (!rsnd_rdai_is_clk_master(rdai)) + return 0; + + /* + * We can't use SRC sync convert + * if it has DVC + */ + if (rsnd_io_to_mod_dvc(io)) + return 0; + + /* + * enable sync convert + */ + ret = rsnd_kctrl_new_s(mod, rtd, + rsnd_io_is_play(io) ? + "SRC Out Rate Switch" : + "SRC In Rate Switch", + rsnd_src_reconvert_update, + &src->sen, 1); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_s(mod, rtd, + rsnd_io_is_play(io) ? + "SRC Out Rate" : + "SRC In Rate", + rsnd_src_reconvert_update, + &src->sync, 192000); + + return ret; +} + static struct rsnd_mod_ops rsnd_src_gen2_ops = { .name = SRC_NAME, + .dma_req = rsnd_src_dma_req, .probe = rsnd_src_probe_gen2, .remove = rsnd_src_remove_gen2, .init = rsnd_src_init_gen2, .quit = rsnd_src_quit, .start = rsnd_src_start_gen2, .stop = rsnd_src_stop_gen2, + .hw_params = rsnd_src_hw_params, + .pcm_new = rsnd_src_pcm_new, }; struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) @@ -810,7 +961,7 @@ static void rsnd_of_parse_src(struct platform_device *pdev, if (!of_data) return; - src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); + src_node = rsnd_src_of_node(priv); if (!src_node) return; @@ -850,7 +1001,7 @@ int rsnd_src_probe(struct platform_device *pdev, struct rsnd_mod_ops *ops; struct clk *clk; char name[RSND_SRC_NAME_SIZE]; - int i, nr; + int i, nr, ret; ops = NULL; if (rsnd_is_gen1(priv)) @@ -890,10 +1041,21 @@ int rsnd_src_probe(struct platform_device *pdev, src->info = &info->src_info[i]; - rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); - - dev_dbg(dev, "SRC%d probed\n", i); + ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); + if (ret) + return ret; } return 0; } + +void rsnd_src_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_src *src; + int i; + + for_each_rsnd_src(src, priv, i) { + rsnd_mod_quit(&src->mod); + } +} diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 9e7b627c08e2..7bb9c087f3dc 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -80,13 +80,13 @@ 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_dma_available(ssi) \ - rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) #define rsnd_ssi_clk_from_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") -static int rsnd_ssi_use_busif(struct rsnd_mod *mod) +int rsnd_ssi_use_busif(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); @@ -416,11 +416,14 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) /* * restart SSI */ - rsnd_ssi_stop(mod, priv); - rsnd_ssi_start(mod, priv); - dev_dbg(dev, "%s[%d] restart\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + rsnd_ssi_stop(mod, priv); + if (ssi->err < 1024) + rsnd_ssi_start(mod, priv); + else + dev_warn(dev, "no more SSI restart\n"); } rsnd_ssi_record_error(ssi, status); @@ -442,12 +445,6 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), ssi); - if (ret) - dev_err(dev, "%s[%d] (PIO) request interrupt failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - else - dev_dbg(dev, "%s[%d] (PIO) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } @@ -474,23 +471,11 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, IRQF_SHARED, dev_name(dev), ssi); if (ret) - goto rsnd_ssi_dma_probe_fail; + return ret; ret = rsnd_dma_init( priv, rsnd_mod_to_dma(mod), - rsnd_info_is_playback(priv, ssi), dma_id); - if (ret) - goto rsnd_ssi_dma_probe_fail; - - dev_dbg(dev, "%s[%d] (DMA) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return ret; - -rsnd_ssi_dma_probe_fail: - dev_err(dev, "%s[%d] (DMA) is failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } @@ -502,7 +487,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct device *dev = rsnd_priv_to_dev(priv); int irq = ssi->info->irq; - rsnd_dma_quit(priv, rsnd_mod_to_dma(mod)); + rsnd_dma_quit(rsnd_mod_to_dma(mod)); /* PIO will request IRQ again */ devm_free_irq(dev, irq, ssi); @@ -554,14 +539,25 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, return 0; } -static char *rsnd_ssi_dma_name(struct rsnd_mod *mod) +static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod) { - return rsnd_ssi_use_busif(mod) ? "ssiu" : SSI_NAME; + 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)) + name = is_play ? "rxu" : "txu"; + else + name = is_play ? "rx" : "tx"; + + return rsnd_dma_request_channel(rsnd_ssi_of_node(priv), + mod, name); } static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .name = SSI_NAME, - .dma_name = rsnd_ssi_dma_name, + .dma_req = rsnd_ssi_dma_req, .probe = rsnd_ssi_dma_probe, .remove = rsnd_ssi_dma_remove, .init = rsnd_ssi_init, @@ -636,7 +632,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, if (!of_data) return; - node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi"); + node = rsnd_ssi_of_node(priv); if (!node) return; @@ -697,7 +693,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, struct clk *clk; struct rsnd_ssi *ssi; char name[RSND_SSI_NAME_SIZE]; - int i, nr; + int i, nr, ret; rsnd_of_parse_ssi(pdev, of_data, priv); @@ -732,10 +728,23 @@ int rsnd_ssi_probe(struct platform_device *pdev, else if (rsnd_ssi_pio_available(ssi)) ops = &rsnd_ssi_pio_ops; - rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i); + ret = rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i); + if (ret) + return ret; rsnd_ssi_parent_clk_setup(priv, ssi); } return 0; } + +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) { + rsnd_mod_quit(&ssi->mod); + } +} diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e5c990889dcc..23732523f87c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -292,6 +292,9 @@ static const struct file_operations codec_reg_fops = { static void soc_init_component_debugfs(struct snd_soc_component *component) { + if (!component->card->debugfs_card_root) + return; + if (component->debugfs_prefix) { char *name; @@ -455,6 +458,9 @@ static const struct file_operations platform_list_fops = { static void soc_init_card_debugfs(struct snd_soc_card *card) { + if (!snd_soc_debugfs_root) + return; + card->debugfs_card_root = debugfs_create_dir(card->name, snd_soc_debugfs_root); if (!card->debugfs_card_root) { @@ -476,6 +482,34 @@ static void soc_cleanup_card_debugfs(struct snd_soc_card *card) debugfs_remove_recursive(card->debugfs_card_root); } + +static void snd_soc_debugfs_init(void) +{ + snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); + if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) { + pr_warn("ASoC: Failed to create debugfs directory\n"); + snd_soc_debugfs_root = NULL; + return; + } + + if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, + &codec_list_fops)) + pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); + + if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, + &dai_list_fops)) + pr_warn("ASoC: Failed to create DAI list debugfs file\n"); + + if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, + &platform_list_fops)) + pr_warn("ASoC: Failed to create platform list debugfs file\n"); +} + +static void snd_soc_debugfs_exit(void) +{ + debugfs_remove_recursive(snd_soc_debugfs_root); +} + #else #define soc_init_codec_debugfs NULL @@ -497,6 +531,15 @@ static inline void soc_init_card_debugfs(struct snd_soc_card *card) static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) { } + +static inline void snd_soc_debugfs_init(void) +{ +} + +static inline void snd_soc_debugfs_exit(void) +{ +} + #endif struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, @@ -595,15 +638,9 @@ int snd_soc_suspend(struct device *dev) cpu_dai->driver->suspend(cpu_dai); } - /* close any waiting streams and save state */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai **codec_dais = card->rtd[i].codec_dais; + /* close any waiting streams */ + for (i = 0; i < card->num_rtd; i++) flush_delayed_work(&card->rtd[i].delayed_work); - for (j = 0; j < card->rtd[i].num_codecs; j++) { - codec_dais[j]->codec->dapm.suspend_bias_level = - codec_dais[j]->codec->dapm.bias_level; - } - } for (i = 0; i < card->num_rtd; i++) { @@ -1261,7 +1298,8 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, capture_w = cpu_dai->capture_widget; if (play_w && capture_w) { ret = snd_soc_dapm_new_pcm(card, dai_link->params, - capture_w, play_w); + dai_link->num_params, capture_w, + play_w); if (ret != 0) { dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", play_w->name, capture_w->name, ret); @@ -1273,7 +1311,8 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, capture_w = codec_dai->capture_widget; if (play_w && capture_w) { ret = snd_soc_dapm_new_pcm(card, dai_link->params, - capture_w, play_w); + dai_link->num_params, capture_w, + play_w); if (ret != 0) { dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", play_w->name, capture_w->name, ret); @@ -1322,21 +1361,17 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) } } + if (dai_link->dai_fmt) + snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); + ret = soc_post_component_init(rtd, dai_link->name); if (ret) return ret; #ifdef CONFIG_DEBUG_FS /* add DPCM sysfs entries */ - if (dai_link->dynamic) { - ret = soc_dpcm_debugfs_add(rtd); - if (ret < 0) { - dev_err(rtd->dev, - "ASoC: failed to add dpcm sysfs entries: %d\n", - ret); - return ret; - } - } + if (dai_link->dynamic) + soc_dpcm_debugfs_add(rtd); #endif if (cpu_dai->driver->compress_dai) { @@ -1426,7 +1461,6 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num) /* unregister the rtd device */ if (rtd->dev_registered) { - device_remove_file(rtd->dev, &dev_attr_codec_reg); device_unregister(rtd->dev); rtd->dev_registered = 0; } @@ -1560,6 +1594,8 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) goto base_error; } + soc_init_card_debugfs(card); + card->dapm.bias_level = SND_SOC_BIAS_OFF; card->dapm.dev = card->dev; card->dapm.card = card; @@ -1578,6 +1614,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, card->num_dapm_widgets); + if (card->of_dapm_widgets) + snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets, + card->num_of_dapm_widgets); + /* initialise the sound card only once */ if (card->probe) { ret = card->probe(card); @@ -1633,11 +1673,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); - for (i = 0; i < card->num_links; i++) { - if (card->dai_link[i].dai_fmt) - snd_soc_runtime_set_dai_fmt(&card->rtd[i], - card->dai_link[i].dai_fmt); - } + if (card->of_dapm_routes) + snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, + card->num_of_dapm_routes); snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), "%s", card->name); @@ -1694,6 +1732,7 @@ card_probe_error: if (card->remove) card->remove(card); + soc_cleanup_card_debugfs(card); snd_card_free(card->snd_card); base_error: @@ -2372,8 +2411,6 @@ int snd_soc_register_card(struct snd_soc_card *card) snd_soc_initialize_card_lists(card); - soc_init_card_debugfs(card); - card->rtd = devm_kzalloc(card->dev, sizeof(struct snd_soc_pcm_runtime) * (card->num_links + card->num_aux_devs), @@ -2404,7 +2441,7 @@ int snd_soc_register_card(struct snd_soc_card *card) ret = snd_soc_instantiate_card(card); if (ret != 0) - soc_cleanup_card_debugfs(card); + return ret; /* deactivate pins to sleep state */ for (i = 0; i < card->num_rtd; i++) { @@ -2755,7 +2792,7 @@ int snd_soc_register_component(struct device *dev, ret = snd_soc_register_dais(cmpnt, dai_drv, num_dai, true); if (ret < 0) { - dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret); + dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); goto err_cleanup; } @@ -3076,7 +3113,7 @@ int snd_soc_register_codec(struct device *dev, ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false); if (ret < 0) { - dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret); + dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); goto err_cleanup; } @@ -3242,8 +3279,8 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, widgets[i].name = wname; } - card->dapm_widgets = widgets; - card->num_dapm_widgets = num_widgets; + card->of_dapm_widgets = widgets; + card->num_of_dapm_widgets = num_widgets; return 0; } @@ -3327,8 +3364,8 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } } - card->num_dapm_routes = num_routes; - card->dapm_routes = routes; + card->num_of_dapm_routes = num_routes; + card->of_dapm_routes = routes; return 0; } @@ -3587,26 +3624,7 @@ EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs); static int __init snd_soc_init(void) { -#ifdef CONFIG_DEBUG_FS - snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); - if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) { - pr_warn("ASoC: Failed to create debugfs directory\n"); - snd_soc_debugfs_root = NULL; - } - - if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, - &codec_list_fops)) - pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); - - if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, - &dai_list_fops)) - pr_warn("ASoC: Failed to create DAI list debugfs file\n"); - - if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, - &platform_list_fops)) - pr_warn("ASoC: Failed to create platform list debugfs file\n"); -#endif - + snd_soc_debugfs_init(); snd_soc_util_init(); return platform_driver_register(&soc_driver); @@ -3616,9 +3634,9 @@ module_init(snd_soc_init); static void __exit snd_soc_exit(void) { snd_soc_util_exit(); + snd_soc_debugfs_exit(); #ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(snd_soc_debugfs_root); #endif platform_driver_unregister(&soc_driver); } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b6f88202b8c9..defe0f0082b5 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -473,16 +473,6 @@ struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( } EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm); -/** - * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol - * @kcontrol: The kcontrol - */ -struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) -{ - return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol)); -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); - static void dapm_reset(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; @@ -853,6 +843,36 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w) return 0; } +/* create new dapm dai link control */ +static int dapm_new_dai_link(struct snd_soc_dapm_widget *w) +{ + int i, ret; + struct snd_kcontrol *kcontrol; + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_card *card = dapm->card->snd_card; + + /* create control for links with > 1 config */ + if (w->num_params <= 1) + return 0; + + /* add kcontrol */ + for (i = 0; i < w->num_kcontrols; i++) { + kcontrol = snd_soc_cnew(&w->kcontrol_news[i], w, + w->name, NULL); + ret = snd_ctl_add(card, kcontrol); + if (ret < 0) { + dev_err(dapm->dev, + "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", + w->name, w->kcontrol_news[i].name, ret); + return ret; + } + kcontrol->private_data = w; + w->kcontrols[i] = kcontrol; + } + + return 0; +} + /* We implement power down on suspend by checking the power state of * the ALSA card - when we are suspending the ALSA state for the card * is set to D3. @@ -1898,6 +1918,9 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, { struct dentry *d; + if (!parent) + return; + dapm->debugfs_dapm = debugfs_create_dir("dapm", parent); if (!dapm->debugfs_dapm) { @@ -2719,6 +2742,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) case snd_soc_dapm_out_drv: dapm_new_pga(w); break; + case snd_soc_dapm_dai_link: + dapm_new_dai_link(w); + break; default: break; } @@ -3193,7 +3219,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, { struct snd_soc_dapm_path *source_p, *sink_p; struct snd_soc_dai *source, *sink; - const struct snd_soc_pcm_stream *config = w->params; + const struct snd_soc_pcm_stream *config = w->params + w->params_select; struct snd_pcm_substream substream; struct snd_pcm_hw_params *params = NULL; u64 fmt; @@ -3285,22 +3311,97 @@ out: return ret; } +static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = w->params_select; + + return 0; +} + +static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); + + /* Can't change the config when widget is already powered */ + if (w->power) + return -EBUSY; + + if (ucontrol->value.integer.value[0] == w->params_select) + return 0; + + if (ucontrol->value.integer.value[0] >= w->num_params) + return -EINVAL; + + w->params_select = ucontrol->value.integer.value[0]; + + return 0; +} + int snd_soc_dapm_new_pcm(struct snd_soc_card *card, const struct snd_soc_pcm_stream *params, + unsigned int num_params, struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { struct snd_soc_dapm_widget template; struct snd_soc_dapm_widget *w; - size_t len; char *link_name; - int ret; - - len = strlen(source->name) + strlen(sink->name) + 2; - link_name = devm_kzalloc(card->dev, len, GFP_KERNEL); - if (!link_name) + int ret, count; + unsigned long private_value; + const char **w_param_text; + struct soc_enum w_param_enum[] = { + SOC_ENUM_SINGLE(0, 0, 0, NULL), + }; + struct snd_kcontrol_new kcontrol_dai_link[] = { + SOC_ENUM_EXT(NULL, w_param_enum[0], + snd_soc_dapm_dai_link_get, + snd_soc_dapm_dai_link_put), + }; + const struct snd_soc_pcm_stream *config = params; + + w_param_text = devm_kcalloc(card->dev, num_params, + sizeof(char *), GFP_KERNEL); + if (!w_param_text) return -ENOMEM; - snprintf(link_name, len, "%s-%s", source->name, sink->name); + + link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", + source->name, sink->name); + if (!link_name) { + ret = -ENOMEM; + goto outfree_w_param; + } + + for (count = 0 ; count < num_params; count++) { + if (!config->stream_name) { + dev_warn(card->dapm.dev, + "ASoC: anonymous config %d for dai link %s\n", + count, link_name); + w_param_text[count] = + devm_kasprintf(card->dev, GFP_KERNEL, + "Anonymous Configuration %d", + count); + if (!w_param_text[count]) { + ret = -ENOMEM; + goto outfree_link_name; + } + } else { + w_param_text[count] = devm_kmemdup(card->dev, + config->stream_name, + strlen(config->stream_name) + 1, + GFP_KERNEL); + if (!w_param_text[count]) { + ret = -ENOMEM; + goto outfree_link_name; + } + } + config++; + } + w_param_enum[0].items = num_params; + w_param_enum[0].texts = w_param_text; memset(&template, 0, sizeof(template)); template.reg = SND_SOC_NOPM; @@ -3309,6 +3410,30 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, template.event = snd_soc_dai_link_event; template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD; + template.num_kcontrols = 1; + /* duplicate w_param_enum on heap so that memory persists */ + private_value = + (unsigned long) devm_kmemdup(card->dev, + (void *)(kcontrol_dai_link[0].private_value), + sizeof(struct soc_enum), GFP_KERNEL); + if (!private_value) { + dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", + link_name); + ret = -ENOMEM; + goto outfree_link_name; + } + kcontrol_dai_link[0].private_value = private_value; + /* duplicate kcontrol_dai_link on heap so that memory persists */ + template.kcontrol_news = + devm_kmemdup(card->dev, &kcontrol_dai_link[0], + sizeof(struct snd_kcontrol_new), + GFP_KERNEL); + if (!template.kcontrol_news) { + dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", + link_name); + ret = -ENOMEM; + goto outfree_private_value; + } dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); @@ -3316,15 +3441,32 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, if (!w) { dev_err(card->dev, "ASoC: Failed to create %s widget\n", link_name); - return -ENOMEM; + ret = -ENOMEM; + goto outfree_kcontrol_news; } w->params = params; + w->num_params = num_params; ret = snd_soc_dapm_add_path(&card->dapm, source, w, NULL, NULL); if (ret) - return ret; + goto outfree_w; return snd_soc_dapm_add_path(&card->dapm, w, sink, NULL, NULL); + +outfree_w: + devm_kfree(card->dev, w); +outfree_kcontrol_news: + devm_kfree(card->dev, (void *)template.kcontrol_news); +outfree_private_value: + devm_kfree(card->dev, (void *)private_value); +outfree_link_name: + devm_kfree(card->dev, link_name); +outfree_w_param: + for (count = 0 ; count < num_params; count++) + devm_kfree(card->dev, (void *)w_param_text[count]); + devm_kfree(card->dev, w_param_text); + + return ret; } int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 4380dcc064a5..9f60c25c4568 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -22,30 +22,42 @@ #include <trace/events/asoc.h> /** - * snd_soc_jack_new - Create a new jack - * @codec: ASoC codec + * snd_soc_card_jack_new - Create a new jack + * @card: ASoC card * @id: an identifying string for this jack * @type: a bitmask of enum snd_jack_type values that can be detected by * this jack * @jack: structure to use for the jack + * @pins: Array of jack pins to be added to the jack or NULL + * @num_pins: Number of elements in the @pins array * * Creates a new jack object. * * Returns zero if successful, or a negative error code on failure. * On success jack will be initialised. */ -int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, - struct snd_soc_jack *jack) +int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, + struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins, + unsigned int num_pins) { + int ret; + mutex_init(&jack->mutex); - jack->codec = codec; + jack->card = card; INIT_LIST_HEAD(&jack->pins); INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); - return snd_jack_new(codec->component.card->snd_card, id, type, &jack->jack); + ret = snd_jack_new(card->snd_card, id, type, &jack->jack); + if (ret) + return ret; + + if (num_pins) + return snd_soc_jack_add_pins(jack, num_pins, pins); + + return 0; } -EXPORT_SYMBOL_GPL(snd_soc_jack_new); +EXPORT_SYMBOL_GPL(snd_soc_card_jack_new); /** * snd_soc_jack_report - Report the current status for a jack @@ -63,7 +75,6 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_new); */ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) { - struct snd_soc_codec *codec; struct snd_soc_dapm_context *dapm; struct snd_soc_jack_pin *pin; unsigned int sync = 0; @@ -74,8 +85,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) if (!jack) return; - codec = jack->codec; - dapm = &codec->dapm; + dapm = &jack->card->dapm; mutex_lock(&jack->mutex); @@ -175,12 +185,12 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, for (i = 0; i < count; i++) { if (!pins[i].pin) { - dev_err(jack->codec->dev, "ASoC: No name for pin %d\n", + dev_err(jack->card->dev, "ASoC: No name for pin %d\n", i); return -EINVAL; } if (!pins[i].mask) { - dev_err(jack->codec->dev, "ASoC: No mask for pin %d" + dev_err(jack->card->dev, "ASoC: No mask for pin %d" " (%s)\n", i, pins[i].pin); return -EINVAL; } @@ -260,7 +270,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) static irqreturn_t gpio_handler(int irq, void *data) { struct snd_soc_jack_gpio *gpio = data; - struct device *dev = gpio->jack->codec->component.card->dev; + struct device *dev = gpio->jack->card->dev; trace_snd_soc_jack_irq(gpio->name); @@ -299,7 +309,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, for (i = 0; i < count; i++) { if (!gpios[i].name) { - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: No name for gpio at index %d\n", i); ret = -EINVAL; goto undo; @@ -320,7 +330,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, } else { /* legacy GPIO number */ if (!gpio_is_valid(gpios[i].gpio)) { - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: Invalid gpio %d\n", gpios[i].gpio); ret = -EINVAL; @@ -350,7 +360,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (gpios[i].wake) { ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1); if (ret != 0) - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: Failed to mark GPIO at index %d as wake source: %d\n", i, ret); } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 6b0136e7cb88..35fe58f4fa86 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1097,8 +1097,9 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, stream ? "<-" : "->", be->dai_link->name); #ifdef CONFIG_DEBUG_FS - dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644, - fe->debugfs_dpcm_root, &dpcm->state); + if (fe->debugfs_dpcm_root) + dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644, + fe->debugfs_dpcm_root, &dpcm->state); #endif return 1; } @@ -2511,6 +2512,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) /* DAPM dai link stream work */ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + pcm->nonatomic = rtd->dai_link->nonatomic; rtd->pcm = pcm; pcm->private_data = rtd; @@ -2802,10 +2804,13 @@ static const struct file_operations dpcm_state_fops = { .llseek = default_llseek, }; -int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) +void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) { if (!rtd->dai_link) - return 0; + return; + + if (!rtd->card->debugfs_card_root) + return; rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name, rtd->card->debugfs_card_root); @@ -2813,13 +2818,11 @@ int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) dev_dbg(rtd->dev, "ASoC: Failed to create dpcm debugfs directory %s\n", rtd->dai_link->name); - return -EINVAL; + return; } rtd->debugfs_dpcm_state = debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root, rtd, &dpcm_state_fops); - - return 0; } #endif diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 769aca2fc5f5..ba272e21a6fa 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -101,16 +101,12 @@ static const struct snd_kcontrol_new tegra_alc5632_controls[] = { static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(rtd->card); - snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, - &tegra_alc5632_hs_jack); - snd_soc_jack_add_pins(&tegra_alc5632_hs_jack, - ARRAY_SIZE(tegra_alc5632_hs_jack_pins), - tegra_alc5632_hs_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET, + &tegra_alc5632_hs_jack, + tegra_alc5632_hs_jack_pins, + ARRAY_SIZE(tegra_alc5632_hs_jack_pins)); if (gpio_is_valid(machine->gpio_hp_det)) { tegra_alc5632_hp_jack_gpio.gpio = machine->gpio_hp_det; @@ -119,7 +115,7 @@ static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) &tegra_alc5632_hp_jack_gpio); } - snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "MICBIAS1"); return 0; } diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c index af3fb997b752..902da36581d1 100644 --- a/sound/soc/tegra/tegra_max98090.c +++ b/sound/soc/tegra/tegra_max98090.c @@ -133,24 +133,26 @@ static const struct snd_soc_dapm_widget tegra_max98090_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), }; static const struct snd_kcontrol_new tegra_max98090_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("Speakers"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), + SOC_DAPM_PIN_SWITCH("Int Mic"), }; static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; struct tegra_max98090 *machine = snd_soc_card_get_drvdata(rtd->card); if (gpio_is_valid(machine->gpio_hp_det)) { - snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE, - &tegra_max98090_hp_jack); - snd_soc_jack_add_pins(&tegra_max98090_hp_jack, - ARRAY_SIZE(tegra_max98090_hp_jack_pins), - tegra_max98090_hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphones", + SND_JACK_HEADPHONE, + &tegra_max98090_hp_jack, + tegra_max98090_hp_jack_pins, + ARRAY_SIZE(tegra_max98090_hp_jack_pins)); tegra_max98090_hp_jack_gpio.gpio = machine->gpio_hp_det; snd_soc_jack_add_gpios(&tegra_max98090_hp_jack, @@ -159,11 +161,11 @@ static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd) } if (gpio_is_valid(machine->gpio_mic_det)) { - snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, - &tegra_max98090_mic_jack); - snd_soc_jack_add_pins(&tegra_max98090_mic_jack, - ARRAY_SIZE(tegra_max98090_mic_jack_pins), - tegra_max98090_mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, + &tegra_max98090_mic_jack, + tegra_max98090_mic_jack_pins, + ARRAY_SIZE(tegra_max98090_mic_jack_pins)); tegra_max98090_mic_jack_gpio.gpio = machine->gpio_mic_det; snd_soc_jack_add_gpios(&tegra_max98090_mic_jack, diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index ed759a3076b8..773daecaa5e8 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c @@ -108,15 +108,11 @@ static const struct snd_kcontrol_new tegra_rt5640_controls[] = { static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(rtd->card); - snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE, - &tegra_rt5640_hp_jack); - snd_soc_jack_add_pins(&tegra_rt5640_hp_jack, - ARRAY_SIZE(tegra_rt5640_hp_jack_pins), - tegra_rt5640_hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphones", SND_JACK_HEADPHONE, + &tegra_rt5640_hp_jack, tegra_rt5640_hp_jack_pins, + ARRAY_SIZE(tegra_rt5640_hp_jack_pins)); if (gpio_is_valid(machine->gpio_hp_det)) { tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det; diff --git a/sound/soc/tegra/tegra_rt5677.c b/sound/soc/tegra/tegra_rt5677.c index e4cf978a6e3a..1470873ecde6 100644 --- a/sound/soc/tegra/tegra_rt5677.c +++ b/sound/soc/tegra/tegra_rt5677.c @@ -141,15 +141,11 @@ static const struct snd_kcontrol_new tegra_rt5677_controls[] = { static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(rtd->card); - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, - &tegra_rt5677_hp_jack); - snd_soc_jack_add_pins(&tegra_rt5677_hp_jack, 1, - &tegra_rt5677_hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, + &tegra_rt5677_hp_jack, + &tegra_rt5677_hp_jack_pins, 1); if (gpio_is_valid(machine->gpio_hp_det)) { tegra_rt5677_hp_jack_gpio.gpio = machine->gpio_hp_det; @@ -158,10 +154,9 @@ static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd) } - snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, - &tegra_rt5677_mic_jack); - snd_soc_jack_add_pins(&tegra_rt5677_mic_jack, 1, - &tegra_rt5677_mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE, + &tegra_rt5677_mic_jack, + &tegra_rt5677_mic_jack_pins, 1); if (gpio_is_valid(machine->gpio_mic_present)) { tegra_rt5677_mic_jack_gpio.gpio = machine->gpio_mic_present; @@ -169,7 +164,7 @@ static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd) &tegra_rt5677_mic_jack_gpio); } - snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "MICBIAS1"); return 0; } @@ -331,7 +326,6 @@ static const struct of_device_id tegra_rt5677_of_match[] = { static struct platform_driver tegra_rt5677_driver = { .driver = { .name = DRV_NAME, - .owner = THIS_MODULE, .pm = &snd_soc_pm_ops, .of_match_table = tegra_rt5677_of_match, }, diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index e52420dae2b4..21604009bc1a 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -171,31 +171,28 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = codec_dai->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_card *card = rtd->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); if (gpio_is_valid(machine->gpio_hp_det)) { tegra_wm8903_hp_jack_gpio.gpio = machine->gpio_hp_det; - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, - &tegra_wm8903_hp_jack); - snd_soc_jack_add_pins(&tegra_wm8903_hp_jack, - ARRAY_SIZE(tegra_wm8903_hp_jack_pins), - tegra_wm8903_hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &tegra_wm8903_hp_jack, + tegra_wm8903_hp_jack_pins, + ARRAY_SIZE(tegra_wm8903_hp_jack_pins)); snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack, 1, &tegra_wm8903_hp_jack_gpio); } - snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, - &tegra_wm8903_mic_jack); - snd_soc_jack_add_pins(&tegra_wm8903_mic_jack, - ARRAY_SIZE(tegra_wm8903_mic_jack_pins), - tegra_wm8903_mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE, + &tegra_wm8903_mic_jack, + tegra_wm8903_mic_jack_pins, + ARRAY_SIZE(tegra_wm8903_mic_jack_pins)); wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE, 0); - snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(&card->dapm, "MICBIAS"); return 0; } diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c index 2868b4839bc0..6492f8143ff1 100644 --- a/sound/soc/tegra/tegra_wm9712.c +++ b/sound/soc/tegra/tegra_wm9712.c @@ -46,11 +46,7 @@ static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = { static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - return snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + return snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "Mic Bias"); } static struct snd_soc_dai_link tegra_wm9712_dai = { diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index aa65370db82a..b81a7a4c938b 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -362,7 +362,7 @@ struct snd_soc_ops mop500_ab8500_ops[] = { int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &rtd->card->dapm; struct device *dev = rtd->card->dev; struct mop500_ab8500_drvdata *drvdata; int ret; @@ -407,23 +407,23 @@ int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_dapm_disable_pin(&codec->dapm, "Earpiece"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Speaker Left"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Speaker Right"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "LineOut Left"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "LineOut Right"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Vibra 1"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Vibra 2"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Mic 1"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Mic 2"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "LineIn Left"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "LineIn Right"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 1"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 2"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 3"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 4"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 5"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 6"); + ret = snd_soc_dapm_disable_pin(dapm, "Earpiece"); + ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Left"); + ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Right"); + ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Left"); + ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Right"); + ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 1"); + ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 2"); + ret |= snd_soc_dapm_disable_pin(dapm, "Mic 1"); + ret |= snd_soc_dapm_disable_pin(dapm, "Mic 2"); + ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Left"); + ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Right"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 1"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 2"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 3"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 4"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 5"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 6"); return ret; } diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index dc9df007d3e3..337c317ead6f 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -192,6 +192,7 @@ static const struct rc_config { { USB_ID(0x041e, 0x3040), 2, 2, 6, 6, 2, 0x6e91 }, /* Live! 24-bit */ { USB_ID(0x041e, 0x3042), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 */ { USB_ID(0x041e, 0x30df), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */ + { USB_ID(0x041e, 0x3237), 0, 1, 1, 1, 1, 0x000d }, /* Usb X-Fi S51 Pro */ { USB_ID(0x041e, 0x3048), 2, 2, 6, 6, 2, 0x6e91 }, /* Toshiba SB0500 */ }; diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 753a47de8459..7c5a70139278 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1113,24 +1113,37 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs, bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) { - /* MS Lifecam HD-5000 doesn't support reading the sample rate. */ - return chip->usb_id == USB_ID(0x045E, 0x076D); + /* devices which do not support reading the sample rate. */ + switch (chip->usb_id) { + case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */ + case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */ + case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ + return true; + } + return false; } /* Marantz/Denon USB DACs need a vendor cmd to switch * between PCM and native DSD mode */ +static bool is_marantz_denon_dac(unsigned int id) +{ + switch (id) { + case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */ + case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ + case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ + return true; + } + return false; +} + int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, struct audioformat *fmt) { struct usb_device *dev = subs->dev; int err; - switch (subs->stream->chip->usb_id) { - case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */ - case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ - case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ - + if (is_marantz_denon_dac(subs->stream->chip->usb_id)) { /* First switch to alt set 0, otherwise the mode switch cmd * will not be accepted by the DAC */ @@ -1203,17 +1216,10 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, /* Marantz/Denon devices with USB DAC functionality need a delay * after each class compliant request */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x154e) && - (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) { - - switch (le16_to_cpu(dev->descriptor.idProduct)) { - case 0x1003: /* Denon DA300-USB */ - case 0x3005: /* Marantz HD-DAC1 */ - case 0x3006: /* Marantz SA-14S1 */ - mdelay(20); - break; - } - } + if (is_marantz_denon_dac(USB_ID(le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct))) + && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) + mdelay(20); /* Zoom R16/24 needs a tiny delay here, otherwise requests like * get/set frequency return as failed despite actually succeeding. @@ -1268,15 +1274,9 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, } /* Denon/Marantz devices with USB DAC functionality */ - switch (chip->usb_id) { - case USB_ID(0x154e, 0x1003): /* Denon DA300-USB */ - case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ - case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ + if (is_marantz_denon_dac(chip->usb_id)) { if (fp->altsetting == 2) return SNDRV_PCM_FMTBIT_DSD_U32_BE; - break; - default: - break; } return 0; |