diff options
Diffstat (limited to 'sound')
543 files changed, 21359 insertions, 6665 deletions
diff --git a/sound/Kconfig b/sound/Kconfig index 0ddfb717b81d..f0e15822e858 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -39,8 +39,6 @@ config SOUND_OSS_CORE_PRECLAIM source "sound/oss/dmasound/Kconfig" -if !UML - menuconfig SND tristate "Advanced Linux Sound Architecture" help @@ -103,8 +101,6 @@ source "sound/virtio/Kconfig" endif # SND -endif # !UML - endif # SOUND config AC97_BUS diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c index 6067c04ce4c0..3173e9d98927 100644 --- a/sound/ac97/bus.c +++ b/sound/ac97/bus.c @@ -462,7 +462,7 @@ static ssize_t vendor_id_show(struct device *dev, return sysfs_emit(buf, "%08x", codec->vendor_id); } -DEVICE_ATTR_RO(vendor_id); +static DEVICE_ATTR_RO(vendor_id); static struct attribute *ac97_dev_attrs[] = { &dev_attr_vendor_id.attr, diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c index a9e502a6cdeb..07df5cc0f2d7 100644 --- a/sound/aoa/soundbus/i2sbus/pcm.c +++ b/sound/aoa/soundbus/i2sbus/pcm.c @@ -972,7 +972,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, goto out_put_ci_module; snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, &i2sbus_playback_ops); - dev->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].dev.parent = + dev->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].dev->parent = &dev->ofdev.dev; i2sdev->out.created = 1; } @@ -989,7 +989,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, goto out_put_ci_module; snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, &i2sbus_record_ops); - dev->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].dev.parent = + dev->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].dev->parent = &dev->ofdev.dev; i2sdev->in.created = 1; } diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index c8912b8a1dc5..402b5f66dcc3 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -12,13 +12,12 @@ #include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mutex.h> #include <linux/types.h> #include <linux/io.h> -#include <linux/of.h> -#include <linux/of_device.h> #include <sound/core.h> #include <sound/initval.h> diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 30f73097447b..619371aa9964 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -546,7 +546,7 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, if (stream->runtime->dma_buffer_p) { if (buffer_size > stream->runtime->dma_buffer_p->bytes) - dev_err(&stream->device->dev, + dev_err(stream->device->dev, "Not enough DMA buffer"); else buffer = stream->runtime->dma_buffer_p->area; @@ -1070,7 +1070,7 @@ static int snd_compress_dev_register(struct snd_device *device) /* register compressed device */ ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, compr->device, - &snd_compr_file_ops, compr, &compr->dev); + &snd_compr_file_ops, compr, compr->dev); if (ret < 0) { pr_err("snd_register_device failed %d\n", ret); return ret; @@ -1084,7 +1084,7 @@ static int snd_compress_dev_disconnect(struct snd_device *device) struct snd_compr *compr; compr = device->device_data; - snd_unregister_device(&compr->dev); + snd_unregister_device(compr->dev); return 0; } @@ -1158,7 +1158,7 @@ static int snd_compress_dev_free(struct snd_device *device) compr = device->device_data; snd_compress_proc_done(compr); - put_device(&compr->dev); + put_device(compr->dev); return 0; } @@ -1189,12 +1189,16 @@ int snd_compress_new(struct snd_card *card, int device, snd_compress_set_id(compr, id); - snd_device_initialize(&compr->dev, card); - dev_set_name(&compr->dev, "comprC%iD%i", card->number, device); + ret = snd_device_alloc(&compr->dev, card); + if (ret) + return ret; + dev_set_name(compr->dev, "comprC%iD%i", card->number, device); ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops); if (ret == 0) snd_compress_proc_init(compr); + else + put_device(compr->dev); return ret; } diff --git a/sound/core/control.c b/sound/core/control.c index 8386b53acdcd..59c8658966d4 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -39,6 +39,9 @@ static LIST_HEAD(snd_control_compat_ioctls); #endif static struct snd_ctl_layer_ops *snd_ctl_layer; +static int snd_ctl_remove_locked(struct snd_card *card, + struct snd_kcontrol *kcontrol); + static int snd_ctl_open(struct inode *inode, struct file *file) { unsigned long flags; @@ -466,11 +469,13 @@ static int __snd_ctl_add_replace(struct snd_card *card, struct snd_kcontrol *old; int err; + lockdep_assert_held_write(&card->controls_rwsem); + id = kcontrol->id; if (id.index > UINT_MAX - kcontrol->count) return -EINVAL; - old = snd_ctl_find_id(card, &id); + old = snd_ctl_find_id_locked(card, &id); if (!old) { if (mode == CTL_REPLACE) return -EINVAL; @@ -483,7 +488,7 @@ static int __snd_ctl_add_replace(struct snd_card *card, return -EBUSY; } - err = snd_ctl_remove(card, old); + err = snd_ctl_remove_locked(card, old); if (err < 0) return err; } @@ -575,6 +580,8 @@ static int __snd_ctl_remove(struct snd_card *card, { unsigned int idx; + lockdep_assert_held_write(&card->controls_rwsem); + if (snd_BUG_ON(!card || !kcontrol)) return -EINVAL; list_del(&kcontrol->list); @@ -589,20 +596,32 @@ static int __snd_ctl_remove(struct snd_card *card, return 0; } +static inline int snd_ctl_remove_locked(struct snd_card *card, + struct snd_kcontrol *kcontrol) +{ + return __snd_ctl_remove(card, kcontrol, true); +} + /** * snd_ctl_remove - remove the control from the card and release it * @card: the card instance * @kcontrol: the control instance to remove * * Removes the control from the card and then releases the instance. - * You don't need to call snd_ctl_free_one(). You must be in - * the write lock - down_write(&card->controls_rwsem). + * You don't need to call snd_ctl_free_one(). * * Return: 0 if successful, or a negative error code on failure. + * + * Note that this function takes card->controls_rwsem lock internally. */ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol) { - return __snd_ctl_remove(card, kcontrol, true); + int ret; + + down_write(&card->controls_rwsem); + ret = snd_ctl_remove_locked(card, kcontrol); + up_write(&card->controls_rwsem); + return ret; } EXPORT_SYMBOL(snd_ctl_remove); @@ -622,12 +641,12 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id) int ret; down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, id); + kctl = snd_ctl_find_id_locked(card, id); if (kctl == NULL) { up_write(&card->controls_rwsem); return -ENOENT; } - ret = snd_ctl_remove(card, kctl); + ret = snd_ctl_remove_locked(card, kctl); up_write(&card->controls_rwsem); return ret; } @@ -651,7 +670,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file, int idx, ret; down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, id); + kctl = snd_ctl_find_id_locked(card, id); if (kctl == NULL) { ret = -ENOENT; goto error; @@ -665,7 +684,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file, ret = -EBUSY; goto error; } - ret = snd_ctl_remove(card, kctl); + ret = snd_ctl_remove_locked(card, kctl); error: up_write(&card->controls_rwsem); return ret; @@ -692,7 +711,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, int ret; down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, id); + kctl = snd_ctl_find_id_locked(card, id); if (kctl == NULL) { ret = -ENOENT; goto unlock; @@ -746,7 +765,7 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id, int saved_numid; down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, src_id); + kctl = snd_ctl_find_id_locked(card, src_id); if (kctl == NULL) { up_write(&card->controls_rwsem); return -ENOENT; @@ -769,11 +788,12 @@ EXPORT_SYMBOL(snd_ctl_rename_id); * * Renames the specified control on the card to the new name. * - * Make sure to take the control write lock - down_write(&card->controls_rwsem). + * Note that this function takes card->controls_rwsem lock internally. */ void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl, const char *name) { + down_write(&card->controls_rwsem); remove_hash_entries(card, kctl); if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0) @@ -781,6 +801,7 @@ void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl, name, kctl->id.name); add_hash_entries(card, kctl); + up_write(&card->controls_rwsem); } EXPORT_SYMBOL(snd_ctl_rename); @@ -799,7 +820,7 @@ snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid) #endif /* !CONFIG_SND_CTL_FAST_LOOKUP */ /** - * snd_ctl_find_numid - find the control instance with the given number-id + * snd_ctl_find_numid_locked - find the control instance with the given number-id * @card: the card instance * @numid: the number-id to search * @@ -809,22 +830,46 @@ snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid) * (if the race condition can happen). * * Return: The pointer of the instance if found, or %NULL if not. - * */ -struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid) +struct snd_kcontrol * +snd_ctl_find_numid_locked(struct snd_card *card, unsigned int numid) { if (snd_BUG_ON(!card || !numid)) return NULL; + lockdep_assert_held(&card->controls_rwsem); #ifdef CONFIG_SND_CTL_FAST_LOOKUP return xa_load(&card->ctl_numids, numid); #else return snd_ctl_find_numid_slow(card, numid); #endif } +EXPORT_SYMBOL(snd_ctl_find_numid_locked); + +/** + * snd_ctl_find_numid - find the control instance with the given number-id + * @card: the card instance + * @numid: the number-id to search + * + * Finds the control instance with the given number-id from the card. + * + * Return: The pointer of the instance if found, or %NULL if not. + * + * Note that this function takes card->controls_rwsem lock internally. + */ +struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, + unsigned int numid) +{ + struct snd_kcontrol *kctl; + + down_read(&card->controls_rwsem); + kctl = snd_ctl_find_numid_locked(card, numid); + up_read(&card->controls_rwsem); + return kctl; +} EXPORT_SYMBOL(snd_ctl_find_numid); /** - * snd_ctl_find_id - find the control instance with the given id + * snd_ctl_find_id_locked - find the control instance with the given id * @card: the card instance * @id: the id to search * @@ -834,17 +879,17 @@ EXPORT_SYMBOL(snd_ctl_find_numid); * (if the race condition can happen). * * Return: The pointer of the instance if found, or %NULL if not. - * */ -struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, - struct snd_ctl_elem_id *id) +struct snd_kcontrol *snd_ctl_find_id_locked(struct snd_card *card, + const struct snd_ctl_elem_id *id) { struct snd_kcontrol *kctl; if (snd_BUG_ON(!card || !id)) return NULL; + lockdep_assert_held(&card->controls_rwsem); if (id->numid != 0) - return snd_ctl_find_numid(card, id->numid); + return snd_ctl_find_numid_locked(card, id->numid); #ifdef CONFIG_SND_CTL_FAST_LOOKUP kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id)); if (kctl && elem_id_matches(kctl, id)) @@ -859,6 +904,29 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, return NULL; } +EXPORT_SYMBOL(snd_ctl_find_id_locked); + +/** + * snd_ctl_find_id - find the control instance with the given id + * @card: the card instance + * @id: the id to search + * + * Finds the control instance with the given id from the card. + * + * Return: The pointer of the instance if found, or %NULL if not. + * + * Note that this function takes card->controls_rwsem lock internally. + */ +struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, + const struct snd_ctl_elem_id *id) +{ + struct snd_kcontrol *kctl; + + down_read(&card->controls_rwsem); + kctl = snd_ctl_find_id_locked(card, id); + up_read(&card->controls_rwsem); + return kctl; +} EXPORT_SYMBOL(snd_ctl_find_id); static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, @@ -1173,7 +1241,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl, int result; down_read(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, &info->id); + kctl = snd_ctl_find_id_locked(card, &info->id); if (kctl == NULL) result = -ENOENT; else @@ -1212,7 +1280,7 @@ static int snd_ctl_elem_read(struct snd_card *card, int ret; down_read(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, &control->id); + kctl = snd_ctl_find_id_locked(card, &control->id); if (kctl == NULL) { ret = -ENOENT; goto unlock; @@ -1289,7 +1357,7 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, int result; down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, &control->id); + kctl = snd_ctl_find_id_locked(card, &control->id); if (kctl == NULL) { up_write(&card->controls_rwsem); return -ENOENT; @@ -1370,7 +1438,7 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file, if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT; down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, &id); + kctl = snd_ctl_find_id_locked(card, &id); if (kctl == NULL) { result = -ENOENT; } else { @@ -1398,7 +1466,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT; down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, &id); + kctl = snd_ctl_find_id_locked(card, &id); if (kctl == NULL) { result = -ENOENT; } else { @@ -1507,6 +1575,8 @@ static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf, int i; int change; + lockdep_assert_held_write(&ue->card->controls_rwsem); + if (size > 1024 * 128) /* sane value */ return -EINVAL; @@ -1583,6 +1653,8 @@ static int snd_ctl_elem_init_enum_names(struct user_element *ue) unsigned int i; const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr; + lockdep_assert_held_write(&ue->card->controls_rwsem); + buf_len = ue->info.value.enumerated.names_length; if (buf_len > 64 * 1024) return -EINVAL; @@ -1887,6 +1959,8 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, struct snd_ctl_elem_id id; struct snd_kcontrol_volatile *vd; + lockdep_assert_held(&file->card->controls_rwsem); + if (copy_from_user(&header, buf, sizeof(header))) return -EFAULT; @@ -1900,7 +1974,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, container_size = header.length; container = buf->tlv; - kctl = snd_ctl_find_numid(file->card, header.numid); + kctl = snd_ctl_find_numid_locked(file->card, header.numid); if (kctl == NULL) return -ENOENT; @@ -2315,7 +2389,7 @@ static int snd_ctl_dev_register(struct snd_device *device) int err; err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, - &snd_ctl_f_ops, card, &card->ctl_dev); + &snd_ctl_f_ops, card, card->ctl_dev); if (err < 0) return err; down_read(&card->controls_rwsem); @@ -2351,7 +2425,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device) up_read(&snd_ctl_layer_rwsem); up_read(&card->controls_rwsem); - return snd_unregister_device(&card->ctl_dev); + return snd_unregister_device(card->ctl_dev); } /* @@ -2373,7 +2447,7 @@ static int snd_ctl_dev_free(struct snd_device *device) xa_destroy(&card->ctl_hash); #endif up_write(&card->controls_rwsem); - put_device(&card->ctl_dev); + put_device(card->ctl_dev); return 0; } @@ -2395,12 +2469,14 @@ int snd_ctl_create(struct snd_card *card) if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS)) return -ENXIO; - snd_device_initialize(&card->ctl_dev, card); - dev_set_name(&card->ctl_dev, "controlC%d", card->number); + err = snd_device_alloc(&card->ctl_dev, card); + if (err < 0) + return err; + dev_set_name(card->ctl_dev, "controlC%d", card->number); err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); if (err < 0) - put_device(&card->ctl_dev); + put_device(card->ctl_dev); return err; } diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 9cae5d74335c..0e8b1bfb040e 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -173,7 +173,7 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, int err; down_read(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, id); + kctl = snd_ctl_find_id_locked(card, id); if (! kctl) { up_read(&card->controls_rwsem); return -ENOENT; diff --git a/sound/core/control_led.c b/sound/core/control_led.c index ee77547bf8dc..a78eb48927c7 100644 --- a/sound/core/control_led.c +++ b/sound/core/control_led.c @@ -251,7 +251,7 @@ static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id, card = snd_card_ref(card_number); if (card) { down_write(&card->controls_rwsem); - kctl = snd_ctl_find_id(card, id); + kctl = snd_ctl_find_id_locked(card, id); if (kctl) { ioff = snd_ctl_get_ioff(kctl, id); vd = &kctl->vd[ioff]; @@ -688,7 +688,7 @@ static void snd_ctl_led_sysfs_add(struct snd_card *card) goto cerr; led->cards[card->number] = led_card; snprintf(link_name, sizeof(link_name), "led-%s", led->name); - WARN(sysfs_create_link(&card->ctl_dev.kobj, &led_card->dev.kobj, link_name), + WARN(sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj, link_name), "can't create symlink to controlC%i device\n", card->number); WARN(sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, "card"), "can't create symlink to card%i\n", card->number); @@ -714,7 +714,7 @@ static void snd_ctl_led_sysfs_remove(struct snd_card *card) if (!led_card) continue; snprintf(link_name, sizeof(link_name), "led-%s", led->name); - sysfs_remove_link(&card->ctl_dev.kobj, link_name); + sysfs_remove_link(&card->ctl_dev->kobj, link_name); sysfs_remove_link(&led_card->dev.kobj, "card"); device_unregister(&led_card->dev); led->cards[card->number] = NULL; diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index e95fa275c289..de7476034f2c 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -338,9 +338,14 @@ static const struct file_operations snd_hwdep_f_ops = .mmap = snd_hwdep_mmap, }; -static void release_hwdep_device(struct device *dev) +static void snd_hwdep_free(struct snd_hwdep *hwdep) { - kfree(container_of(dev, struct snd_hwdep, dev)); + if (!hwdep) + return; + if (hwdep->private_free) + hwdep->private_free(hwdep); + put_device(hwdep->dev); + kfree(hwdep); } /** @@ -382,16 +387,20 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device, if (id) strscpy(hwdep->id, id, sizeof(hwdep->id)); - snd_device_initialize(&hwdep->dev, card); - hwdep->dev.release = release_hwdep_device; - dev_set_name(&hwdep->dev, "hwC%iD%i", card->number, device); + err = snd_device_alloc(&hwdep->dev, card); + if (err < 0) { + snd_hwdep_free(hwdep); + return err; + } + + dev_set_name(hwdep->dev, "hwC%iD%i", card->number, device); #ifdef CONFIG_SND_OSSEMUL hwdep->oss_type = -1; #endif err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops); if (err < 0) { - put_device(&hwdep->dev); + snd_hwdep_free(hwdep); return err; } @@ -403,12 +412,7 @@ EXPORT_SYMBOL(snd_hwdep_new); static int snd_hwdep_dev_free(struct snd_device *device) { - struct snd_hwdep *hwdep = device->device_data; - if (!hwdep) - return 0; - if (hwdep->private_free) - hwdep->private_free(hwdep); - put_device(&hwdep->dev); + snd_hwdep_free(device->device_data); return 0; } @@ -426,9 +430,9 @@ static int snd_hwdep_dev_register(struct snd_device *device) list_add_tail(&hwdep->list, &snd_hwdep_devices); err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device, - &snd_hwdep_f_ops, hwdep, &hwdep->dev); + &snd_hwdep_f_ops, hwdep, hwdep->dev); if (err < 0) { - dev_err(&hwdep->dev, "unable to register\n"); + dev_err(hwdep->dev, "unable to register\n"); list_del(&hwdep->list); mutex_unlock(®ister_mutex); return err; @@ -439,12 +443,12 @@ static int snd_hwdep_dev_register(struct snd_device *device) if (hwdep->oss_type >= 0) { if (hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM && hwdep->device) - dev_warn(&hwdep->dev, + dev_warn(hwdep->dev, "only hwdep device 0 can be registered as OSS direct FM device!\n"); else if (snd_register_oss_device(hwdep->oss_type, card, hwdep->device, &snd_hwdep_f_ops, hwdep) < 0) - dev_warn(&hwdep->dev, + dev_warn(hwdep->dev, "unable to register OSS compatibility device\n"); else hwdep->ossreg = 1; @@ -471,7 +475,7 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device) if (hwdep->ossreg) snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); #endif - snd_unregister_device(&hwdep->dev); + snd_unregister_device(hwdep->dev); list_del_init(&hwdep->list); mutex_unlock(&hwdep->open_mutex); mutex_unlock(®ister_mutex); diff --git a/sound/core/init.c b/sound/core/init.c index baef2688d0cf..d61bde1225f2 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -111,28 +111,36 @@ static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int), return mask; /* unchanged */ } -/* the default release callback set in snd_device_initialize() below; - * this is just NOP for now, as almost all jobs are already done in - * dev_free callback of snd_device chain instead. - */ -static void default_release(struct device *dev) +/* the default release callback set in snd_device_alloc() */ +static void default_release_alloc(struct device *dev) { + kfree(dev); } /** - * snd_device_initialize - Initialize struct device for sound devices - * @dev: device to initialize + * snd_device_alloc - Allocate and initialize struct device for sound devices + * @dev_p: pointer to store the allocated device * @card: card to assign, optional + * + * For releasing the allocated device, call put_device(). */ -void snd_device_initialize(struct device *dev, struct snd_card *card) +int snd_device_alloc(struct device **dev_p, struct snd_card *card) { + struct device *dev; + + *dev_p = NULL; + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; device_initialize(dev); if (card) dev->parent = &card->card_dev; dev->class = &sound_class; - dev->release = default_release; + dev->release = default_release_alloc; + *dev_p = dev; + return 0; } -EXPORT_SYMBOL_GPL(snd_device_initialize); +EXPORT_SYMBOL_GPL(snd_device_alloc); static int snd_card_init(struct snd_card *card, struct device *parent, int idx, const char *xid, struct module *module, diff --git a/sound/core/jack.c b/sound/core/jack.c index 03d155ed362b..e0f034e7275c 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -66,12 +66,10 @@ static int snd_jack_dev_free(struct snd_device *device) struct snd_card *card = device->card; struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl; - down_write(&card->controls_rwsem); list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) { list_del_init(&jack_kctl->list); snd_ctl_remove(card, jack_kctl->kctl); } - up_write(&card->controls_rwsem); if (jack->private_free) jack->private_free(jack); diff --git a/sound/core/memory.c b/sound/core/memory.c index 5d894dc32f7d..2d2d0094c897 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -9,6 +9,7 @@ #include <linux/io.h> #include <linux/uaccess.h> #include <sound/core.h> +#include <sound/pcm.h> /** * copy_to_user_fromio - copy data from mmio-space to user-space @@ -22,8 +23,29 @@ */ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count) { + struct iov_iter iter; + + if (import_ubuf(ITER_DEST, dst, count, &iter)) + return -EFAULT; + return copy_to_iter_fromio(&iter, (const void __iomem *)src, count); +} +EXPORT_SYMBOL(copy_to_user_fromio); + +/** + * copy_to_iter_fromio - copy data from mmio-space to iov_iter + * @dst: the destination iov_iter + * @src: the source pointer on mmio + * @count: the data size to copy in bytes + * + * Copies the data from mmio-space to iov_iter. + * + * Return: Zero if successful, or non-zero on failure. + */ +int copy_to_iter_fromio(struct iov_iter *dst, const void __iomem *src, + size_t count) +{ #if defined(__i386__) || defined(CONFIG_SPARC32) - return copy_to_user(dst, (const void __force*)src, count) ? -EFAULT : 0; + return copy_to_iter((const void __force *)src, count, dst) == count ? 0 : -EFAULT; #else char buf[256]; while (count) { @@ -31,16 +53,15 @@ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size if (c > sizeof(buf)) c = sizeof(buf); memcpy_fromio(buf, (void __iomem *)src, c); - if (copy_to_user(dst, buf, c)) + if (copy_to_iter(buf, c, dst) != c) return -EFAULT; count -= c; - dst += c; src += c; } return 0; #endif } -EXPORT_SYMBOL(copy_to_user_fromio); +EXPORT_SYMBOL(copy_to_iter_fromio); /** * copy_from_user_toio - copy data from user-space to mmio-space @@ -54,22 +75,41 @@ EXPORT_SYMBOL(copy_to_user_fromio); */ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count) { + struct iov_iter iter; + + if (import_ubuf(ITER_SOURCE, (void __user *)src, count, &iter)) + return -EFAULT; + return copy_from_iter_toio((void __iomem *)dst, &iter, count); +} +EXPORT_SYMBOL(copy_from_user_toio); + +/** + * copy_from_iter_toio - copy data from iov_iter to mmio-space + * @dst: the destination pointer on mmio-space + * @src: the source iov_iter + * @count: the data size to copy in bytes + * + * Copies the data from iov_iter to mmio-space. + * + * Return: Zero if successful, or non-zero on failure. + */ +int copy_from_iter_toio(void __iomem *dst, struct iov_iter *src, size_t count) +{ #if defined(__i386__) || defined(CONFIG_SPARC32) - return copy_from_user((void __force *)dst, src, count) ? -EFAULT : 0; + return copy_from_iter((void __force *)dst, count, src) == count ? 0 : -EFAULT; #else char buf[256]; while (count) { size_t c = count; if (c > sizeof(buf)) c = sizeof(buf); - if (copy_from_user(buf, src, c)) + if (copy_from_iter(buf, c, src) != c) return -EFAULT; memcpy_toio(dst, buf, c); count -= c; dst += c; - src += c; } return 0; #endif } -EXPORT_SYMBOL(copy_from_user_toio); +EXPORT_SYMBOL(copy_from_iter_toio); diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 9620115cfdc0..dae2da380835 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -524,7 +524,7 @@ static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, c id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(id.name, name, sizeof(id.name)); id.index = index; - return snd_ctl_find_id(card, &id); + return snd_ctl_find_id_locked(card, &id); } static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer, @@ -540,7 +540,7 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; down_read(&card->controls_rwsem); - kctl = snd_ctl_find_numid(card, numid); + kctl = snd_ctl_find_numid_locked(card, numid); if (!kctl) { up_read(&card->controls_rwsem); return; @@ -579,7 +579,7 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; down_read(&card->controls_rwsem); - kctl = snd_ctl_find_numid(card, numid); + kctl = snd_ctl_find_numid_locked(card, numid); if (!kctl) { up_read(&card->controls_rwsem); return; @@ -645,7 +645,7 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; down_read(&card->controls_rwsem); - kctl = snd_ctl_find_numid(card, numid); + kctl = snd_ctl_find_numid_locked(card, numid); if (!kctl) { up_read(&card->controls_rwsem); return; @@ -688,7 +688,7 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, if (numid == ID_UNKNOWN) return; down_read(&card->controls_rwsem); - kctl = snd_ctl_find_numid(card, numid); + kctl = snd_ctl_find_numid_locked(card, numid); if (!kctl) { up_read(&card->controls_rwsem); return; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 9d95e3731123..20bb2d7c8d4b 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -604,7 +604,7 @@ static const struct attribute_group *pcm_dev_attr_groups[]; #ifdef CONFIG_PM_SLEEP static int do_pcm_suspend(struct device *dev) { - struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); + struct snd_pcm_str *pstr = dev_get_drvdata(dev); if (!pstr->pcm->no_device_suspend) snd_pcm_suspend_all(pstr->pcm); @@ -650,11 +650,14 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) if (!substream_count) return 0; - snd_device_initialize(&pstr->dev, pcm->card); - pstr->dev.groups = pcm_dev_attr_groups; - pstr->dev.type = &pcm_dev_type; - dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, + err = snd_device_alloc(&pstr->dev, pcm->card); + if (err < 0) + return err; + dev_set_name(pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); + pstr->dev->groups = pcm_dev_attr_groups; + pstr->dev->type = &pcm_dev_type; + dev_set_drvdata(pstr->dev, pstr); if (!pcm->internal) { err = snd_pcm_stream_proc_init(pstr); @@ -814,9 +817,7 @@ static void free_chmap(struct snd_pcm_str *pstr) if (pstr->chmap_kctl) { struct snd_card *card = pstr->pcm->card; - down_write(&card->controls_rwsem); snd_ctl_remove(card, pstr->chmap_kctl); - up_write(&card->controls_rwsem); pstr->chmap_kctl = NULL; } } @@ -847,7 +848,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr) #endif free_chmap(pstr); if (pstr->substream_count) - put_device(&pstr->dev); + put_device(pstr->dev); } #if IS_ENABLED(CONFIG_SND_PCM_OSS) @@ -1017,7 +1018,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) static ssize_t pcm_class_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); + struct snd_pcm_str *pstr = dev_get_drvdata(dev); struct snd_pcm *pcm = pstr->pcm; const char *str; static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { @@ -1078,7 +1079,7 @@ static int snd_pcm_dev_register(struct snd_device *device) /* register pcm */ err = snd_register_device(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx], pcm, - &pcm->streams[cidx].dev); + pcm->streams[cidx].dev); if (err < 0) { list_del_init(&pcm->list); goto unlock; @@ -1125,7 +1126,8 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) pcm_call_notify(pcm, n_disconnect); for (cidx = 0; cidx < 2; cidx++) { - snd_unregister_device(&pcm->streams[cidx].dev); + if (pcm->streams[cidx].dev) + snd_unregister_device(pcm->streams[cidx].dev); free_chmap(&pcm->streams[cidx]); } mutex_unlock(&pcm->open_mutex); diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 42c2ada8e888..c96483091f30 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -253,10 +253,14 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, goto error; } - if (refine) + if (refine) { err = snd_pcm_hw_refine(substream, data); - else + if (err < 0) + goto error; + err = fixup_unreferenced_params(substream, data); + } else { err = snd_pcm_hw_params(substream, data); + } if (err < 0) goto error; if (copy_to_user(data32, data, sizeof(*data32)) || diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 9c121a921b04..4859fb1caec9 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1973,10 +1973,11 @@ static int wait_for_avail(struct snd_pcm_substream *substream, typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - void *buf, unsigned long bytes); + struct iov_iter *iter, unsigned long bytes); typedef int (*pcm_copy_f)(struct snd_pcm_substream *, snd_pcm_uframes_t, void *, - snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f); + snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f, + bool); /* calculate the target DMA-buffer position to be written/read */ static void *get_dma_ptr(struct snd_pcm_runtime *runtime, @@ -1986,32 +1987,24 @@ static void *get_dma_ptr(struct snd_pcm_runtime *runtime, channel * (runtime->dma_bytes / runtime->channels); } -/* default copy_user ops for write; used for both interleaved and non- modes */ +/* default copy ops for write; used for both interleaved and non- modes */ static int default_write_copy(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - void *buf, unsigned long bytes) + struct iov_iter *iter, unsigned long bytes) { - if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff), - (void __user *)buf, bytes)) + if (!copy_from_iter(get_dma_ptr(substream->runtime, channel, hwoff), + bytes, iter)) return -EFAULT; return 0; } -/* default copy_kernel ops for write */ -static int default_write_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long hwoff, - void *buf, unsigned long bytes) -{ - memcpy(get_dma_ptr(substream->runtime, channel, hwoff), buf, bytes); - return 0; -} - /* fill silence instead of copy data; called as a transfer helper * from __snd_pcm_lib_write() or directly from noninterleaved_copy() when * a NULL buffer is passed */ static int fill_silence(struct snd_pcm_substream *substream, int channel, - unsigned long hwoff, void *buf, unsigned long bytes) + unsigned long hwoff, struct iov_iter *iter, + unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2027,25 +2020,41 @@ static int fill_silence(struct snd_pcm_substream *substream, int channel, return 0; } -/* default copy_user ops for read; used for both interleaved and non- modes */ +/* default copy ops for read; used for both interleaved and non- modes */ static int default_read_copy(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - void *buf, unsigned long bytes) + struct iov_iter *iter, unsigned long bytes) { - if (copy_to_user((void __user *)buf, - get_dma_ptr(substream->runtime, channel, hwoff), - bytes)) + if (!copy_to_iter(get_dma_ptr(substream->runtime, channel, hwoff), + bytes, iter)) return -EFAULT; return 0; } -/* default copy_kernel ops for read */ -static int default_read_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long hwoff, - void *buf, unsigned long bytes) +/* call transfer with the filled iov_iter */ +static int do_transfer(struct snd_pcm_substream *substream, int c, + unsigned long hwoff, void *data, unsigned long bytes, + pcm_transfer_f transfer, bool in_kernel) { - memcpy(buf, get_dma_ptr(substream->runtime, channel, hwoff), bytes); - return 0; + struct iov_iter iter; + int err, type; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + type = ITER_SOURCE; + else + type = ITER_DEST; + + if (in_kernel) { + struct kvec kvec = { data, bytes }; + + iov_iter_kvec(&iter, type, &kvec, 1, bytes); + return transfer(substream, c, hwoff, &iter, bytes); + } + + err = import_ubuf(type, (__force void __user *)data, bytes, &iter); + if (err) + return err; + return transfer(substream, c, hwoff, &iter, bytes); } /* call transfer function with the converted pointers and sizes; @@ -2055,7 +2064,8 @@ static int interleaved_copy(struct snd_pcm_substream *substream, snd_pcm_uframes_t hwoff, void *data, snd_pcm_uframes_t off, snd_pcm_uframes_t frames, - pcm_transfer_f transfer) + pcm_transfer_f transfer, + bool in_kernel) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2063,7 +2073,9 @@ static int interleaved_copy(struct snd_pcm_substream *substream, hwoff = frames_to_bytes(runtime, hwoff); off = frames_to_bytes(runtime, off); frames = frames_to_bytes(runtime, frames); - return transfer(substream, 0, hwoff, data + off, frames); + + return do_transfer(substream, 0, hwoff, data + off, frames, transfer, + in_kernel); } /* call transfer function with the converted pointers and sizes for each @@ -2073,7 +2085,8 @@ static int noninterleaved_copy(struct snd_pcm_substream *substream, snd_pcm_uframes_t hwoff, void *data, snd_pcm_uframes_t off, snd_pcm_uframes_t frames, - pcm_transfer_f transfer) + pcm_transfer_f transfer, + bool in_kernel) { struct snd_pcm_runtime *runtime = substream->runtime; int channels = runtime->channels; @@ -2091,8 +2104,8 @@ static int noninterleaved_copy(struct snd_pcm_substream *substream, if (!data || !*bufs) err = fill_silence(substream, c, hwoff, NULL, frames); else - err = transfer(substream, c, hwoff, *bufs + off, - frames); + err = do_transfer(substream, c, hwoff, *bufs + off, + frames, transfer, in_kernel); if (err < 0) return err; } @@ -2108,10 +2121,10 @@ static int fill_silence_frames(struct snd_pcm_substream *substream, if (substream->runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || substream->runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) return interleaved_copy(substream, off, NULL, 0, frames, - fill_silence); + fill_silence, true); else return noninterleaved_copy(substream, off, NULL, 0, frames, - fill_silence); + fill_silence, true); } /* sanity-check for read/write methods */ @@ -2121,7 +2134,7 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area)) + if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area)) return -EINVAL; if (runtime->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; @@ -2226,15 +2239,9 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, transfer = fill_silence; else return -EINVAL; - } else if (in_kernel) { - if (substream->ops->copy_kernel) - transfer = substream->ops->copy_kernel; - else - transfer = is_playback ? - default_write_copy_kernel : default_read_copy_kernel; } else { - if (substream->ops->copy_user) - transfer = (pcm_transfer_f)substream->ops->copy_user; + if (substream->ops->copy) + transfer = substream->ops->copy; else transfer = is_playback ? default_write_copy : default_read_copy; @@ -2307,7 +2314,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, if (!is_playback) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU); err = writer(substream, appl_ofs, data, offset, frames, - transfer); + transfer, in_kernel); if (is_playback) snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); snd_pcm_stream_lock_irq(substream); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 95fc56e403b1..bd9ddf412b46 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -809,7 +809,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->boundary *= 2; /* clear the buffer for avoiding possible kernel info leaks */ - if (runtime->dma_area && !substream->ops->copy_user) { + if (runtime->dma_area && !substream->ops->copy) { size_t size = runtime->dma_bytes; if (runtime->info & SNDRV_PCM_INFO_MMAP) diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 2d3cec908154..ba06484ac4aa 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -44,11 +44,11 @@ static LIST_HEAD(snd_rawmidi_devices); static DEFINE_MUTEX(register_mutex); #define rmidi_err(rmidi, fmt, args...) \ - dev_err(&(rmidi)->dev, fmt, ##args) + dev_err((rmidi)->dev, fmt, ##args) #define rmidi_warn(rmidi, fmt, args...) \ - dev_warn(&(rmidi)->dev, fmt, ##args) + dev_warn((rmidi)->dev, fmt, ##args) #define rmidi_dbg(rmidi, fmt, args...) \ - dev_dbg(&(rmidi)->dev, fmt, ##args) + dev_dbg((rmidi)->dev, fmt, ##args) struct snd_rawmidi_status32 { s32 stream; @@ -1877,11 +1877,6 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi, return 0; } -static void release_rawmidi_device(struct device *dev) -{ - kfree(container_of(dev, struct snd_rawmidi, dev)); -} - /* used for both rawmidi and ump */ int snd_rawmidi_init(struct snd_rawmidi *rmidi, struct snd_card *card, char *id, int device, @@ -1906,12 +1901,13 @@ int snd_rawmidi_init(struct snd_rawmidi *rmidi, if (id != NULL) strscpy(rmidi->id, id, sizeof(rmidi->id)); - snd_device_initialize(&rmidi->dev, card); - rmidi->dev.release = release_rawmidi_device; + err = snd_device_alloc(&rmidi->dev, card); + if (err < 0) + return err; if (rawmidi_is_ump(rmidi)) - dev_set_name(&rmidi->dev, "umpC%iD%i", card->number, device); + dev_set_name(rmidi->dev, "umpC%iD%i", card->number, device); else - dev_set_name(&rmidi->dev, "midiC%iD%i", card->number, device); + dev_set_name(rmidi->dev, "midiC%iD%i", card->number, device); err = snd_rawmidi_alloc_substreams(rmidi, &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], @@ -1996,7 +1992,8 @@ int snd_rawmidi_free(struct snd_rawmidi *rmidi) snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); if (rmidi->private_free) rmidi->private_free(rmidi); - put_device(&rmidi->dev); + put_device(rmidi->dev); + kfree(rmidi); return 0; } EXPORT_SYMBOL_GPL(snd_rawmidi_free); @@ -2038,7 +2035,7 @@ static int snd_rawmidi_dev_register(struct snd_device *device) err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device, - &snd_rawmidi_f_ops, rmidi, &rmidi->dev); + &snd_rawmidi_f_ops, rmidi, rmidi->dev); if (err < 0) { rmidi_err(rmidi, "unable to register\n"); goto error; @@ -2103,7 +2100,7 @@ static int snd_rawmidi_dev_register(struct snd_device *device) return 0; error_unregister: - snd_unregister_device(&rmidi->dev); + snd_unregister_device(rmidi->dev); error: mutex_lock(®ister_mutex); list_del(&rmidi->list); @@ -2142,7 +2139,7 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device) rmidi->ossreg = 0; } #endif /* CONFIG_SND_OSSEMUL */ - snd_unregister_device(&rmidi->dev); + snd_unregister_device(rmidi->dev); mutex_unlock(&rmidi->open_mutex); mutex_unlock(®ister_mutex); return 0; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index e3f9ea67d019..42a705141050 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -2721,7 +2721,7 @@ static const struct file_operations snd_seq_f_ops = .compat_ioctl = snd_seq_ioctl_compat, }; -static struct device seq_dev; +static struct device *seq_dev; /* * register sequencer device @@ -2730,15 +2730,17 @@ int __init snd_sequencer_device_init(void) { int err; - snd_device_initialize(&seq_dev, NULL); - dev_set_name(&seq_dev, "seq"); + err = snd_device_alloc(&seq_dev, NULL); + if (err < 0) + return err; + dev_set_name(seq_dev, "seq"); mutex_lock(®ister_mutex); err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0, - &snd_seq_f_ops, NULL, &seq_dev); + &snd_seq_f_ops, NULL, seq_dev); mutex_unlock(®ister_mutex); if (err < 0) { - put_device(&seq_dev); + put_device(seq_dev); return err; } @@ -2752,6 +2754,6 @@ int __init snd_sequencer_device_init(void) */ void snd_sequencer_device_done(void) { - snd_unregister_device(&seq_dev); - put_device(&seq_dev); + snd_unregister_device(seq_dev); + put_device(seq_dev); } diff --git a/sound/core/timer.c b/sound/core/timer.c index 9d0d2a5c2e15..e6e551d4a29e 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -2301,7 +2301,7 @@ static void snd_timer_free_all(void) snd_timer_free(timer); } -static struct device timer_dev; +static struct device *timer_dev; /* * ENTRY functions @@ -2311,8 +2311,10 @@ static int __init alsa_timer_init(void) { int err; - snd_device_initialize(&timer_dev, NULL); - dev_set_name(&timer_dev, "timer"); + err = snd_device_alloc(&timer_dev, NULL); + if (err < 0) + return err; + dev_set_name(timer_dev, "timer"); #ifdef SNDRV_OSS_INFO_DEV_TIMERS snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, @@ -2326,7 +2328,7 @@ static int __init alsa_timer_init(void) } err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, - &snd_timer_f_ops, NULL, &timer_dev); + &snd_timer_f_ops, NULL, timer_dev); if (err < 0) { pr_err("ALSA: unable to register timer device (%i)\n", err); snd_timer_free_all(); @@ -2337,15 +2339,15 @@ static int __init alsa_timer_init(void) return 0; put_timer: - put_device(&timer_dev); + put_device(timer_dev); return err; } static void __exit alsa_timer_exit(void) { - snd_unregister_device(&timer_dev); + snd_unregister_device(timer_dev); snd_timer_free_all(); - put_device(&timer_dev); + put_device(timer_dev); snd_timer_proc_done(); #ifdef SNDRV_OSS_INFO_DEV_TIMERS snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); diff --git a/sound/core/ump.c b/sound/core/ump.c index 246348766ec1..3bef1944e955 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -13,10 +13,10 @@ #include <sound/ump.h> #include <sound/ump_convert.h> -#define ump_err(ump, fmt, args...) dev_err(&(ump)->core.dev, fmt, ##args) -#define ump_warn(ump, fmt, args...) dev_warn(&(ump)->core.dev, fmt, ##args) -#define ump_info(ump, fmt, args...) dev_info(&(ump)->core.dev, fmt, ##args) -#define ump_dbg(ump, fmt, args...) dev_dbg(&(ump)->core.dev, fmt, ##args) +#define ump_err(ump, fmt, args...) dev_err((ump)->core.dev, fmt, ##args) +#define ump_warn(ump, fmt, args...) dev_warn((ump)->core.dev, fmt, ##args) +#define ump_info(ump, fmt, args...) dev_info((ump)->core.dev, fmt, ##args) +#define ump_dbg(ump, fmt, args...) dev_dbg((ump)->core.dev, fmt, ##args) static int snd_ump_dev_register(struct snd_rawmidi *rmidi); static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi); @@ -984,7 +984,7 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream) { struct snd_ump_endpoint *ump = substream->rmidi->private_data; int dir = substream->stream; - int group = substream->number; + int group = ump->legacy_mapping[substream->number]; int err; mutex_lock(&ump->open_mutex); @@ -1016,7 +1016,7 @@ static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream) { struct snd_ump_endpoint *ump = substream->rmidi->private_data; int dir = substream->stream; - int group = substream->number; + int group = ump->legacy_mapping[substream->number]; mutex_lock(&ump->open_mutex); spin_lock_irq(&ump->legacy_locks[dir]); @@ -1123,21 +1123,62 @@ static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src, spin_unlock_irqrestore(&ump->legacy_locks[dir], flags); } +/* Fill ump->legacy_mapping[] for groups to be used for legacy rawmidi */ +static int fill_legacy_mapping(struct snd_ump_endpoint *ump) +{ + struct snd_ump_block *fb; + unsigned int group_maps = 0; + int i, num; + + if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) { + list_for_each_entry(fb, &ump->block_list, list) { + for (i = 0; i < fb->info.num_groups; i++) + group_maps |= 1U << (fb->info.first_group + i); + } + if (!group_maps) + ump_info(ump, "No UMP Group is found in FB\n"); + } + + /* use all groups for non-static case */ + if (!group_maps) + group_maps = (1U << SNDRV_UMP_MAX_GROUPS) - 1; + + num = 0; + for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) + if (group_maps & (1U << i)) + ump->legacy_mapping[num++] = i; + + return num; +} + +static void fill_substream_names(struct snd_ump_endpoint *ump, + struct snd_rawmidi *rmidi, int dir) +{ + struct snd_rawmidi_substream *s; + + list_for_each_entry(s, &rmidi->streams[dir].substreams, list) + snprintf(s->name, sizeof(s->name), "Group %d (%.16s)", + ump->legacy_mapping[s->number] + 1, ump->info.name); +} + int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump, char *id, int device) { struct snd_rawmidi *rmidi; bool input, output; - int err; + int err, num; - ump->out_cvts = kcalloc(16, sizeof(*ump->out_cvts), GFP_KERNEL); + ump->out_cvts = kcalloc(SNDRV_UMP_MAX_GROUPS, + sizeof(*ump->out_cvts), GFP_KERNEL); if (!ump->out_cvts) return -ENOMEM; + num = fill_legacy_mapping(ump); + input = ump->core.info_flags & SNDRV_RAWMIDI_INFO_INPUT; output = ump->core.info_flags & SNDRV_RAWMIDI_INFO_OUTPUT; err = snd_rawmidi_new(ump->core.card, id, device, - output ? 16 : 0, input ? 16 : 0, + output ? num : 0, input ? num : 0, &rmidi); if (err < 0) { kfree(ump->out_cvts); @@ -1150,10 +1191,17 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump, if (output) snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ump_legacy_output_ops); + snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)", + ump->info.name); rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP; rmidi->ops = &snd_ump_legacy_ops; rmidi->private_data = ump; ump->legacy_rmidi = rmidi; + if (input) + fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT); + if (output) + fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT); + ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id); return 0; } diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index d0f11f37889b..378d2c7c3d4a 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -280,6 +280,34 @@ int _snd_ctl_add_follower(struct snd_kcontrol *master, } EXPORT_SYMBOL(_snd_ctl_add_follower); +/** + * snd_ctl_add_followers - add multiple followers to vmaster + * @card: card instance + * @master: the target vmaster kcontrol object + * @list: NULL-terminated list of name strings of followers to be added + * + * Adds the multiple follower kcontrols with the given names. + * Returns 0 for success or a negative error code. + */ +int snd_ctl_add_followers(struct snd_card *card, struct snd_kcontrol *master, + const char * const *list) +{ + struct snd_kcontrol *follower; + int err; + + for (; *list; list++) { + follower = snd_ctl_find_id_mixer(card, *list); + if (follower) { + err = snd_ctl_add_follower(master, follower); + if (err < 0) + return err; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_ctl_add_followers); + /* * ctl callbacks for master controls */ diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 41c171468c1e..6debd8e95cb7 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -111,6 +111,7 @@ config SND_ALOOP config SND_PCMTEST tristate "Virtual PCM test driver" + depends on DEBUG_FS select SND_PCM help Say 'Y' or 'M' to include support for the Virtual PCM test driver. diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 9c17b49a2ae1..4317677ba24a 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -626,14 +626,7 @@ static int alloc_fake_buffer(void) static int dummy_pcm_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *dst, unsigned long bytes) -{ - return 0; /* do nothing */ -} - -static int dummy_pcm_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *dst, unsigned long bytes) + struct iov_iter *iter, unsigned long bytes) { return 0; /* do nothing */ } @@ -667,8 +660,7 @@ static const struct snd_pcm_ops dummy_pcm_ops_no_buf = { .prepare = dummy_pcm_prepare, .trigger = dummy_pcm_trigger, .pointer = dummy_pcm_pointer, - .copy_user = dummy_pcm_copy, - .copy_kernel = dummy_pcm_copy_kernel, + .copy = dummy_pcm_copy, .fill_silence = dummy_pcm_silence, .page = dummy_pcm_page, }; diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c index 291e7fe47893..b59b78a09224 100644 --- a/sound/drivers/pcmtest.c +++ b/sound/drivers/pcmtest.c @@ -41,8 +41,6 @@ #include <linux/debugfs.h> #include <linux/delay.h> -#define DEVNAME "pcmtestd" -#define CARD_NAME "pcm-test-card" #define TIMER_PER_SEC 5 #define TIMER_INTERVAL (HZ / TIMER_PER_SEC) #define DELAY_JIFFIES HZ @@ -65,6 +63,7 @@ static int inject_delay; static bool inject_hwpars_err; static bool inject_prepare_err; static bool inject_trigger_err; +static bool inject_open_err; static short fill_mode = FILL_MODE_PAT; @@ -73,11 +72,11 @@ static u8 ioctl_reset_test; static struct dentry *driver_debug_dir; module_param(index, int, 0444); -MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard"); +MODULE_PARM_DESC(index, "Index value for pcmtest soundcard"); module_param(id, charp, 0444); -MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard"); +MODULE_PARM_DESC(id, "ID string for pcmtest soundcard"); module_param(enable, bool, 0444); -MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); +MODULE_PARM_DESC(enable, "Enable pcmtest soundcard."); module_param(fill_mode, short, 0600); MODULE_PARM_DESC(fill_mode, "Buffer fill mode: rand(0) or pattern(1)"); module_param(inject_delay, int, 0600); @@ -88,6 +87,8 @@ module_param(inject_prepare_err, bool, 0600); MODULE_PARM_DESC(inject_prepare_err, "Inject EINVAL error in the 'prepare' callback"); module_param(inject_trigger_err, bool, 0600); MODULE_PARM_DESC(inject_trigger_err, "Inject EINVAL error in the 'trigger' callback"); +module_param(inject_open_err, bool, 0600); +MODULE_PARM_DESC(inject_open_err, "Inject EBUSY error in the 'open' callback"); struct pcmtst { struct snd_pcm *pcm; @@ -107,6 +108,7 @@ struct pcmtst_buf_iter { size_t total_bytes; // Total bytes read/written size_t chan_block; // Bytes in one channel buffer when non-interleaved struct snd_pcm_substream *substream; + bool suspend; // We need to pause timer without shutting it down struct timer_list timer_instance; }; @@ -114,7 +116,8 @@ static struct snd_pcm_hardware snd_pcmtst_hw = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_NONINTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID), + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_8000_48000, .rate_min = 8000, @@ -140,7 +143,8 @@ static inline void inc_buf_pos(struct pcmtst_buf_iter *v_iter, size_t by, size_t { v_iter->total_bytes += by; v_iter->buf_pos += by; - v_iter->buf_pos %= bytes; + if (v_iter->buf_pos >= bytes) + v_iter->buf_pos %= bytes; } /* @@ -196,10 +200,10 @@ static void check_buf_block_ni(struct pcmtst_buf_iter *v_iter, struct snd_pcm_ru u8 current_byte; for (i = 0; i < v_iter->b_rw; i++) { - current_byte = runtime->dma_area[buf_pos_n(v_iter, channels, i % channels)]; + ch_num = i % channels; + current_byte = runtime->dma_area[buf_pos_n(v_iter, channels, ch_num)]; if (!current_byte) break; - ch_num = i % channels; if (current_byte != patt_bufs[ch_num].buf[(v_iter->total_bytes / channels) % patt_bufs[ch_num].len]) { v_iter->is_buf_corrupted = true; @@ -225,7 +229,7 @@ static void check_buf_block(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runti /* * Fill buffer in the non-interleaved mode. The order of samples is C0, ..., C0, C1, ..., C1, C2... - * The channel buffers lay in the DMA buffer continuously (see default copy_user and copy_kernel + * The channel buffers lay in the DMA buffer continuously (see default copy * handlers in the pcm_lib.c file). * * Here we increment the DMA buffer position every time we write a byte to any channel 'buffer'. @@ -239,7 +243,7 @@ static void fill_block_pattern_n(struct pcmtst_buf_iter *v_iter, struct snd_pcm_ for (i = 0; i < v_iter->b_rw; i++) { ch_num = i % channels; - runtime->dma_area[buf_pos_n(v_iter, channels, i % channels)] = + runtime->dma_area[buf_pos_n(v_iter, channels, ch_num)] = patt_bufs[ch_num].buf[(v_iter->total_bytes / channels) % patt_bufs[ch_num].len]; inc_buf_pos(v_iter, 1, runtime->dma_bytes); @@ -344,6 +348,9 @@ static void timer_timeout(struct timer_list *data) v_iter = from_timer(v_iter, data, timer_instance); substream = v_iter->substream; + if (v_iter->suspend) + return; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !v_iter->is_buf_corrupted) check_buf_block(v_iter, substream->runtime); else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) @@ -356,7 +363,9 @@ static void timer_timeout(struct timer_list *data) v_iter->period_pos %= v_iter->period_bytes; snd_pcm_period_elapsed(substream); } - mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL + inject_delay); + + if (!v_iter->suspend) + mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL + inject_delay); } static int snd_pcmtst_pcm_open(struct snd_pcm_substream *substream) @@ -364,23 +373,22 @@ static int snd_pcmtst_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct pcmtst_buf_iter *v_iter; + if (inject_open_err) + return -EBUSY; + v_iter = kzalloc(sizeof(*v_iter), GFP_KERNEL); if (!v_iter) return -ENOMEM; + v_iter->substream = substream; runtime->hw = snd_pcmtst_hw; runtime->private_data = v_iter; - v_iter->substream = substream; - v_iter->buf_pos = 0; - v_iter->is_buf_corrupted = false; - v_iter->period_pos = 0; - v_iter->total_bytes = 0; playback_capture_test = 0; ioctl_reset_test = 0; timer_setup(&v_iter->timer_instance, timer_timeout, 0); - mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL); + return 0; } @@ -395,26 +403,40 @@ static int snd_pcmtst_pcm_close(struct snd_pcm_substream *substream) return 0; } +static inline void reset_buf_iterator(struct pcmtst_buf_iter *v_iter) +{ + v_iter->buf_pos = 0; + v_iter->is_buf_corrupted = false; + v_iter->period_pos = 0; + v_iter->total_bytes = 0; +} + +static inline void start_pcmtest_timer(struct pcmtst_buf_iter *v_iter) +{ + v_iter->suspend = false; + mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL); +} + static int snd_pcmtst_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct pcmtst_buf_iter *v_iter = runtime->private_data; + struct pcmtst_buf_iter *v_iter = substream->runtime->private_data; if (inject_trigger_err) return -EINVAL; - - v_iter->sample_bytes = runtime->sample_bits / 8; - v_iter->period_bytes = frames_to_bytes(runtime, runtime->period_size); - if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED || - runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) { - v_iter->chan_block = runtime->dma_bytes / runtime->channels; - v_iter->interleaved = false; - } else { - v_iter->interleaved = true; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + reset_buf_iterator(v_iter); + start_pcmtest_timer(v_iter); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + start_pcmtest_timer(v_iter); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + // We can't call timer_shutdown_sync here, as it is forbidden to sleep here + v_iter->suspend = true; + break; } - // We want to record RATE * ch_cnt samples per sec, it is rate * sample_bytes * ch_cnt bytes - v_iter->s_rw_ch = runtime->rate / TIMER_PER_SEC; - v_iter->b_rw = v_iter->s_rw_ch * v_iter->sample_bytes * runtime->channels; return 0; } @@ -446,8 +468,24 @@ static void pcmtst_pdev_release(struct device *dev) static int snd_pcmtst_pcm_prepare(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcmtst_buf_iter *v_iter = runtime->private_data; + if (inject_prepare_err) return -EINVAL; + + v_iter->sample_bytes = samples_to_bytes(runtime, 1); + v_iter->period_bytes = snd_pcm_lib_period_bytes(substream); + v_iter->interleaved = true; + if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) { + v_iter->chan_block = snd_pcm_lib_buffer_bytes(substream) / runtime->channels; + v_iter->interleaved = false; + } + // We want to record RATE * ch_cnt samples per sec, it is rate * sample_bytes * ch_cnt bytes + v_iter->s_rw_ch = runtime->rate / TIMER_PER_SEC; + v_iter->b_rw = v_iter->s_rw_ch * v_iter->sample_bytes * runtime->channels; + return 0; } diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c index e1f864dc7939..c8db6c75d133 100644 --- a/sound/drivers/serial-generic.c +++ b/sound/drivers/serial-generic.c @@ -16,7 +16,7 @@ #include <linux/io.h> #include <linux/ioport.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/serdev.h> #include <linux/serial_reg.h> #include <linux/slab.h> @@ -366,7 +366,7 @@ MODULE_DEVICE_TABLE(of, snd_serial_generic_dt_ids); static struct serdev_device_driver snd_serial_generic_driver = { .driver = { .name = "snd-serial-generic", - .of_match_table = of_match_ptr(snd_serial_generic_dt_ids), + .of_match_table = snd_serial_generic_dt_ids, }, .probe = snd_serial_generic_probe, }; diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index a5f3fbf28b8c..bac8712f9014 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -2,5 +2,5 @@ snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \ dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \ dice-alesis.o dice-extension.o dice-mytek.o dice-presonus.o \ - dice-harman.o dice-focusrite.o + dice-harman.o dice-focusrite.o dice-weiss.o obj-$(CONFIG_SND_DICE) += snd-dice.o diff --git a/sound/firewire/dice/dice-weiss.c b/sound/firewire/dice/dice-weiss.c new file mode 100644 index 000000000000..129d43408956 --- /dev/null +++ b/sound/firewire/dice/dice-weiss.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +// dice-weiss.c - a part of driver for DICE based devices +// +// Copyright (c) 2023 Rolf Anderegg and Michele Perrone + +#include "dice.h" + +struct dice_weiss_spec { + unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; +}; + +// Weiss DAC202: 192kHz 2-channel DAC +static const struct dice_weiss_spec dac202 = { + .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, + .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, +}; + +// Weiss MAN301: 192kHz 2-channel music archive network player +static const struct dice_weiss_spec man301 = { + .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, + .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, +}; + +// Weiss INT202: 192kHz unidirectional 2-channel digital Firewire nterface +static const struct dice_weiss_spec int202 = { + .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, + .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, +}; + +// Weiss INT203: 192kHz bidirectional 2-channel digital Firewire nterface +static const struct dice_weiss_spec int203 = { + .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, + .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, +}; + +// Weiss ADC2: 192kHz A/D converter with microphone preamps and line nputs +static const struct dice_weiss_spec adc2 = { + .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, + .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, +}; + +// Weiss DAC2/Minerva: 192kHz 2-channel DAC +static const struct dice_weiss_spec dac2_minerva = { + .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, + .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, +}; + +// Weiss Vesta: 192kHz 2-channel Firewire to AES/EBU interface +static const struct dice_weiss_spec vesta = { + .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, + .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} }, +}; + +// Weiss AFI1: 192kHz 24-channel Firewire to ADAT or AES/EBU interface +static const struct dice_weiss_spec afi1 = { + .tx_pcm_chs = {{24, 16, 8}, {0, 0, 0} }, + .rx_pcm_chs = {{24, 16, 8}, {0, 0, 0} }, +}; + +int snd_dice_detect_weiss_formats(struct snd_dice *dice) +{ + static const struct { + u32 model_id; + const struct dice_weiss_spec *spec; + } *entry, entries[] = { + {0x000007, &dac202}, + {0x000008, &dac202}, // Maya edition: same audio I/O as DAC202. + {0x000006, &int202}, + {0x00000a, &int203}, + {0x00000b, &man301}, + {0x000001, &adc2}, + {0x000003, &dac2_minerva}, + {0x000002, &vesta}, + {0x000004, &afi1}, + }; + struct fw_csr_iterator it; + int key, val, model_id; + int i; + + model_id = 0; + fw_csr_iterator_init(&it, dice->unit->directory); + while (fw_csr_iterator_next(&it, &key, &val)) { + if (key == CSR_MODEL) { + model_id = val; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(entries); ++i) { + entry = entries + i; + if (entry->model_id == model_id) + break; + } + if (i == ARRAY_SIZE(entries)) + return -ENODEV; + + memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int)); + memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int)); + + return 0; +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 6c93e6e4982c..d362e4251c68 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -392,6 +392,69 @@ static const struct ieee1394_device_id dice_id_table[] = { .model_id = 0x0000de, .driver_data = (kernel_ulong_t)snd_dice_detect_focusrite_pro40_tcd3070_formats, }, + // Weiss DAC202: 192kHz 2-channel DAC + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_WEISS, + .model_id = 0x000007, + .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats, + }, + // Weiss DAC202: 192kHz 2-channel DAC (Maya edition) + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_WEISS, + .model_id = 0x000008, + .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats, + }, + // Weiss MAN301: 192kHz 2-channel music archive network player + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_WEISS, + .model_id = 0x00000b, + .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats, + }, + // Weiss INT202: 192kHz unidirectional 2-channel digital Firewire face + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_WEISS, + .model_id = 0x000006, + .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats, + }, + // Weiss INT203: 192kHz bidirectional 2-channel digital Firewire face + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_WEISS, + .model_id = 0x00000a, + .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats, + }, + // Weiss ADC2: 192kHz A/D converter with microphone preamps and inputs + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_WEISS, + .model_id = 0x000001, + .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats, + }, + // Weiss DAC2/Minerva: 192kHz 2-channel DAC + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_WEISS, + .model_id = 0x000003, + .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats, + }, + // Weiss Vesta: 192kHz 2-channel Firewire to AES/EBU interface + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_WEISS, + .model_id = 0x000002, + .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats, + }, + // Weiss AFI1: 192kHz 24-channel Firewire to ADAT or AES/EBU face + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_WEISS, + .model_id = 0x000004, + .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats, + }, { .match_flags = IEEE1394_MATCH_VERSION, .version = DICE_INTERFACE, diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 674f7d552c2e..4c0ad7335998 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -232,5 +232,6 @@ int snd_dice_detect_mytek_formats(struct snd_dice *dice); int snd_dice_detect_presonus_formats(struct snd_dice *dice); int snd_dice_detect_harman_formats(struct snd_dice *dice); int snd_dice_detect_focusrite_pro40_tcd3070_formats(struct snd_dice *dice); +int snd_dice_detect_weiss_formats(struct snd_dice *dice); #endif diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 161a9711cd63..b428537f284c 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -11,11 +11,6 @@ #include <sound/hda_i915.h> #include <sound/hda_register.h> -#define IS_HSW_CONTROLLER(pci) (((pci)->device == 0x0a0c) || \ - ((pci)->device == 0x0c0c) || \ - ((pci)->device == 0x0d0c) || \ - ((pci)->device == 0x160c)) - /** * snd_hdac_i915_set_bclk - Reprogram BCLK for HSW/BDW * @bus: HDA core bus @@ -39,7 +34,7 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus) if (!acomp || !acomp->ops || !acomp->ops->get_cdclk_freq) return; /* only for i915 binding */ - if (!IS_HSW_CONTROLLER(pci)) + if (!HDA_CONTROLLER_IS_HSW(pci)) return; /* only HSW/BDW */ cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev); @@ -80,14 +75,20 @@ static bool connectivity_check(struct pci_dev *i915, struct pci_dev *hdac) if (bus_a == bus_b) return true; - /* - * on i915 discrete GPUs with embedded HDA audio, the two - * devices are connected via 2nd level PCI bridge - */ bus_a = bus_a->parent; bus_b = bus_b->parent; + + /* connected via parent bus (may be NULL!) */ + if (bus_a == bus_b) + return true; + if (!bus_a || !bus_b) return false; + + /* + * on i915 discrete GPUs with embedded HDA audio, the two + * devices are connected via 2nd level PCI bridge + */ bus_a = bus_a->parent; bus_b = bus_b->parent; if (bus_a && bus_a == bus_b) diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index 317bdf6dcbef..24a948baf1bc 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -50,14 +50,7 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD) { .flags = FLAG_SOF, - .device = 0x119a, - }, -#endif -/* Broxton-T */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) - { - .flags = FLAG_SOF, - .device = 0x1a98, + .device = PCI_DEVICE_ID_INTEL_SST_TNG, }, #endif /* @@ -68,7 +61,7 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) { .flags = FLAG_SOF, - .device = 0x5a98, + .device = PCI_DEVICE_ID_INTEL_HDA_APL, .dmi_table = (const struct dmi_system_id []) { { .ident = "Up Squared", @@ -82,14 +75,14 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SOF, - .device = 0x5a98, + .device = PCI_DEVICE_ID_INTEL_HDA_APL, .codec_hid = &essx_83x6, }, #endif #if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL) { .flags = FLAG_SST, - .device = 0x5a98, + .device = PCI_DEVICE_ID_INTEL_HDA_APL, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -110,7 +103,7 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL) { .flags = FLAG_SST, - .device = 0x9d70, + .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -123,14 +116,14 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC, - .device = 0x9d70, + .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP, }, #endif /* Kabylake-LP */ #if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) { .flags = FLAG_SST, - .device = 0x9d71, + .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -143,7 +136,7 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC, - .device = 0x9d71, + .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP, }, #endif @@ -155,7 +148,7 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) { .flags = FLAG_SOF, - .device = 0x3198, + .device = PCI_DEVICE_ID_INTEL_HDA_GML, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -168,16 +161,16 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SOF, - .device = 0x3198, + .device = PCI_DEVICE_ID_INTEL_HDA_GML, .codec_hid = &essx_83x6, }, #endif /* - * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy - * HDAudio driver except for Google Chromebooks and when DMICs are - * present. Two cases are required since Coreboot does not expose NHLT - * tables. + * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake, AlderLake, + * RaptorLake use legacy HDAudio driver except for Google Chromebooks + * and when DMICs are present. Two cases are required since Coreboot + * does not expose NHLT tables. * * When the Chromebook quirk is not present, it's based on information * that no such device exists. When the quirk is present, it could be @@ -188,7 +181,7 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) { .flags = FLAG_SOF, - .device = 0x9dc8, + .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -207,12 +200,12 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SOF, - .device = 0x09dc8, + .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP, .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x9dc8, + .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP, }, #endif @@ -220,7 +213,7 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE) { .flags = FLAG_SOF, - .device = 0xa348, + .device = PCI_DEVICE_ID_INTEL_HDA_CNL_H, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -233,7 +226,7 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0xa348, + .device = PCI_DEVICE_ID_INTEL_HDA_CNL_H, }, #endif @@ -241,7 +234,7 @@ static const struct config_entry config_table[] = { /* Cometlake-LP */ { .flags = FLAG_SOF, - .device = 0x02c8, + .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -267,17 +260,17 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SOF, - .device = 0x02c8, + .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP, .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x02c8, + .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP, }, /* Cometlake-H */ { .flags = FLAG_SOF, - .device = 0x06c8, + .device = PCI_DEVICE_ID_INTEL_HDA_CML_H, .dmi_table = (const struct dmi_system_id []) { { .matches = { @@ -296,12 +289,12 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SOF, - .device = 0x06c8, + .device = PCI_DEVICE_ID_INTEL_HDA_CML_H, .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x06c8, + .device = PCI_DEVICE_ID_INTEL_HDA_CML_H, }, #endif @@ -309,7 +302,7 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) { .flags = FLAG_SOF, - .device = 0x34c8, + .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -322,12 +315,12 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SOF, - .device = 0x34c8, + .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP, .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x34c8, + .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP, }, #endif @@ -335,7 +328,7 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE) { .flags = FLAG_SOF, - .device = 0x4dc8, + .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -348,12 +341,12 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SOF, - .device = 0x4dc8, + .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N, .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, - .device = 0x4dc8, + .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N, }, #endif @@ -361,7 +354,7 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE) { .flags = FLAG_SOF, - .device = 0xa0c8, + .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP, .dmi_table = (const struct dmi_system_id []) { { .ident = "Google Chromebooks", @@ -380,16 +373,16 @@ static const struct config_entry config_table[] = { }, { .flags = FLAG_SOF, - .device = 0xa0c8, + .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP, .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0xa0c8, + .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x43c8, + .device = PCI_DEVICE_ID_INTEL_HDA_TGL_H, }, #endif @@ -397,78 +390,121 @@ static const struct config_entry config_table[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE) { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, - .device = 0x4b55, + .device = PCI_DEVICE_ID_INTEL_HDA_EHL_0, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, - .device = 0x4b58, + .device = PCI_DEVICE_ID_INTEL_HDA_EHL_3, }, #endif -/* Alder Lake */ +/* Alder Lake / Raptor Lake */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_ALDERLAKE) - /* Alderlake-S */ { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x7ad0, + .device = PCI_DEVICE_ID_INTEL_HDA_ADL_S, }, - /* RaptorLake-S */ { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x7a50, + .device = PCI_DEVICE_ID_INTEL_HDA_RPL_S, }, - /* Alderlake-P */ { .flags = FLAG_SOF, - .device = 0x51c8, + .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + { + .flags = FLAG_SOF, + .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P, .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x51c8, + .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x51cd, + .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PX, }, - /* Alderlake-PS */ { .flags = FLAG_SOF, - .device = 0x51c9, + .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PS, .codec_hid = &essx_83x6, }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x51c9, + .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PS, }, - /* Alderlake-M */ { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x51cc, + .device = PCI_DEVICE_ID_INTEL_HDA_ADL_M, + }, + { + .flags = FLAG_SOF, + .device = PCI_DEVICE_ID_INTEL_HDA_ADL_N, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } }, - /* Alderlake-N */ { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x54c8, + .device = PCI_DEVICE_ID_INTEL_HDA_ADL_N, + }, + { + .flags = FLAG_SOF, + .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } }, - /* RaptorLake-P */ { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x51ca, + .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0, + }, + { + .flags = FLAG_SOF, + .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x51cb, + .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1, }, - /* RaptorLake-M */ { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x51ce, + .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M, }, - /* RaptorLake-PX */ { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x51cf, + .device = PCI_DEVICE_ID_INTEL_HDA_RPL_PX, }, #endif @@ -477,10 +513,18 @@ static const struct config_entry config_table[] = { /* Meteorlake-P */ { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x7e28, + .device = PCI_DEVICE_ID_INTEL_HDA_MTL, }, #endif +/* Lunar Lake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_LUNARLAKE) + /* Lunarlake-P */ + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = PCI_DEVICE_ID_INTEL_HDA_LNL_P, + }, +#endif }; static const struct config_entry *snd_intel_dsp_find_config @@ -549,7 +593,7 @@ int snd_intel_dsp_driver_probe(struct pci_dev *pci) const struct config_entry *cfg; /* Intel vendor only */ - if (pci->vendor != 0x8086) + if (pci->vendor != PCI_VENDOR_ID_INTEL) return SND_INTEL_DSP_DRIVER_ANY; /* @@ -557,12 +601,12 @@ int snd_intel_dsp_driver_probe(struct pci_dev *pci) * for HDMI/DP support, ignore kernel parameter */ switch (pci->device) { - case 0x160c: /* Broadwell */ - case 0x0a0c: /* Haswell */ - case 0x0c0c: - case 0x0d0c: - case 0x0f04: /* Baytrail */ - case 0x2284: /* Braswell */ + case PCI_DEVICE_ID_INTEL_HDA_BDW: + case PCI_DEVICE_ID_INTEL_HDA_HSW_0: + case PCI_DEVICE_ID_INTEL_HDA_HSW_2: + case PCI_DEVICE_ID_INTEL_HDA_HSW_3: + case PCI_DEVICE_ID_INTEL_HDA_BYT: + case PCI_DEVICE_ID_INTEL_HDA_BSW: return SND_INTEL_DSP_DRIVER_ANY; } diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index 388db5fb65bd..850544725da7 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -369,7 +369,7 @@ static int playback_copy_ack(struct snd_pcm_substream *substream, static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream, int voice, unsigned long pos, - void __user *src, unsigned long count) + struct iov_iter *src, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; @@ -379,27 +379,11 @@ static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream, bpos = get_bpos(pcmp, voice, pos, len); if (bpos < 0) return pos; - if (copy_from_user(runtime->dma_area + bpos, src, len)) + if (copy_from_iter(runtime->dma_area + bpos, len, src) != len) return -EFAULT; return playback_copy_ack(substream, bpos, len); } -static int snd_gf1_pcm_playback_copy_kernel(struct snd_pcm_substream *substream, - int voice, unsigned long pos, - void *src, unsigned long count) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct gus_pcm_private *pcmp = runtime->private_data; - unsigned int len = count; - int bpos; - - bpos = get_bpos(pcmp, voice, pos, len); - if (bpos < 0) - return pos; - memcpy(runtime->dma_area + bpos, src, len); - return playback_copy_ack(substream, bpos, len); -} - static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream, int voice, unsigned long pos, unsigned long count) @@ -830,8 +814,7 @@ static const struct snd_pcm_ops snd_gf1_pcm_playback_ops = { .prepare = snd_gf1_pcm_playback_prepare, .trigger = snd_gf1_pcm_playback_trigger, .pointer = snd_gf1_pcm_playback_pointer, - .copy_user = snd_gf1_pcm_playback_copy, - .copy_kernel = snd_gf1_pcm_playback_copy_kernel, + .copy = snd_gf1_pcm_playback_copy, .fill_silence = snd_gf1_pcm_playback_silence, }; diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c index e02029677743..a6405772d537 100644 --- a/sound/isa/sb/emu8000.c +++ b/sound/isa/sb/emu8000.c @@ -1040,10 +1040,8 @@ snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu) __error: for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { - down_write(&card->controls_rwsem); if (emu->controls[i]) snd_ctl_remove(card, emu->controls[i]); - up_write(&card->controls_rwsem); } return err; } diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c index c8afc4347c54..c05935c2edc4 100644 --- a/sound/isa/sb/emu8000_pcm.c +++ b/sound/isa/sb/emu8000_pcm.c @@ -409,39 +409,25 @@ do { \ return -EAGAIN;\ } while (0) -enum { - COPY_USER, COPY_KERNEL, FILL_SILENCE, -}; - -#define GET_VAL(sval, buf, mode) \ +#define GET_VAL(sval, iter) \ do { \ - switch (mode) { \ - case FILL_SILENCE: \ + if (!iter) \ sval = 0; \ - break; \ - case COPY_KERNEL: \ - sval = *buf++; \ - break; \ - default: \ - if (get_user(sval, (unsigned short __user *)buf)) \ - return -EFAULT; \ - buf++; \ - break; \ - } \ + else if (copy_from_iter(&sval, 2, iter) != 2) \ + return -EFAULT; \ } while (0) #ifdef USE_NONINTERLEAVE -#define LOOP_WRITE(rec, offset, _buf, count, mode) \ +#define LOOP_WRITE(rec, offset, iter, count) \ do { \ struct snd_emu8000 *emu = (rec)->emu; \ - unsigned short *buf = (__force unsigned short *)(_buf); \ snd_emu8000_write_wait(emu, 1); \ EMU8000_SMALW_WRITE(emu, offset); \ while (count > 0) { \ unsigned short sval; \ CHECK_SCHEDULER(); \ - GET_VAL(sval, buf, mode); \ + GET_VAL(sval, iter); \ EMU8000_SMLD_WRITE(emu, sval); \ count--; \ } \ @@ -450,27 +436,14 @@ enum { /* copy one channel block */ static int emu8k_pcm_copy(struct snd_pcm_substream *subs, int voice, unsigned long pos, - void __user *src, unsigned long count) + struct iov_iter *src, unsigned long count) { struct snd_emu8k_pcm *rec = subs->runtime->private_data; /* convert to word unit */ pos = (pos << 1) + rec->loop_start[voice]; count <<= 1; - LOOP_WRITE(rec, pos, src, count, COPY_USER); - return 0; -} - -static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs, - int voice, unsigned long pos, - void *src, unsigned long count) -{ - struct snd_emu8k_pcm *rec = subs->runtime->private_data; - - /* convert to word unit */ - pos = (pos << 1) + rec->loop_start[voice]; - count <<= 1; - LOOP_WRITE(rec, pos, src, count, COPY_KERNEL); + LOOP_WRITE(rec, pos, src, count); return 0; } @@ -483,16 +456,15 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs, /* convert to word unit */ pos = (pos << 1) + rec->loop_start[voice]; count <<= 1; - LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE); + LOOP_WRITE(rec, pos, USER_SOCKPTR(NULL), count); return 0; } #else /* interleave */ -#define LOOP_WRITE(rec, pos, _buf, count, mode) \ +#define LOOP_WRITE(rec, pos, iter, count) \ do { \ struct snd_emu8000 *emu = rec->emu; \ - unsigned short *buf = (__force unsigned short *)(_buf); \ snd_emu8000_write_wait(emu, 1); \ EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); \ if (rec->voices > 1) \ @@ -500,11 +472,11 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs, while (count > 0) { \ unsigned short sval; \ CHECK_SCHEDULER(); \ - GET_VAL(sval, buf, mode); \ + GET_VAL(sval, iter); \ EMU8000_SMLD_WRITE(emu, sval); \ if (rec->voices > 1) { \ CHECK_SCHEDULER(); \ - GET_VAL(sval, buf, mode); \ + GET_VAL(sval, iter); \ EMU8000_SMRD_WRITE(emu, sval); \ } \ count--; \ @@ -518,27 +490,14 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs, */ static int emu8k_pcm_copy(struct snd_pcm_substream *subs, int voice, unsigned long pos, - void __user *src, unsigned long count) -{ - struct snd_emu8k_pcm *rec = subs->runtime->private_data; - - /* convert to frames */ - pos = bytes_to_frames(subs->runtime, pos); - count = bytes_to_frames(subs->runtime, count); - LOOP_WRITE(rec, pos, src, count, COPY_USER); - return 0; -} - -static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs, - int voice, unsigned long pos, - void *src, unsigned long count) + struct iov_iter *src, unsigned long count) { struct snd_emu8k_pcm *rec = subs->runtime->private_data; /* convert to frames */ pos = bytes_to_frames(subs->runtime, pos); count = bytes_to_frames(subs->runtime, count); - LOOP_WRITE(rec, pos, src, count, COPY_KERNEL); + LOOP_WRITE(rec, pos, src, count); return 0; } @@ -550,7 +509,7 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs, /* convert to frames */ pos = bytes_to_frames(subs->runtime, pos); count = bytes_to_frames(subs->runtime, count); - LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE); + LOOP_WRITE(rec, pos, NULL, count); return 0; } #endif @@ -666,8 +625,7 @@ static const struct snd_pcm_ops emu8k_pcm_ops = { .prepare = emu8k_pcm_prepare, .trigger = emu8k_pcm_trigger, .pointer = emu8k_pcm_pointer, - .copy_user = emu8k_pcm_copy, - .copy_kernel = emu8k_pcm_copy_kernel, + .copy = emu8k_pcm_copy, .fill_silence = emu8k_pcm_silence, }; diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index 7ad8c5f7b664..8d8357019719 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -1080,7 +1080,6 @@ static void snd_sb_qsound_destroy(struct snd_sb_csp * p) card = p->chip->card; - down_write(&card->controls_rwsem); if (p->qsound_switch) { snd_ctl_remove(card, p->qsound_switch); p->qsound_switch = NULL; @@ -1089,7 +1088,6 @@ static void snd_sb_qsound_destroy(struct snd_sb_csp * p) snd_ctl_remove(card, p->qsound_space); p->qsound_space = NULL; } - up_write(&card->controls_rwsem); /* cancel pending transfer of QSound parameters */ spin_lock_irqsave (&p->q_lock, flags); diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 80a65b8ad7b9..25f93e56cfc7 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -2069,10 +2069,9 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, .dev_disconnect = snd_ac97_dev_disconnect, }; - if (!rac97) - return -EINVAL; - if (snd_BUG_ON(!bus || !template)) + if (snd_BUG_ON(!bus || !template || !rac97)) return -EINVAL; + *rac97 = NULL; if (snd_BUG_ON(template->num >= 4)) return -EINVAL; if (bus->codec[template->num]) diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 4b5f33de70d5..1d786bd5ce3e 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -3431,11 +3431,7 @@ static const char * const follower_sws_vt1616[] = { static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97, const char *name) { - struct snd_ctl_elem_id id; - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, name); - return snd_ctl_find_id(ac97->bus->card, &id); + return snd_ctl_find_id_mixer(ac97->bus->card, name); } /* create a virtual master control and add followers */ @@ -3444,7 +3440,6 @@ static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name, const char * const *followers) { struct snd_kcontrol *kctl; - const char * const *s; int err; kctl = snd_ctl_make_virtual_master(name, tlv); @@ -3454,20 +3449,7 @@ static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name, if (err < 0) return err; - for (s = followers; *s; s++) { - struct snd_kcontrol *sctl; - - sctl = snd_ac97_find_mixer_ctl(ac97, *s); - if (!sctl) { - dev_dbg(ac97->bus->card->dev, - "Cannot find follower %s, skipped\n", *s); - continue; - } - err = snd_ctl_add_follower(kctl, sctl); - if (err < 0) - return err; - } - return 0; + return snd_ctl_add_followers(ac97->bus->card, kctl, followers); } static int patch_vt1616_specific(struct snd_ac97 * ac97) diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h index 3aebec763fb8..04a5cf6572cd 100644 --- a/sound/pci/asihpi/hpi.h +++ b/sound/pci/asihpi/hpi.h @@ -1191,19 +1191,6 @@ u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode, u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode); -u16 hpi_adapter_get_assert2(u16 adapter_index, u16 *p_assert_count, - char *psz_assert, u32 *p_param1, u32 *p_param2, - u32 *p_dsp_string_addr, u16 *p_processor_id); - -u16 hpi_adapter_test_assert(u16 adapter_index, u16 assert_id); - -u16 hpi_adapter_enable_capability(u16 adapter_index, u16 capability, u32 key); - -u16 hpi_adapter_self_test(u16 adapter_index); - -u16 hpi_adapter_debug_read(u16 adapter_index, u32 dsp_address, char *p_bytes, - int *count_bytes); - u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 paramter1, u16 paramter2); @@ -1488,9 +1475,6 @@ u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY); u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI); -u16 hpi_pad_get_program_type_string(u32 h_control, const u32 data_type, - const u32 pTY, char *psz_string, const u32 string_length); - /****************************/ /* AES/EBU Receiver control */ /****************************/ diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h index 6859d51389f5..e569e3b33b8e 100644 --- a/sound/pci/asihpi/hpi_internal.h +++ b/sound/pci/asihpi/hpi_internal.h @@ -1394,17 +1394,12 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index, void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr); /* used in PnP OS/driver */ -u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource, - u16 *pw_adapter_index); - u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer, struct hpi_hostbuffer_status **pp_status); u16 hpi_instream_host_buffer_get_info(u32 h_instream, u8 **pp_buffer, struct hpi_hostbuffer_status **pp_status); -u16 hpi_adapter_restart(u16 adapter_index); - /* The following 3 functions were last declared in header files for driver 3.10. HPI_ControlQuery() used to be the recommended way diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index f6381c098d4f..1d5a899b2c24 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -706,19 +706,9 @@ static int remove_ctl(struct snd_card *card, const char *name) return snd_ctl_remove_id(card, &id); } -static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name) -{ - struct snd_ctl_elem_id sid; - memset(&sid, 0, sizeof(sid)); - /* FIXME: strcpy is bad. */ - strcpy(sid.name, name); - sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - return snd_ctl_find_id(card, &sid); -} - static int rename_ctl(struct snd_card *card, const char *src, const char *dst) { - struct snd_kcontrol *kctl = ctl_find(card, src); + struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src); if (kctl) { snd_ctl_rename(card, kctl, dst); return 0; @@ -761,16 +751,6 @@ static const char * const follower_sws[] = { NULL }; -static void add_followers(struct snd_card *card, - struct snd_kcontrol *master, const char * const *list) -{ - for (; *list; list++) { - struct snd_kcontrol *follower = ctl_find(card, *list); - if (follower) - snd_ctl_add_follower(master, follower); - } -} - int snd_ca0106_mixer(struct snd_ca0106 *emu) { int err; @@ -852,7 +832,9 @@ int snd_ca0106_mixer(struct snd_ca0106 *emu) err = snd_ctl_add(card, vmaster); if (err < 0) return err; - add_followers(card, vmaster, follower_vols); + err = snd_ctl_add_followers(card, vmaster, follower_vols); + if (err < 0) + return err; if (emu->details->spi_dac) { vmaster = snd_ctl_make_virtual_master("Master Playback Switch", @@ -862,7 +844,9 @@ int snd_ca0106_mixer(struct snd_ca0106 *emu) err = snd_ctl_add(card, vmaster); if (err < 0) return err; - add_followers(card, vmaster, follower_sws); + err = snd_ctl_add_followers(card, vmaster, follower_sws); + if (err < 0) + return err; } strcpy(card->mixername, "CA0106"); diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 6d25c12d9ef0..1415baac9c36 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2734,12 +2734,8 @@ static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) } for (idx = 0; idx < CM_SAVED_MIXERS; idx++) { - struct snd_ctl_elem_id elem_id; struct snd_kcontrol *ctl; - memset(&elem_id, 0, sizeof(elem_id)); - elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(elem_id.name, cm_saved_mixer[idx].name); - ctl = snd_ctl_find_id(cm->card, &elem_id); + ctl = snd_ctl_find_id_mixer(cm->card, cm_saved_mixer[idx].name); if (ctl) cm->mixer_res_ctl[idx] = ctl; } diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 7d882b33d45e..f3a94bb537bd 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2449,7 +2449,6 @@ static int cs46xx_detect_codec(struct snd_cs46xx *chip, int codec) int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device) { struct snd_card *card = chip->card; - struct snd_ctl_elem_id id; int err; unsigned int idx; static const struct snd_ac97_bus_ops ops = { @@ -2490,10 +2489,8 @@ int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device) } /* get EAPD mixer switch (for voyetra hack) */ - memset(&id, 0, sizeof(id)); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "External Amplifier"); - chip->eapd_switch = snd_ctl_find_id(chip->card, &id); + chip->eapd_switch = snd_ctl_find_id_mixer(chip->card, + "External Amplifier"); #ifdef CONFIG_SND_CS46XX_NEW_DSP if (chip->nr_ac97_codecs == 1) { diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 23adace1b969..fe72e7d77241 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -2,9 +2,7 @@ /* * The driver for the EMU10K1 (SB Live!) based soundcards * Copyright (c) by Jaroslav Kysela <perex@perex.cz> - * - * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> - * Added support for Audigy 2 Value. + * James Courtier-Dutton <James@superbug.co.uk> */ #include <linux/init.h> @@ -176,9 +174,6 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci, if (err < 0) return err; - if (emu->card_capabilities->emu_model) - schedule_delayed_work(&emu->emu1010.firmware_work, 0); - pci_set_drvdata(pci, card); dev++; return 0; @@ -194,7 +189,8 @@ static int snd_emu10k1_suspend(struct device *dev) emu->suspend = 1; - cancel_delayed_work_sync(&emu->emu1010.firmware_work); + cancel_work_sync(&emu->emu1010.firmware_work); + cancel_work_sync(&emu->emu1010.clock_work); snd_ac97_suspend(emu->ac97); @@ -224,9 +220,6 @@ static int snd_emu10k1_resume(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D0); - if (emu->card_capabilities->emu_model) - schedule_delayed_work(&emu->emu1010.firmware_work, 0); - return 0; } diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 58ed72de6403..de5c41e578e1 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1,19 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> + * James Courtier-Dutton <James@superbug.co.uk> + * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> * Creative Labs, Inc. - * Routines for control of EMU10K1 chips - * - * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk> - * Added support for Audigy 2 Value. - * Added EMU 1010 support. - * General bug fixes and enhancements. * - * BUGS: - * -- - * - * TODO: - * -- + * Routines for control of EMU10K1 chips */ #include <linux/sched.h> @@ -391,7 +383,10 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu) } #endif - snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE); + if (emu->card_capabilities->emu_model) + snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE | INTE_A_GPIOENABLE); + else + snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE); } int snd_emu10k1_done(struct snd_emu10k1 *emu) @@ -664,7 +659,6 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, u16 reg; u8 value; __always_unused u16 write_post; - unsigned long flags; if (!fw_entry) return -EIO; @@ -676,7 +670,7 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, * GPIO5 -> FPGA DIN * FPGA CONFIG OFF -> FPGA PGMN */ - spin_lock_irqsave(&emu->emu_lock, flags); + spin_lock_irq(&emu->emu_lock); outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 100uS. */ write_post = inw(emu->port + A_GPIO); udelay(100); @@ -699,7 +693,7 @@ static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, /* After programming, set GPIO bit 4 high again. */ outw(0x10, emu->port + A_GPIO); write_post = inw(emu->port + A_GPIO); - spin_unlock_irqrestore(&emu->emu_lock, flags); + spin_unlock_irq(&emu->emu_lock); return 0; } @@ -745,14 +739,13 @@ static void emu1010_firmware_work(struct work_struct *work) int err; emu = container_of(work, struct snd_emu10k1, - emu1010.firmware_work.work); + emu1010.firmware_work); if (emu->card->shutdown) return; #ifdef CONFIG_PM_SLEEP if (emu->suspend) return; #endif - snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); /* OPTIONS: Which cards are attached to the EMU */ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) { /* Audio Dock attached */ @@ -763,13 +756,8 @@ static void emu1010_firmware_work(struct work_struct *work) EMU_HANA_FPGA_CONFIG_AUDIODOCK); err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw); if (err < 0) - goto next; - + return; snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0); - snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); - dev_info(emu->card->dev, - "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", tmp); - /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp); dev_info(emu->card->dev, "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp); @@ -778,7 +766,7 @@ static void emu1010_firmware_work(struct work_struct *work) dev_info(emu->card->dev, "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n", tmp); - goto next; + return; } dev_info(emu->card->dev, "emu1010: Audio Dock Firmware loaded\n"); @@ -790,18 +778,48 @@ static void emu1010_firmware_work(struct work_struct *work) msleep(10); /* Unmute all. Default is muted after a firmware load */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); - } else if (!reg && emu->emu1010.last_reg) { + } +} + +static void emu1010_clock_work(struct work_struct *work) +{ + struct snd_emu10k1 *emu; + struct snd_ctl_elem_id id; + + emu = container_of(work, struct snd_emu10k1, + emu1010.clock_work); + if (emu->card->shutdown) + return; +#ifdef CONFIG_PM_SLEEP + if (emu->suspend) + return; +#endif + + spin_lock_irq(&emu->reg_lock); + // This is the only thing that can actually happen. + emu->emu1010.clock_source = emu->emu1010.clock_fallback; + emu->emu1010.wclock = 1 - emu->emu1010.clock_source; + snd_emu1010_update_clock(emu); + spin_unlock_irq(&emu->reg_lock); + snd_ctl_build_ioff(&id, emu->ctl_clock_source, 0); + snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE, &id); +} + +static void emu1010_interrupt(struct snd_emu10k1 *emu) +{ + u32 sts; + + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &sts); + if (sts & EMU_HANA_IRQ_DOCK_LOST) { /* Audio Dock removed */ dev_info(emu->card->dev, "emu1010: Audio Dock detached\n"); /* The hardware auto-mutes all, so we unmute again */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); + } else if (sts & EMU_HANA_IRQ_DOCK) { + schedule_work(&emu->emu1010.firmware_work); } - - next: - emu->emu1010.last_reg = reg; - if (!emu->card->shutdown) - schedule_delayed_work(&emu->emu1010.firmware_work, - msecs_to_jiffies(1000)); + if (sts & EMU_HANA_IRQ_WCLK_CHANGED) + schedule_work(&emu->emu1010.clock_work); } /* @@ -870,6 +888,8 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ®); dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg); + if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) + schedule_work(&emu->emu1010.firmware_work); if (emu->card_capabilities->no_adat) { emu->emu1010.optical_in = 0; /* IN_SPDIF */ emu->emu1010.optical_out = 0; /* OUT_SPDIF */ @@ -895,10 +915,12 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) /* MIDI routing */ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, EMU_HANA_MIDI_INA_FROM_HAMOA | EMU_HANA_MIDI_INB_FROM_DOCK2); snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, EMU_HANA_MIDI_OUT_DOCK2 | EMU_HANA_MIDI_OUT_SYNC2); - /* IRQ Enable: All on */ - /* snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x0f); */ - /* IRQ Enable: All off */ - snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00); + + emu->gpio_interrupt = emu1010_interrupt; + // Note: The Audigy INTE is set later + snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, + EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST | EMU_HANA_IRQ_WCLK_CHANGED); + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ®); // Clear pending IRQs emu->emu1010.clock_source = 1; /* 48000 */ emu->emu1010.clock_fallback = 1; /* 48000 */ @@ -938,7 +960,8 @@ static void snd_emu10k1_free(struct snd_card *card) /* Disable 48Volt power to Audio Dock */ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0); } - cancel_delayed_work_sync(&emu->emu1010.firmware_work); + cancel_work_sync(&emu->emu1010.firmware_work); + cancel_work_sync(&emu->emu1010.clock_work); release_firmware(emu->firmware); release_firmware(emu->dock_fw); snd_util_memhdr_free(emu->memhdr); @@ -1517,7 +1540,8 @@ int snd_emu10k1_create(struct snd_card *card, emu->irq = -1; emu->synth = NULL; emu->get_synth_voice = NULL; - INIT_DELAYED_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work); + INIT_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work); + INIT_WORK(&emu->emu1010.clock_work, emu1010_clock_work); /* read revision & serial */ emu->revision = pci->revision; pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c index 759e66e1105a..68dfcb24b889 100644 --- a/sound/pci/emu10k1/emu10k1_synth.c +++ b/sound/pci/emu10k1/emu10k1_synth.c @@ -22,7 +22,6 @@ static int snd_emu10k1_synth_probe(struct device *_dev) struct snd_emux *emux; struct snd_emu10k1 *hw; struct snd_emu10k1_synth_arg *arg; - unsigned long flags; arg = SNDRV_SEQ_DEVICE_ARGPTR(dev); if (arg == NULL) @@ -56,10 +55,10 @@ static int snd_emu10k1_synth_probe(struct device *_dev) return -ENOMEM; } - spin_lock_irqsave(&hw->voice_lock, flags); + spin_lock_irq(&hw->voice_lock); hw->synth = emux; hw->get_synth_voice = snd_emu10k1_synth_get_voice; - spin_unlock_irqrestore(&hw->voice_lock, flags); + spin_unlock_irq(&hw->voice_lock); dev->driver_data = emux; @@ -71,7 +70,6 @@ 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; if (dev->driver_data == NULL) return 0; /* not registered actually */ @@ -79,10 +77,10 @@ static int snd_emu10k1_synth_remove(struct device *_dev) emux = dev->driver_data; hw = emux->hw; - spin_lock_irqsave(&hw->voice_lock, flags); + spin_lock_irq(&hw->voice_lock); hw->synth = NULL; hw->get_synth_voice = NULL; - spin_unlock_irqrestore(&hw->voice_lock, flags); + spin_unlock_irq(&hw->voice_lock); snd_emux_free(emux); return 0; diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 9904bcfee106..03efc317e05f 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -1,17 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> + * James Courtier-Dutton <James@superbug.co.uk> + * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> * Creative Labs, Inc. - * Routines for effect processor FX8010 - * - * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk> - * Added EMU 1010 support. * - * BUGS: - * -- - * - * TODO: - * -- + * Routines for effect processor FX8010 */ #include <linux/pci.h> @@ -799,13 +793,10 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu, if (snd_emu10k1_look_for_ctl(emu, &gctl->id)) continue; gctl_id = (struct snd_ctl_elem_id *)&gctl->id; - down_read(&emu->card->controls_rwsem); if (snd_ctl_find_id(emu->card, gctl_id)) { - up_read(&emu->card->controls_rwsem); err = -EEXIST; goto __error; } - up_read(&emu->card->controls_rwsem); if (gctl_id->iface != SNDRV_CTL_ELEM_IFACE_MIXER && gctl_id->iface != SNDRV_CTL_ELEM_IFACE_PCM) { err = -EINVAL; @@ -977,11 +968,9 @@ static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu, in_kernel); if (err < 0) return err; - down_write(&card->controls_rwsem); ctl = snd_emu10k1_look_for_ctl(emu, &id); if (ctl) snd_ctl_remove(card, ctl->kcontrol); - up_write(&card->controls_rwsem); } return 0; } diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index f9500cd50a4b..0a32ea53d8c6 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -2,18 +2,12 @@ /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz>, * Takashi Iwai <tiwai@suse.de> + * Lee Revell <rlrevell@joe-job.com> + * James Courtier-Dutton <James@superbug.co.uk> + * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> * Creative Labs, Inc. - * Routines for control of EMU10K1 chips / mixer routines - * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com> - * - * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk> - * Added EMU 1010 support. - * - * BUGS: - * -- * - * TODO: - * -- + * Routines for control of EMU10K1 chips / mixer routines */ #include <linux/time.h> @@ -770,18 +764,21 @@ static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ct struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); unsigned int mask = snd_emu1010_adc_pad_regs[kcontrol->private_value]; unsigned int val, cache; + int change; + val = ucontrol->value.integer.value[0]; cache = emu->emu1010.adc_pads; if (val == 1) cache = cache | mask; else cache = cache & ~mask; - if (cache != emu->emu1010.adc_pads) { + change = (cache != emu->emu1010.adc_pads); + if (change) { snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache ); emu->emu1010.adc_pads = cache; } - return 0; + return change; } static const struct snd_kcontrol_new emu1010_adc_pads_ctl = { @@ -983,17 +980,21 @@ static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol, val = ucontrol->value.enumerated.item[0] ; if (val >= emu_ci->num) return -EINVAL; + spin_lock_irq(&emu->reg_lock); change = (emu->emu1010.clock_source != val); if (change) { emu->emu1010.clock_source = val; emu->emu1010.wclock = emu_ci->vals[val]; + snd_emu1010_update_clock(emu); snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE); snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, emu->emu1010.wclock); + spin_unlock_irq(&emu->reg_lock); + msleep(10); // Allow DLL to settle snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); - - snd_emu1010_update_clock(emu); + } else { + spin_unlock_irq(&emu->reg_lock); } return change; } @@ -1190,7 +1191,6 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol, unsigned int ngain, ogain; u16 gpio; int change = 0; - unsigned long flags; u32 source; /* If the capture source has changed, * update the capture volume from the cached value @@ -1204,13 +1204,13 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol, change = (emu->i2c_capture_source != source_id); if (change) { snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */ - spin_lock_irqsave(&emu->emu_lock, flags); + spin_lock_irq(&emu->emu_lock); gpio = inw(emu->port + A_IOCFG); if (source_id==0) outw(gpio | 0x4, emu->port + A_IOCFG); else outw(gpio & ~0x4, emu->port + A_IOCFG); - spin_unlock_irqrestore(&emu->emu_lock, flags); + spin_unlock_irq(&emu->emu_lock); ngain = emu->i2c_capture_volume[source_id][0]; /* Left */ ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ @@ -1354,7 +1354,6 @@ static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol, struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); int change; unsigned int reg, val, tmp; - unsigned long flags; switch(ucontrol->value.enumerated.item[0]) { case 0: @@ -1372,14 +1371,14 @@ static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol, } - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); reg = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); tmp = reg & ~A_SPDIF_RATE_MASK; tmp |= val; change = (tmp != reg); if (change) snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp); - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); return change; } @@ -1496,7 +1495,6 @@ static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol, static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - unsigned long flags; struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); struct snd_emu10k1_pcm_mixer *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)]; @@ -1504,7 +1502,7 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol, int num_efx = emu->audigy ? 8 : 4; int mask = emu->audigy ? 0x3f : 0x0f; - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); for (voice = 0; voice < 3; voice++) for (idx = 0; idx < num_efx; idx++) { val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask; @@ -1524,7 +1522,7 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol, &mix->send_routing[0][0]); } } - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); return change; } @@ -1566,14 +1564,13 @@ static int snd_emu10k1_send_volume_get(struct snd_kcontrol *kcontrol, static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - unsigned long flags; struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); struct snd_emu10k1_pcm_mixer *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)]; int change = 0, idx, val; int num_efx = emu->audigy ? 8 : 4; - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); for (idx = 0; idx < 3*num_efx; idx++) { val = ucontrol->value.integer.value[idx] & 255; if (mix->send_volume[idx/num_efx][idx%num_efx] != val) { @@ -1592,7 +1589,7 @@ static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol, &mix->send_volume[0][0]); } } - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); return change; } @@ -1632,13 +1629,12 @@ static int snd_emu10k1_attn_get(struct snd_kcontrol *kcontrol, static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - unsigned long flags; struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); struct snd_emu10k1_pcm_mixer *mix = &emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)]; int change = 0, idx, val; - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); for (idx = 0; idx < 3; idx++) { unsigned uval = ucontrol->value.integer.value[idx] & 0x1ffff; val = uval * 0x8000U / 0xffffU; @@ -1655,7 +1651,7 @@ static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol, snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]); } } - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); return change; } @@ -1701,7 +1697,6 @@ static int snd_emu10k1_efx_send_routing_get(struct snd_kcontrol *kcontrol, static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - unsigned long flags; struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch]; @@ -1709,7 +1704,7 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol, int num_efx = emu->audigy ? 8 : 4; int mask = emu->audigy ? 0x3f : 0x0f; - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); for (idx = 0; idx < num_efx; idx++) { val = ucontrol->value.integer.value[idx] & mask; if (mix->send_routing[0][idx] != val) { @@ -1724,7 +1719,7 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol, &mix->send_routing[0][0]); } } - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); return change; } @@ -1766,14 +1761,13 @@ static int snd_emu10k1_efx_send_volume_get(struct snd_kcontrol *kcontrol, static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - unsigned long flags; struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch]; int change = 0, idx, val; int num_efx = emu->audigy ? 8 : 4; - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); for (idx = 0; idx < num_efx; idx++) { val = ucontrol->value.integer.value[idx] & 255; if (mix->send_volume[0][idx] != val) { @@ -1787,7 +1781,7 @@ static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol, &mix->send_volume[0][0]); } } - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); return change; } @@ -1826,14 +1820,13 @@ static int snd_emu10k1_efx_attn_get(struct snd_kcontrol *kcontrol, static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - unsigned long flags; struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch]; int change = 0, val; unsigned uval; - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); uval = ucontrol->value.integer.value[0] & 0x1ffff; val = uval * 0x8000U / 0xffffU; if (mix->attn[0] != val) { @@ -1845,7 +1838,7 @@ static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol, snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[ch]->number, mix->attn[0]); } } - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); return change; } @@ -1881,7 +1874,6 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol, static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - unsigned long flags; struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); unsigned int reg, val, sw; int change = 0; @@ -1889,7 +1881,7 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol, sw = ucontrol->value.integer.value[0]; if (emu->card_capabilities->invert_shared_spdif) sw = !sw; - spin_lock_irqsave(&emu->emu_lock, flags); + spin_lock_irq(&emu->emu_lock); if ( emu->card_capabilities->i2c_adc) { /* Do nothing for Audigy 2 ZS Notebook */ } else if (emu->audigy) { @@ -1910,7 +1902,7 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol, reg |= val; outl(reg | val, emu->port + HCFG); } - spin_unlock_irqrestore(&emu->emu_lock, flags); + spin_unlock_irq(&emu->emu_lock); return change; } @@ -1990,18 +1982,9 @@ static int remove_ctl(struct snd_card *card, const char *name) return snd_ctl_remove_id(card, &id); } -static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name) -{ - struct snd_ctl_elem_id sid; - memset(&sid, 0, sizeof(sid)); - strcpy(sid.name, name); - sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - return snd_ctl_find_id(card, &sid); -} - static int rename_ctl(struct snd_card *card, const char *src, const char *dst) { - struct snd_kcontrol *kctl = ctl_find(card, src); + struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src); if (kctl) { snd_ctl_rename(card, kctl, dst); return 0; @@ -2342,8 +2325,8 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, emu1010_map_source(emu_ri, emu_ri->out_dflts[i]); snd_emu1010_apply_sources(emu); - err = snd_ctl_add(card, - snd_ctl_new1(&snd_emu1010_clock_source, emu)); + kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu); + err = snd_ctl_add(card, kctl); if (err < 0) return err; err = snd_ctl_add(card, diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index 3ce9b2129ce6..747c34b3d566 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -104,10 +104,9 @@ static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int st static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack) { - unsigned long flags; int timeout, ok; - spin_lock_irqsave(&midi->input_lock, flags); + spin_lock_irq(&midi->input_lock); mpu401_write_data(emu, midi, 0x00); /* mpu401_clear_rx(emu, midi); */ @@ -126,7 +125,7 @@ static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_mid } else { ok = 1; } - spin_unlock_irqrestore(&midi->input_lock, flags); + spin_unlock_irq(&midi->input_lock); if (!ok) { dev_err(emu->card->dev, "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n", @@ -142,22 +141,21 @@ static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream) { struct snd_emu10k1 *emu; struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; - unsigned long flags; emu = midi->emu; if (snd_BUG_ON(!emu)) return -ENXIO; - spin_lock_irqsave(&midi->open_lock, flags); + spin_lock_irq(&midi->open_lock); midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT; midi->substream_input = substream; if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) { - spin_unlock_irqrestore(&midi->open_lock, flags); + spin_unlock_irq(&midi->open_lock); if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1)) goto error_out; if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1)) goto error_out; } else { - spin_unlock_irqrestore(&midi->open_lock, flags); + spin_unlock_irq(&midi->open_lock); } return 0; @@ -169,22 +167,21 @@ static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream) { struct snd_emu10k1 *emu; struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; - unsigned long flags; emu = midi->emu; if (snd_BUG_ON(!emu)) return -ENXIO; - spin_lock_irqsave(&midi->open_lock, flags); + spin_lock_irq(&midi->open_lock); midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT; midi->substream_output = substream; if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { - spin_unlock_irqrestore(&midi->open_lock, flags); + spin_unlock_irq(&midi->open_lock); if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1)) goto error_out; if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1)) goto error_out; } else { - spin_unlock_irqrestore(&midi->open_lock, flags); + spin_unlock_irq(&midi->open_lock); } return 0; @@ -196,21 +193,20 @@ static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream) { struct snd_emu10k1 *emu; struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; - unsigned long flags; int err = 0; emu = midi->emu; if (snd_BUG_ON(!emu)) return -ENXIO; - spin_lock_irqsave(&midi->open_lock, flags); + spin_lock_irq(&midi->open_lock); snd_emu10k1_intr_disable(emu, midi->rx_enable); midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT; midi->substream_input = NULL; if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) { - spin_unlock_irqrestore(&midi->open_lock, flags); + spin_unlock_irq(&midi->open_lock); err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0); } else { - spin_unlock_irqrestore(&midi->open_lock, flags); + spin_unlock_irq(&midi->open_lock); } return err; } @@ -219,21 +215,20 @@ static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream { struct snd_emu10k1 *emu; struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; - unsigned long flags; int err = 0; emu = midi->emu; if (snd_BUG_ON(!emu)) return -ENXIO; - spin_lock_irqsave(&midi->open_lock, flags); + spin_lock_irq(&midi->open_lock); snd_emu10k1_intr_disable(emu, midi->tx_enable); midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT; midi->substream_output = NULL; if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) { - spin_unlock_irqrestore(&midi->open_lock, flags); + spin_unlock_irq(&midi->open_lock); err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0); } else { - spin_unlock_irqrestore(&midi->open_lock, flags); + spin_unlock_irq(&midi->open_lock); } return err; } @@ -256,7 +251,6 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr { struct snd_emu10k1 *emu; struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data; - unsigned long flags; emu = midi->emu; if (snd_BUG_ON(!emu)) @@ -267,13 +261,13 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr unsigned char byte; /* try to send some amount of bytes here before interrupts */ - spin_lock_irqsave(&midi->output_lock, flags); + spin_lock_irq(&midi->output_lock); while (max > 0) { if (mpu401_output_ready(emu, midi)) { if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) || snd_rawmidi_transmit(substream, &byte, 1) != 1) { /* no more data */ - spin_unlock_irqrestore(&midi->output_lock, flags); + spin_unlock_irq(&midi->output_lock); return; } mpu401_write_data(emu, midi, byte); @@ -282,7 +276,7 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr break; } } - spin_unlock_irqrestore(&midi->output_lock, flags); + spin_unlock_irq(&midi->output_lock); snd_emu10k1_intr_enable(emu, midi->tx_enable); } else { snd_emu10k1_intr_disable(emu, midi->tx_enable); diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 387288d623d7..7f4c1b38d6ec 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1,15 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> + * Lee Revell <rlrevell@joe-job.com> + * James Courtier-Dutton <James@superbug.co.uk> + * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> * Creative Labs, Inc. - * Routines for control of EMU10K1 chips / PCM routines - * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com> - * - * BUGS: - * -- * - * TODO: - * -- + * Routines for control of EMU10K1 chips / PCM routines */ #include <linux/pci.h> @@ -343,9 +340,7 @@ static void snd_emu10k1_pcm_init_voices(struct snd_emu10k1 *emu, unsigned int end_addr, struct snd_emu10k1_pcm_mixer *mix) { - unsigned long flags; - - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); snd_emu10k1_pcm_init_voice(emu, evoice, w_16, stereo, start_addr, end_addr, &mix->send_routing[stereo][0], @@ -355,7 +350,7 @@ static void snd_emu10k1_pcm_init_voices(struct snd_emu10k1 *emu, start_addr, end_addr, &mix->send_routing[2][0], &mix->send_volume[2][0]); - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); } static void snd_emu10k1_pcm_init_extra_voice(struct snd_emu10k1 *emu, diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 7e2cc532471f..2f80fd91017c 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -1,17 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> + * Lee Revell <rlrevell@joe-job.com> + * James Courtier-Dutton <James@superbug.co.uk> + * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> * Creative Labs, Inc. - * Routines for control of EMU10K1 chips / proc interface routines - * - * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk> - * Added EMU 1010 support. * - * BUGS: - * -- - * - * TODO: - * -- + * Routines for control of EMU10K1 chips / proc interface routines */ #include <linux/slab.h> @@ -536,15 +531,14 @@ static unsigned int snd_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn) { - unsigned long flags; unsigned int regptr, val; regptr = (reg << 16) | chn; - spin_lock_irqsave(&emu->emu_lock, flags); + spin_lock_irq(&emu->emu_lock); outl(regptr, emu->port + iobase + PTR); val = inl(emu->port + iobase + DATA); - spin_unlock_irqrestore(&emu->emu_lock, flags); + spin_unlock_irq(&emu->emu_lock); return val; } @@ -555,14 +549,13 @@ static void snd_ptr_write(struct snd_emu10k1 *emu, unsigned int data) { unsigned int regptr; - unsigned long flags; regptr = (reg << 16) | chn; - spin_lock_irqsave(&emu->emu_lock, flags); + spin_lock_irq(&emu->emu_lock); outl(regptr, emu->port + iobase + PTR); outl(data, emu->port + iobase + DATA); - spin_unlock_irqrestore(&emu->emu_lock, flags); + spin_unlock_irq(&emu->emu_lock); } diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index a0d66ce3ee83..74df2330015f 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -1,14 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> + * Lee Revell <rlrevell@joe-job.com> + * James Courtier-Dutton <James@superbug.co.uk> + * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> * Creative Labs, Inc. - * Routines for control of EMU10K1 chips - * - * BUGS: - * -- * - * TODO: - * -- + * Routines for control of EMU10K1 chips */ #include <linux/time.h> @@ -302,6 +300,8 @@ static void snd_emu1010_fpga_read_locked(struct snd_emu10k1 *emu, u32 reg, u32 * { // The higest input pin is used as the designated interrupt trigger, // so it needs to be masked out. + // But note that any other input pin change will also cause an IRQ, + // so using this function often causes an IRQ as a side effect. u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f; if (snd_BUG_ON(reg > 0x3f)) return; diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c index a813ef8c2f8d..71aa90b9cc88 100644 --- a/sound/pci/emu10k1/irq.c +++ b/sound/pci/emu10k1/irq.c @@ -3,12 +3,6 @@ * Copyright (c) by Jaroslav Kysela <perex@perex.cz> * Creative Labs, Inc. * Routines for IRQ control of EMU10K1 chips - * - * BUGS: - * -- - * - * TODO: - * -- */ #include <linux/time.h> @@ -149,6 +143,13 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id) outl(0, emu->port + INTE2); status &= ~IPR_P16V; } + if (status & IPR_A_GPIO) { + if (emu->gpio_interrupt) + emu->gpio_interrupt(emu); + else + snd_emu10k1_intr_disable(emu, INTE_A_GPIOENABLE); + status &= ~IPR_A_GPIO; + } if (status) { dev_err(emu->card->dev, diff --git a/sound/pci/emu10k1/p16v.h b/sound/pci/emu10k1/p16v.h index 9d429ad1feff..95ab8071751b 100644 --- a/sound/pci/emu10k1/p16v.h +++ b/sound/pci/emu10k1/p16v.h @@ -2,62 +2,6 @@ /* * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> * Driver p16v chips - * Version: 0.21 - * - * FEATURES currently supported: - * Output fixed at S32_LE, 2 channel to hw:0,0 - * Rates: 44.1, 48, 96, 192. - * - * Changelog: - * 0.8 - * Use separate card based buffer for periods table. - * 0.9 - * Use 2 channel output streams instead of 8 channel. - * (8 channel output streams might be good for ASIO type output) - * Corrected speaker output, so Front -> Front etc. - * 0.10 - * Fixed missed interrupts. - * 0.11 - * Add Sound card model number and names. - * Add Analog volume controls. - * 0.12 - * Corrected playback interrupts. Now interrupt per period, instead of half period. - * 0.13 - * Use single trigger for multichannel. - * 0.14 - * Mic capture now works at fixed: S32_LE, 96000Hz, Stereo. - * 0.15 - * Force buffer_size / period_size == INTEGER. - * 0.16 - * Update p16v.c to work with changed alsa api. - * 0.17 - * Update p16v.c to work with changed alsa api. Removed boot_devs. - * 0.18 - * Merging with snd-emu10k1 driver. - * 0.19 - * One stereo channel at 24bit now works. - * 0.20 - * Added better register defines. - * 0.21 - * Split from p16v.c - * - * BUGS: - * Some stability problems when unloading the snd-p16v kernel module. - * -- - * - * TODO: - * SPDIF out. - * Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz. - * Currently capture fixed at 48000Hz. - * - * -- - * GENERAL INFO: - * Model: SB0240 - * P16V Chip: CA0151-DBS - * Audigy 2 Chip: CA0102-IAT - * AC97 Codec: STAC 9721 - * ADC: Philips 1361T (Stereo 24bit) - * DAC: CS4382-K (8-channel, 24bit, 192Khz) * * This code was initially based on code from ALSA's emu10k1x.c which is: * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> diff --git a/sound/pci/emu10k1/p17v.h b/sound/pci/emu10k1/p17v.h index d4ada1c430c8..ee4f4ab4b79c 100644 --- a/sound/pci/emu10k1/p17v.h +++ b/sound/pci/emu10k1/p17v.h @@ -2,7 +2,6 @@ /* * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> * Driver p17v chips - * Version: 0.01 */ /******************************************************************************/ diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c index f3c78adf3248..bb2478319361 100644 --- a/sound/pci/emu10k1/timer.c +++ b/sound/pci/emu10k1/timer.c @@ -2,13 +2,9 @@ /* * Copyright (c) by Lee Revell <rlrevell@joe-job.com> * Clemens Ladisch <clemens@ladisch.de> - * Routines for control of EMU10K1 chips - * - * BUGS: - * -- + * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> * - * TODO: - * -- + * Routines for control of EMU10K1 chips */ #include <linux/time.h> diff --git a/sound/pci/emu10k1/tina2.h b/sound/pci/emu10k1/tina2.h index 7fd235345292..e3fcb290271c 100644 --- a/sound/pci/emu10k1/tina2.h +++ b/sound/pci/emu10k1/tina2.h @@ -2,7 +2,6 @@ /* * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> * Driver tina2 chips - * Version: 0.1 */ /********************************************************************************************************/ diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c index 6939498e26f0..77fb5427aaed 100644 --- a/sound/pci/emu10k1/voice.c +++ b/sound/pci/emu10k1/voice.c @@ -1,17 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> - * Creative Labs, Inc. * Lee Revell <rlrevell@joe-job.com> - * Routines for control of EMU10K1 chips - voice manager - * - * Rewrote voice allocator for multichannel support - rlrevell 12/2004 - * - * BUGS: - * -- + * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> + * Creative Labs, Inc. * - * TODO: - * -- + * Routines for control of EMU10K1 chips - voice manager */ #include <linux/time.h> diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index e34ec6f89e7e..ec598ba1a883 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -824,7 +824,7 @@ static snd_pcm_uframes_t snd_es1938_playback_pointer(struct snd_pcm_substream *s static int snd_es1938_capture_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *dst, unsigned long count) + struct iov_iter *dst, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct es1938 *chip = snd_pcm_substream_chip(substream); @@ -832,36 +832,17 @@ static int snd_es1938_capture_copy(struct snd_pcm_substream *substream, if (snd_BUG_ON(pos + count > chip->dma1_size)) return -EINVAL; if (pos + count < chip->dma1_size) { - if (copy_to_user(dst, runtime->dma_area + pos + 1, count)) + if (copy_to_iter(runtime->dma_area + pos + 1, count, dst) != count) return -EFAULT; } else { - if (copy_to_user(dst, runtime->dma_area + pos + 1, count - 1)) + if (copy_to_iter(runtime->dma_area + pos + 1, count - 1, dst) != count - 1) return -EFAULT; - if (put_user(runtime->dma_area[0], - ((unsigned char __user *)dst) + count - 1)) + if (copy_to_iter(runtime->dma_area, 1, dst) != 1) return -EFAULT; } return 0; } -static int snd_es1938_capture_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *dst, unsigned long count) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct es1938 *chip = snd_pcm_substream_chip(substream); - - if (snd_BUG_ON(pos + count > chip->dma1_size)) - return -EINVAL; - if (pos + count < chip->dma1_size) { - memcpy(dst, runtime->dma_area + pos + 1, count); - } else { - memcpy(dst, runtime->dma_area + pos + 1, count - 1); - runtime->dma_area[0] = *((unsigned char *)dst + count - 1); - } - return 0; -} - /* ---------------------------------------------------------------------- * Audio1 Capture (ADC) * ----------------------------------------------------------------------*/ @@ -987,8 +968,7 @@ static const struct snd_pcm_ops snd_es1938_capture_ops = { .prepare = snd_es1938_capture_prepare, .trigger = snd_es1938_capture_trigger, .pointer = snd_es1938_capture_pointer, - .copy_user = snd_es1938_capture_copy, - .copy_kernel = snd_es1938_capture_copy_kernel, + .copy = snd_es1938_capture_copy, }; static int snd_es1938_new_pcm(struct es1938 *chip, int device) diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 4a7e20bb11bc..4bc0f53c223b 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2005,9 +2005,6 @@ snd_es1968_mixer(struct es1968 *chip) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; -#ifndef CONFIG_SND_ES1968_INPUT - struct snd_ctl_elem_id elem_id; -#endif int err; static const struct snd_ac97_bus_ops ops = { .write = snd_es1968_ac97_write, @@ -2027,14 +2024,10 @@ snd_es1968_mixer(struct es1968 *chip) #ifndef CONFIG_SND_ES1968_INPUT /* attach master switch / volumes for h/w volume control */ - memset(&elem_id, 0, sizeof(elem_id)); - elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(elem_id.name, "Master Playback Switch"); - chip->master_switch = snd_ctl_find_id(chip->card, &elem_id); - memset(&elem_id, 0, sizeof(elem_id)); - elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(elem_id.name, "Master Playback Volume"); - chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); + chip->master_switch = snd_ctl_find_id_mixer(chip->card, + "Master Playback Switch"); + chip->master_volume = snd_ctl_find_id_mixer(chip->card, + "Master Playback Volume"); #endif return 0; diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 886255a03e8b..0d7502d6e060 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -104,6 +104,7 @@ config SND_HDA_SCODEC_CS35L41_I2C tristate "Build CS35L41 HD-audio side codec support for I2C Bus" depends on I2C depends on ACPI + depends on EFI depends on SND_SOC select SND_SOC_CS35L41_LIB select SND_HDA_SCODEC_CS35L41 @@ -119,6 +120,7 @@ config SND_HDA_SCODEC_CS35L41_SPI tristate "Build CS35L41 HD-audio codec support for SPI Bus" depends on SPI_MASTER depends on ACPI + depends on EFI depends on SND_SOC select SND_SOC_CS35L41_LIB select SND_HDA_SCODEC_CS35L41 @@ -130,6 +132,53 @@ config SND_HDA_SCODEC_CS35L41_SPI comment "Set to Y if you want auto-loading the side codec driver" depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m +config SND_HDA_SCODEC_CS35L56 + tristate + +config SND_HDA_SCODEC_CS35L56_I2C + tristate "Build CS35L56 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI || COMPILE_TEST + depends on SND_SOC + select CS_DSP + select SND_HDA_GENERIC + select SND_SOC_CS35L56_SHARED + select SND_HDA_SCODEC_CS35L56 + select SND_HDA_CS_DSP_CONTROLS + help + Say Y or M here to include CS35L56 amplifier support with + I2C control. + +config SND_HDA_SCODEC_CS35L56_SPI + tristate "Build CS35L56 HD-audio side codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI || COMPILE_TEST + depends on SND_SOC + select CS_DSP + select SND_HDA_GENERIC + select SND_SOC_CS35L56_SHARED + select SND_HDA_SCODEC_CS35L56 + select SND_HDA_CS_DSP_CONTROLS + help + Say Y or M here to include CS35L56 amplifier support with + SPI control. + +config SND_HDA_SCODEC_TAS2781_I2C + tristate "Build TAS2781 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on EFI + depends on SND_SOC + select SND_SOC_TAS2781_COMLIB + select SND_SOC_TAS2781_FMWLIB + select CRC32_SARWATE + help + Say Y or M here to include TAS2781 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m + config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" select SND_HDA_GENERIC diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 00d306104484..f00fc9ed6096 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -28,10 +28,14 @@ snd-hda-codec-via-objs := patch_via.o snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o # side codecs -snd-hda-scodec-cs35l41-objs := cs35l41_hda.o +snd-hda-scodec-cs35l41-objs := cs35l41_hda.o cs35l41_hda_property.o snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o +snd-hda-scodec-cs35l56-objs := cs35l56_hda.o +snd-hda-scodec-cs35l56-i2c-objs := cs35l56_hda_i2c.o +snd-hda-scodec-cs35l56-spi-objs := cs35l56_hda_spi.o snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o +snd-hda-scodec-tas2781-i2c-objs := tas2781_hda_i2c.o # common driver obj-$(CONFIG_SND_HDA) := snd-hda-codec.o @@ -55,7 +59,11 @@ obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o +obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o # this must be the last entry after codec drivers; # otherwise the codec patches won't be hooked before the PCI probe diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index ce5faa620517..f9b77353c266 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -19,6 +19,7 @@ #include "hda_component.h" #include "cs35l41_hda.h" #include "hda_cs_dsp_ctl.h" +#include "cs35l41_hda_property.h" #define CS35L41_FIRMWARE_ROOT "cirrus/" #define CS35L41_PART "cs35l41" @@ -58,8 +59,6 @@ static const struct reg_sequence cs35l41_hda_config[] = { { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON { CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL - { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB - { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB }; static const struct reg_sequence cs35l41_hda_config_dsp[] = { @@ -82,6 +81,14 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = { { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON { CS35L41_DSP1_RX5_SRC, 0x00000029 }, // DSP1RX5 SRC = VBSTMON +}; + +static const struct reg_sequence cs35l41_hda_unmute[] = { + { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB + { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB +}; + +static const struct reg_sequence cs35l41_hda_unmute_dsp[] = { { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB { CS35L41_AMP_GAIN_CTRL, 0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB }; @@ -483,73 +490,159 @@ static void cs35l41_irq_release(struct cs35l41_hda *cs35l41) cs35l41->irq_errors = 0; } -static void cs35l41_hda_playback_hook(struct device *dev, int action) +static void cs35l41_hda_play_start(struct device *dev) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); struct regmap *reg = cs35l41->regmap; - int ret = 0; + + dev_dbg(dev, "Play (Start)\n"); + + if (cs35l41->playback_started) { + dev_dbg(dev, "Playback already started."); + return; + } + + cs35l41->playback_started = true; + + if (cs35l41->firmware_running) { + regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, + ARRAY_SIZE(cs35l41_hda_config_dsp)); + regmap_update_bits(reg, CS35L41_PWR_CTRL2, + CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, + 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT); + cs35l41_set_cspl_mbox_cmd(cs35l41->dev, reg, CSPL_MBOX_CMD_RESUME); + } else { + regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config)); + } + regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT); + if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) + regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001); + +} + +static void cs35l41_hda_play_done(struct device *dev) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + struct regmap *reg = cs35l41->regmap; + + dev_dbg(dev, "Play (Complete)\n"); + + cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 1, NULL, + cs35l41->firmware_running); + if (cs35l41->firmware_running) { + regmap_multi_reg_write(reg, cs35l41_hda_unmute_dsp, + ARRAY_SIZE(cs35l41_hda_unmute_dsp)); + } else { + regmap_multi_reg_write(reg, cs35l41_hda_unmute, + ARRAY_SIZE(cs35l41_hda_unmute)); + } +} + +static void cs35l41_hda_pause_start(struct device *dev) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + struct regmap *reg = cs35l41->regmap; + + dev_dbg(dev, "Pause (Start)\n"); + + regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute)); + cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 0, NULL, + cs35l41->firmware_running); +} + +static void cs35l41_hda_pause_done(struct device *dev) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + struct regmap *reg = cs35l41->regmap; + + dev_dbg(dev, "Pause (Complete)\n"); + + regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT); + if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) + regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001); + if (cs35l41->firmware_running) { + cs35l41_set_cspl_mbox_cmd(dev, reg, CSPL_MBOX_CMD_PAUSE); + regmap_update_bits(reg, CS35L41_PWR_CTRL2, + CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, + 0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT); + } + cs35l41_irq_release(cs35l41); + cs35l41->playback_started = false; +} + +static void cs35l41_hda_pre_playback_hook(struct device *dev, int action) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); switch (action) { - case HDA_GEN_PCM_ACT_OPEN: - pm_runtime_get_sync(dev); + case HDA_GEN_PCM_ACT_CLEANUP: mutex_lock(&cs35l41->fw_mutex); - cs35l41->playback_started = true; - if (cs35l41->firmware_running) { - regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, - ARRAY_SIZE(cs35l41_hda_config_dsp)); - regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, - CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, - 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT); - cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, - CSPL_MBOX_CMD_RESUME); - } else { - regmap_multi_reg_write(reg, cs35l41_hda_config, - ARRAY_SIZE(cs35l41_hda_config)); - } - ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2, - CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT); - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) - regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001); + cs35l41_hda_pause_start(dev); mutex_unlock(&cs35l41->fw_mutex); break; + default: + break; + } +} +static void cs35l41_hda_playback_hook(struct device *dev, int action) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + /* + * All amps must be resumed before we can start playing back. + * This ensures, for external boost, that all amps are in AMP_SAFE mode. + * Do this in HDA_GEN_PCM_ACT_OPEN, since this is run prior to any of the + * other actions. + */ + pm_runtime_get_sync(dev); + break; case HDA_GEN_PCM_ACT_PREPARE: mutex_lock(&cs35l41->fw_mutex); - ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 1, NULL); + cs35l41_hda_play_start(dev); mutex_unlock(&cs35l41->fw_mutex); break; case HDA_GEN_PCM_ACT_CLEANUP: mutex_lock(&cs35l41->fw_mutex); - regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute)); - ret = cs35l41_global_enable(reg, cs35l41->hw_cfg.bst_type, 0, NULL); + cs35l41_hda_pause_done(dev); mutex_unlock(&cs35l41->fw_mutex); break; case HDA_GEN_PCM_ACT_CLOSE: mutex_lock(&cs35l41->fw_mutex); - ret = regmap_update_bits(reg, CS35L41_PWR_CTRL2, - CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT); - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) - regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001); - if (cs35l41->firmware_running) { - cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, - CSPL_MBOX_CMD_PAUSE); - regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, - CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, - 0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT); + if (!cs35l41->firmware_running && cs35l41->request_fw_load && + !cs35l41->fw_request_ongoing) { + dev_info(dev, "Requesting Firmware Load after HDA_GEN_PCM_ACT_CLOSE\n"); + cs35l41->fw_request_ongoing = true; + schedule_work(&cs35l41->fw_load_work); } - cs35l41_irq_release(cs35l41); - cs35l41->playback_started = false; mutex_unlock(&cs35l41->fw_mutex); + /* + * Playback must be finished for all amps before we start runtime suspend. + * This ensures no amps are playing back when we start putting them to sleep. + */ pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); break; default: - dev_warn(cs35l41->dev, "Playback action not supported: %d\n", action); break; } +} - if (ret) - dev_err(cs35l41->dev, "Regmap access fail: %d\n", ret); +static void cs35l41_hda_post_playback_hook(struct device *dev, int action) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + mutex_lock(&cs35l41->fw_mutex); + cs35l41_hda_play_done(dev); + mutex_unlock(&cs35l41->fw_mutex); + break; + default: + break; + } } static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot, @@ -572,21 +665,62 @@ static int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsi rx_slot); } -static void cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41) +static int cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41) { + int ret = 0; + mutex_lock(&cs35l41->fw_mutex); if (cs35l41->firmware_running) { regcache_cache_only(cs35l41->regmap, false); - cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap); + ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap); + if (ret) { + dev_warn(cs35l41->dev, "Unable to exit Hibernate."); + goto err; + } + + /* Test key needs to be unlocked to allow the OTP settings to re-apply */ + cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); + ret = regcache_sync(cs35l41->regmap); + cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); + if (ret) { + dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret); + goto err; + } + + if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) + cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg); + cs35l41_shutdown_dsp(cs35l41); cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type); + } +err: + regcache_cache_only(cs35l41->regmap, true); + regcache_mark_dirty(cs35l41->regmap); + + mutex_unlock(&cs35l41->fw_mutex); - regcache_cache_only(cs35l41->regmap, true); - regcache_mark_dirty(cs35l41->regmap); + return ret; +} + +static int cs35l41_system_suspend_prep(struct device *dev) +{ + struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "System Suspend Prepare\n"); + + if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) { + dev_err_once(cs35l41->dev, "System Suspend not supported\n"); + return 0; /* don't block the whole system suspend */ } + + mutex_lock(&cs35l41->fw_mutex); + if (cs35l41->playback_started) + cs35l41_hda_pause_start(dev); mutex_unlock(&cs35l41->fw_mutex); + + return 0; } static int cs35l41_system_suspend(struct device *dev) @@ -601,18 +735,28 @@ static int cs35l41_system_suspend(struct device *dev) return 0; /* don't block the whole system suspend */ } + mutex_lock(&cs35l41->fw_mutex); + if (cs35l41->playback_started) + cs35l41_hda_pause_done(dev); + mutex_unlock(&cs35l41->fw_mutex); + ret = pm_runtime_force_suspend(dev); - if (ret) + if (ret) { + dev_err(dev, "System Suspend Failed, unable to runtime suspend: %d\n", ret); return ret; + } /* Shutdown DSP before system suspend */ - cs35l41_ready_for_reset(cs35l41); + ret = cs35l41_ready_for_reset(cs35l41); + + if (ret) + dev_err(dev, "System Suspend Failed, not ready for Reset: %d\n", ret); /* * Reset GPIO may be shared, so cannot reset here. * However beyond this point, amps may be powered down. */ - return 0; + return ret; } static int cs35l41_system_resume(struct device *dev) @@ -635,9 +779,14 @@ static int cs35l41_system_resume(struct device *dev) usleep_range(2000, 2100); ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(dev, "System Resume Failed: Unable to runtime resume: %d\n", ret); + return ret; + } mutex_lock(&cs35l41->fw_mutex); - if (!ret && cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) { + + if (cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) { cs35l41->fw_request_ongoing = true; schedule_work(&cs35l41->fw_load_work); } @@ -669,20 +818,6 @@ static int cs35l41_runtime_suspend(struct device *dev) mutex_lock(&cs35l41->fw_mutex); - if (cs35l41->playback_started) { - regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute, - ARRAY_SIZE(cs35l41_hda_mute)); - cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0, NULL); - regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, - CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT); - if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) - regmap_write(cs35l41->regmap, CS35L41_GPIO1_CTRL1, 0x00000001); - regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, - CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, - 0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT); - cs35l41->playback_started = false; - } - if (cs35l41->firmware_running) { ret = cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type); @@ -778,7 +913,12 @@ static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41) goto clean_dsp; } - cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, CSPL_MBOX_CMD_PAUSE); + ret = cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, CSPL_MBOX_CMD_PAUSE); + if (ret) { + dev_err(cs35l41->dev, "Error waiting for DSP to pause: %u\n", ret); + goto clean_dsp; + } + cs35l41->firmware_running = true; return 0; @@ -937,6 +1077,7 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); struct hda_component *comps = master_data; + unsigned int sleep_flags; int ret = 0; if (!comps || cs35l41->index < 0 || cs35l41->index >= HDA_MAX_COMPONENTS) @@ -971,12 +1112,26 @@ static int cs35l41_hda_bind(struct device *dev, struct device *master, void *mas ret = cs35l41_create_controls(cs35l41); comps->playback_hook = cs35l41_hda_playback_hook; + comps->pre_playback_hook = cs35l41_hda_pre_playback_hook; + comps->post_playback_hook = cs35l41_hda_post_playback_hook; mutex_unlock(&cs35l41->fw_mutex); + sleep_flags = lock_system_sleep(); + if (!device_link_add(&comps->codec->core.dev, cs35l41->dev, DL_FLAG_STATELESS)) + dev_warn(dev, "Unable to create device link\n"); + unlock_system_sleep(sleep_flags); + pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); + dev_info(cs35l41->dev, + "CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n", + cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type, + cs35l41->hw_cfg.gpio1.func == CS35l41_VSPK_SWITCH, + cs35l41->hw_cfg.spk_pos ? 'R' : 'L', + cs35l41->firmware_running, cs35l41->speaker_id); + return ret; } @@ -984,9 +1139,14 @@ static void cs35l41_hda_unbind(struct device *dev, struct device *master, void * { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); struct hda_component *comps = master_data; + unsigned int sleep_flags; - if (comps[cs35l41->index].dev == dev) + if (comps[cs35l41->index].dev == dev) { memset(&comps[cs35l41->index], 0, sizeof(*comps)); + sleep_flags = lock_system_sleep(); + device_link_remove(&comps->codec->core.dev, cs35l41->dev); + unlock_system_sleep(sleep_flags); + } } static const struct component_ops cs35l41_hda_comp_ops = { @@ -1156,8 +1316,7 @@ static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos); } -static int cs35l41_get_speaker_id(struct device *dev, int amp_index, - int num_amps, int fixed_gpio_id) +int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id) { struct gpio_desc *speaker_id_desc; int speaker_id = -ENODEV; @@ -1211,49 +1370,6 @@ static int cs35l41_get_speaker_id(struct device *dev, int amp_index, return speaker_id; } -/* - * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work. - * And devices created by serial-multi-instantiate don't have their device struct - * pointing to the correct fwnode, so acpi_dev must be used here. - * And devm functions expect that the device requesting the resource has the correct - * fwnode. - */ -static int cs35l41_no_acpi_dsd(struct cs35l41_hda *cs35l41, struct device *physdev, int id, - const char *hid) -{ - struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; - - /* check I2C address to assign the index */ - cs35l41->index = id == 0x40 ? 0 : 1; - cs35l41->channel_index = 0; - cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); - cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); - hw_cfg->spk_pos = cs35l41->index; - hw_cfg->gpio2.func = CS35L41_INTERRUPT; - hw_cfg->gpio2.valid = true; - hw_cfg->valid = true; - - if (strncmp(hid, "CLSA0100", 8) == 0) { - hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH; - } else if (strncmp(hid, "CLSA0101", 8) == 0) { - hw_cfg->bst_type = CS35L41_EXT_BOOST; - hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH; - hw_cfg->gpio1.valid = true; - } else { - /* - * Note: CLSA010(0/1) are special cases which use a slightly different design. - * All other HIDs e.g. CSC3551 require valid ACPI _DSD properties to be supported. - */ - dev_err(cs35l41->dev, "Error: ACPI _DSD Properties are missing for HID %s.\n", hid); - hw_cfg->valid = false; - hw_cfg->gpio1.valid = false; - hw_cfg->gpio2.valid = false; - return -EINVAL; - } - - return 0; -} - static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id) { struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; @@ -1279,12 +1395,17 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i sub = NULL; cs35l41->acpi_subsystem_id = sub; + ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid); + if (!ret) { + dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n"); + goto put_physdev; + } + property = "cirrus,dev-index"; ret = device_property_count_u32(physdev, property); - if (ret <= 0) { - ret = cs35l41_no_acpi_dsd(cs35l41, physdev, id, hid); - goto err_put_physdev; - } + if (ret <= 0) + goto err; + if (ret > ARRAY_SIZE(values)) { ret = -EINVAL; goto err; @@ -1374,7 +1495,10 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i err: dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret); -err_put_physdev: + hw_cfg->valid = false; + hw_cfg->gpio1.valid = false; + hw_cfg->gpio2.valid = false; +put_physdev: put_device(physdev); return ret; @@ -1477,6 +1601,11 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i if (ret) goto err; + ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute, + ARRAY_SIZE(cs35l41_hda_mute)); + if (ret) + goto err; + INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work); mutex_init(&cs35l41->fw_mutex); @@ -1542,6 +1671,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, SND_HDA_SCODEC_CS35L41); const struct dev_pm_ops cs35l41_hda_pm_ops = { RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, cs35l41_runtime_idle) + .prepare = cs35l41_system_suspend_prep, SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume) }; EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41); diff --git a/sound/pci/hda/cs35l41_hda.h b/sound/pci/hda/cs35l41_hda.h index bdb35f3be68a..b93bf762976e 100644 --- a/sound/pci/hda/cs35l41_hda.h +++ b/sound/pci/hda/cs35l41_hda.h @@ -83,5 +83,6 @@ extern const struct dev_pm_ops cs35l41_hda_pm_ops; int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq, struct regmap *regmap); void cs35l41_hda_remove(struct device *dev); +int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id); #endif /*__CS35L41_HDA_H__*/ diff --git a/sound/pci/hda/cs35l41_hda_property.c b/sound/pci/hda/cs35l41_hda_property.c new file mode 100644 index 000000000000..b62a4e6968e2 --- /dev/null +++ b/sound/pci/hda/cs35l41_hda_property.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS35L41 ALSA HDA Property driver +// +// Copyright 2023 Cirrus Logic, Inc. +// +// Author: Stefan Binding <sbinding@opensource.cirrus.com> + +#include <linux/gpio/consumer.h> +#include <linux/string.h> +#include "cs35l41_hda_property.h" + +/* + * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work. + * And devices created by serial-multi-instantiate don't have their device struct + * pointing to the correct fwnode, so acpi_dev must be used here. + * And devm functions expect that the device requesting the resource has the correct + * fwnode. + */ +static int lenovo_legion_no_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid) +{ + struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; + + /* check I2C address to assign the index */ + cs35l41->index = id == 0x40 ? 0 : 1; + cs35l41->channel_index = 0; + cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH); + cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2); + hw_cfg->spk_pos = cs35l41->index; + hw_cfg->gpio2.func = CS35L41_INTERRUPT; + hw_cfg->gpio2.valid = true; + hw_cfg->valid = true; + + if (strcmp(hid, "CLSA0100") == 0) { + hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH; + } else if (strcmp(hid, "CLSA0101") == 0) { + hw_cfg->bst_type = CS35L41_EXT_BOOST; + hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH; + hw_cfg->gpio1.valid = true; + } + + return 0; +} + +/* + * Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type. + * We can override the _DSD to correct the boost type here. + * Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists + * in the ACPI. + */ +static int hp_vision_acpi_fix(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid) +{ + struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; + + dev_info(cs35l41->dev, "Adding DSD properties for %s\n", cs35l41->acpi_subsystem_id); + + cs35l41->index = id; + cs35l41->channel_index = 0; + cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 1, GPIOD_OUT_HIGH); + cs35l41->speaker_id = -ENOENT; + hw_cfg->spk_pos = cs35l41->index ? 1 : 0; // right:left + hw_cfg->gpio1.func = CS35L41_NOT_USED; + hw_cfg->gpio1.valid = true; + hw_cfg->gpio2.func = CS35L41_INTERRUPT; + hw_cfg->gpio2.valid = true; + hw_cfg->bst_type = CS35L41_INT_BOOST; + hw_cfg->bst_ind = 1000; + hw_cfg->bst_ipk = 4500; + hw_cfg->bst_cap = 24; + hw_cfg->valid = true; + + return 0; +} + +struct cs35l41_prop_model { + const char *hid; + const char *ssid; + int (*add_prop)(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid); +}; + +static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { + { "CLSA0100", NULL, lenovo_legion_no_acpi }, + { "CLSA0101", NULL, lenovo_legion_no_acpi }, + { "CSC3551", "103C89C6", hp_vision_acpi_fix }, + {} +}; + +int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid) +{ + const struct cs35l41_prop_model *model; + + for (model = cs35l41_prop_model_table; model->hid; model++) { + if (!strcmp(model->hid, hid) && + (!model->ssid || + (cs35l41->acpi_subsystem_id && + !strcmp(model->ssid, cs35l41->acpi_subsystem_id)))) + return model->add_prop(cs35l41, physdev, id, hid); + } + + return -ENOENT; +} diff --git a/sound/pci/hda/cs35l41_hda_property.h b/sound/pci/hda/cs35l41_hda_property.h new file mode 100644 index 000000000000..fd834042e2fd --- /dev/null +++ b/sound/pci/hda/cs35l41_hda_property.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * CS35L41 ALSA HDA Property driver + * + * Copyright 2023 Cirrus Logic, Inc. + * + * Author: Stefan Binding <sbinding@opensource.cirrus.com> + */ + +#ifndef CS35L41_HDA_PROP_H +#define CS35L41_HDA_PROP_H + +#include <linux/device.h> +#include "cs35l41_hda.h" + +int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id, + const char *hid); +#endif /* CS35L41_HDA_PROP_H */ diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c new file mode 100644 index 000000000000..76b9c685560b --- /dev/null +++ b/sound/pci/hda/cs35l56_hda.c @@ -0,0 +1,1034 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// HDA audio driver for Cirrus Logic CS35L56 smart amp +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// + +#include <linux/acpi.h> +#include <linux/debugfs.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/hda_codec.h> +#include <sound/tlv.h> +#include "cs35l56_hda.h" +#include "hda_component.h" +#include "hda_cs_dsp_ctl.h" +#include "hda_generic.h" + + /* + * The cs35l56_hda_dai_config[] reg sequence configures the device as + * ASP1_BCLK_FREQ = 3.072 MHz + * ASP1_RX_WIDTH = 32 cycles per slot, ASP1_TX_WIDTH = 32 cycles per slot, ASP1_FMT = I2S + * ASP1_DOUT_HIZ_CONTROL = Hi-Z during unused timeslots + * ASP1_RX_WL = 24 bits per sample + * ASP1_TX_WL = 24 bits per sample + * ASP1_RXn_EN 1..3 and ASP1_TXn_EN 1..4 disabled + */ +static const struct reg_sequence cs35l56_hda_dai_config[] = { + { CS35L56_ASP1_CONTROL1, 0x00000021 }, + { CS35L56_ASP1_CONTROL2, 0x20200200 }, + { CS35L56_ASP1_CONTROL3, 0x00000003 }, + { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, + { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, + { CS35L56_ASP1_ENABLES1, 0x00000000 }, +}; + +static void cs35l56_hda_play(struct cs35l56_hda *cs35l56) +{ + unsigned int val; + int ret; + + pm_runtime_get_sync(cs35l56->base.dev); + ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PLAY); + if (ret == 0) { + /* Wait for firmware to enter PS0 power state */ + ret = regmap_read_poll_timeout(cs35l56->base.regmap, + CS35L56_TRANSDUCER_ACTUAL_PS, + val, (val == CS35L56_PS0), + CS35L56_PS0_POLL_US, + CS35L56_PS0_TIMEOUT_US); + if (ret) + dev_warn(cs35l56->base.dev, "PS0 wait failed: %d\n", ret); + } + regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1, + BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) | + cs35l56->asp_tx_mask); + cs35l56->playing = true; +} + +static void cs35l56_hda_pause(struct cs35l56_hda *cs35l56) +{ + cs35l56->playing = false; + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE); + regmap_clear_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1, + BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) | + BIT(CS35L56_ASP_TX1_EN_SHIFT) | BIT(CS35L56_ASP_TX2_EN_SHIFT) | + BIT(CS35L56_ASP_TX3_EN_SHIFT) | BIT(CS35L56_ASP_TX4_EN_SHIFT)); + + pm_runtime_mark_last_busy(cs35l56->base.dev); + pm_runtime_put_autosuspend(cs35l56->base.dev); +} + +static void cs35l56_hda_playback_hook(struct device *dev, int action) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + + dev_dbg(cs35l56->base.dev, "%s()%d: action: %d\n", __func__, __LINE__, action); + + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + if (cs35l56->playing) + break; + + /* If we're suspended: flag that resume should start playback */ + if (cs35l56->suspended) { + cs35l56->playing = true; + break; + } + + cs35l56_hda_play(cs35l56); + break; + case HDA_GEN_PCM_ACT_CLEANUP: + if (!cs35l56->playing) + break; + + cs35l56_hda_pause(cs35l56); + break; + default: + break; + } +} + +static int __maybe_unused cs35l56_hda_runtime_suspend(struct device *dev) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + + if (cs35l56->cs_dsp.booted) + cs_dsp_stop(&cs35l56->cs_dsp); + + return cs35l56_runtime_suspend_common(&cs35l56->base); +} + +static int __maybe_unused cs35l56_hda_runtime_resume(struct device *dev) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + int ret; + + ret = cs35l56_runtime_resume_common(&cs35l56->base, false); + if (ret < 0) + return ret; + + if (cs35l56->cs_dsp.booted) { + ret = cs_dsp_run(&cs35l56->cs_dsp); + if (ret) { + dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret); + goto err; + } + } + + return 0; + +err: + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE); + regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + CS35L56_MBOX_CMD_HIBERNATE_NOW); + + regcache_cache_only(cs35l56->base.regmap, true); + + return ret; +} + +static int cs35l56_hda_mixer_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = CS35L56_NUM_INPUT_SRC; + if (uinfo->value.enumerated.item >= CS35L56_NUM_INPUT_SRC) + uinfo->value.enumerated.item = CS35L56_NUM_INPUT_SRC - 1; + strscpy(uinfo->value.enumerated.name, cs35l56_tx_input_texts[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; + unsigned int reg_val; + int i; + + regmap_read(cs35l56->base.regmap, kcontrol->private_value, ®_val); + reg_val &= CS35L56_ASP_TXn_SRC_MASK; + + for (i = 0; i < CS35L56_NUM_INPUT_SRC; ++i) { + if (cs35l56_tx_input_values[i] == reg_val) { + ucontrol->value.enumerated.item[0] = i; + break; + } + } + + return 0; +} + +static int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; + unsigned int item = ucontrol->value.enumerated.item[0]; + bool changed; + + if (item >= CS35L56_NUM_INPUT_SRC) + return -EINVAL; + + regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value, + CS35L56_INPUT_MASK, cs35l56_tx_input_values[item], + &changed); + + return changed; +} + +static int cs35l56_hda_posture_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = CS35L56_MAIN_POSTURE_MIN; + uinfo->value.integer.max = CS35L56_MAIN_POSTURE_MAX; + return 0; +} + +static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; + unsigned int pos; + int ret; + + ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_POSTURE_NUMBER, &pos); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = pos; + + return ret; +} + +static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; + unsigned long pos = ucontrol->value.integer.value[0]; + bool changed; + int ret; + + if ((pos < CS35L56_MAIN_POSTURE_MIN) || + (pos > CS35L56_MAIN_POSTURE_MAX)) + return -EINVAL; + + ret = regmap_update_bits_check(cs35l56->base.regmap, + CS35L56_MAIN_POSTURE_NUMBER, + CS35L56_MAIN_POSTURE_MASK, + pos, &changed); + if (ret) + return ret; + + return changed; +} + +static const struct { + const char *name; + unsigned int reg; +} cs35l56_hda_mixer_controls[] = { + { "ASP1 TX1 Source", CS35L56_ASP1TX1_INPUT }, + { "ASP1 TX2 Source", CS35L56_ASP1TX2_INPUT }, + { "ASP1 TX3 Source", CS35L56_ASP1TX3_INPUT }, + { "ASP1 TX4 Source", CS35L56_ASP1TX4_INPUT }, +}; + +static const DECLARE_TLV_DB_SCALE(cs35l56_hda_vol_tlv, -10000, 25, 0); + +static int cs35l56_hda_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.step = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = CS35L56_MAIN_RENDER_USER_VOLUME_MAX - + CS35L56_MAIN_RENDER_USER_VOLUME_MIN; + + return 0; +} + +static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; + unsigned int raw_vol; + int vol; + int ret; + + ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_RENDER_USER_VOLUME, &raw_vol); + + if (ret) + return ret; + + vol = (s16)(raw_vol & 0xFFFF); + vol >>= CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT; + + if (vol & BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT)) + vol |= ~((int)(BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT) - 1)); + + ucontrol->value.integer.value[0] = vol - CS35L56_MAIN_RENDER_USER_VOLUME_MIN; + + return 0; +} + +static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data; + long vol = ucontrol->value.integer.value[0]; + unsigned int raw_vol; + bool changed; + int ret; + + if ((vol < 0) || (vol > (CS35L56_MAIN_RENDER_USER_VOLUME_MAX - + CS35L56_MAIN_RENDER_USER_VOLUME_MIN))) + return -EINVAL; + + raw_vol = (vol + CS35L56_MAIN_RENDER_USER_VOLUME_MIN) << + CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT; + + ret = regmap_update_bits_check(cs35l56->base.regmap, + CS35L56_MAIN_RENDER_USER_VOLUME, + CS35L56_MAIN_RENDER_USER_VOLUME_MASK, + raw_vol, &changed); + if (ret) + return ret; + + return changed; +} + +static void cs35l56_hda_create_controls(struct cs35l56_hda *cs35l56) +{ + struct snd_kcontrol_new ctl_template = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = cs35l56_hda_posture_info, + .get = cs35l56_hda_posture_get, + .put = cs35l56_hda_posture_put, + }; + char name[64]; + int i; + + snprintf(name, sizeof(name), "%s Posture Number", cs35l56->amp_name); + ctl_template.name = name; + cs35l56->posture_ctl = snd_ctl_new1(&ctl_template, cs35l56); + if (snd_ctl_add(cs35l56->codec->card, cs35l56->posture_ctl)) + dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name); + + /* Mixer controls */ + ctl_template.info = cs35l56_hda_mixer_info; + ctl_template.get = cs35l56_hda_mixer_get; + ctl_template.put = cs35l56_hda_mixer_put; + + BUILD_BUG_ON(ARRAY_SIZE(cs35l56->mixer_ctl) != ARRAY_SIZE(cs35l56_hda_mixer_controls)); + + for (i = 0; i < ARRAY_SIZE(cs35l56_hda_mixer_controls); ++i) { + snprintf(name, sizeof(name), "%s %s", cs35l56->amp_name, + cs35l56_hda_mixer_controls[i].name); + ctl_template.private_value = cs35l56_hda_mixer_controls[i].reg; + cs35l56->mixer_ctl[i] = snd_ctl_new1(&ctl_template, cs35l56); + if (snd_ctl_add(cs35l56->codec->card, cs35l56->mixer_ctl[i])) { + dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", + ctl_template.name); + } + } + + ctl_template.info = cs35l56_hda_vol_info; + ctl_template.get = cs35l56_hda_vol_get; + ctl_template.put = cs35l56_hda_vol_put; + ctl_template.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ); + ctl_template.tlv.p = cs35l56_hda_vol_tlv; + snprintf(name, sizeof(name), "%s Speaker Playback Volume", cs35l56->amp_name); + ctl_template.name = name; + cs35l56->volume_ctl = snd_ctl_new1(&ctl_template, cs35l56); + if (snd_ctl_add(cs35l56->codec->card, cs35l56->volume_ctl)) + dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name); +} + +static void cs35l56_hda_remove_controls(struct cs35l56_hda *cs35l56) +{ + int i; + + for (i = ARRAY_SIZE(cs35l56->mixer_ctl) - 1; i >= 0; i--) + snd_ctl_remove(cs35l56->codec->card, cs35l56->mixer_ctl[i]); + + snd_ctl_remove(cs35l56->codec->card, cs35l56->posture_ctl); + snd_ctl_remove(cs35l56->codec->card, cs35l56->volume_ctl); +} + +static const struct cs_dsp_client_ops cs35l56_hda_client_ops = { + .control_remove = hda_cs_dsp_control_remove, +}; + +static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56, + const struct firmware **firmware, char **filename, + const char *dir, const char *system_name, + const char *amp_name, + const char *filetype) +{ + char *s, c; + int ret = 0; + + if (system_name && amp_name) + *filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc-%s-%s.%s", dir, + cs35l56->base.secured ? "s" : "", cs35l56->base.rev, + system_name, amp_name, filetype); + else if (system_name) + *filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc-%s.%s", dir, + cs35l56->base.secured ? "s" : "", cs35l56->base.rev, + system_name, filetype); + else + *filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc.%s", dir, + cs35l56->base.secured ? "s" : "", cs35l56->base.rev, + filetype); + + if (!*filename) + return -ENOMEM; + + /* + * Make sure that filename is lower-case and any non alpha-numeric + * characters except full stop and forward slash are replaced with + * hyphens. + */ + s = *filename; + while (*s) { + c = *s; + if (isalnum(c)) + *s = tolower(c); + else if (c != '.' && c != '/') + *s = '-'; + s++; + } + + ret = firmware_request_nowarn(firmware, *filename, cs35l56->base.dev); + if (ret) { + dev_dbg(cs35l56->base.dev, "Failed to request '%s'\n", *filename); + kfree(*filename); + *filename = NULL; + return ret; + } + + dev_dbg(cs35l56->base.dev, "Found '%s'\n", *filename); + + return 0; +} + +static const char cirrus_dir[] = "cirrus/"; +static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, + const struct firmware **wmfw_firmware, + char **wmfw_filename, + const struct firmware **coeff_firmware, + char **coeff_filename) +{ + const char *system_name = cs35l56->system_name; + const char *amp_name = cs35l56->amp_name; + int ret; + + if (system_name && amp_name) { + if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, + cirrus_dir, system_name, amp_name, "wmfw")) { + cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, + cirrus_dir, system_name, amp_name, "bin"); + return; + } + } + + if (system_name) { + if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, + cirrus_dir, system_name, NULL, "wmfw")) { + if (amp_name) + cs35l56_hda_request_firmware_file(cs35l56, + coeff_firmware, coeff_filename, + cirrus_dir, system_name, + amp_name, "bin"); + if (!*coeff_firmware) + cs35l56_hda_request_firmware_file(cs35l56, + coeff_firmware, coeff_filename, + cirrus_dir, system_name, + NULL, "bin"); + return; + } + } + + ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename, + cirrus_dir, NULL, NULL, "wmfw"); + if (!ret) { + cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, + cirrus_dir, NULL, NULL, "bin"); + return; + } + + /* When a firmware file is not found must still search for the coeff files */ + if (system_name) { + if (amp_name) + cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, + cirrus_dir, system_name, amp_name, "bin"); + if (!*coeff_firmware) + cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, + cirrus_dir, system_name, NULL, "bin"); + } + + if (!*coeff_firmware) + cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename, + cirrus_dir, NULL, NULL, "bin"); +} + +static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware, + char *wmfw_filename, + const struct firmware *coeff_firmware, + char *coeff_filename) +{ + if (wmfw_firmware) + release_firmware(wmfw_firmware); + kfree(wmfw_filename); + + if (coeff_firmware) + release_firmware(coeff_firmware); + kfree(coeff_filename); +} + +static void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56) +{ + struct hda_cs_dsp_ctl_info info; + + info.device_name = cs35l56->amp_name; + info.fw_type = HDA_CS_DSP_FW_MISC; + info.card = cs35l56->codec->card; + + hda_cs_dsp_add_controls(&cs35l56->cs_dsp, &info); +} + +static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) +{ + const struct firmware *coeff_firmware = NULL; + const struct firmware *wmfw_firmware = NULL; + char *coeff_filename = NULL; + char *wmfw_filename = NULL; + unsigned int firmware_missing; + int ret = 0; + + /* Prepare for a new DSP power-up */ + if (cs35l56->base.fw_patched) + cs_dsp_power_down(&cs35l56->cs_dsp); + + cs35l56->base.fw_patched = false; + + pm_runtime_get_sync(cs35l56->base.dev); + + ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing); + if (ret) { + dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret); + goto err_pm_put; + } + + firmware_missing &= CS35L56_FIRMWARE_MISSING; + + /* + * Firmware can only be downloaded if the CS35L56 is secured or is + * running from the built-in ROM. If it is secured the BIOS will have + * downloaded firmware, and the wmfw/bin files will only contain + * tunings that are safe to download with the firmware running. + */ + if (cs35l56->base.secured || firmware_missing) { + cs35l56_hda_request_firmware_files(cs35l56, &wmfw_firmware, &wmfw_filename, + &coeff_firmware, &coeff_filename); + } + + /* + * If the BIOS didn't patch the firmware a bin file is mandatory to + * enable the ASP· + */ + if (!coeff_firmware && firmware_missing) { + dev_err(cs35l56->base.dev, ".bin file required but not found\n"); + ret = -ENOENT; + goto err_fw_release; + } + + mutex_lock(&cs35l56->base.irq_lock); + + /* + * When the device is running in secure mode the firmware files can + * only contain insecure tunings and therefore we do not need to + * shutdown the firmware to apply them and can use the lower cost + * reinit sequence instead. + */ + if (!cs35l56->base.secured && (wmfw_firmware || coeff_firmware)) { + ret = cs35l56_firmware_shutdown(&cs35l56->base); + if (ret) + goto err; + } + + ret = cs_dsp_power_up(&cs35l56->cs_dsp, wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename, "misc"); + if (ret) { + dev_dbg(cs35l56->base.dev, "%s: cs_dsp_power_up ret %d\n", __func__, ret); + goto err; + } + + if (wmfw_filename) + dev_dbg(cs35l56->base.dev, "Loaded WMFW Firmware: %s\n", wmfw_filename); + + if (coeff_filename) + dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename); + + if (cs35l56->base.secured) { + ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); + if (ret) + goto err_powered_up; + } else if (wmfw_firmware || coeff_firmware) { + /* If we downloaded firmware, reset the device and wait for it to boot */ + cs35l56_system_reset(&cs35l56->base, false); + regcache_mark_dirty(cs35l56->base.regmap); + ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); + if (ret) + goto err_powered_up; + } + + /* Disable auto-hibernate so that runtime_pm has control */ + ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); + if (ret) + goto err_powered_up; + + regcache_sync(cs35l56->base.regmap); + + regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, + CS35L56_FIRMWARE_MISSING); + cs35l56->base.fw_patched = true; + + ret = cs_dsp_run(&cs35l56->cs_dsp); + if (ret) + dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret); + +err_powered_up: + if (!cs35l56->base.fw_patched) + cs_dsp_power_down(&cs35l56->cs_dsp); +err: + mutex_unlock(&cs35l56->base.irq_lock); +err_fw_release: + cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename); +err_pm_put: + pm_runtime_put(cs35l56->base.dev); + + return ret; +} + +static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + struct hda_component *comps = master_data; + int ret; + + if (!comps || cs35l56->index < 0 || cs35l56->index >= HDA_MAX_COMPONENTS) + return -EINVAL; + + comps = &comps[cs35l56->index]; + if (comps->dev) + return -EBUSY; + + comps->dev = dev; + cs35l56->codec = comps->codec; + strscpy(comps->name, dev_name(dev), sizeof(comps->name)); + comps->playback_hook = cs35l56_hda_playback_hook; + + ret = cs35l56_hda_fw_load(cs35l56); + if (ret) + return ret; + + cs35l56_hda_create_controls(cs35l56); + cs35l56_hda_add_dsp_controls(cs35l56); + +#if IS_ENABLED(CONFIG_SND_DEBUG) + cs35l56->debugfs_root = debugfs_create_dir(dev_name(cs35l56->base.dev), sound_debugfs_root); + cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root); +#endif + + dev_dbg(cs35l56->base.dev, "Bound\n"); + + return 0; +} + +static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *master_data) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + struct hda_component *comps = master_data; + + cs35l56_hda_remove_controls(cs35l56); + +#if IS_ENABLED(CONFIG_SND_DEBUG) + cs_dsp_cleanup_debugfs(&cs35l56->cs_dsp); + debugfs_remove_recursive(cs35l56->debugfs_root); +#endif + + if (cs35l56->base.fw_patched) + cs_dsp_power_down(&cs35l56->cs_dsp); + + cs_dsp_remove(&cs35l56->cs_dsp); + + if (comps[cs35l56->index].dev == dev) + memset(&comps[cs35l56->index], 0, sizeof(*comps)); + + dev_dbg(cs35l56->base.dev, "Unbound\n"); +} + +static const struct component_ops cs35l56_hda_comp_ops = { + .bind = cs35l56_hda_bind, + .unbind = cs35l56_hda_unbind, +}; + +static int cs35l56_hda_system_suspend(struct device *dev) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + + if (cs35l56->playing) + cs35l56_hda_pause(cs35l56); + + cs35l56->suspended = true; + + /* + * The interrupt line is normally shared, but after we start suspending + * we can't check if our device is the source of an interrupt, and can't + * clear it. Prevent this race by temporarily disabling the parent irq + * until we reach _no_irq. + */ + if (cs35l56->base.irq) + disable_irq(cs35l56->base.irq); + + return pm_runtime_force_suspend(dev); +} + +static int cs35l56_hda_system_suspend_late(struct device *dev) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + + /* + * RESET is usually shared by all amps so it must not be asserted until + * all driver instances have done their suspend() stage. + */ + if (cs35l56->base.reset_gpio) { + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); + cs35l56_wait_min_reset_pulse(); + } + + return 0; +} + +static int cs35l56_hda_system_suspend_no_irq(struct device *dev) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + + /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */ + if (cs35l56->base.irq) + enable_irq(cs35l56->base.irq); + + return 0; +} + +static int cs35l56_hda_system_resume_no_irq(struct device *dev) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + + /* + * WAKE interrupts unmask if the CS35L56 hibernates, which can cause + * spurious interrupts, and the interrupt line is normally shared. + * We can't check if our device is the source of an interrupt, and can't + * clear it, until it has fully resumed. Prevent this race by temporarily + * disabling the parent irq until we complete resume(). + */ + if (cs35l56->base.irq) + disable_irq(cs35l56->base.irq); + + return 0; +} + +static int cs35l56_hda_system_resume_early(struct device *dev) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + + /* Ensure a spec-compliant RESET pulse. */ + if (cs35l56->base.reset_gpio) { + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); + cs35l56_wait_min_reset_pulse(); + + /* Release shared RESET before drivers start resume(). */ + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); + cs35l56_wait_control_port_ready(); + } + + return 0; +} + +static int cs35l56_hda_system_resume(struct device *dev) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + int ret; + + /* Undo pm_runtime_force_suspend() before re-enabling the irq */ + ret = pm_runtime_force_resume(dev); + if (cs35l56->base.irq) + enable_irq(cs35l56->base.irq); + + if (ret) + return ret; + + cs35l56->suspended = false; + + ret = cs35l56_is_fw_reload_needed(&cs35l56->base); + dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret); + if (ret > 0) { + ret = cs35l56_hda_fw_load(cs35l56); + if (ret) + return ret; + } + + if (cs35l56->playing) + cs35l56_hda_play(cs35l56); + + return 0; +} + +static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id) +{ + u32 values[HDA_MAX_COMPONENTS]; + struct acpi_device *adev; + const char *property, *sub; + size_t nval; + int i, ret; + + /* + * ACPI_COMPANION isn't available when this driver was instantiated by + * the serial-multi-instantiate driver, so lookup the node by HID + */ + if (!ACPI_COMPANION(cs35l56->base.dev)) { + adev = acpi_dev_get_first_match_dev("CSC3556", NULL, -1); + if (!adev) { + dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n", + dev_name(cs35l56->base.dev)); + return -ENODEV; + } + ACPI_COMPANION_SET(cs35l56->base.dev, adev); + } + + property = "cirrus,dev-index"; + ret = device_property_count_u32(cs35l56->base.dev, property); + if (ret <= 0) + goto err; + + if (ret > ARRAY_SIZE(values)) { + ret = -EINVAL; + goto err; + } + nval = ret; + + ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval); + if (ret) + goto err; + + cs35l56->index = -1; + for (i = 0; i < nval; i++) { + if (values[i] == id) { + cs35l56->index = i; + break; + } + } + /* + * It's not an error for the ID to be missing: for I2C there can be + * an alias address that is not a real device. So reject silently. + */ + if (cs35l56->index == -1) { + dev_dbg(cs35l56->base.dev, "No index found in %s\n", property); + ret = -ENODEV; + goto err; + } + + sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev)); + + if (IS_ERR(sub)) { + /* If no ACPI SUB, return 0 and fallback to legacy firmware path, otherwise fail */ + if (PTR_ERR(sub) == -ENODATA) + return 0; + else + return PTR_ERR(sub); + } + + cs35l56->system_name = sub; + + cs35l56->base.reset_gpio = devm_gpiod_get_index_optional(cs35l56->base.dev, + "reset", + cs35l56->index, + GPIOD_OUT_LOW); + if (IS_ERR(cs35l56->base.reset_gpio)) { + ret = PTR_ERR(cs35l56->base.reset_gpio); + + /* + * If RESET is shared the first amp to probe will grab the reset + * line and reset all the amps + */ + if (ret != -EBUSY) + return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n"); + + dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n"); + cs35l56->base.reset_gpio = NULL; + } + + return 0; + +err: + if (ret != -ENODEV) + dev_err(cs35l56->base.dev, "Failed property %s: %d\n", property, ret); + + return ret; +} + +int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id) +{ + int ret; + + mutex_init(&cs35l56->base.irq_lock); + dev_set_drvdata(cs35l56->base.dev, cs35l56); + + ret = cs35l56_hda_read_acpi(cs35l56, id); + if (ret) + goto err; + + cs35l56->amp_name = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "AMP%d", + cs35l56->index + 1); + if (!cs35l56->amp_name) { + ret = -ENOMEM; + goto err; + } + + cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp); + cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops; + + if (cs35l56->base.reset_gpio) { + dev_dbg(cs35l56->base.dev, "Hard reset\n"); + + /* + * The GPIOD_OUT_LOW to *_gpiod_get_*() will be ignored if the + * ACPI defines a different default state. So explicitly set low. + */ + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); + cs35l56_wait_min_reset_pulse(); + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); + } + + ret = cs35l56_hw_init(&cs35l56->base); + if (ret < 0) + goto err; + + /* Reset the device and wait for it to boot */ + cs35l56_system_reset(&cs35l56->base, false); + ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); + if (ret) + goto err; + + ret = cs35l56_set_patch(&cs35l56->base); + if (ret) + goto err; + + regcache_mark_dirty(cs35l56->base.regmap); + regcache_sync(cs35l56->base.regmap); + + /* Disable auto-hibernate so that runtime_pm has control */ + ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); + if (ret) + goto err; + + ret = cs_dsp_halo_init(&cs35l56->cs_dsp); + if (ret) { + dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n"); + goto err; + } + + dev_dbg(cs35l56->base.dev, "DSP system name: '%s', amp name: '%s'\n", + cs35l56->system_name, cs35l56->amp_name); + + regmap_multi_reg_write(cs35l56->base.regmap, cs35l56_hda_dai_config, + ARRAY_SIZE(cs35l56_hda_dai_config)); + + /* + * By default only enable one ASP1TXn, where n=amplifier index, + * This prevents multiple amps trying to drive the same slot. + */ + cs35l56->asp_tx_mask = BIT(cs35l56->index); + + pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 3000); + pm_runtime_use_autosuspend(cs35l56->base.dev); + pm_runtime_set_active(cs35l56->base.dev); + pm_runtime_mark_last_busy(cs35l56->base.dev); + pm_runtime_enable(cs35l56->base.dev); + + ret = component_add(cs35l56->base.dev, &cs35l56_hda_comp_ops); + if (ret) { + dev_err(cs35l56->base.dev, "Register component failed: %d\n", ret); + goto pm_err; + } + + cs35l56->base.init_done = true; + + return 0; + +pm_err: + pm_runtime_disable(cs35l56->base.dev); +err: + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_hda_common_probe, SND_HDA_SCODEC_CS35L56); + +void cs35l56_hda_remove(struct device *dev) +{ + struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + + pm_runtime_get_sync(cs35l56->base.dev); + pm_runtime_disable(cs35l56->base.dev); + + component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops); + + kfree(cs35l56->system_name); + pm_runtime_put_noidle(cs35l56->base.dev); + + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_hda_remove, SND_HDA_SCODEC_CS35L56); + +const struct dev_pm_ops cs35l56_hda_pm_ops = { + SET_RUNTIME_PM_OPS(cs35l56_hda_runtime_suspend, cs35l56_hda_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend, cs35l56_hda_system_resume) + LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_late, + cs35l56_hda_system_resume_early) + NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_no_irq, + cs35l56_hda_system_resume_no_irq) +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56); + +MODULE_DESCRIPTION("CS35L56 HDA Driver"); +MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(FW_CS_DSP); diff --git a/sound/pci/hda/cs35l56_hda.h b/sound/pci/hda/cs35l56_hda.h new file mode 100644 index 000000000000..6e5bc5397db5 --- /dev/null +++ b/sound/pci/hda/cs35l56_hda.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * HDA audio driver for Cirrus Logic CS35L56 smart amp + * + * Copyright (C) 2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef __CS35L56_HDA_H__ +#define __CS35L56_HDA_H__ + +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/firmware/cirrus/cs_dsp.h> +#include <linux/firmware/cirrus/wmfw.h> +#include <linux/regulator/consumer.h> +#include <sound/cs35l56.h> + +struct dentry; + +struct cs35l56_hda { + struct cs35l56_base base; + struct hda_codec *codec; + + int index; + const char *system_name; + const char *amp_name; + + struct cs_dsp cs_dsp; + bool playing; + bool suspended; + u8 asp_tx_mask; + + struct snd_kcontrol *posture_ctl; + struct snd_kcontrol *volume_ctl; + struct snd_kcontrol *mixer_ctl[4]; + +#if IS_ENABLED(CONFIG_SND_DEBUG) + struct dentry *debugfs_root; +#endif +}; + +extern const struct dev_pm_ops cs35l56_hda_pm_ops; + +int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id); +void cs35l56_hda_remove(struct device *dev); + +#endif /*__CS35L56_HDA_H__*/ diff --git a/sound/pci/hda/cs35l56_hda_i2c.c b/sound/pci/hda/cs35l56_hda_i2c.c new file mode 100644 index 000000000000..83e4acdd89ac --- /dev/null +++ b/sound/pci/hda/cs35l56_hda_i2c.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// CS35L56 HDA audio driver I2C binding +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "cs35l56_hda.h" + +static int cs35l56_hda_i2c_probe(struct i2c_client *clt) +{ + struct cs35l56_hda *cs35l56; + int ret; + + cs35l56 = devm_kzalloc(&clt->dev, sizeof(*cs35l56), GFP_KERNEL); + if (!cs35l56) + return -ENOMEM; + + cs35l56->base.dev = &clt->dev; + cs35l56->base.can_hibernate = true; + cs35l56->base.regmap = devm_regmap_init_i2c(clt, &cs35l56_regmap_i2c); + if (IS_ERR(cs35l56->base.regmap)) { + ret = PTR_ERR(cs35l56->base.regmap); + dev_err(cs35l56->base.dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = cs35l56_hda_common_probe(cs35l56, clt->addr); + if (ret) + return ret; + ret = cs35l56_irq_request(&cs35l56->base, clt->irq); + if (ret < 0) + cs35l56_hda_remove(cs35l56->base.dev); + + return ret; +} + +static void cs35l56_hda_i2c_remove(struct i2c_client *clt) +{ + cs35l56_hda_remove(&clt->dev); +} + +static const struct i2c_device_id cs35l56_hda_i2c_id[] = { + { "cs35l56-hda", 0 }, + {} +}; + +static struct i2c_driver cs35l56_hda_i2c_driver = { + .driver = { + .name = "cs35l56-hda", + .pm = &cs35l56_hda_pm_ops, + }, + .id_table = cs35l56_hda_i2c_id, + .probe = cs35l56_hda_i2c_probe, + .remove = cs35l56_hda_i2c_remove, +}; +module_i2c_driver(cs35l56_hda_i2c_driver); + +MODULE_DESCRIPTION("HDA CS35L56 I2C driver"); +MODULE_IMPORT_NS(SND_HDA_SCODEC_CS35L56); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/cs35l56_hda_spi.c b/sound/pci/hda/cs35l56_hda_spi.c new file mode 100644 index 000000000000..756aec342eab --- /dev/null +++ b/sound/pci/hda/cs35l56_hda_spi.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// CS35L56 HDA audio driver SPI binding +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "cs35l56_hda.h" + +static int cs35l56_hda_spi_probe(struct spi_device *spi) +{ + struct cs35l56_hda *cs35l56; + int ret; + + cs35l56 = devm_kzalloc(&spi->dev, sizeof(*cs35l56), GFP_KERNEL); + if (!cs35l56) + return -ENOMEM; + + cs35l56->base.dev = &spi->dev; + cs35l56->base.regmap = devm_regmap_init_spi(spi, &cs35l56_regmap_spi); + if (IS_ERR(cs35l56->base.regmap)) { + ret = PTR_ERR(cs35l56->base.regmap); + dev_err(cs35l56->base.dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = cs35l56_hda_common_probe(cs35l56, spi->chip_select); + if (ret) + return ret; + ret = cs35l56_irq_request(&cs35l56->base, spi->irq); + if (ret < 0) + cs35l56_hda_remove(cs35l56->base.dev); + + return ret; +} + +static void cs35l56_hda_spi_remove(struct spi_device *spi) +{ + cs35l56_hda_remove(&spi->dev); +} + +static const struct spi_device_id cs35l56_hda_spi_id[] = { + { "cs35l56-hda", 0 }, + {} +}; + +static struct spi_driver cs35l56_hda_spi_driver = { + .driver = { + .name = "cs35l56-hda", + .pm = &cs35l56_hda_pm_ops, + }, + .id_table = cs35l56_hda_spi_id, + .probe = cs35l56_hda_spi_probe, + .remove = cs35l56_hda_spi_remove, +}; +module_spi_driver(cs35l56_hda_spi_driver); + +MODULE_DESCRIPTION("HDA CS35L56 SPI driver"); +MODULE_IMPORT_NS(SND_HDA_SCODEC_CS35L56); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index df63d66af1ab..579b11beac71 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -8,6 +8,8 @@ #ifndef __SOUND_HDA_AUTO_PARSER_H #define __SOUND_HDA_AUTO_PARSER_H +#include "hda_local.h" + /* * Helper for automatic pin configuration */ diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index bd19f92aeeec..33af707a65ab 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1769,10 +1769,8 @@ void snd_hda_ctls_clear(struct hda_codec *codec) int i; struct hda_nid_item *items = codec->mixers.list; - down_write(&codec->card->controls_rwsem); for (i = 0; i < codec->mixers.used; i++) snd_ctl_remove(codec->card, items[i].kctl); - up_write(&codec->card->controls_rwsem); snd_array_free(&codec->mixers); snd_array_free(&codec->nids); } diff --git a/sound/pci/hda/hda_component.h b/sound/pci/hda/hda_component.h index 534e845b9cd1..f170aec967c1 100644 --- a/sound/pci/hda/hda_component.h +++ b/sound/pci/hda/hda_component.h @@ -15,5 +15,7 @@ struct hda_component { struct device *dev; char name[HDA_MAX_NAME_SIZE]; struct hda_codec *codec; + void (*pre_playback_hook)(struct device *dev, int action); void (*playback_hook)(struct device *dev, int action); + void (*post_playback_hook)(struct device *dev, int action); }; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 34eba40cc6e6..a8eea8367629 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -9,6 +9,9 @@ #define __SOUND_HDA_GENERIC_H #include <linux/leds.h> +#include "hda_auto_parser.h" + +struct hda_jack_callback; /* table entry for multi-io paths */ struct hda_multi_io { diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 125e97fe0b1c..727f39acedfc 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -114,8 +114,8 @@ int snd_hda_create_hwdep(struct hda_codec *codec) #endif /* for sysfs */ - hwdep->dev.groups = snd_hda_dev_attr_groups; - dev_set_drvdata(&hwdep->dev, codec); + hwdep->dev->groups = snd_hda_dev_attr_groups; + dev_set_drvdata(hwdep->dev, codec); return 0; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index ef831770ca7d..765d95e79861 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -330,18 +330,6 @@ enum { #define needs_eld_notify_link(chip) false #endif -#define CONTROLLER_IN_GPU(pci) (((pci)->vendor == 0x8086) && \ - (((pci)->device == 0x0a0c) || \ - ((pci)->device == 0x0c0c) || \ - ((pci)->device == 0x0d0c) || \ - ((pci)->device == 0x160c) || \ - ((pci)->device == 0x490d) || \ - ((pci)->device == 0x4f90) || \ - ((pci)->device == 0x4f91) || \ - ((pci)->device == 0x4f92))) - -#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98) - static const char * const driver_short_names[] = { [AZX_DRIVER_ICH] = "HDA Intel", [AZX_DRIVER_PCH] = "HDA Intel PCH", @@ -573,7 +561,7 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset) snd_hdac_set_codec_wakeup(bus, false); /* reduce dma latency to avoid noise */ - if (IS_BXT(pci)) + if (HDA_CONTROLLER_IS_APL(pci)) bxt_reduce_dma_latency(chip); if (bus->mlcap != NULL) @@ -2175,7 +2163,7 @@ static int azx_probe(struct pci_dev *pci, #endif /* CONFIG_SND_HDA_PATCH_LOADER */ #ifndef CONFIG_SND_HDA_I915 - if (CONTROLLER_IN_GPU(pci)) + if (HDA_CONTROLLER_IN_GPU(pci)) dev_err(card->dev, "Haswell/Broadwell HDMI/DP must build in CONFIG_SND_HDA_I915\n"); #endif @@ -2283,7 +2271,7 @@ static int azx_probe_continue(struct azx *chip) * for other chips, still continue probing as other * codecs can be on the same link. */ - if (CONTROLLER_IN_GPU(pci)) { + if (HDA_CONTROLLER_IN_GPU(pci)) { dev_err(chip->card->dev, "HSW/BDW HD-audio HDMI/DP requires binding with gfx driver\n"); goto out_free; @@ -2294,7 +2282,7 @@ static int azx_probe_continue(struct azx *chip) } /* HSW/BDW controllers need this power */ - if (CONTROLLER_IN_GPU(pci)) + if (HDA_CONTROLLER_IN_GPU(pci)) hda->need_i915_power = true; } @@ -2428,333 +2416,262 @@ static void azx_shutdown(struct pci_dev *pci) /* PCI IDs */ static const struct pci_device_id azx_ids[] = { /* CPT */ - { PCI_DEVICE(0x8086, 0x1c20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, + { PCI_DEVICE_DATA(INTEL, HDA_CPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) }, /* PBG */ - { PCI_DEVICE(0x8086, 0x1d20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, + { PCI_DEVICE_DATA(INTEL, HDA_PBG, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) }, /* Panther Point */ - { PCI_DEVICE(0x8086, 0x1e20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, + { PCI_DEVICE_DATA(INTEL, HDA_PPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) }, /* Lynx Point */ - { PCI_DEVICE(0x8086, 0x8c20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + { PCI_DEVICE_DATA(INTEL, HDA_LPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, /* 9 Series */ - { PCI_DEVICE(0x8086, 0x8ca0), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + { PCI_DEVICE_DATA(INTEL, HDA_9_SERIES, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, /* Wellsburg */ - { PCI_DEVICE(0x8086, 0x8d20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, - { PCI_DEVICE(0x8086, 0x8d21), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + { PCI_DEVICE_DATA(INTEL, HDA_WBG_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, + { PCI_DEVICE_DATA(INTEL, HDA_WBG_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, /* Lewisburg */ - { PCI_DEVICE(0x8086, 0xa1f0), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, - { PCI_DEVICE(0x8086, 0xa270), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, + { PCI_DEVICE_DATA(INTEL, HDA_LBG_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_LBG_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE) }, /* Lynx Point-LP */ - { PCI_DEVICE(0x8086, 0x9c20), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + { PCI_DEVICE_DATA(INTEL, HDA_LPT_LP_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, /* Lynx Point-LP */ - { PCI_DEVICE(0x8086, 0x9c21), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + { PCI_DEVICE_DATA(INTEL, HDA_LPT_LP_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, /* Wildcat Point-LP */ - { PCI_DEVICE(0x8086, 0x9ca0), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, - /* Sunrise Point */ - { PCI_DEVICE(0x8086, 0xa170), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, - /* Sunrise Point-LP */ - { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, + { PCI_DEVICE_DATA(INTEL, HDA_WPT_LP, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) }, + /* Skylake (Sunrise Point) */ + { PCI_DEVICE_DATA(INTEL, HDA_SKL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + /* Skylake-LP (Sunrise Point-LP) */ + { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Kabylake */ - { PCI_DEVICE(0x8086, 0xa171), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, + { PCI_DEVICE_DATA(INTEL, HDA_KBL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Kabylake-LP */ - { PCI_DEVICE(0x8086, 0x9d71), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, + { PCI_DEVICE_DATA(INTEL, HDA_KBL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Kabylake-H */ - { PCI_DEVICE(0x8086, 0xa2f0), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE }, + { PCI_DEVICE_DATA(INTEL, HDA_KBL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Coffelake */ - { PCI_DEVICE(0x8086, 0xa348), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Cannonlake */ - { PCI_DEVICE(0x8086, 0x9dc8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* CometLake-LP */ - { PCI_DEVICE(0x8086, 0x02C8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* CometLake-H */ - { PCI_DEVICE(0x8086, 0x06C8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0xf1c8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_CML_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_RKL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* CometLake-S */ - { PCI_DEVICE(0x8086, 0xa3f0), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_CML_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* CometLake-R */ - { PCI_DEVICE(0x8086, 0xf0c8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_CML_R, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Icelake */ - { PCI_DEVICE(0x8086, 0x34c8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Icelake-H */ - { PCI_DEVICE(0x8086, 0x3dc8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Jasperlake */ - { PCI_DEVICE(0x8086, 0x38c8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0x4dc8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Tigerlake */ - { PCI_DEVICE(0x8086, 0xa0c8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Tigerlake-H */ - { PCI_DEVICE(0x8086, 0x43c8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* DG1 */ - { PCI_DEVICE(0x8086, 0x490d), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_DG1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* DG2 */ - { PCI_DEVICE(0x8086, 0x4f90), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0x4f91), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0x4f92), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_DG2_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_DG2_1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_DG2_2, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Alderlake-S */ - { PCI_DEVICE(0x8086, 0x7ad0), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Alderlake-P */ - { PCI_DEVICE(0x8086, 0x51c8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0x51c9), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0x51cd), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Alderlake-M */ - { PCI_DEVICE(0x8086, 0x51cc), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Alderlake-N */ - { PCI_DEVICE(0x8086, 0x54c8), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Elkhart Lake */ - { PCI_DEVICE(0x8086, 0x4b55), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0x4b58), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Raptor Lake */ - { PCI_DEVICE(0x8086, 0x7a50), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0x51ca), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0x51cb), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0x51ce), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - { PCI_DEVICE(0x8086, 0x51cf), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - /* Meteorlake-P */ - { PCI_DEVICE(0x8086, 0x7e28), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + { PCI_DEVICE_DATA(INTEL, HDA_MTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Lunarlake-P */ - { PCI_DEVICE(0x8086, 0xa828), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, - /* Broxton-P(Apollolake) */ - { PCI_DEVICE(0x8086, 0x5a98), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, - /* Broxton-T */ - { PCI_DEVICE(0x8086, 0x1a98), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, + { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + /* Arrow Lake-S */ + { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, + /* Apollolake (Broxton-P) */ + { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, /* Gemini-Lake */ - { PCI_DEVICE(0x8086, 0x3198), - .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, + { PCI_DEVICE_DATA(INTEL, HDA_GML, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, /* Haswell */ - { PCI_DEVICE(0x8086, 0x0a0c), - .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, - { PCI_DEVICE(0x8086, 0x0c0c), - .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, - { PCI_DEVICE(0x8086, 0x0d0c), - .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL }, + { PCI_DEVICE_DATA(INTEL, HDA_HSW_0, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) }, + { PCI_DEVICE_DATA(INTEL, HDA_HSW_2, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) }, + { PCI_DEVICE_DATA(INTEL, HDA_HSW_3, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) }, /* Broadwell */ - { PCI_DEVICE(0x8086, 0x160c), - .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_BROADWELL }, + { PCI_DEVICE_DATA(INTEL, HDA_BDW, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_BROADWELL) }, /* 5 Series/3400 */ - { PCI_DEVICE(0x8086, 0x3b56), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, - { PCI_DEVICE(0x8086, 0x3b57), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, + { PCI_DEVICE_DATA(INTEL, HDA_5_3400_SERIES_0, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM) }, + { PCI_DEVICE_DATA(INTEL, HDA_5_3400_SERIES_1, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM) }, /* Poulsbo */ - { PCI_DEVICE(0x8086, 0x811b), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE | - AZX_DCAPS_POSFIX_LPIB }, + { PCI_DEVICE_DATA(INTEL, HDA_POULSBO, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE | + AZX_DCAPS_POSFIX_LPIB) }, /* Oaktrail */ - { PCI_DEVICE(0x8086, 0x080a), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE }, + { PCI_DEVICE_DATA(INTEL, HDA_OAKTRAIL, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE) }, /* BayTrail */ - { PCI_DEVICE(0x8086, 0x0f04), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BAYTRAIL }, + { PCI_DEVICE_DATA(INTEL, HDA_BYT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BAYTRAIL) }, /* Braswell */ - { PCI_DEVICE(0x8086, 0x2284), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BRASWELL }, + { PCI_DEVICE_DATA(INTEL, HDA_BSW, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BRASWELL) }, /* ICH6 */ - { PCI_DEVICE(0x8086, 0x2668), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + { PCI_DEVICE_DATA(INTEL, HDA_ICH6, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, /* ICH7 */ - { PCI_DEVICE(0x8086, 0x27d8), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + { PCI_DEVICE_DATA(INTEL, HDA_ICH7, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, /* ESB2 */ - { PCI_DEVICE(0x8086, 0x269a), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + { PCI_DEVICE_DATA(INTEL, HDA_ESB2, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, /* ICH8 */ - { PCI_DEVICE(0x8086, 0x284b), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + { PCI_DEVICE_DATA(INTEL, HDA_ICH8, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, /* ICH9 */ - { PCI_DEVICE(0x8086, 0x293e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + { PCI_DEVICE_DATA(INTEL, HDA_ICH9_0, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, /* ICH9 */ - { PCI_DEVICE(0x8086, 0x293f), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + { PCI_DEVICE_DATA(INTEL, HDA_ICH9_1, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, /* ICH10 */ - { PCI_DEVICE(0x8086, 0x3a3e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + { PCI_DEVICE_DATA(INTEL, HDA_ICH10_0, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, /* ICH10 */ - { PCI_DEVICE(0x8086, 0x3a6e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + { PCI_DEVICE_DATA(INTEL, HDA_ICH10_1, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) }, /* Generic Intel */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .class_mask = 0xffffff, .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_NO_ALIGN_BUFSIZE }, /* ATI SB 450/600/700/800/900 */ - { PCI_DEVICE(0x1002, 0x437b), + { PCI_VDEVICE(ATI, 0x437b), .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB }, - { PCI_DEVICE(0x1002, 0x4383), + { PCI_VDEVICE(ATI, 0x4383), .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB }, /* AMD Hudson */ - { PCI_DEVICE(0x1022, 0x780d), + { PCI_VDEVICE(AMD, 0x780d), .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB }, /* AMD, X370 & co */ - { PCI_DEVICE(0x1022, 0x1457), + { PCI_VDEVICE(AMD, 0x1457), .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB }, /* AMD, X570 & co */ - { PCI_DEVICE(0x1022, 0x1487), + { PCI_VDEVICE(AMD, 0x1487), .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB }, /* AMD Stoney */ - { PCI_DEVICE(0x1022, 0x157a), + { PCI_VDEVICE(AMD, 0x157a), .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB | AZX_DCAPS_PM_RUNTIME }, /* AMD Raven */ - { PCI_DEVICE(0x1022, 0x15e3), + { PCI_VDEVICE(AMD, 0x15e3), .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB }, /* ATI HDMI */ - { PCI_DEVICE(0x1002, 0x0002), + { PCI_VDEVICE(ATI, 0x0002), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0x1308), + { PCI_VDEVICE(ATI, 0x1308), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_DEVICE(0x1002, 0x157a), + { PCI_VDEVICE(ATI, 0x157a), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_DEVICE(0x1002, 0x15b3), + { PCI_VDEVICE(ATI, 0x15b3), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_DEVICE(0x1002, 0x793b), + { PCI_VDEVICE(ATI, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0x7919), + { PCI_VDEVICE(ATI, 0x7919), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0x960f), + { PCI_VDEVICE(ATI, 0x960f), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0x970f), + { PCI_VDEVICE(ATI, 0x970f), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0x9840), + { PCI_VDEVICE(ATI, 0x9840), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_DEVICE(0x1002, 0xaa00), + { PCI_VDEVICE(ATI, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa08), + { PCI_VDEVICE(ATI, 0xaa08), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa10), + { PCI_VDEVICE(ATI, 0xaa10), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa18), + { PCI_VDEVICE(ATI, 0xaa18), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa20), + { PCI_VDEVICE(ATI, 0xaa20), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa28), + { PCI_VDEVICE(ATI, 0xaa28), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa30), + { PCI_VDEVICE(ATI, 0xaa30), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa38), + { PCI_VDEVICE(ATI, 0xaa38), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa40), + { PCI_VDEVICE(ATI, 0xaa40), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa48), + { PCI_VDEVICE(ATI, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa50), + { PCI_VDEVICE(ATI, 0xaa50), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa58), + { PCI_VDEVICE(ATI, 0xaa58), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa60), + { PCI_VDEVICE(ATI, 0xaa60), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa68), + { PCI_VDEVICE(ATI, 0xaa68), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa80), + { PCI_VDEVICE(ATI, 0xaa80), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa88), + { PCI_VDEVICE(ATI, 0xaa88), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa90), + { PCI_VDEVICE(ATI, 0xaa90), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0xaa98), + { PCI_VDEVICE(ATI, 0xaa98), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, - { PCI_DEVICE(0x1002, 0x9902), + { PCI_VDEVICE(ATI, 0x9902), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_DEVICE(0x1002, 0xaaa0), + { PCI_VDEVICE(ATI, 0xaaa0), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_DEVICE(0x1002, 0xaaa8), + { PCI_VDEVICE(ATI, 0xaaa8), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_DEVICE(0x1002, 0xaab0), + { PCI_VDEVICE(ATI, 0xaab0), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, - { PCI_DEVICE(0x1002, 0xaac0), + { PCI_VDEVICE(ATI, 0xaac0), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xaac8), + { PCI_VDEVICE(ATI, 0xaac8), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xaad8), + { PCI_VDEVICE(ATI, 0xaad8), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xaae0), + { PCI_VDEVICE(ATI, 0xaae0), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xaae8), + { PCI_VDEVICE(ATI, 0xaae8), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xaaf0), + { PCI_VDEVICE(ATI, 0xaaf0), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xaaf8), + { PCI_VDEVICE(ATI, 0xaaf8), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xab00), + { PCI_VDEVICE(ATI, 0xab00), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xab08), + { PCI_VDEVICE(ATI, 0xab08), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xab10), + { PCI_VDEVICE(ATI, 0xab10), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xab18), + { PCI_VDEVICE(ATI, 0xab18), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xab20), + { PCI_VDEVICE(ATI, 0xab20), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xab28), + { PCI_VDEVICE(ATI, 0xab28), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xab30), + { PCI_VDEVICE(ATI, 0xab30), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, - { PCI_DEVICE(0x1002, 0xab38), + { PCI_VDEVICE(ATI, 0xab38), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, /* GLENFLY */ @@ -2764,15 +2681,15 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_GFHDMI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT }, /* VIA VT8251/VT8237A */ - { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA }, + { PCI_VDEVICE(VIA, 0x3288), .driver_data = AZX_DRIVER_VIA }, /* VIA GFX VT7122/VX900 */ - { PCI_DEVICE(0x1106, 0x9170), .driver_data = AZX_DRIVER_GENERIC }, + { PCI_VDEVICE(VIA, 0x9170), .driver_data = AZX_DRIVER_GENERIC }, /* VIA GFX VT6122/VX11 */ - { PCI_DEVICE(0x1106, 0x9140), .driver_data = AZX_DRIVER_GENERIC }, + { PCI_VDEVICE(VIA, 0x9140), .driver_data = AZX_DRIVER_GENERIC }, /* SIS966 */ - { PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS }, + { PCI_VDEVICE(SI, 0x7502), .driver_data = AZX_DRIVER_SIS }, /* ULI M5461 */ - { PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI }, + { PCI_VDEVICE(AL, 0x5461), .driver_data = AZX_DRIVER_ULI }, /* NVIDIA MCP */ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, @@ -2785,9 +2702,9 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT }, /* Creative X-Fi (CA0110-IBG) */ /* CTHDA chips */ - { PCI_DEVICE(0x1102, 0x0010), + { PCI_VDEVICE(CREATIVE, 0x0010), .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA }, - { PCI_DEVICE(0x1102, 0x0012), + { PCI_VDEVICE(CREATIVE, 0x0012), .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA }, #if !IS_ENABLED(CONFIG_SND_CTXFI) /* the following entry conflicts with snd-ctxfi driver, @@ -2801,18 +2718,18 @@ static const struct pci_device_id azx_ids[] = { AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB }, #else /* this entry seems still valid -- i.e. without emu20kx chip */ - { PCI_DEVICE(0x1102, 0x0009), + { PCI_VDEVICE(CREATIVE, 0x0009), .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND | AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB }, #endif /* CM8888 */ - { PCI_DEVICE(0x13f6, 0x5011), + { PCI_VDEVICE(CMEDIA, 0x5011), .driver_data = AZX_DRIVER_CMEDIA | AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_SNOOP_OFF }, /* Vortex86MX */ - { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC }, + { PCI_VDEVICE(RDC, 0x3010), .driver_data = AZX_DRIVER_GENERIC }, /* VMware HDAudio */ - { PCI_DEVICE(0x15ad, 0x1977), .driver_data = AZX_DRIVER_GENERIC }, + { PCI_VDEVICE(VMWARE, 0x1977), .driver_data = AZX_DRIVER_GENERIC }, /* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, @@ -2823,11 +2740,11 @@ static const struct pci_device_id azx_ids[] = { .class_mask = 0xffffff, .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI }, /* Zhaoxin */ - { PCI_DEVICE(0x1d17, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN }, + { PCI_VDEVICE(ZHAOXIN, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN }, /* Loongson HDAudio*/ - {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA), + { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA), .driver_data = AZX_DRIVER_LOONGSON }, - {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI), + { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI), .driver_data = AZX_DRIVER_LOONGSON }, { 0, } }; diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 9d0ab043880b..d967e70a7058 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -16,7 +16,8 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/mutex.h> -#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/time.h> @@ -378,14 +379,14 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) } /* driver name */ - strncpy(card->driver, drv_name, sizeof(card->driver)); + strscpy(card->driver, drv_name, sizeof(card->driver)); /* shortname for card */ sname = of_get_property(np, "nvidia,model", NULL); if (!sname) sname = drv_name; if (strlen(sname) > sizeof(card->shortname)) dev_info(card->dev, "truncating shortname for card\n"); - strncpy(card->shortname, sname, sizeof(card->shortname)); + strscpy(card->shortname, sname, sizeof(card->shortname)); /* longname for card */ snprintf(card->longname, sizeof(card->longname), diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 260d3e64f658..1cde2a69bdb4 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -4632,8 +4632,9 @@ HDA_CODEC_ENTRY(0x80862819, "DG2 HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281f, "Raptorlake-P HDMI", patch_i915_adlp_hdmi), -HDA_CODEC_ENTRY(0x8086281d, "Meteorlake HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x8086281d, "Meteor Lake HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x8086281f, "Raptor Lake P HDMI", patch_i915_adlp_hdmi), +HDA_CODEC_ENTRY(0x80862820, "Lunar Lake HDMI", patch_i915_adlp_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index dc7b7a407638..a07df6f92960 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4639,6 +4639,22 @@ static void alc236_fixup_hp_mute_led_coefbit2(struct hda_codec *codec, } } +static void alc245_fixup_hp_mute_led_coefbit(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_coef.idx = 0x0b; + spec->mute_led_coef.mask = 3 << 2; + spec->mute_led_coef.on = 2 << 2; + spec->mute_led_coef.off = 1 << 2; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); + } +} + /* turn on/off mic-mute LED per capture hook by coef bit */ static int coef_micmute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) @@ -6716,12 +6732,20 @@ static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_ int i; for (i = 0; i < HDA_MAX_COMPONENTS; i++) { - if (spec->comps[i].dev) + if (spec->comps[i].dev && spec->comps[i].pre_playback_hook) + spec->comps[i].pre_playback_hook(spec->comps[i].dev, action); + } + for (i = 0; i < HDA_MAX_COMPONENTS; i++) { + if (spec->comps[i].dev && spec->comps[i].playback_hook) spec->comps[i].playback_hook(spec->comps[i].dev, action); } + for (i = 0; i < HDA_MAX_COMPONENTS; i++) { + if (spec->comps[i].dev && spec->comps[i].post_playback_hook) + spec->comps[i].post_playback_hook(spec->comps[i].dev, action); + } } -struct cs35l41_dev_name { +struct scodec_dev_name { const char *bus; const char *hid; int index; @@ -6730,7 +6754,7 @@ struct cs35l41_dev_name { /* match the device name in a slightly relaxed manner */ static int comp_match_cs35l41_dev_name(struct device *dev, void *data) { - struct cs35l41_dev_name *p = data; + struct scodec_dev_name *p = data; const char *d = dev_name(dev); int n = strlen(p->bus); char tmp[32]; @@ -6746,12 +6770,32 @@ static int comp_match_cs35l41_dev_name(struct device *dev, void *data) return !strcmp(d + n, tmp); } +static int comp_match_tas2781_dev_name(struct device *dev, + void *data) +{ + struct scodec_dev_name *p = data; + const char *d = dev_name(dev); + int n = strlen(p->bus); + char tmp[32]; + + /* check the bus name */ + if (strncmp(d, p->bus, n)) + return 0; + /* skip the bus number */ + if (isdigit(d[n])) + n++; + /* the rest must be exact matching */ + snprintf(tmp, sizeof(tmp), "-%s:00", p->hid); + + return !strcmp(d + n, tmp); +} + static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus, const char *hid, int count) { struct device *dev = hda_codec_dev(cdc); struct alc_spec *spec = cdc->spec; - struct cs35l41_dev_name *rec; + struct scodec_dev_name *rec; int ret, i; switch (action) { @@ -6779,6 +6823,41 @@ static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char } } +static void tas2781_generic_fixup(struct hda_codec *cdc, int action, + const char *bus, const char *hid) +{ + struct device *dev = hda_codec_dev(cdc); + struct alc_spec *spec = cdc->spec; + struct scodec_dev_name *rec; + int ret; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + rec = devm_kmalloc(dev, sizeof(*rec), GFP_KERNEL); + if (!rec) + return; + rec->bus = bus; + rec->hid = hid; + rec->index = 0; + spec->comps[0].codec = cdc; + component_match_add(dev, &spec->match, + comp_match_tas2781_dev_name, rec); + ret = component_master_add_with_match(dev, &comp_master_ops, + spec->match); + if (ret) + codec_err(cdc, + "Fail to register component aggregator %d\n", + ret); + else + spec->gen.pcm_playback_hook = + comp_generic_playback_hook; + break; + case HDA_FIXUP_ACT_FREE: + component_master_del(dev, &comp_master_ops); + break; + } +} + static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2); @@ -6806,6 +6885,12 @@ static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const st cs35l41_generic_fixup(cdc, action, "i2c", "CLSA0101", 2); } +static void tas2781_fixup_i2c(struct hda_codec *cdc, + const struct hda_fixup *fix, int action) +{ + tas2781_generic_fixup(cdc, action, "i2c", "TIAS2781"); +} + /* for alc295_fixup_hp_top_speakers */ #include "hp_x360_helper.c" @@ -7231,6 +7316,9 @@ enum { ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS, ALC236_FIXUP_DELL_DUAL_CODECS, ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, + ALC287_FIXUP_TAS2781_I2C, + ALC245_FIXUP_HP_MUTE_LED_COEFBIT, + ALC245_FIXUP_HP_X360_MUTE_LEDS, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -9309,6 +9397,22 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_THINKPAD_ACPI, }, + [ALC287_FIXUP_TAS2781_I2C] = { + .type = HDA_FIXUP_FUNC, + .v.func = tas2781_fixup_i2c, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, + [ALC245_FIXUP_HP_MUTE_LED_COEFBIT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_mute_led_coefbit, + }, + [ALC245_FIXUP_HP_X360_MUTE_LEDS] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_hp_mute_led_coefbit, + .chained = true, + .chain_id = ALC245_FIXUP_HP_GPIO_LED + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -9551,6 +9655,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED), SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED), @@ -9582,6 +9687,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED), @@ -9889,6 +9995,20 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6), SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x387e, "Yoga S780-16 pro Quad YC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3881, "YB9 dual power mode2 YC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3884, "Y780 YG DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38c3, "Y980 DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38cb, "Y790 YG DUAL", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x38cd, "Y790 VECO DUAL", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c new file mode 100644 index 000000000000..37114fd61a38 --- /dev/null +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -0,0 +1,856 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// TAS2781 HDA I2C driver +// +// Copyright 2023 Texas Instruments, Inc. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> + +#include <linux/acpi.h> +#include <linux/crc8.h> +#include <linux/crc32.h> +#include <linux/efi.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <sound/hda_codec.h> +#include <sound/soc.h> +#include <sound/tas2781.h> +#include <sound/tlv.h> +#include <sound/tas2781-tlv.h> + +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_component.h" +#include "hda_jack.h" +#include "hda_generic.h" + +#define TASDEVICE_SPEAKER_CALIBRATION_SIZE 20 + +/* No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD + * Define two controls, one is Volume control callbacks, the other is + * flag setting control callbacks. + */ + +/* Volume control callbacks for tas2781 */ +#define ACARD_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \ + xhandler_get, xhandler_put, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_range, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.reg = xreg, .rreg = xreg, .shift = xshift, \ + .rshift = xshift, .min = xmin, .max = xmax, \ + .invert = xinvert} } + +/* Flag control callbacks for tas2781 */ +#define ACARD_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, \ + .info = snd_ctl_boolean_mono_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = xdata } + +enum calib_data { + R0_VAL = 0, + INV_R0, + R0LOW, + POWER, + TLIM, + CALIB_MAX +}; + +static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) +{ + struct tasdevice_priv *tas_priv = data; + struct acpi_resource_i2c_serialbus *sb; + + if (i2c_acpi_get_i2c_resource(ares, &sb)) { + if (tas_priv->ndev < TASDEVICE_MAX_CHANNELS && + sb->slave_address != TAS2781_GLOBAL_ADDR) { + tas_priv->tasdevice[tas_priv->ndev].dev_addr = + (unsigned int)sb->slave_address; + tas_priv->ndev++; + } + } + return 1; +} + +static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) +{ + struct acpi_device *adev; + struct device *physdev; + LIST_HEAD(resources); + const char *sub; + int ret; + + adev = acpi_dev_get_first_match_dev(hid, NULL, -1); + if (!adev) { + dev_err(p->dev, + "Failed to find an ACPI device for %s\n", hid); + return -ENODEV; + } + + ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p); + if (ret < 0) + goto err; + + acpi_dev_free_resource_list(&resources); + strscpy(p->dev_name, hid, sizeof(p->dev_name)); + physdev = get_device(acpi_get_first_physical_node(adev)); + acpi_dev_put(adev); + + /* No side-effect to the playback even if subsystem_id is NULL*/ + sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); + if (IS_ERR(sub)) + sub = NULL; + + p->acpi_subsystem_id = sub; + + put_device(physdev); + + return 0; + +err: + dev_err(p->dev, "read acpi error, ret: %d\n", ret); + acpi_dev_put(adev); + + return ret; +} + +static void tas2781_hda_playback_hook(struct device *dev, int action) +{ + struct tasdevice_priv *tas_priv = dev_get_drvdata(dev); + + dev_dbg(tas_priv->dev, "%s: action = %d\n", __func__, action); + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + pm_runtime_get_sync(dev); + mutex_lock(&tas_priv->codec_lock); + tasdevice_tuning_switch(tas_priv, 0); + mutex_unlock(&tas_priv->codec_lock); + break; + case HDA_GEN_PCM_ACT_CLOSE: + mutex_lock(&tas_priv->codec_lock); + tasdevice_tuning_switch(tas_priv, 1); + mutex_unlock(&tas_priv->codec_lock); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + break; + default: + dev_dbg(tas_priv->dev, "Playback action not supported: %d\n", + action); + break; + } +} + +static int tasdevice_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1; + + return 0; +} + +static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id; + + return 0; +} + +static int tasdevice_hda_clamp(int val, int max) +{ + if (val > max) + val = max; + + if (val < 0) + val = 0; + return val; +} + +static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + int nr_profile = ucontrol->value.integer.value[0]; + int max = tas_priv->rcabin.ncfgs - 1; + int val, ret = 0; + + val = tasdevice_hda_clamp(nr_profile, max); + + if (tas_priv->rcabin.profile_cfg_id != val) { + tas_priv->rcabin.profile_cfg_id = val; + ret = 1; + } + + return ret; +} + +static int tasdevice_info_programs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_fw->nr_programs - 1; + + return 0; +} + +static int tasdevice_info_config(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_fw->nr_configurations - 1; + + return 0; +} + +static int tasdevice_program_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas_priv->cur_prog; + + return 0; +} + +static int tasdevice_program_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + int nr_program = ucontrol->value.integer.value[0]; + int max = tas_fw->nr_programs - 1; + int val, ret = 0; + + val = tasdevice_hda_clamp(nr_program, max); + + if (tas_priv->cur_prog != val) { + tas_priv->cur_prog = val; + ret = 1; + } + + return ret; +} + +static int tasdevice_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas_priv->cur_conf; + + return 0; +} + +static int tasdevice_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + int nr_config = ucontrol->value.integer.value[0]; + int max = tas_fw->nr_configurations - 1; + int val, ret = 0; + + val = tasdevice_hda_clamp(nr_config, max); + + if (tas_priv->cur_conf != val) { + tas_priv->cur_conf = val; + ret = 1; + } + + return ret; +} + +/* + * tas2781_digital_getvol - get the volum control + * @kcontrol: control pointer + * @ucontrol: User data + * Customer Kcontrol for tas2781 is primarily for regmap booking, paging + * depends on internal regmap mechanism. + * tas2781 contains book and page two-level register map, especially + * book switching will set the register BXXP00R7F, after switching to the + * correct book, then leverage the mechanism for paging to access the + * register. + */ +static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_digital_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_amp_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + /* The check of the given value is in tasdevice_digital_putvol. */ + return tasdevice_digital_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + /* The check of the given value is in tasdevice_amp_putvol. */ + return tasdevice_amp_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status; + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + tas_priv->force_fwload_status ? "ON" : "OFF"); + + return 0; +} + +static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + bool change, val = (bool)ucontrol->value.integer.value[0]; + + if (tas_priv->force_fwload_status == val) + change = false; + else { + change = true; + tas_priv->force_fwload_status = val; + } + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + tas_priv->force_fwload_status ? "ON" : "OFF"); + + return change; +} + +static const struct snd_kcontrol_new tas2781_snd_controls[] = { + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, + 1, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, amp_vol_tlv), + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL, + 0, 0, 200, 1, tas2781_digital_getvol, + tas2781_digital_putvol, dvc_tlv), + ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, + tas2781_force_fwload_get, tas2781_force_fwload_put), +}; + +static const struct snd_kcontrol_new tas2781_prof_ctrl = { + .name = "Speaker Profile Id", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_profile, + .get = tasdevice_get_profile_id, + .put = tasdevice_set_profile_id, +}; + +static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl = { + .name = "Speaker Program Id", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_programs, + .get = tasdevice_program_get, + .put = tasdevice_program_put, +}; + +static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl = { + .name = "Speaker Config Id", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_config, + .get = tasdevice_config_get, + .put = tasdevice_config_put, +}; + +static void tas2781_apply_calib(struct tasdevice_priv *tas_priv) +{ + static const unsigned char page_array[CALIB_MAX] = { + 0x17, 0x18, 0x18, 0x0d, 0x18 + }; + static const unsigned char rgno_array[CALIB_MAX] = { + 0x74, 0x0c, 0x14, 0x3c, 0x7c + }; + unsigned char *data; + int i, j, rc; + + for (i = 0; i < tas_priv->ndev; i++) { + data = tas_priv->cali_data.data + + i * TASDEVICE_SPEAKER_CALIBRATION_SIZE; + for (j = 0; j < CALIB_MAX; j++) { + rc = tasdevice_dev_bulk_write(tas_priv, i, + TASDEVICE_REG(0, page_array[j], rgno_array[j]), + &(data[4 * j]), 4); + if (rc < 0) + dev_err(tas_priv->dev, + "chn %d calib %d bulk_wr err = %d\n", + i, j, rc); + } + } +} + +/* Update the calibrate data, including speaker impedance, f0, etc, into algo. + * Calibrate data is done by manufacturer in the factory. These data are used + * by Algo for calucating the speaker temperature, speaker membrance excursion + * and f0 in real time during playback. + */ +static int tas2781_save_calibration(struct tasdevice_priv *tas_priv) +{ + efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, + 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3); + static efi_char16_t efi_name[] = L"CALI_DATA"; + struct tm *tm = &tas_priv->tm; + unsigned int attr, crc; + unsigned int *tmp_val; + efi_status_t status; + + /* Lenovo devices */ + if (tas_priv->catlog_id == LENOVO) + efi_guid = EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, 0x09, + 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92); + + tas_priv->cali_data.total_sz = 0; + /* Get real size of UEFI variable */ + status = efi.get_variable(efi_name, &efi_guid, &attr, + &tas_priv->cali_data.total_sz, tas_priv->cali_data.data); + if (status == EFI_BUFFER_TOO_SMALL) { + /* Allocate data buffer of data_size bytes */ + tas_priv->cali_data.data = devm_kzalloc(tas_priv->dev, + tas_priv->cali_data.total_sz, GFP_KERNEL); + if (!tas_priv->cali_data.data) + return -ENOMEM; + /* Get variable contents into buffer */ + status = efi.get_variable(efi_name, &efi_guid, &attr, + &tas_priv->cali_data.total_sz, + tas_priv->cali_data.data); + if (status != EFI_SUCCESS) + return -EINVAL; + } + + tmp_val = (unsigned int *)tas_priv->cali_data.data; + + crc = crc32(~0, tas_priv->cali_data.data, 84) ^ ~0; + dev_dbg(tas_priv->dev, "cali crc 0x%08x PK tmp_val 0x%08x\n", + crc, tmp_val[21]); + + if (crc == tmp_val[21]) { + time64_to_tm(tmp_val[20], 0, tm); + dev_dbg(tas_priv->dev, "%4ld-%2d-%2d, %2d:%2d:%2d\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + tas2781_apply_calib(tas_priv); + } else + tas_priv->cali_data.total_sz = 0; + + return 0; +} + +static void tasdev_fw_ready(const struct firmware *fmw, void *context) +{ + struct tasdevice_priv *tas_priv = context; + struct hda_codec *codec = tas_priv->codec; + int i, ret; + + pm_runtime_get_sync(tas_priv->dev); + mutex_lock(&tas_priv->codec_lock); + + ret = tasdevice_rca_parser(tas_priv, fmw); + if (ret) + goto out; + + ret = snd_ctl_add(codec->card, + snd_ctl_new1(&tas2781_prof_ctrl, tas_priv)); + if (ret) { + dev_err(tas_priv->dev, + "Failed to add KControl %s = %d\n", + tas2781_prof_ctrl.name, ret); + goto out; + } + + for (i = 0; i < ARRAY_SIZE(tas2781_snd_controls); i++) { + ret = snd_ctl_add(codec->card, + snd_ctl_new1(&tas2781_snd_controls[i], tas_priv)); + if (ret) { + dev_err(tas_priv->dev, + "Failed to add KControl %s = %d\n", + tas2781_snd_controls[i].name, ret); + goto out; + } + } + + tasdevice_dsp_remove(tas_priv); + + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; + scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%04X.bin", + codec->core.subsystem_id & 0xffff); + ret = tasdevice_dsp_parser(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dspfw load %s error\n", + tas_priv->coef_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + ret = snd_ctl_add(codec->card, + snd_ctl_new1(&tas2781_dsp_prog_ctrl, tas_priv)); + if (ret) { + dev_err(tas_priv->dev, + "Failed to add KControl %s = %d\n", + tas2781_dsp_prog_ctrl.name, ret); + goto out; + } + + ret = snd_ctl_add(codec->card, + snd_ctl_new1(&tas2781_dsp_conf_ctrl, tas_priv)); + if (ret) { + dev_err(tas_priv->dev, + "Failed to add KControl %s = %d\n", + tas2781_dsp_conf_ctrl.name, ret); + goto out; + } + + tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; + tasdevice_prmg_load(tas_priv, 0); + + /* If calibrated data occurs error, dsp will still works with default + * calibrated data inside algo. + */ + tas2781_save_calibration(tas_priv); + +out: + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + /*If DSP FW fail, kcontrol won't be created */ + tasdevice_config_info_remove(tas_priv); + tasdevice_dsp_remove(tas_priv); + } + mutex_unlock(&tas_priv->codec_lock); + if (fmw) + release_firmware(fmw); + pm_runtime_mark_last_busy(tas_priv->dev); + pm_runtime_put_autosuspend(tas_priv->dev); +} + +static int tas2781_hda_bind(struct device *dev, struct device *master, + void *master_data) +{ + struct tasdevice_priv *tas_priv = dev_get_drvdata(dev); + struct hda_component *comps = master_data; + struct hda_codec *codec; + unsigned int subid; + int ret; + + if (!comps || tas_priv->index < 0 || + tas_priv->index >= HDA_MAX_COMPONENTS) + return -EINVAL; + + comps = &comps[tas_priv->index]; + if (comps->dev) + return -EBUSY; + + codec = comps->codec; + subid = codec->core.subsystem_id >> 16; + + switch (subid) { + case 0x17aa: + tas_priv->catlog_id = LENOVO; + break; + default: + tas_priv->catlog_id = OTHERS; + break; + } + + pm_runtime_get_sync(dev); + + comps->dev = dev; + + strscpy(comps->name, dev_name(dev), sizeof(comps->name)); + + ret = tascodec_init(tas_priv, codec, tasdev_fw_ready); + if (!ret) + comps->playback_hook = tas2781_hda_playback_hook; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static void tas2781_hda_unbind(struct device *dev, + struct device *master, void *master_data) +{ + struct tasdevice_priv *tas_priv = dev_get_drvdata(dev); + struct hda_component *comps = master_data; + + if (comps[tas_priv->index].dev == dev) + memset(&comps[tas_priv->index], 0, sizeof(*comps)); + + tasdevice_config_info_remove(tas_priv); + tasdevice_dsp_remove(tas_priv); + + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; +} + +static const struct component_ops tas2781_hda_comp_ops = { + .bind = tas2781_hda_bind, + .unbind = tas2781_hda_unbind, +}; + +static void tas2781_hda_remove(struct device *dev) +{ + struct tasdevice_priv *tas_priv = dev_get_drvdata(dev); + + pm_runtime_get_sync(tas_priv->dev); + pm_runtime_disable(tas_priv->dev); + + component_del(tas_priv->dev, &tas2781_hda_comp_ops); + + pm_runtime_put_noidle(tas_priv->dev); + + tasdevice_remove(tas_priv); +} + +static int tas2781_hda_i2c_probe(struct i2c_client *clt) +{ + struct tasdevice_priv *tas_priv; + const char *device_name; + int ret; + + if (strstr(dev_name(&clt->dev), "TIAS2781")) + device_name = "TIAS2781"; + else + return -ENODEV; + + tas_priv = tasdevice_kzalloc(clt); + if (!tas_priv) + return -ENOMEM; + + tas_priv->irq_info.irq = clt->irq; + ret = tas2781_read_acpi(tas_priv, device_name); + if (ret) + return dev_err_probe(tas_priv->dev, ret, + "Platform not supported\n"); + + ret = tasdevice_init(tas_priv); + if (ret) + goto err; + + pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000); + pm_runtime_use_autosuspend(tas_priv->dev); + pm_runtime_mark_last_busy(tas_priv->dev); + pm_runtime_set_active(tas_priv->dev); + pm_runtime_get_noresume(tas_priv->dev); + pm_runtime_enable(tas_priv->dev); + + pm_runtime_put_autosuspend(tas_priv->dev); + + ret = component_add(tas_priv->dev, &tas2781_hda_comp_ops); + if (ret) { + dev_err(tas_priv->dev, "Register component failed: %d\n", ret); + pm_runtime_disable(tas_priv->dev); + goto err; + } + + tas2781_reset(tas_priv); +err: + if (ret) + tas2781_hda_remove(&clt->dev); + return ret; +} + +static void tas2781_hda_i2c_remove(struct i2c_client *clt) +{ + tas2781_hda_remove(&clt->dev); +} + +static int tas2781_runtime_suspend(struct device *dev) +{ + struct tasdevice_priv *tas_priv = dev_get_drvdata(dev); + int i; + + dev_dbg(tas_priv->dev, "Runtime Suspend\n"); + + mutex_lock(&tas_priv->codec_lock); + + if (tas_priv->playback_started) { + tasdevice_tuning_switch(tas_priv, 1); + tas_priv->playback_started = false; + } + + for (i = 0; i < tas_priv->ndev; i++) { + tas_priv->tasdevice[i].cur_book = -1; + tas_priv->tasdevice[i].cur_prog = -1; + tas_priv->tasdevice[i].cur_conf = -1; + } + + regcache_cache_only(tas_priv->regmap, true); + regcache_mark_dirty(tas_priv->regmap); + + mutex_unlock(&tas_priv->codec_lock); + + return 0; +} + +static int tas2781_runtime_resume(struct device *dev) +{ + struct tasdevice_priv *tas_priv = dev_get_drvdata(dev); + unsigned long calib_data_sz = + tas_priv->ndev * TASDEVICE_SPEAKER_CALIBRATION_SIZE; + int ret; + + dev_dbg(tas_priv->dev, "Runtime Resume\n"); + + mutex_lock(&tas_priv->codec_lock); + + regcache_cache_only(tas_priv->regmap, false); + ret = regcache_sync(tas_priv->regmap); + if (ret) { + dev_err(tas_priv->dev, + "Failed to restore register cache: %d\n", ret); + goto out; + } + + tasdevice_prmg_load(tas_priv, tas_priv->cur_prog); + + /* If calibrated data occurs error, dsp will still works with default + * calibrated data inside algo. + */ + if (tas_priv->cali_data.total_sz > calib_data_sz) + tas2781_apply_calib(tas_priv); + +out: + mutex_unlock(&tas_priv->codec_lock); + + return ret; +} + +static int tas2781_system_suspend(struct device *dev) +{ + struct tasdevice_priv *tas_priv = dev_get_drvdata(dev); + int ret; + + dev_dbg(tas_priv->dev, "System Suspend\n"); + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + /* Shutdown chip before system suspend */ + regcache_cache_only(tas_priv->regmap, false); + tasdevice_tuning_switch(tas_priv, 1); + regcache_cache_only(tas_priv->regmap, true); + regcache_mark_dirty(tas_priv->regmap); + + /* + * Reset GPIO may be shared, so cannot reset here. + * However beyond this point, amps may be powered down. + */ + return 0; +} + +static int tas2781_system_resume(struct device *dev) +{ + struct tasdevice_priv *tas_priv = dev_get_drvdata(dev); + unsigned long calib_data_sz = + tas_priv->ndev * TASDEVICE_SPEAKER_CALIBRATION_SIZE; + int i, ret; + + dev_dbg(tas_priv->dev, "System Resume\n"); + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + mutex_lock(&tas_priv->codec_lock); + + for (i = 0; i < tas_priv->ndev; i++) { + tas_priv->tasdevice[i].cur_book = -1; + tas_priv->tasdevice[i].cur_prog = -1; + tas_priv->tasdevice[i].cur_conf = -1; + } + tas2781_reset(tas_priv); + tasdevice_prmg_load(tas_priv, tas_priv->cur_prog); + + /* If calibrated data occurs error, dsp will still work with default + * calibrated data inside algo. + */ + if (tas_priv->cali_data.total_sz > calib_data_sz) + tas2781_apply_calib(tas_priv); + mutex_unlock(&tas_priv->codec_lock); + + return 0; +} + +static const struct dev_pm_ops tas2781_hda_pm_ops = { + RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume) +}; + +static const struct i2c_device_id tas2781_hda_i2c_id[] = { + { "tas2781-hda", 0 }, + {} +}; + +static const struct acpi_device_id tas2781_acpi_hda_match[] = { + {"TIAS2781", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match); + +static struct i2c_driver tas2781_hda_i2c_driver = { + .driver = { + .name = "tas2781-hda", + .acpi_match_table = tas2781_acpi_hda_match, + .pm = &tas2781_hda_pm_ops, + }, + .id_table = tas2781_hda_i2c_id, + .probe = tas2781_hda_i2c_probe, + .remove = tas2781_hda_i2c_remove, +}; +module_i2c_driver(tas2781_hda_i2c_driver); + +MODULE_DESCRIPTION("TAS2781 HDA Driver"); +MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB); diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index f0f8324b08b6..d679842ae1bd 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -408,30 +408,6 @@ static const char * const follower_vols[] = { static DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1); -static struct snd_kcontrol *ctl_find(struct snd_card *card, - const char *name) -{ - struct snd_ctl_elem_id sid = {0}; - - strscpy(sid.name, name, sizeof(sid.name)); - sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - return snd_ctl_find_id(card, &sid); -} - -static void add_followers(struct snd_card *card, - struct snd_kcontrol *master, - const char * const *list) -{ - for (; *list; list++) { - struct snd_kcontrol *follower = ctl_find(card, *list); - /* dev_dbg(card->dev, "add_followers - %s\n", *list); */ - if (follower) { - /* dev_dbg(card->dev, "follower %s found\n", *list); */ - snd_ctl_add_follower(master, follower); - } - } -} - static int juli_add_controls(struct snd_ice1712 *ice) { struct juli_spec *spec = ice->spec; @@ -454,10 +430,12 @@ static int juli_add_controls(struct snd_ice1712 *ice) juli_master_db_scale); if (!vmaster) return -ENOMEM; - add_followers(ice->card, vmaster, follower_vols); err = snd_ctl_add(ice->card, vmaster); if (err < 0) return err; + err = snd_ctl_add_followers(ice->card, vmaster, follower_vols); + if (err < 0) + return err; /* only capture SPDIF over AK4114 */ return snd_ak4114_build(spec->ak4114, NULL, diff --git a/sound/pci/ice1712/psc724.c b/sound/pci/ice1712/psc724.c index 82cf365cda10..0818e42c94ca 100644 --- a/sound/pci/ice1712/psc724.c +++ b/sound/pci/ice1712/psc724.c @@ -177,7 +177,6 @@ static bool psc724_get_master_switch(struct snd_ice1712 *ice) static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected) { struct psc724_spec *spec = ice->spec; - struct snd_ctl_elem_id elem_id; struct snd_kcontrol *kctl; u16 power = spec->wm8776.regs[WM8776_REG_PWRDOWN] & ~WM8776_PWR_HPPD; @@ -187,17 +186,15 @@ static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected) snd_wm8776_set_power(&spec->wm8776, power); spec->hp_connected = hp_connected; /* notify about master speaker mute change */ - memset(&elem_id, 0, sizeof(elem_id)); - elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strscpy(elem_id.name, "Master Speakers Playback Switch", - sizeof(elem_id.name)); - kctl = snd_ctl_find_id(ice->card, &elem_id); - snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + kctl = snd_ctl_find_id_mixer(ice->card, + "Master Speakers Playback Switch"); + if (kctl) + snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); /* and headphone mute change */ - strscpy(elem_id.name, spec->wm8776.ctl[WM8776_CTL_HP_SW].name, - sizeof(elem_id.name)); - kctl = snd_ctl_find_id(ice->card, &elem_id); - snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + kctl = snd_ctl_find_id_mixer(ice->card, + spec->wm8776.ctl[WM8776_CTL_HP_SW].name); + if (kctl) + snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); } static void psc724_update_hp_jack_state(struct work_struct *work) diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c index 20b3e8f94719..f61ee9f5c754 100644 --- a/sound/pci/ice1712/quartet.c +++ b/sound/pci/ice1712/quartet.c @@ -766,26 +766,6 @@ static const char * const follower_vols[] = { static DECLARE_TLV_DB_SCALE(qtet_master_db_scale, -6350, 50, 1); -static struct snd_kcontrol *ctl_find(struct snd_card *card, - const char *name) -{ - struct snd_ctl_elem_id sid = {0}; - - strscpy(sid.name, name, sizeof(sid.name)); - sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - return snd_ctl_find_id(card, &sid); -} - -static void add_followers(struct snd_card *card, - struct snd_kcontrol *master, const char * const *list) -{ - for (; *list; list++) { - struct snd_kcontrol *follower = ctl_find(card, *list); - if (follower) - snd_ctl_add_follower(master, follower); - } -} - static int qtet_add_controls(struct snd_ice1712 *ice) { struct qtet_spec *spec = ice->spec; @@ -806,10 +786,12 @@ static int qtet_add_controls(struct snd_ice1712 *ice) qtet_master_db_scale); if (!vmaster) return -ENOMEM; - add_followers(ice->card, vmaster, follower_vols); err = snd_ctl_add(ice->card, vmaster); if (err < 0) return err; + err = snd_ctl_add_followers(ice->card, vmaster, follower_vols); + if (err < 0) + return err; /* only capture SPDIF over AK4113 */ return snd_ak4113_build(spec->ak4113, ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); diff --git a/sound/pci/ice1712/wm8776.c b/sound/pci/ice1712/wm8776.c index 6eda86119dff..493425697bb4 100644 --- a/sound/pci/ice1712/wm8776.c +++ b/sound/pci/ice1712/wm8776.c @@ -34,13 +34,9 @@ static void snd_wm8776_activate_ctl(struct snd_wm8776 *wm, struct snd_card *card = wm->card; struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; - struct snd_ctl_elem_id elem_id; unsigned int index_offset; - memset(&elem_id, 0, sizeof(elem_id)); - strscpy(elem_id.name, ctl_name, sizeof(elem_id.name)); - elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - kctl = snd_ctl_find_id(card, &elem_id); + kctl = snd_ctl_find_id_mixer(card, ctl_name); if (!kctl) return; index_offset = snd_ctl_get_ioff(kctl, &kctl->id); diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 33b4f95d65b3..5c2cac201a28 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -1285,8 +1285,7 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun } static int snd_korg1212_copy_to(struct snd_pcm_substream *substream, - void __user *dst, int pos, int count, - bool in_kernel) + struct iov_iter *dst, int pos, int count) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); @@ -1306,24 +1305,20 @@ static int snd_korg1212_copy_to(struct snd_pcm_substream *substream, #if K1212_DEBUG_LEVEL > 0 if ( (void *) src < (void *) korg1212->recordDataBufsPtr || (void *) src > (void *) korg1212->recordDataBufsPtr[8].bufferData ) { - printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_to KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i); + printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_to KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst->kvec.iov_base, i); return -EFAULT; } #endif - if (in_kernel) - memcpy((__force void *)dst, src, size); - else if (copy_to_user(dst, src, size)) + if (copy_to_iter(src, size, dst) != size) return -EFAULT; src++; - dst += size; } return 0; } static int snd_korg1212_copy_from(struct snd_pcm_substream *substream, - void __user *src, int pos, int count, - bool in_kernel) + struct iov_iter *src, int pos, int count) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); @@ -1345,16 +1340,13 @@ static int snd_korg1212_copy_from(struct snd_pcm_substream *substream, #if K1212_DEBUG_LEVEL > 0 if ( (void *) dst < (void *) korg1212->playDataBufsPtr || (void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) { - printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_from KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i); + printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_from KERNEL EFAULT, src=%p dst=%p iter=%d\n", src->kvec.iov_base, dst, i); return -EFAULT; } #endif - if (in_kernel) - memcpy(dst, (__force void *)src, size); - else if (copy_from_user(dst, src, size)) + if (copy_from_iter(dst, size, src) != size) return -EFAULT; dst++; - src += size; } return 0; @@ -1642,17 +1634,9 @@ static snd_pcm_uframes_t snd_korg1212_capture_pointer(struct snd_pcm_substream * static int snd_korg1212_playback_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *src, unsigned long count) + struct iov_iter *src, unsigned long count) { - return snd_korg1212_copy_from(substream, src, pos, count, false); -} - -static int snd_korg1212_playback_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *src, unsigned long count) -{ - return snd_korg1212_copy_from(substream, (void __user *)src, - pos, count, true); + return snd_korg1212_copy_from(substream, src, pos, count); } static int snd_korg1212_playback_silence(struct snd_pcm_substream *substream, @@ -1670,17 +1654,9 @@ static int snd_korg1212_playback_silence(struct snd_pcm_substream *substream, static int snd_korg1212_capture_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *dst, unsigned long count) -{ - return snd_korg1212_copy_to(substream, dst, pos, count, false); -} - -static int snd_korg1212_capture_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *dst, unsigned long count) + struct iov_iter *dst, unsigned long count) { - return snd_korg1212_copy_to(substream, (void __user *)dst, - pos, count, true); + return snd_korg1212_copy_to(substream, dst, pos, count); } static const struct snd_pcm_ops snd_korg1212_playback_ops = { @@ -1691,8 +1667,7 @@ static const struct snd_pcm_ops snd_korg1212_playback_ops = { .prepare = snd_korg1212_prepare, .trigger = snd_korg1212_trigger, .pointer = snd_korg1212_playback_pointer, - .copy_user = snd_korg1212_playback_copy, - .copy_kernel = snd_korg1212_playback_copy_kernel, + .copy = snd_korg1212_playback_copy, .fill_silence = snd_korg1212_playback_silence, }; @@ -1704,8 +1679,7 @@ static const struct snd_pcm_ops snd_korg1212_capture_ops = { .prepare = snd_korg1212_prepare, .trigger = snd_korg1212_trigger, .pointer = snd_korg1212_capture_pointer, - .copy_user = snd_korg1212_capture_copy, - .copy_kernel = snd_korg1212_capture_copy_kernel, + .copy = snd_korg1212_capture_copy, }; /* diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 261850775c80..305cbd24a391 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2029,9 +2029,6 @@ static int snd_m3_mixer(struct snd_m3 *chip) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; -#ifndef CONFIG_SND_MAESTRO3_INPUT - struct snd_ctl_elem_id elem_id; -#endif int err; static const struct snd_ac97_bus_ops ops = { .write = snd_m3_ac97_write, @@ -2054,14 +2051,10 @@ static int snd_m3_mixer(struct snd_m3 *chip) snd_ac97_write(chip->ac97, AC97_PCM, 0); #ifndef CONFIG_SND_MAESTRO3_INPUT - memset(&elem_id, 0, sizeof(elem_id)); - elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(elem_id.name, "Master Playback Switch"); - chip->master_switch = snd_ctl_find_id(chip->card, &elem_id); - memset(&elem_id, 0, sizeof(elem_id)); - elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(elem_id.name, "Master Playback Volume"); - chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); + chip->master_switch = snd_ctl_find_id_mixer(chip->card, + "Master Playback Switch"); + chip->master_volume = snd_ctl_find_id_mixer(chip->card, + "Master Playback Volume"); #endif return 0; diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index f99a1e96e923..34f90829e656 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -691,26 +691,12 @@ snd_nm256_playback_silence(struct snd_pcm_substream *substream, static int snd_nm256_playback_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *src, unsigned long count) + struct iov_iter *src, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - if (copy_from_user_toio(s->bufptr + pos, src, count)) - return -EFAULT; - return 0; -} - -static int -snd_nm256_playback_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *src, unsigned long count) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct nm256_stream *s = runtime->private_data; - - memcpy_toio(s->bufptr + pos, src, count); - return 0; + return copy_from_iter_toio(s->bufptr + pos, src, count); } /* @@ -719,26 +705,12 @@ snd_nm256_playback_copy_kernel(struct snd_pcm_substream *substream, static int snd_nm256_capture_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *dst, unsigned long count) + struct iov_iter *dst, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - if (copy_to_user_fromio(dst, s->bufptr + pos, count)) - return -EFAULT; - return 0; -} - -static int -snd_nm256_capture_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *dst, unsigned long count) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct nm256_stream *s = runtime->private_data; - - memcpy_fromio(dst, s->bufptr + pos, count); - return 0; + return copy_to_iter_fromio(dst, s->bufptr + pos, count); } #endif /* !__i386__ */ @@ -909,8 +881,7 @@ static const struct snd_pcm_ops snd_nm256_playback_ops = { .trigger = snd_nm256_playback_trigger, .pointer = snd_nm256_playback_pointer, #ifndef __i386__ - .copy_user = snd_nm256_playback_copy, - .copy_kernel = snd_nm256_playback_copy_kernel, + .copy = snd_nm256_playback_copy, .fill_silence = snd_nm256_playback_silence, #endif .mmap = snd_pcm_lib_mmap_iomem, @@ -924,8 +895,7 @@ static const struct snd_pcm_ops snd_nm256_capture_ops = { .trigger = snd_nm256_capture_trigger, .pointer = snd_nm256_capture_pointer, #ifndef __i386__ - .copy_user = snd_nm256_capture_copy, - .copy_kernel = snd_nm256_capture_copy_kernel, + .copy = snd_nm256_capture_copy, #endif .mmap = snd_pcm_lib_mmap_iomem, }; diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 9c0ac025e143..02144bbee6d5 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -252,48 +252,24 @@ static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, /* copy callback for halfduplex mode */ static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *src, unsigned long count) + struct iov_iter *src, unsigned long count) { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, - src, count)) - return -EFAULT; - return 0; -} - -static int snd_rme32_playback_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *src, unsigned long count) -{ - struct rme32 *rme32 = snd_pcm_substream_chip(substream); - - memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, src, count); - return 0; + return copy_from_iter_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, + src, count); } /* copy callback for halfduplex mode */ static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *dst, unsigned long count) + struct iov_iter *dst, unsigned long count) { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - if (copy_to_user_fromio(dst, - rme32->iobase + RME32_IO_DATA_BUFFER + pos, - count)) - return -EFAULT; - return 0; -} - -static int snd_rme32_capture_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *dst, unsigned long count) -{ - struct rme32 *rme32 = snd_pcm_substream_chip(substream); - - memcpy_fromio(dst, rme32->iobase + RME32_IO_DATA_BUFFER + pos, count); - return 0; + return copy_to_iter_fromio(dst, + rme32->iobase + RME32_IO_DATA_BUFFER + pos, + count); } /* @@ -1194,8 +1170,7 @@ static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = { .prepare = snd_rme32_playback_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_playback_pointer, - .copy_user = snd_rme32_playback_copy, - .copy_kernel = snd_rme32_playback_copy_kernel, + .copy = snd_rme32_playback_copy, .fill_silence = snd_rme32_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1207,8 +1182,7 @@ static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = { .prepare = snd_rme32_capture_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_capture_pointer, - .copy_user = snd_rme32_capture_copy, - .copy_kernel = snd_rme32_capture_copy_kernel, + .copy = snd_rme32_capture_copy, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1219,8 +1193,7 @@ static const struct snd_pcm_ops snd_rme32_playback_adat_ops = { .prepare = snd_rme32_playback_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_playback_pointer, - .copy_user = snd_rme32_playback_copy, - .copy_kernel = snd_rme32_playback_copy_kernel, + .copy = snd_rme32_playback_copy, .fill_silence = snd_rme32_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1232,8 +1205,7 @@ static const struct snd_pcm_ops snd_rme32_capture_adat_ops = { .prepare = snd_rme32_capture_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_capture_pointer, - .copy_user = snd_rme32_capture_copy, - .copy_kernel = snd_rme32_capture_copy_kernel, + .copy = snd_rme32_capture_copy, .mmap = snd_pcm_lib_mmap_iomem, }; diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index bccb7e0d3d11..6b5ffb18197b 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -320,48 +320,26 @@ snd_rme96_playback_silence(struct snd_pcm_substream *substream, static int snd_rme96_playback_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *src, unsigned long count) + struct iov_iter *src, unsigned long count) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - return copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, + return copy_from_iter_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, count); } static int -snd_rme96_playback_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *src, unsigned long count) -{ - struct rme96 *rme96 = snd_pcm_substream_chip(substream); - - memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, count); - return 0; -} - -static int snd_rme96_capture_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *dst, unsigned long count) + struct iov_iter *dst, unsigned long count) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - return copy_to_user_fromio(dst, + return copy_to_iter_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, count); } -static int -snd_rme96_capture_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *dst, unsigned long count) -{ - struct rme96 *rme96 = snd_pcm_substream_chip(substream); - - memcpy_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, count); - return 0; -} - /* * Digital output capabilities (S/PDIF) */ @@ -1518,8 +1496,7 @@ static const struct snd_pcm_ops snd_rme96_playback_spdif_ops = { .prepare = snd_rme96_playback_prepare, .trigger = snd_rme96_playback_trigger, .pointer = snd_rme96_playback_pointer, - .copy_user = snd_rme96_playback_copy, - .copy_kernel = snd_rme96_playback_copy_kernel, + .copy = snd_rme96_playback_copy, .fill_silence = snd_rme96_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1531,8 +1508,7 @@ static const struct snd_pcm_ops snd_rme96_capture_spdif_ops = { .prepare = snd_rme96_capture_prepare, .trigger = snd_rme96_capture_trigger, .pointer = snd_rme96_capture_pointer, - .copy_user = snd_rme96_capture_copy, - .copy_kernel = snd_rme96_capture_copy_kernel, + .copy = snd_rme96_capture_copy, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1543,8 +1519,7 @@ static const struct snd_pcm_ops snd_rme96_playback_adat_ops = { .prepare = snd_rme96_playback_prepare, .trigger = snd_rme96_playback_trigger, .pointer = snd_rme96_playback_pointer, - .copy_user = snd_rme96_playback_copy, - .copy_kernel = snd_rme96_playback_copy_kernel, + .copy = snd_rme96_playback_copy, .fill_silence = snd_rme96_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, }; @@ -1556,8 +1531,7 @@ static const struct snd_pcm_ops snd_rme96_capture_adat_ops = { .prepare = snd_rme96_capture_prepare, .trigger = snd_rme96_capture_trigger, .pointer = snd_rme96_capture_pointer, - .copy_user = snd_rme96_capture_copy, - .copy_kernel = snd_rme96_capture_copy_kernel, + .copy = snd_rme96_capture_copy, .mmap = snd_pcm_lib_mmap_iomem, }; diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 65add92c88aa..e7d1b43471a2 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -3961,7 +3961,7 @@ static signed char *hdsp_channel_buffer_location(struct hdsp *hdsp, static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *src, unsigned long count) + struct iov_iter *src, unsigned long count) { struct hdsp *hdsp = snd_pcm_substream_chip(substream); signed char *channel_buf; @@ -3972,28 +3972,14 @@ static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream, channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_from_user(channel_buf + pos, src, count)) + if (copy_from_iter(channel_buf + pos, count, src) != count) return -EFAULT; return 0; } -static int snd_hdsp_playback_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *src, unsigned long count) -{ - struct hdsp *hdsp = snd_pcm_substream_chip(substream); - signed char *channel_buf; - - channel_buf = hdsp_channel_buffer_location(hdsp, substream->pstr->stream, channel); - if (snd_BUG_ON(!channel_buf)) - return -EIO; - memcpy(channel_buf + pos, src, count); - return 0; -} - static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *dst, unsigned long count) + struct iov_iter *dst, unsigned long count) { struct hdsp *hdsp = snd_pcm_substream_chip(substream); signed char *channel_buf; @@ -4004,25 +3990,11 @@ static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream, channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_to_user(dst, channel_buf + pos, count)) + if (copy_to_iter(channel_buf + pos, count, dst) != count) return -EFAULT; return 0; } -static int snd_hdsp_capture_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *dst, unsigned long count) -{ - struct hdsp *hdsp = snd_pcm_substream_chip(substream); - signed char *channel_buf; - - channel_buf = hdsp_channel_buffer_location(hdsp, substream->pstr->stream, channel); - if (snd_BUG_ON(!channel_buf)) - return -EIO; - memcpy(dst, channel_buf + pos, count); - return 0; -} - static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream, int channel, unsigned long pos, unsigned long count) @@ -4950,8 +4922,7 @@ static const struct snd_pcm_ops snd_hdsp_playback_ops = { .prepare = snd_hdsp_prepare, .trigger = snd_hdsp_trigger, .pointer = snd_hdsp_hw_pointer, - .copy_user = snd_hdsp_playback_copy, - .copy_kernel = snd_hdsp_playback_copy_kernel, + .copy = snd_hdsp_playback_copy, .fill_silence = snd_hdsp_hw_silence, }; @@ -4963,8 +4934,7 @@ static const struct snd_pcm_ops snd_hdsp_capture_ops = { .prepare = snd_hdsp_prepare, .trigger = snd_hdsp_trigger, .pointer = snd_hdsp_hw_pointer, - .copy_user = snd_hdsp_capture_copy, - .copy_kernel = snd_hdsp_capture_copy_kernel, + .copy = snd_hdsp_capture_copy, }; static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp) diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index e7c320afefe8..d066c70ae160 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -1844,7 +1844,7 @@ static signed char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652, static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *src, unsigned long count) + struct iov_iter *src, unsigned long count) { struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); signed char *channel_buf; @@ -1857,30 +1857,14 @@ static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_from_user(channel_buf + pos, src, count)) + if (copy_from_iter(channel_buf + pos, count, src) != count) return -EFAULT; return 0; } -static int snd_rme9652_playback_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *src, unsigned long count) -{ - struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); - signed char *channel_buf; - - channel_buf = rme9652_channel_buffer_location(rme9652, - substream->pstr->stream, - channel); - if (snd_BUG_ON(!channel_buf)) - return -EIO; - memcpy(channel_buf + pos, src, count); - return 0; -} - static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *dst, unsigned long count) + struct iov_iter *dst, unsigned long count) { struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); signed char *channel_buf; @@ -1893,27 +1877,11 @@ static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_to_user(dst, channel_buf + pos, count)) + if (copy_to_iter(channel_buf + pos, count, dst) != count) return -EFAULT; return 0; } -static int snd_rme9652_capture_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *dst, unsigned long count) -{ - struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); - signed char *channel_buf; - - channel_buf = rme9652_channel_buffer_location(rme9652, - substream->pstr->stream, - channel); - if (snd_BUG_ON(!channel_buf)) - return -EIO; - memcpy(dst, channel_buf + pos, count); - return 0; -} - static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream, int channel, unsigned long pos, unsigned long count) @@ -2370,8 +2338,7 @@ static const struct snd_pcm_ops snd_rme9652_playback_ops = { .prepare = snd_rme9652_prepare, .trigger = snd_rme9652_trigger, .pointer = snd_rme9652_hw_pointer, - .copy_user = snd_rme9652_playback_copy, - .copy_kernel = snd_rme9652_playback_copy_kernel, + .copy = snd_rme9652_playback_copy, .fill_silence = snd_rme9652_hw_silence, }; @@ -2383,8 +2350,7 @@ static const struct snd_pcm_ops snd_rme9652_capture_ops = { .prepare = snd_rme9652_prepare, .trigger = snd_rme9652_trigger, .pointer = snd_rme9652_hw_pointer, - .copy_user = snd_rme9652_capture_copy, - .copy_kernel = snd_rme9652_capture_copy_kernel, + .copy = snd_rme9652_capture_copy, }; static int snd_rme9652_create_pcm(struct snd_card *card, diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 361b83fd721e..d8666ff7bdfa 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1984,11 +1984,7 @@ static int snd_via8233_init_misc(struct via82xx *chip) /* when no h/w PCM volume control is found, use DXS volume control * as the PCM vol control */ - struct snd_ctl_elem_id sid; - memset(&sid, 0, sizeof(sid)); - strcpy(sid.name, "PCM Playback Volume"); - sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - if (! snd_ctl_find_id(chip->card, &sid)) { + if (!snd_ctl_find_id_mixer(chip->card, "PCM Playback Volume")) { dev_info(chip->card->dev, "Using DXS as PCM Playback\n"); err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_pcmdxs_volume_control, chip)); diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 53d558b2806c..659866cfe3b4 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -11,6 +11,7 @@ #include <asm/nvram.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/of.h> #include <linux/slab.h> #include <sound/core.h> #include "pmac.h" diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c index 4fb990ab2ceb..400a886562b1 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -9,6 +9,7 @@ #include <linux/io.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/of.h> #include <sound/core.h> #include "pmac.h" #include "burgundy.h" diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index 8cf571955c9d..95ba3abd4e47 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -158,12 +158,12 @@ static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd) static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *src, unsigned long count) + struct iov_iter *src, unsigned long count) { /* channel is not used (interleaved data) */ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); - if (copy_from_user_toio(chip->data_buffer + pos, src, count)) + if (copy_from_iter_toio(chip->data_buffer + pos, src, count)) return -EFAULT; chip->buffer_end = chip->data_buffer + pos + count; @@ -175,24 +175,6 @@ static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, return 0; } -static int snd_sh_dac_pcm_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void *src, unsigned long count) -{ - /* channel is not used (interleaved data) */ - struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); - - memcpy_toio(chip->data_buffer + pos, src, count); - chip->buffer_end = chip->data_buffer + pos + count; - - if (chip->empty) { - chip->empty = 0; - dac_audio_start_timer(chip); - } - - return 0; -} - static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream, int channel, unsigned long pos, unsigned long count) @@ -227,8 +209,7 @@ static const struct snd_pcm_ops snd_sh_dac_pcm_ops = { .prepare = snd_sh_dac_pcm_prepare, .trigger = snd_sh_dac_pcm_trigger, .pointer = snd_sh_dac_pcm_pointer, - .copy_user = snd_sh_dac_pcm_copy, - .copy_kernel = snd_sh_dac_pcm_copy_kernel, + .copy = snd_sh_dac_pcm_copy, .fill_silence = snd_sh_dac_pcm_silence, .mmap = snd_pcm_lib_mmap_iomem, }; diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index bfa9622e1ab1..439fa631c342 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -38,6 +38,17 @@ config SND_SOC_TOPOLOGY bool select SND_DYNAMIC_MINORS +config SND_SOC_TOPOLOGY_BUILD + bool "Build topology core" + select SND_SOC_TOPOLOGY + depends on KUNIT + help + This option exists to facilitate running the KUnit tests for + the topology core, KUnit is frequently tested in virtual + environments with minimal drivers enabled but the topology + core is usually selected by drivers. There is little reason + to enable it if not doing a KUnit build. + config SND_SOC_TOPOLOGY_KUNIT_TEST tristate "KUnit tests for SoC topology" depends on KUNIT diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c index d5b6f5187f8e..7b2563075743 100644 --- a/sound/soc/adi/axi-i2s.c +++ b/sound/soc/adi/axi-i2s.c @@ -147,6 +147,7 @@ static int axi_i2s_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops axi_i2s_dai_ops = { + .probe = axi_i2s_dai_probe, .startup = axi_i2s_startup, .shutdown = axi_i2s_shutdown, .trigger = axi_i2s_trigger, @@ -154,7 +155,6 @@ static const struct snd_soc_dai_ops axi_i2s_dai_ops = { }; static struct snd_soc_dai_driver axi_i2s_dai = { - .probe = axi_i2s_dai_probe, .ops = &axi_i2s_dai_ops, .symmetric_rate = 1, }; diff --git a/sound/soc/adi/axi-spdif.c b/sound/soc/adi/axi-spdif.c index e4c99bbc9cdd..10545bd99704 100644 --- a/sound/soc/adi/axi-spdif.c +++ b/sound/soc/adi/axi-spdif.c @@ -148,6 +148,7 @@ static void axi_spdif_shutdown(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops axi_spdif_dai_ops = { + .probe = axi_spdif_dai_probe, .startup = axi_spdif_startup, .shutdown = axi_spdif_shutdown, .trigger = axi_spdif_trigger, @@ -155,7 +156,6 @@ static const struct snd_soc_dai_ops axi_spdif_dai_ops = { }; static struct snd_soc_dai_driver axi_spdif_dai = { - .probe = axi_spdif_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 1dd8579e8034..273688c05317 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -79,14 +79,15 @@ config SND_SOC_AMD_ACP5x ACP DMA driver, CPU DAI driver. config SND_SOC_AMD_VANGOGH_MACH - tristate "AMD Vangogh support for NAU8821 CS35L41" + tristate "AMD Vangogh support for NAU8821/CS35L41/MAX98388" select SND_SOC_NAU8821 select SND_SOC_CS35L41_SPI + select SND_SOC_MAX98388 select SND_AMD_ACP_CONFIG depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER help This option enables machine driver for Vangogh platform - using NAU8821 and CS35L41 codecs. + using NAU8821 and either CS35L41 or MAX98388 codecs. Say m if you have such a device. If unsure select "N". diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 0932473b6394..f27c27580009 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -47,6 +47,20 @@ static const struct config_entry config_table[] = { {} }, }, + { + .flags = FLAG_AMD_SOF, + .device = ACP_PCI_DEV_ID, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Valve"), + DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"), + }, + }, + {} + }, + }, }; int snd_amd_acp_find_config(struct pci_dev *pci) @@ -82,6 +96,11 @@ static struct snd_soc_acpi_codecs amp_max = { .codecs = {"MX98360A"} }; +static struct snd_soc_acpi_codecs amp_max98388 = { + .num_codecs = 1, + .codecs = {"ADS8388"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { { .id = "10EC5682", @@ -130,6 +149,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { }; EXPORT_SYMBOL(snd_soc_acpi_amd_sof_machines); +struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[] = { + { + .id = "NVTN2020", + .drv_name = "nau8821-max", + .pdata = &acp_quirk_data, + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_max98388, + .fw_filename = "sof-vangogh.ri", + .sof_tplg_filename = "sof-vangogh-nau8821-max.tplg", + }, + {}, +}; +EXPORT_SYMBOL(snd_soc_acpi_amd_vangogh_sof_machines); + struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_sof_machines[] = { { .id = "AMDI1019", diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 7464ca2b596c..9e3133bec2b1 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -28,6 +28,21 @@ #define RT5682_PLL_FREQ (48000 * 512) static struct snd_soc_jack cz_jack; +static struct snd_soc_jack_pin cz_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, +}; + static struct clk *da7219_dai_wclk; static struct clk *da7219_dai_bclk; static struct clk *rt5682_dai_wclk; @@ -66,11 +81,13 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) if (IS_ERR(da7219_dai_bclk)) return PTR_ERR(da7219_dai_bclk); - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &cz_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &cz_jack, + cz_jack_pins, + ARRAY_SIZE(cz_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; @@ -146,11 +163,13 @@ static int cz_rt5682_init(struct snd_soc_pcm_runtime *rtd) if (IS_ERR(rt5682_dai_bclk)) return PTR_ERR(rt5682_dai_bclk); - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &cz_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &cz_jack, + cz_jack_pins, + ARRAY_SIZE(cz_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; @@ -631,6 +650,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { static const struct snd_soc_dapm_widget cz_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), }; @@ -654,6 +674,7 @@ static const struct snd_soc_dapm_route cz_rt5682_audio_route[] = { static const struct snd_kcontrol_new cz_mc_controls[] = { SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("Speakers"), + SOC_DAPM_PIN_SWITCH("Line Out"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Int Mic"), }; diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c index 532aa98a2241..c8ed1e0b1ccd 100644 --- a/sound/soc/amd/acp-rt5645.c +++ b/sound/soc/amd/acp-rt5645.c @@ -42,6 +42,16 @@ #define CZ_PLAT_CLK 24000000 static struct snd_soc_jack cz_jack; +static struct snd_soc_jack_pin cz_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; static int cz_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -76,11 +86,13 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd) codec = asoc_rtd_to_codec(rtd, 0)->component; card = rtd->card; - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &cz_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &cz_jack, + cz_jack_pins, + ARRAY_SIZE(cz_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index ce0037810743..631cdf96d637 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -18,6 +18,9 @@ if SND_SOC_AMD_ACP_COMMON config SND_SOC_AMD_ACP_PDM tristate +config SND_SOC_AMD_ACP_LEGACY_COMMON + tristate + config SND_SOC_AMD_ACP_I2S tristate @@ -27,6 +30,7 @@ config SND_SOC_AMD_ACP_PCM config SND_SOC_AMD_ACP_PCI tristate "AMD ACP PCI Driver Support" + select SND_SOC_AMD_ACP_LEGACY_COMMON depends on X86 && PCI help This options enables generic PCI driver for ACP device. @@ -36,6 +40,7 @@ config SND_AMD_ASOC_RENOIR select SND_SOC_AMD_ACP_PCM select SND_SOC_AMD_ACP_I2S select SND_SOC_AMD_ACP_PDM + select SND_SOC_AMD_ACP_LEGACY_COMMON depends on X86 && PCI help This option enables Renoir I2S support on AMD platform. @@ -45,6 +50,7 @@ config SND_AMD_ASOC_REMBRANDT select SND_SOC_AMD_ACP_PCM select SND_SOC_AMD_ACP_I2S select SND_SOC_AMD_ACP_PDM + select SND_SOC_AMD_ACP_LEGACY_COMMON depends on X86 && PCI help This option enables Rembrandt I2S support on AMD platform. @@ -61,6 +67,8 @@ config SND_SOC_AMD_MACH_COMMON select SND_SOC_MAX98357A select SND_SOC_RT5682S select SND_SOC_NAU8825 + select SND_SOC_NAU8821 + select SND_SOC_MAX98388 help This option enables common Machine driver module for ACP. diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index d9abb0ee5218..4e65fdbc8dca 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -8,6 +8,7 @@ snd-acp-pcm-objs := acp-platform.o snd-acp-i2s-objs := acp-i2s.o snd-acp-pdm-objs := acp-pdm.o +snd-acp-legacy-common-objs := acp-legacy-common.o snd-acp-pci-objs := acp-pci.o #platform specific driver @@ -22,6 +23,7 @@ snd-acp-sof-mach-objs := acp-sof-mach.o obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o +obj-$(CONFIG_SND_SOC_AMD_ACP_LEGACY_COMMON) += snd-acp-legacy-common.o obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 09b6511c0a26..df350014966a 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -149,6 +149,7 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_ dev_err(dev, "Invalid dai id %x\n", dai->driver->id); return -EINVAL; } + adata->xfer_tx_resolution[dai->driver->id - 1] = xfer_resolution; } else { switch (dai->driver->id) { case I2S_BT_INSTANCE: @@ -167,6 +168,7 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_ dev_err(dev, "Invalid dai id %x\n", dai->driver->id); return -EINVAL; } + adata->xfer_rx_resolution[dai->driver->id - 1] = xfer_resolution; } val = readl(adata->acp_base + reg_val); @@ -537,17 +539,7 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d return 0; } -const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { - .startup = acp_i2s_startup, - .hw_params = acp_i2s_hwparams, - .prepare = acp_i2s_prepare, - .trigger = acp_i2s_trigger, - .set_fmt = acp_i2s_set_fmt, - .set_tdm_slot = acp_i2s_set_tdm_slot, -}; -EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON); - -int asoc_acp_i2s_probe(struct snd_soc_dai *dai) +static int acp_i2s_probe(struct snd_soc_dai *dai) { struct device *dev = dai->component->dev; struct acp_dev_data *adata = dev_get_drvdata(dev); @@ -567,7 +559,17 @@ int asoc_acp_i2s_probe(struct snd_soc_dai *dai) return 0; } -EXPORT_SYMBOL_NS_GPL(asoc_acp_i2s_probe, SND_SOC_ACP_COMMON); + +const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { + .probe = acp_i2s_probe, + .startup = acp_i2s_startup, + .hw_params = acp_i2s_hwparams, + .prepare = acp_i2s_prepare, + .trigger = acp_i2s_trigger, + .set_fmt = acp_i2s_set_fmt, + .set_tdm_slot = acp_i2s_set_tdm_slot, +}; +EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c new file mode 100644 index 000000000000..ba58165cc6e6 --- /dev/null +++ b/sound/soc/amd/acp/acp-legacy-common.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Advanced Micro Devices, Inc. +// +// Authors: Syed Saba Kareem <Syed.SabaKareem@amd.com> +// + +/* + * Common file to be used by amd platforms + */ + +#include "amd.h" +#include <linux/pci.h> +#include <linux/export.h> + +void acp_enable_interrupts(struct acp_dev_data *adata) +{ + struct acp_resource *rsrc = adata->rsrc; + u32 ext_intr_ctrl; + + writel(0x01, ACP_EXTERNAL_INTR_ENB(adata)); + ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); + ext_intr_ctrl |= ACP_ERROR_MASK; + writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); +} +EXPORT_SYMBOL_NS_GPL(acp_enable_interrupts, SND_SOC_ACP_COMMON); + +void acp_disable_interrupts(struct acp_dev_data *adata) +{ + struct acp_resource *rsrc = adata->rsrc; + + writel(ACP_EXT_INTR_STAT_CLEAR_MASK, ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); + writel(0x00, ACP_EXTERNAL_INTR_ENB(adata)); +} +EXPORT_SYMBOL_NS_GPL(acp_disable_interrupts, SND_SOC_ACP_COMMON); + +static void set_acp_pdm_ring_buffer(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct acp_stream *stream = runtime->private_data; + struct device *dev = dai->component->dev; + struct acp_dev_data *adata = dev_get_drvdata(dev); + + u32 physical_addr, pdm_size, period_bytes; + + period_bytes = frames_to_bytes(runtime, runtime->period_size); + pdm_size = frames_to_bytes(runtime, runtime->buffer_size); + physical_addr = stream->reg_offset + MEM_WINDOW_START; + + /* Init ACP PDM Ring buffer */ + writel(physical_addr, adata->acp_base + ACP_WOV_RX_RINGBUFADDR); + writel(pdm_size, adata->acp_base + ACP_WOV_RX_RINGBUFSIZE); + writel(period_bytes, adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE); + writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL); +} + +static void set_acp_pdm_clk(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct device *dev = dai->component->dev; + struct acp_dev_data *adata = dev_get_drvdata(dev); + unsigned int pdm_ctrl; + + /* Enable default ACP PDM clk */ + writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL); + pdm_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL); + pdm_ctrl |= PDM_MISC_CTRL_MASK; + writel(pdm_ctrl, adata->acp_base + ACP_WOV_MISC_CTRL); + set_acp_pdm_ring_buffer(substream, dai); +} + +void restore_acp_pdm_params(struct snd_pcm_substream *substream, + struct acp_dev_data *adata) +{ + struct snd_soc_dai *dai; + struct snd_soc_pcm_runtime *soc_runtime; + u32 ext_int_ctrl; + + soc_runtime = asoc_substream_to_rtd(substream); + dai = asoc_rtd_to_cpu(soc_runtime, 0); + /* Programming channel mask and sampling rate */ + writel(adata->ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS); + writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR); + + /* Enabling ACP Pdm interuppts */ + ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0)); + ext_int_ctrl |= PDM_DMA_INTR_MASK; + writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0)); + set_acp_pdm_clk(substream, dai); +} +EXPORT_SYMBOL_NS_GPL(restore_acp_pdm_params, SND_SOC_ACP_COMMON); + +static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct device *dev = dai->component->dev; + struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_resource *rsrc = adata->rsrc; + struct acp_stream *stream = substream->runtime->private_data; + u32 reg_dma_size, reg_fifo_size, reg_fifo_addr; + u32 phy_addr, acp_fifo_addr, ext_int_ctrl; + unsigned int dir = substream->stream; + + switch (dai->driver->id) { + case I2S_SP_INSTANCE: + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + reg_dma_size = ACP_I2S_TX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + SP_PB_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_I2S_TX_FIFOADDR; + reg_fifo_size = ACP_I2S_TX_FIFOSIZE; + phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR); + } else { + reg_dma_size = ACP_I2S_RX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + SP_CAPT_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_I2S_RX_FIFOADDR; + reg_fifo_size = ACP_I2S_RX_FIFOSIZE; + phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR); + } + break; + case I2S_BT_INSTANCE: + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + reg_dma_size = ACP_BT_TX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + BT_PB_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_BT_TX_FIFOADDR; + reg_fifo_size = ACP_BT_TX_FIFOSIZE; + phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR); + } else { + reg_dma_size = ACP_BT_RX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + BT_CAPT_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_BT_RX_FIFOADDR; + reg_fifo_size = ACP_BT_RX_FIFOSIZE; + phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR); + } + break; + case I2S_HS_INSTANCE: + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + reg_dma_size = ACP_HS_TX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + HS_PB_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_HS_TX_FIFOADDR; + reg_fifo_size = ACP_HS_TX_FIFOSIZE; + phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_HS_TX_RINGBUFADDR); + } else { + reg_dma_size = ACP_HS_RX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + HS_CAPT_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_HS_RX_FIFOADDR; + reg_fifo_size = ACP_HS_RX_FIFOSIZE; + phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_HS_RX_RINGBUFADDR); + } + break; + default: + dev_err(dev, "Invalid dai id %x\n", dai->driver->id); + return -EINVAL; + } + + writel(DMA_SIZE, adata->acp_base + reg_dma_size); + writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr); + writel(FIFO_SIZE, adata->acp_base + reg_fifo_size); + + ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); + ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) | + BIT(BT_RX_THRESHOLD(rsrc->offset)) | + BIT(I2S_TX_THRESHOLD(rsrc->offset)) | + BIT(BT_TX_THRESHOLD(rsrc->offset)) | + BIT(HS_RX_THRESHOLD(rsrc->offset)) | + BIT(HS_TX_THRESHOLD(rsrc->offset)); + + writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); + return 0; +} + +int restore_acp_i2s_params(struct snd_pcm_substream *substream, + struct acp_dev_data *adata, + struct acp_stream *stream) +{ + struct snd_soc_dai *dai; + struct snd_soc_pcm_runtime *soc_runtime; + u32 tdm_fmt, reg_val, fmt_reg, val; + + soc_runtime = asoc_substream_to_rtd(substream); + dai = asoc_rtd_to_cpu(soc_runtime, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tdm_fmt = adata->tdm_tx_fmt[stream->dai_id - 1]; + switch (stream->dai_id) { + case I2S_BT_INSTANCE: + reg_val = ACP_BTTDM_ITER; + fmt_reg = ACP_BTTDM_TXFRMT; + break; + case I2S_SP_INSTANCE: + reg_val = ACP_I2STDM_ITER; + fmt_reg = ACP_I2STDM_TXFRMT; + break; + case I2S_HS_INSTANCE: + reg_val = ACP_HSTDM_ITER; + fmt_reg = ACP_HSTDM_TXFRMT; + break; + default: + pr_err("Invalid dai id %x\n", stream->dai_id); + return -EINVAL; + } + val = adata->xfer_tx_resolution[stream->dai_id - 1] << 3; + } else { + tdm_fmt = adata->tdm_rx_fmt[stream->dai_id - 1]; + switch (stream->dai_id) { + case I2S_BT_INSTANCE: + reg_val = ACP_BTTDM_IRER; + fmt_reg = ACP_BTTDM_RXFRMT; + break; + case I2S_SP_INSTANCE: + reg_val = ACP_I2STDM_IRER; + fmt_reg = ACP_I2STDM_RXFRMT; + break; + case I2S_HS_INSTANCE: + reg_val = ACP_HSTDM_IRER; + fmt_reg = ACP_HSTDM_RXFRMT; + break; + default: + pr_err("Invalid dai id %x\n", stream->dai_id); + return -EINVAL; + } + val = adata->xfer_rx_resolution[stream->dai_id - 1] << 3; + } + writel(val, adata->acp_base + reg_val); + if (adata->tdm_mode == TDM_ENABLE) { + writel(tdm_fmt, adata->acp_base + fmt_reg); + val = readl(adata->acp_base + reg_val); + writel(val | 0x2, adata->acp_base + reg_val); + } + return set_acp_i2s_dma_fifo(substream, dai); +} +EXPORT_SYMBOL_NS_GPL(restore_acp_i2s_params, SND_SOC_ACP_COMMON); + +static int acp_power_on(struct acp_chip_info *chip) +{ + u32 val, acp_pgfsm_stat_reg, acp_pgfsm_ctrl_reg; + void __iomem *base; + + base = chip->base; + switch (chip->acp_rev) { + case ACP3X_DEV: + acp_pgfsm_stat_reg = ACP_PGFSM_STATUS; + acp_pgfsm_ctrl_reg = ACP_PGFSM_CONTROL; + break; + case ACP6X_DEV: + acp_pgfsm_stat_reg = ACP6X_PGFSM_STATUS; + acp_pgfsm_ctrl_reg = ACP6X_PGFSM_CONTROL; + break; + default: + return -EINVAL; + } + + val = readl(base + acp_pgfsm_stat_reg); + if (val == ACP_POWERED_ON) + return 0; + + if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS) + writel(ACP_PGFSM_CNTL_POWER_ON_MASK, base + acp_pgfsm_ctrl_reg); + + return readl_poll_timeout(base + acp_pgfsm_stat_reg, val, + !val, DELAY_US, ACP_TIMEOUT); +} + +static int acp_reset(void __iomem *base) +{ + u32 val; + int ret; + + writel(1, base + ACP_SOFT_RESET); + ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK, + DELAY_US, ACP_TIMEOUT); + if (ret) + return ret; + + writel(0, base + ACP_SOFT_RESET); + return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT); +} + +int acp_init(struct acp_chip_info *chip) +{ + int ret; + + /* power on */ + ret = acp_power_on(chip); + if (ret) { + pr_err("ACP power on failed\n"); + return ret; + } + writel(0x01, chip->base + ACP_CONTROL); + + /* Reset */ + ret = acp_reset(chip->base); + if (ret) { + pr_err("ACP reset failed\n"); + return ret; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(acp_init, SND_SOC_ACP_COMMON); + +int acp_deinit(void __iomem *base) +{ + int ret; + + /* Reset */ + ret = acp_reset(base); + if (ret) + return ret; + + writel(0, base + ACP_CONTROL); + return 0; +} +EXPORT_SYMBOL_NS_GPL(acp_deinit, SND_SOC_ACP_COMMON); + +int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data) +{ + pci_write_config_dword(dev, 0x60, smn_addr); + pci_write_config_dword(dev, 0x64, data); + return 0; +} +EXPORT_SYMBOL_NS_GPL(smn_write, SND_SOC_ACP_COMMON); + +int smn_read(struct pci_dev *dev, u32 smn_addr) +{ + u32 data; + + pci_write_config_dword(dev, 0x60, smn_addr); + pci_read_config_dword(dev, 0x64, &data); + return data; +} +EXPORT_SYMBOL_NS_GPL(smn_read, SND_SOC_ACP_COMMON); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index 676ad50638d0..6d57d17ddfd7 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -75,23 +75,6 @@ static struct acp_card_drvdata rt5682s_rt1019_rmb_data = { .tdm_mode = false, }; -static const struct snd_kcontrol_new acp_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone Jack"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Spk"), - SOC_DAPM_PIN_SWITCH("Left Spk"), - SOC_DAPM_PIN_SWITCH("Right Spk"), - -}; - -static const struct snd_soc_dapm_widget acp_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_SPK("Spk", NULL), - SND_SOC_DAPM_SPK("Left Spk", NULL), - SND_SOC_DAPM_SPK("Right Spk", NULL), -}; - static int acp_asoc_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; @@ -110,11 +93,8 @@ static int acp_asoc_probe(struct platform_device *pdev) card->dev = dev; card->owner = THIS_MODULE; card->name = pdev->id_entry->name; - card->dapm_widgets = acp_widgets; - card->num_dapm_widgets = ARRAY_SIZE(acp_widgets); - card->controls = acp_controls; - card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + /* Widgets and controls added per-codec in acp-mach-common.c */ acp_card_drvdata = card->drvdata; dmi_id = dmi_first_match(acp_quirk_table); diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index 6da17140beea..a06af82b8056 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -25,12 +25,17 @@ #include "../../codecs/rt1019.h" #include "../../codecs/rt5682s.h" #include "../../codecs/nau8825.h" +#include "../../codecs/nau8821.h" #include "acp-mach.h" #define PCO_PLAT_CLK 48000000 #define RT5682_PLL_FREQ (48000 * 512) #define DUAL_CHANNEL 2 #define FOUR_CHANNEL 4 +#define NAU8821_CODEC_DAI "nau8821-hifi" +#define NAU8821_BCLK 1536000 +#define NAU8821_FREQ_OUT 12288000 +#define MAX98388_CODEC_DAI "max98388-aif1" #define TDM_MODE_ENABLE 1 @@ -46,8 +51,6 @@ const struct dmi_system_id acp_quirk_table[] = { }; EXPORT_SYMBOL_GPL(acp_quirk_table); -static struct snd_soc_jack pco_jack; - static const unsigned int channels[] = { DUAL_CHANNEL, }; @@ -81,6 +84,28 @@ static int acp_clk_enable(struct acp_card_drvdata *drvdata, SND_SOC_DAILINK_DEF(rt5682, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1"))); +static struct snd_soc_jack rt5682_jack; +static struct snd_soc_jack_pin rt5682_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_kcontrol_new rt5682_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static const struct snd_soc_dapm_widget rt5682_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + static const struct snd_soc_dapm_route rt5682_map[] = { { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, @@ -104,22 +129,38 @@ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd) drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk"); drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk"); - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack); + ret = snd_soc_dapm_new_controls(&card->dapm, rt5682_widgets, + ARRAY_SIZE(rt5682_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, rt5682_controls, + ARRAY_SIZE(rt5682_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &rt5682_jack, + rt5682_jack_pins, + ARRAY_SIZE(rt5682_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; } - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - ret = snd_soc_component_set_jack(component, &pco_jack, NULL); + ret = snd_soc_component_set_jack(component, &rt5682_jack, NULL); if (ret) { dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); return ret; @@ -269,6 +310,28 @@ static const struct snd_soc_ops acp_card_rt5682_ops = { SND_SOC_DAILINK_DEF(rt5682s, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RTL5682:00", "rt5682s-aif1"))); +static struct snd_soc_jack rt5682s_jack; +static struct snd_soc_jack_pin rt5682s_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_kcontrol_new rt5682s_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static const struct snd_soc_dapm_widget rt5682s_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + static const struct snd_soc_dapm_route rt5682s_map[] = { { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, @@ -293,22 +356,38 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd) drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk"); } - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack); + ret = snd_soc_dapm_new_controls(&card->dapm, rt5682s_widgets, + ARRAY_SIZE(rt5682s_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, rt5682s_controls, + ARRAY_SIZE(rt5682s_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &rt5682s_jack, + rt5682s_jack_pins, + ARRAY_SIZE(rt5682s_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; } - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - ret = snd_soc_component_set_jack(component, &pco_jack, NULL); + ret = snd_soc_component_set_jack(component, &rt5682s_jack, NULL); if (ret) { dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); return ret; @@ -434,6 +513,16 @@ SND_SOC_DAILINK_DEF(rt1019, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"), COMP_CODEC("i2c-10EC1019:01", "rt1019-aif"))); +static const struct snd_kcontrol_new rt1019_controls[] = { + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_soc_dapm_widget rt1019_widgets[] = { + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), +}; + static const struct snd_soc_dapm_route rt1019_map_lr[] = { { "Left Spk", NULL, "Left SPO" }, { "Right Spk", NULL, "Right SPO" }, @@ -454,10 +543,25 @@ static int acp_card_rt1019_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; + int ret; if (drvdata->amp_codec_id != RT1019) return -EINVAL; + ret = snd_soc_dapm_new_controls(&card->dapm, rt1019_widgets, + ARRAY_SIZE(rt1019_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, rt1019_controls, + ARRAY_SIZE(rt1019_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + return snd_soc_dapm_add_routes(&rtd->card->dapm, rt1019_map_lr, ARRAY_SIZE(rt1019_map_lr)); } @@ -588,6 +692,14 @@ static const struct snd_soc_ops acp_card_rt1019_ops = { SND_SOC_DAILINK_DEF(max98360a, DAILINK_COMP_ARRAY(COMP_CODEC("MX98360A:00", "HiFi"))); +static const struct snd_kcontrol_new max98360a_controls[] = { + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +static const struct snd_soc_dapm_widget max98360a_widgets[] = { + SND_SOC_DAPM_SPK("Spk", NULL), +}; + static const struct snd_soc_dapm_route max98360a_map[] = { {"Spk", NULL, "Speaker"}, }; @@ -596,10 +708,25 @@ static int acp_card_maxim_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; + int ret; if (drvdata->amp_codec_id != MAX98360A) return -EINVAL; + ret = snd_soc_dapm_new_controls(&card->dapm, max98360a_widgets, + ARRAY_SIZE(max98360a_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, max98360a_controls, + ARRAY_SIZE(max98360a_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + return snd_soc_dapm_add_routes(&rtd->card->dapm, max98360a_map, ARRAY_SIZE(max98360a_map)); } @@ -661,10 +788,137 @@ static const struct snd_soc_ops acp_card_maxim_ops = { .hw_params = acp_card_maxim_hw_params, }; +SND_SOC_DAILINK_DEF(max98388, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", "max98388-aif1"), + COMP_CODEC("i2c-ADS8388:01", "max98388-aif1"))); + +static const struct snd_kcontrol_new max98388_controls[] = { + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_soc_dapm_widget max98388_widgets[] = { + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), +}; + +static const struct snd_soc_dapm_route max98388_map[] = { + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, +}; + +static struct snd_soc_codec_conf max98388_conf[] = { + { + .dlc = COMP_CODEC_CONF("i2c-ADS8388:00"), + .name_prefix = "Left", + }, + { + .dlc = COMP_CODEC_CONF("i2c-ADS8388:01"), + .name_prefix = "Right", + }, +}; + +static const unsigned int max98388_format[] = {16}; + +static struct snd_pcm_hw_constraint_list constraints_sample_bits_max = { + .list = max98388_format, + .count = ARRAY_SIZE(max98388_format), +}; + +static int acp_card_max98388_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &constraints_sample_bits_max); + + return 0; +} + +static int acp_card_max98388_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct acp_card_drvdata *drvdata = card->drvdata; + int ret; + + if (drvdata->amp_codec_id != MAX98388) + return -EINVAL; + + ret = snd_soc_dapm_new_controls(&card->dapm, max98388_widgets, + ARRAY_SIZE(max98388_widgets)); + + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, max98388_controls, + ARRAY_SIZE(max98388_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + return snd_soc_dapm_add_routes(&rtd->card->dapm, max98388_map, + ARRAY_SIZE(max98388_map)); +} + +static int acp_max98388_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = + snd_soc_card_get_codec_dai(card, + MAX98388_CODEC_DAI); + int ret; + + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF); + if (ret < 0) + return ret; + + return ret; +} + +static const struct snd_soc_ops acp_max98388_ops = { + .startup = acp_card_max98388_startup, + .hw_params = acp_max98388_hw_params, +}; + /* Declare nau8825 codec components */ SND_SOC_DAILINK_DEF(nau8825, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508825:00", "nau8825-hifi"))); +static struct snd_soc_jack nau8825_jack; +static struct snd_soc_jack_pin nau8825_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_kcontrol_new nau8825_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static const struct snd_soc_dapm_widget nau8825_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + static const struct snd_soc_dapm_route nau8825_map[] = { { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, @@ -683,22 +937,38 @@ static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd) if (drvdata->hs_codec_id != NAU8825) return -EINVAL; - ret = snd_soc_card_jack_new(card, "Headset Jack", + ret = snd_soc_dapm_new_controls(&card->dapm, nau8825_widgets, + ARRAY_SIZE(nau8825_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, nau8825_controls, + ARRAY_SIZE(nau8825_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack); + &nau8825_jack, + nau8825_jack_pins, + ARRAY_SIZE(nau8825_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; } - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - ret = snd_soc_component_set_jack(component, &pco_jack, NULL); + ret = snd_soc_component_set_jack(component, &nau8825_jack, NULL); if (ret) { dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); return ret; @@ -790,6 +1060,189 @@ static const struct snd_soc_ops acp_card_nau8825_ops = { .hw_params = acp_nau8825_hw_params, }; +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + int ret = 0; + + codec_dai = snd_soc_card_get_codec_dai(card, NAU8821_CODEC_DAI); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_INTERNAL, + 0, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "set sysclk err = %d\n", ret); + return -EIO; + } + } else { + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(codec_dai->dev, "can't set FS clock %d\n", ret); + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, NAU8821_BCLK, + NAU8821_FREQ_OUT); + if (ret < 0) + dev_err(codec_dai->dev, "can't set FLL: %d\n", ret); + } + return ret; +} + +static struct snd_soc_jack nau8821_jack; +static struct snd_soc_jack_pin nau8821_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_kcontrol_new nau8821_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static const struct snd_soc_dapm_widget nau8821_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route nau8821_audio_route[] = { + /* HP jack connectors - unknown if we have jack detection */ + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + { "MICL", NULL, "Headset Mic" }, + { "MICR", NULL, "Headset Mic" }, + { "DMIC", NULL, "Int Mic" }, + { "Headphone Jack", NULL, "Platform Clock" }, + { "Headset Mic", NULL, "Platform Clock" }, + { "Int Mic", NULL, "Platform Clock" }, +}; + +static const unsigned int nau8821_format[] = {16}; + +static struct snd_pcm_hw_constraint_list constraints_sample_bits = { + .list = nau8821_format, + .count = ARRAY_SIZE(nau8821_format), +}; + +static int acp_8821_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + int ret; + + dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name); + + ret = snd_soc_dapm_new_controls(&card->dapm, nau8821_widgets, + ARRAY_SIZE(nau8821_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + // Don't need to add routes if widget addition failed + return ret; + } + + ret = snd_soc_add_card_controls(card, nau8821_controls, + ARRAY_SIZE(nau8821_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &nau8821_jack, + nau8821_jack_pins, + ARRAY_SIZE(nau8821_jack_pins)); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); + return ret; + } + + snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + + nau8821_enable_jack_detect(component, &nau8821_jack); + + return snd_soc_dapm_add_routes(&rtd->card->dapm, nau8821_audio_route, + ARRAY_SIZE(nau8821_audio_route)); +} + +static int acp_8821_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &constraints_sample_bits); + return 0; +} + +static int acp_nau8821_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct acp_card_drvdata *drvdata = card->drvdata; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + int ret; + unsigned int fmt; + + if (drvdata->soc_mclk) + fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + else + fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret < 0) { + dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(card->dev, "can't set FS clock %d\n", ret); + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, snd_soc_params_to_bclk(params), + params_rate(params) * 256); + if (ret < 0) + dev_err(card->dev, "can't set FLL: %d\n", ret); + + return ret; +} + +static const struct snd_soc_ops acp_8821_ops = { + .startup = acp_8821_startup, + .hw_params = acp_nau8821_hw_params, +}; + +SND_SOC_DAILINK_DEF(nau8821, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00", + "nau8821-hifi"))); + /* Declare DMIC codec components */ SND_SOC_DAILINK_DEF(dmic_codec, DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); @@ -920,6 +1373,12 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) links[i].init = acp_card_rt5682s_init; links[i].ops = &acp_card_rt5682s_ops; } + if (drv_data->hs_codec_id == NAU8821) { + links[i].codecs = nau8821; + links[i].num_codecs = ARRAY_SIZE(nau8821); + links[i].init = acp_8821_init; + links[i].ops = &acp_8821_ops; + } i++; } @@ -1007,6 +1466,14 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) links[i].ops = &acp_card_maxim_ops; links[i].init = acp_card_maxim_init; } + if (drv_data->amp_codec_id == MAX98388) { + links[i].codecs = max98388; + links[i].num_codecs = ARRAY_SIZE(max98388); + links[i].ops = &acp_max98388_ops; + links[i].init = acp_card_max98388_init; + card->codec_conf = max98388_conf; + card->num_configs = ARRAY_SIZE(max98388_conf); + } if (drv_data->amp_codec_id == RT1019) { links[i].codecs = rt1019; links[i].num_codecs = ARRAY_SIZE(rt1019); diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index 165f407697c0..2b3ec6594023 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -41,6 +41,8 @@ enum codec_endpoints { MAX98360A, RT5682S, NAU8825, + NAU8821, + MAX98388, }; enum platform_end_point { diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index 8154fbfd1229..a32c14a109b7 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -16,6 +16,7 @@ #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #include "amd.h" #include "../mach-config.h" @@ -106,6 +107,7 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id goto unregister_dmic_dev; } + acp_init(chip); res = devm_kcalloc(&pci->dev, num_res, sizeof(struct resource), GFP_KERNEL); if (!res) { ret = -ENOMEM; @@ -139,7 +141,12 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id ret = PTR_ERR(pdev); goto unregister_dmic_dev; } - + chip->chip_pdev = pdev; + dev_set_drvdata(&pci->dev, chip); + pm_runtime_set_autosuspend_delay(&pci->dev, 2000); + pm_runtime_use_autosuspend(&pci->dev); + pm_runtime_put_noidle(&pci->dev); + pm_runtime_allow(&pci->dev); return ret; unregister_dmic_dev: @@ -152,12 +159,56 @@ disable_pci: return ret; }; +static int __maybe_unused snd_acp_suspend(struct device *dev) +{ + struct acp_chip_info *chip; + int ret; + + chip = dev_get_drvdata(dev); + ret = acp_deinit(chip->base); + if (ret) + dev_err(dev, "ACP de-init failed\n"); + return ret; +} + +static int __maybe_unused snd_acp_resume(struct device *dev) +{ + struct acp_chip_info *chip; + struct acp_dev_data *adata; + struct device child; + int ret; + + chip = dev_get_drvdata(dev); + ret = acp_init(chip); + if (ret) + dev_err(dev, "ACP init failed\n"); + child = chip->chip_pdev->dev; + adata = dev_get_drvdata(&child); + if (adata) + acp_enable_interrupts(adata); + return ret; +} + +static const struct dev_pm_ops acp_pm_ops = { + SET_RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume) +}; + static void acp_pci_remove(struct pci_dev *pci) { + struct acp_chip_info *chip; + int ret; + + chip = pci_get_drvdata(pci); + pm_runtime_forbid(&pci->dev); + pm_runtime_get_noresume(&pci->dev); if (dmic_dev) platform_device_unregister(dmic_dev); if (pdev) platform_device_unregister(pdev); + ret = acp_deinit(chip->base); + if (ret) + dev_err(&pci->dev, "ACP de-init failed\n"); } /* PCI IDs */ @@ -173,8 +224,12 @@ static struct pci_driver snd_amd_acp_pci_driver = { .id_table = acp_pci_ids, .probe = acp_pci_probe, .remove = acp_pci_remove, + .driver = { + .pm = &acp_pm_ops, + }, }; module_pci_driver(snd_amd_acp_pci_driver); MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c index f8030b79ac17..f754bf79b5e3 100644 --- a/sound/soc/amd/acp/acp-pdm.c +++ b/sound/soc/amd/acp/acp-pdm.c @@ -25,18 +25,6 @@ #define DRV_NAME "acp-pdm" -#define PDM_DMA_STAT 0x10 -#define PDM_DMA_INTR_MASK 0x10000 -#define PDM_DEC_64 0x2 -#define PDM_CLK_FREQ_MASK 0x07 -#define PDM_MISC_CTRL_MASK 0x10 -#define PDM_ENABLE 0x01 -#define PDM_DISABLE 0x00 -#define DMA_EN_MASK 0x02 -#define DELAY_US 5 -#define PDM_TIMEOUT 1000 -#define ACP_REGION2_OFFSET 0x02000000 - static int acp_dmic_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -135,6 +123,7 @@ static int acp_dmic_hwparams(struct snd_pcm_substream *substream, return -EINVAL; } + adata->ch_mask = ch_mask; if (params_format(hwparams) != SNDRV_PCM_FORMAT_S32_LE) { dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams)); return -EINVAL; diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index f220378ec20e..f516daf6fef4 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -127,7 +127,7 @@ static irqreturn_t i2s_irq_handler(int irq, void *data) return IRQ_NONE; } -static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream) +void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream) { struct acp_resource *rsrc = adata->rsrc; u32 pte_reg, pte_size, reg_val; @@ -143,8 +143,9 @@ static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + pte_size); writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL); } +EXPORT_SYMBOL_NS_GPL(config_pte_for_stream, SND_SOC_ACP_COMMON); -static void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size) +void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size) { struct snd_pcm_substream *substream = stream->substream; struct acp_resource *rsrc = adata->rsrc; @@ -168,6 +169,7 @@ static void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream addr += PAGE_SIZE; } } +EXPORT_SYMBOL_NS_GPL(config_acp_dma, SND_SOC_ACP_COMMON); static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c index 1b997837c7d8..1bf7b2e68a11 100644 --- a/sound/soc/amd/acp/acp-rembrandt.c +++ b/sound/soc/amd/acp/acp-rembrandt.c @@ -19,30 +19,17 @@ #include <sound/soc.h> #include <sound/soc-dai.h> #include <linux/dma-mapping.h> +#include <linux/pci.h> +#include <linux/pm_runtime.h> #include "amd.h" #define DRV_NAME "acp_asoc_rembrandt" -#define ACP6X_PGFSM_CONTROL 0x1024 -#define ACP6X_PGFSM_STATUS 0x1028 - -#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 - -#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01 -#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00 -#define ACP_PGFSM_STATUS_MASK 0x03 -#define ACP_POWERED_ON 0x00 -#define ACP_POWER_ON_IN_PROGRESS 0x01 -#define ACP_POWERED_OFF 0x02 -#define ACP_POWER_OFF_IN_PROGRESS 0x03 - -#define ACP_ERROR_MASK 0x20000000 -#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF - - -static int rmb_acp_init(void __iomem *base); -static int rmb_acp_deinit(void __iomem *base); +#define MP1_C2PMSG_69 0x3B10A14 +#define MP1_C2PMSG_85 0x3B10A54 +#define MP1_C2PMSG_93 0x3B10A74 +#define HOST_BRIDGE_ID 0x14B5 static struct acp_resource rsrc = { .offset = 0, @@ -111,7 +98,6 @@ static struct snd_soc_dai_driver acp_rmb_dai[] = { .rate_max = 48000, }, .ops = &asoc_acp_cpu_dai_ops, - .probe = &asoc_acp_i2s_probe, }, { .name = "acp-i2s-bt", @@ -137,7 +123,6 @@ static struct snd_soc_dai_driver acp_rmb_dai[] = { .rate_max = 48000, }, .ops = &asoc_acp_cpu_dai_ops, - .probe = &asoc_acp_i2s_probe, }, { .name = "acp-i2s-hs", @@ -163,7 +148,6 @@ static struct snd_soc_dai_driver acp_rmb_dai[] = { .rate_max = 48000, }, .ops = &asoc_acp_cpu_dai_ops, - .probe = &asoc_acp_i2s_probe, }, { .name = "acp-pdm-dmic", @@ -180,108 +164,22 @@ static struct snd_soc_dai_driver acp_rmb_dai[] = { }, }; -static int acp6x_power_on(void __iomem *base) -{ - u32 val; - int timeout; - - val = readl(base + ACP6X_PGFSM_STATUS); - - if (val == ACP_POWERED_ON) - return 0; - - if ((val & ACP_PGFSM_STATUS_MASK) != - ACP_POWER_ON_IN_PROGRESS) - writel(ACP_PGFSM_CNTL_POWER_ON_MASK, - base + ACP6X_PGFSM_CONTROL); - timeout = 0; - while (++timeout < 500) { - val = readl(base + ACP6X_PGFSM_STATUS); - if (!val) - return 0; - udelay(1); - } - return -ETIMEDOUT; -} - -static int acp6x_reset(void __iomem *base) +static int acp6x_master_clock_generate(struct device *dev) { - u32 val; - int timeout; - - writel(1, base + ACP_SOFT_RESET); - timeout = 0; - while (++timeout < 500) { - val = readl(base + ACP_SOFT_RESET); - if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK) - break; - cpu_relax(); - } - writel(0, base + ACP_SOFT_RESET); - timeout = 0; - while (++timeout < 500) { - val = readl(base + ACP_SOFT_RESET); - if (!val) - return 0; - cpu_relax(); - } - return -ETIMEDOUT; -} - -static void acp6x_enable_interrupts(struct acp_dev_data *adata) -{ - struct acp_resource *rsrc = adata->rsrc; - u32 ext_intr_ctrl; - - writel(0x01, ACP_EXTERNAL_INTR_ENB(adata)); - ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); - ext_intr_ctrl |= ACP_ERROR_MASK; - writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); -} - -static void acp6x_disable_interrupts(struct acp_dev_data *adata) -{ - struct acp_resource *rsrc = adata->rsrc; - - writel(ACP_EXT_INTR_STAT_CLEAR_MASK, - ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); - writel(0x00, ACP_EXTERNAL_INTR_ENB(adata)); -} + int data = 0; + struct pci_dev *smn_dev; -static int rmb_acp_init(void __iomem *base) -{ - int ret; - - /* power on */ - ret = acp6x_power_on(base); - if (ret) { - pr_err("ACP power on failed\n"); - return ret; - } - writel(0x01, base + ACP_CONTROL); - - /* Reset */ - ret = acp6x_reset(base); - if (ret) { - pr_err("ACP reset failed\n"); - return ret; - } - - return 0; -} - -static int rmb_acp_deinit(void __iomem *base) -{ - int ret = 0; - - /* Reset */ - ret = acp6x_reset(base); - if (ret) { - pr_err("ACP reset failed\n"); - return ret; + smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, HOST_BRIDGE_ID, NULL); + if (!smn_dev) { + dev_err(dev, "Failed to get host bridge device\n"); + return -ENODEV; } - writel(0x00, base + ACP_CONTROL); + smn_write(smn_dev, MP1_C2PMSG_93, 0); + smn_write(smn_dev, MP1_C2PMSG_85, 0xC4); + smn_write(smn_dev, MP1_C2PMSG_69, 0x4); + read_poll_timeout(smn_read, data, data, DELAY_US, + ACP_TIMEOUT, false, smn_dev, MP1_C2PMSG_93); return 0; } @@ -303,8 +201,6 @@ static int rembrandt_audio_probe(struct platform_device *pdev) return -ENODEV; } - rmb_acp_init(chip->base); - adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); if (!adata) return -ENOMEM; @@ -335,9 +231,14 @@ static int rembrandt_audio_probe(struct platform_device *pdev) acp_machine_select(adata); dev_set_drvdata(dev, adata); - acp6x_enable_interrupts(adata); + acp6x_master_clock_generate(dev); + acp_enable_interrupts(adata); acp_platform_register(dev); - + pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); return 0; } @@ -345,19 +246,49 @@ static void rembrandt_audio_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct acp_dev_data *adata = dev_get_drvdata(dev); - struct acp_chip_info *chip = dev_get_platdata(dev); - - rmb_acp_deinit(chip->base); - acp6x_disable_interrupts(adata); + acp_disable_interrupts(adata); acp_platform_unregister(dev); + pm_runtime_disable(&pdev->dev); } +static int __maybe_unused rmb_pcm_resume(struct device *dev) +{ + struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_stream *stream; + struct snd_pcm_substream *substream; + snd_pcm_uframes_t buf_in_frames; + u64 buf_size; + + acp6x_master_clock_generate(dev); + spin_lock(&adata->acp_lock); + list_for_each_entry(stream, &adata->stream_list, list) { + substream = stream->substream; + if (substream && substream->runtime) { + buf_in_frames = (substream->runtime->buffer_size); + buf_size = frames_to_bytes(substream->runtime, buf_in_frames); + config_pte_for_stream(adata, stream); + config_acp_dma(adata, stream, buf_size); + if (stream->dai_id) + restore_acp_i2s_params(substream, adata, stream); + else + restore_acp_pdm_params(substream, adata); + } + } + spin_unlock(&adata->acp_lock); + return 0; +} + +static const struct dev_pm_ops rmb_dma_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume) +}; + static struct platform_driver rembrandt_driver = { .probe = rembrandt_audio_probe, .remove_new = rembrandt_audio_remove, .driver = { .name = "acp_asoc_rembrandt", + .pm = &rmb_dma_pm_ops, }, }; diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index f188365fe214..54235cad9cc9 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -25,20 +25,6 @@ #define DRV_NAME "acp_asoc_renoir" -#define ACP_SOFT_RST_DONE_MASK 0x00010001 - -#define ACP_PWR_ON_MASK 0x01 -#define ACP_PWR_OFF_MASK 0x00 -#define ACP_PGFSM_STAT_MASK 0x03 -#define ACP_POWERED_ON 0x00 -#define ACP_PWR_ON_IN_PROGRESS 0x01 -#define ACP_POWERED_OFF 0x02 -#define DELAY_US 5 -#define ACP_TIMEOUT 500 - -#define ACP_ERROR_MASK 0x20000000 -#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF - static struct acp_resource rsrc = { .offset = 20, .no_of_ctrls = 1, @@ -111,7 +97,6 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { .rate_max = 48000, }, .ops = &asoc_acp_cpu_dai_ops, - .probe = &asoc_acp_i2s_probe, }, { .name = "acp-i2s-bt", @@ -137,7 +122,6 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { .rate_max = 48000, }, .ops = &asoc_acp_cpu_dai_ops, - .probe = &asoc_acp_i2s_probe, }, { .name = "acp-pdm-dmic", @@ -154,89 +138,7 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { }, }; -static int acp3x_power_on(void __iomem *base) -{ - u32 val; - - val = readl(base + ACP_PGFSM_STATUS); - - if (val == ACP_POWERED_ON) - return 0; - - if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS) - writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL); - - return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT); -} - -static int acp3x_reset(void __iomem *base) -{ - u32 val; - int ret; - - writel(1, base + ACP_SOFT_RESET); - - ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK, - DELAY_US, ACP_TIMEOUT); - if (ret) - return ret; - - writel(0, base + ACP_SOFT_RESET); - - return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT); -} - -static void acp3x_enable_interrupts(struct acp_dev_data *adata) -{ - struct acp_resource *rsrc = adata->rsrc; - u32 ext_intr_ctrl; - - writel(0x01, ACP_EXTERNAL_INTR_ENB(adata)); - ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); - ext_intr_ctrl |= ACP_ERROR_MASK; - writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); -} - -static void acp3x_disable_interrupts(struct acp_dev_data *adata) -{ - struct acp_resource *rsrc = adata->rsrc; - - writel(ACP_EXT_INTR_STAT_CLEAR_MASK, - ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); - writel(0x00, ACP_EXTERNAL_INTR_ENB(adata)); -} -static int rn_acp_init(void __iomem *base) -{ - int ret; - - /* power on */ - ret = acp3x_power_on(base); - if (ret) - return ret; - - writel(0x01, base + ACP_CONTROL); - - /* Reset */ - ret = acp3x_reset(base); - if (ret) - return ret; - - return 0; -} - -static int rn_acp_deinit(void __iomem *base) -{ - int ret = 0; - - /* Reset */ - ret = acp3x_reset(base); - if (ret) - return ret; - - writel(0x00, base + ACP_CONTROL); - return 0; -} static int renoir_audio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -256,12 +158,6 @@ static int renoir_audio_probe(struct platform_device *pdev) return -ENODEV; } - ret = rn_acp_init(chip->base); - if (ret) { - dev_err(&pdev->dev, "ACP Init failed\n"); - return -EINVAL; - } - adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); if (!adata) return -ENOMEM; @@ -290,7 +186,7 @@ static int renoir_audio_probe(struct platform_device *pdev) acp_machine_select(adata); dev_set_drvdata(dev, adata); - acp3x_enable_interrupts(adata); + acp_enable_interrupts(adata); acp_platform_register(dev); return 0; @@ -300,17 +196,8 @@ static void renoir_audio_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct acp_dev_data *adata = dev_get_drvdata(dev); - struct acp_chip_info *chip; - int ret; - - chip = dev_get_platdata(&pdev->dev); - - acp3x_disable_interrupts(adata); - - ret = rn_acp_deinit(chip->base); - if (ret) - dev_err(&pdev->dev, "ACP de-init Failed (%pe)\n", ERR_PTR(ret)); + acp_disable_interrupts(adata); acp_platform_unregister(dev); } diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 99a7d3879340..5223033a122f 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -83,20 +83,15 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = { .tdm_mode = false, }; -static const struct snd_kcontrol_new acp_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone Jack"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Spk"), - SOC_DAPM_PIN_SWITCH("Left Spk"), - SOC_DAPM_PIN_SWITCH("Right Spk"), -}; - -static const struct snd_soc_dapm_widget acp_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_SPK("Spk", NULL), - SND_SOC_DAPM_SPK("Left Spk", NULL), - SND_SOC_DAPM_SPK("Right Spk", NULL), +static struct acp_card_drvdata sof_nau8821_max98388_data = { + .hs_cpu_id = I2S_SP, + .amp_cpu_id = I2S_HS, + .dmic_cpu_id = NONE, + .hs_codec_id = NAU8821, + .amp_codec_id = MAX98388, + .dmic_codec_id = NONE, + .soc_mclk = true, + .tdm_mode = false, }; static int acp_sof_probe(struct platform_device *pdev) @@ -117,11 +112,8 @@ static int acp_sof_probe(struct platform_device *pdev) card->dev = dev; card->owner = THIS_MODULE; card->name = pdev->id_entry->name; - card->dapm_widgets = acp_widgets; - card->num_dapm_widgets = ARRAY_SIZE(acp_widgets); - card->controls = acp_controls; - card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + /* Widgets and controls added per-codec in acp-mach-common.c */ acp_card_drvdata = card->drvdata; dmi_id = dmi_first_match(acp_quirk_table); @@ -166,6 +158,10 @@ static const struct platform_device_id board_ids[] = { .name = "rt5682s-hs-rt1019", .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data }, + { + .name = "nau8821-max", + .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data + }, { } }; static struct platform_driver acp_asoc_audio = { @@ -187,4 +183,5 @@ MODULE_ALIAS("platform:rt5682s-max"); MODULE_ALIAS("platform:rt5682s-rt1019"); MODULE_ALIAS("platform:nau8825-max"); MODULE_ALIAS("platform:rt5682s-hs-rt1019"); +MODULE_ALIAS("platform:nau8821-max"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index 12a176a50fd6..d6cfae6ec5f7 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -92,10 +92,43 @@ #define SLOT_WIDTH_24 0x18 #define SLOT_WIDTH_32 0x20 +#define ACP6X_PGFSM_CONTROL 0x1024 +#define ACP6X_PGFSM_STATUS 0x1028 + +#define ACP_SOFT_RST_DONE_MASK 0x00010001 + +#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01 +#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00 +#define ACP_PGFSM_STATUS_MASK 0x03 +#define ACP_POWERED_ON 0x00 +#define ACP_POWER_ON_IN_PROGRESS 0x01 +#define ACP_POWERED_OFF 0x02 +#define ACP_POWER_OFF_IN_PROGRESS 0x03 + +#define ACP_ERROR_MASK 0x20000000 +#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xffffffff + +#define ACP_TIMEOUT 500 +#define DELAY_US 5 +#define ACP_SUSPEND_DELAY_MS 2000 + +#define PDM_DMA_STAT 0x10 +#define PDM_DMA_INTR_MASK 0x10000 +#define PDM_DEC_64 0x2 +#define PDM_CLK_FREQ_MASK 0x07 +#define PDM_MISC_CTRL_MASK 0x10 +#define PDM_ENABLE 0x01 +#define PDM_DISABLE 0x00 +#define DMA_EN_MASK 0x02 +#define DELAY_US 5 +#define PDM_TIMEOUT 1000 +#define ACP_REGION2_OFFSET 0x02000000 + struct acp_chip_info { char *name; /* Platform name */ unsigned int acp_rev; /* ACP Revision id */ void __iomem *base; /* ACP memory PCI base */ + struct platform_device *chip_pdev; }; struct acp_stream { @@ -144,8 +177,11 @@ struct acp_dev_data { u32 lrclk_div; struct acp_resource *rsrc; + u32 ch_mask; u32 tdm_tx_fmt[3]; u32 tdm_rx_fmt[3]; + u32 xfer_tx_resolution[3]; + u32 xfer_rx_resolution[3]; }; union acp_i2stdm_mstrclkgen { @@ -162,15 +198,29 @@ union acp_i2stdm_mstrclkgen { extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops; extern const struct snd_soc_dai_ops acp_dmic_dai_ops; -int asoc_acp_i2s_probe(struct snd_soc_dai *dai); int acp_platform_register(struct device *dev); int acp_platform_unregister(struct device *dev); int acp_machine_select(struct acp_dev_data *adata); +int smn_read(struct pci_dev *dev, u32 smn_addr); +int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data); + +int acp_init(struct acp_chip_info *chip); +int acp_deinit(void __iomem *base); +void acp_enable_interrupts(struct acp_dev_data *adata); +void acp_disable_interrupts(struct acp_dev_data *adata); /* Machine configuration */ int snd_amd_acp_find_config(struct pci_dev *pci); +void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream); +void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size); +void restore_acp_pdm_params(struct snd_pcm_substream *substream, + struct acp_dev_data *adata); + +int restore_acp_i2s_params(struct snd_pcm_substream *substream, + struct acp_dev_data *adata, struct acp_stream *stream); + static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction) { u64 byte_count = 0, low = 0, high = 0; diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c index 0543dda75b99..28ad5f5b9a76 100644 --- a/sound/soc/amd/acp3x-rt5682-max9836.c +++ b/sound/soc/amd/acp3x-rt5682-max9836.c @@ -28,6 +28,17 @@ #define DUAL_CHANNEL 2 static struct snd_soc_jack pco_jack; +static struct snd_soc_jack_pin pco_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static struct clk *rt5682_dai_wclk; static struct clk *rt5682_dai_bclk; static struct gpio_desc *dmic_sel; @@ -86,11 +97,13 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd) rt5682_dai_wclk = clk_get(component->dev, "rt5682-dai-wclk"); rt5682_dai_bclk = clk_get(component->dev, "rt5682-dai-bclk"); - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &pco_jack, + pco_jack_pins, + ARRAY_SIZE(pco_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h index 7b4c625da40d..d392e6d6e6e1 100644 --- a/sound/soc/amd/mach-config.h +++ b/sound/soc/amd/mach-config.h @@ -20,6 +20,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_sof_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[]; struct config_entry { u32 flags; diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c index 324c80fca672..6230d1b12225 100644 --- a/sound/soc/amd/ps/ps-sdw-dma.c +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -488,10 +488,9 @@ static int acp63_sdw_platform_probe(struct platform_device *pdev) return 0; } -static int acp63_sdw_platform_remove(struct platform_device *pdev) +static void acp63_sdw_platform_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - return 0; } static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) @@ -552,7 +551,7 @@ static const struct dev_pm_ops acp63_pm_ops = { static struct platform_driver acp63_sdw_dma_driver = { .probe = acp63_sdw_platform_probe, - .remove = acp63_sdw_platform_remove, + .remove_new = acp63_sdw_platform_remove, .driver = { .name = "amd_ps_sdw_dma", .pm = &acp63_pm_ops, diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c index e5bcd1e6eb73..eda464545866 100644 --- a/sound/soc/amd/vangogh/acp5x-mach.c +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Machine driver for AMD Vangogh platform using NAU8821 & CS35L41 - * codecs. + * Machine driver for AMD Vangogh platform using either + * NAU8821 & CS35L41 or NAU8821 & MAX98388 codecs. * * Copyright 2021 Advanced Micro Devices, Inc. */ @@ -22,7 +22,6 @@ #define DRV_NAME "acp5x_mach" #define DUAL_CHANNEL 2 -#define VG_JUPITER 1 #define ACP5X_NAU8821_BCLK 3072000 #define ACP5X_NAU8821_FREQ_OUT 12288000 #define ACP5X_NAU8821_COMP_NAME "i2c-NVTN2020:00" @@ -30,8 +29,10 @@ #define ACP5X_CS35L41_COMP_LNAME "spi-VLV1776:00" #define ACP5X_CS35L41_COMP_RNAME "spi-VLV1776:01" #define ACP5X_CS35L41_DAI_NAME "cs35l41-pcm" +#define ACP5X_MAX98388_COMP_LNAME "i2c-ADS8388:00" +#define ACP5X_MAX98388_COMP_RNAME "i2c-ADS8388:01" +#define ACP5X_MAX98388_DAI_NAME "max98388-aif1" -static unsigned long acp5x_machine_id; static struct snd_soc_jack vg_headset; SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0"))); @@ -169,6 +170,9 @@ static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME); int ret, bclk; + if (!dai) + return -EINVAL; + ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(card->dev, "can't set FS clock %d\n", ret); @@ -242,7 +246,6 @@ static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream, } return 0; - } static const struct snd_soc_ops acp5x_cs35l41_play_ops = { @@ -292,8 +295,6 @@ static struct snd_soc_dai_link acp5x_8821_35l41_dai[] = { }, }; - - static const struct snd_soc_dapm_widget acp5x_8821_35l41_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -331,16 +332,110 @@ static struct snd_soc_card acp5x_8821_35l41_card = { .num_controls = ARRAY_SIZE(acp5x_8821_controls), }; -static int acp5x_vg_quirk_cb(const struct dmi_system_id *id) +static int acp5x_max98388_startup(struct snd_pcm_substream *substream) { - acp5x_machine_id = VG_JUPITER; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card); + struct snd_pcm_runtime *runtime = substream->runtime; + + machine->play_i2s_instance = I2S_HS_INSTANCE; - return 1; + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + return 0; } +static const struct snd_soc_ops acp5x_max98388_play_ops = { + .startup = acp5x_max98388_startup, +}; + +static struct snd_soc_codec_conf acp5x_max98388_conf[] = { + { + .dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_LNAME), + .name_prefix = "Left", + }, + { + .dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_RNAME), + .name_prefix = "Right", + }, +}; + +SND_SOC_DAILINK_DEF(max98388, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_MAX98388_COMP_LNAME, + ACP5X_MAX98388_DAI_NAME), + COMP_CODEC(ACP5X_MAX98388_COMP_RNAME, + ACP5X_MAX98388_DAI_NAME))); + +static struct snd_soc_dai_link acp5x_8821_98388_dai[] = { + { + .name = "acp5x-8821-play", + .stream_name = "Playback/Capture", + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &acp5x_8821_ops, + .init = acp5x_8821_init, + SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform), + }, + { + .name = "acp5x-max98388-play", + .stream_name = "MAX98388 Playback", + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC, + .dpcm_playback = 1, + .playback_only = 1, + .ops = &acp5x_max98388_play_ops, + SND_SOC_DAILINK_REG(acp5x_bt, max98388, platform), + }, +}; + +static const struct snd_soc_dapm_widget acp5x_8821_98388_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SPK("SPK", NULL), +}; + +static const struct snd_soc_dapm_route acp5x_8821_98388_route[] = { + { "Headphone", NULL, "HPOL" }, + { "Headphone", NULL, "HPOR" }, + { "MICL", NULL, "Headset Mic" }, + { "MICR", NULL, "Headset Mic" }, + { "DMIC", NULL, "Int Mic" }, + + { "Headphone", NULL, "Platform Clock" }, + { "Headset Mic", NULL, "Platform Clock" }, + { "Int Mic", NULL, "Platform Clock" }, + + { "SPK", NULL, "Left BE_OUT" }, + { "SPK", NULL, "Right BE_OUT" }, +}; + +static struct snd_soc_card acp5x_8821_98388_card = { + .name = "acp5x-max98388", + .owner = THIS_MODULE, + .dai_link = acp5x_8821_98388_dai, + .num_links = ARRAY_SIZE(acp5x_8821_98388_dai), + .dapm_widgets = acp5x_8821_98388_widgets, + .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_98388_widgets), + .dapm_routes = acp5x_8821_98388_route, + .num_dapm_routes = ARRAY_SIZE(acp5x_8821_98388_route), + .codec_conf = acp5x_max98388_conf, + .num_configs = ARRAY_SIZE(acp5x_max98388_conf), + .controls = acp5x_8821_controls, + .num_controls = ARRAY_SIZE(acp5x_8821_controls), +}; + static const struct dmi_system_id acp5x_vg_quirk_table[] = { { - .callback = acp5x_vg_quirk_cb, .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"), @@ -351,23 +446,31 @@ static const struct dmi_system_id acp5x_vg_quirk_table[] = { static int acp5x_probe(struct platform_device *pdev) { + const struct dmi_system_id *dmi_id; struct acp5x_platform_info *machine; struct device *dev = &pdev->dev; struct snd_soc_card *card; int ret; + card = (struct snd_soc_card *)device_get_match_data(dev); + if (!card) { + /* + * This is normally the result of directly probing the driver + * in pci-acp5x through platform_device_register_full(), which + * is necessary for the CS35L41 variant, as it doesn't support + * ACPI probing and relies on DMI quirks. + */ + dmi_id = dmi_first_match(acp5x_vg_quirk_table); + if (!dmi_id) + return -ENODEV; + + card = &acp5x_8821_35l41_card; + } + machine = devm_kzalloc(dev, sizeof(*machine), GFP_KERNEL); if (!machine) return -ENOMEM; - dmi_check_system(acp5x_vg_quirk_table); - switch (acp5x_machine_id) { - case VG_JUPITER: - card = &acp5x_8821_35l41_card; - break; - default: - return -ENODEV; - } card->dev = dev; platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, machine); @@ -379,10 +482,17 @@ static int acp5x_probe(struct platform_device *pdev) return 0; } +static const struct acpi_device_id acp5x_acpi_match[] = { + { "AMDI8821", (kernel_ulong_t)&acp5x_8821_98388_card }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, acp5x_acpi_match); + static struct platform_driver acp5x_mach_driver = { .driver = { - .name = "acp5x_mach", + .name = DRV_NAME, .pm = &snd_soc_pm_ops, + .acpi_match_table = acp5x_acpi_match, }, .probe = acp5x_probe, }; @@ -390,6 +500,6 @@ static struct platform_driver acp5x_mach_driver = { module_platform_driver(acp5x_mach_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); -MODULE_DESCRIPTION("NAU8821 & CS35L41 audio support"); +MODULE_DESCRIPTION("NAU8821/CS35L41 & NAU8821/MAX98388 audio support"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c index 69a88dc65165..6c20c643f321 100644 --- a/sound/soc/atmel/atmel-i2s.c +++ b/sound/soc/atmel/atmel-i2s.c @@ -532,13 +532,6 @@ static int atmel_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return err; } -static const struct snd_soc_dai_ops atmel_i2s_dai_ops = { - .prepare = atmel_i2s_prepare, - .trigger = atmel_i2s_trigger, - .hw_params = atmel_i2s_hw_params, - .set_fmt = atmel_i2s_set_dai_fmt, -}; - static int atmel_i2s_dai_probe(struct snd_soc_dai *dai) { struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); @@ -547,8 +540,15 @@ static int atmel_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops atmel_i2s_dai_ops = { + .probe = atmel_i2s_dai_probe, + .prepare = atmel_i2s_prepare, + .trigger = atmel_i2s_trigger, + .hw_params = atmel_i2s_hw_params, + .set_fmt = atmel_i2s_set_dai_fmt, +}; + static struct snd_soc_dai_driver atmel_i2s_dai = { - .probe = atmel_i2s_dai_probe, .playback = { .channels_min = 1, .channels_max = 2, @@ -730,7 +730,7 @@ static void atmel_i2s_remove(struct platform_device *pdev) static struct platform_driver atmel_i2s_driver = { .driver = { .name = "atmel_i2s", - .of_match_table = of_match_ptr(atmel_i2s_dt_ids), + .of_match_table = atmel_i2s_dt_ids, }, .probe = atmel_i2s_probe, .remove_new = atmel_i2s_remove, diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c index efcbdd1a629f..0db7815d230c 100644 --- a/sound/soc/atmel/atmel-pdmic.c +++ b/sound/soc/atmel/atmel-pdmic.c @@ -690,7 +690,7 @@ unregister_codec: static struct platform_driver atmel_pdmic_driver = { .driver = { .name = "atmel-pdmic", - .of_match_table = of_match_ptr(atmel_pdmic_of_match), + .of_match_table = atmel_pdmic_of_match, .pm = &snd_soc_pm_ops, }, .probe = atmel_pdmic_probe, diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c index 7c83d48ca1a0..25ed0b953bfd 100644 --- a/sound/soc/atmel/mchp-i2s-mcc.c +++ b/sound/soc/atmel/mchp-i2s-mcc.c @@ -870,17 +870,6 @@ static int mchp_i2s_mcc_startup(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { - .set_sysclk = mchp_i2s_mcc_set_sysclk, - .set_bclk_ratio = mchp_i2s_mcc_set_bclk_ratio, - .startup = mchp_i2s_mcc_startup, - .trigger = mchp_i2s_mcc_trigger, - .hw_params = mchp_i2s_mcc_hw_params, - .hw_free = mchp_i2s_mcc_hw_free, - .set_fmt = mchp_i2s_mcc_set_dai_fmt, - .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot, -}; - static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) { struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai); @@ -895,6 +884,18 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { + .probe = mchp_i2s_mcc_dai_probe, + .set_sysclk = mchp_i2s_mcc_set_sysclk, + .set_bclk_ratio = mchp_i2s_mcc_set_bclk_ratio, + .startup = mchp_i2s_mcc_startup, + .trigger = mchp_i2s_mcc_trigger, + .hw_params = mchp_i2s_mcc_hw_params, + .hw_free = mchp_i2s_mcc_hw_free, + .set_fmt = mchp_i2s_mcc_set_dai_fmt, + .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot, +}; + #define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000 #define MCHP_I2SMCC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ @@ -906,7 +907,6 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_driver mchp_i2s_mcc_dai = { - .probe = mchp_i2s_mcc_dai_probe, .playback = { .stream_name = "I2SMCC-Playback", .channels_min = 1, @@ -1098,7 +1098,7 @@ static void mchp_i2s_mcc_remove(struct platform_device *pdev) static struct platform_driver mchp_i2s_mcc_driver = { .driver = { .name = "mchp_i2s_mcc", - .of_match_table = of_match_ptr(mchp_i2s_mcc_dt_ids), + .of_match_table = mchp_i2s_mcc_dt_ids, }, .probe = mchp_i2s_mcc_probe, .remove_new = mchp_i2s_mcc_remove, diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index c79c73e6791e..afe213a71212 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -386,7 +386,6 @@ static int mchp_pdmc_open(struct snd_soc_component *component, for (i = 0; i < ARRAY_SIZE(mchp_pdmc_snd_controls); i++) { const struct snd_kcontrol_new *control = &mchp_pdmc_snd_controls[i]; struct snd_ctl_elem_id id; - struct snd_kcontrol *kctl; int err; if (component->name_prefix) @@ -400,17 +399,10 @@ static int mchp_pdmc_open(struct snd_soc_component *component, id.device = control->device; id.subdevice = control->subdevice; id.index = control->index; - kctl = snd_ctl_find_id(component->card->snd_card, &id); - if (!kctl) { - dev_err(component->dev, "Failed to find %s\n", control->name); - continue; - } - err = snd_ctl_remove(component->card->snd_card, kctl); - if (err < 0) { + err = snd_ctl_remove_id(component->card->snd_card, &id); + if (err < 0) dev_err(component->dev, "%d: Failed to remove %s\n", err, control->name); - continue; - } } return 0; @@ -706,13 +698,6 @@ static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { - .set_fmt = mchp_pdmc_set_fmt, - .startup = mchp_pdmc_startup, - .hw_params = mchp_pdmc_hw_params, - .trigger = mchp_pdmc_trigger, -}; - static int mchp_pdmc_add_chmap_ctls(struct snd_pcm *pcm, struct mchp_pdmc *dd) { struct mchp_pdmc_chmap *info; @@ -765,8 +750,16 @@ static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd, return ret; } +static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { + .probe = mchp_pdmc_dai_probe, + .set_fmt = mchp_pdmc_set_fmt, + .startup = mchp_pdmc_startup, + .hw_params = mchp_pdmc_hw_params, + .trigger = mchp_pdmc_trigger, + .pcm_new = &mchp_pdmc_pcm_new, +}; + static struct snd_soc_dai_driver mchp_pdmc_dai = { - .probe = mchp_pdmc_dai_probe, .capture = { .stream_name = "Capture", .channels_min = 1, @@ -777,7 +770,6 @@ static struct snd_soc_dai_driver mchp_pdmc_dai = { .formats = SNDRV_PCM_FMTBIT_S24_LE, }, .ops = &mchp_pdmc_dai_ops, - .pcm_new = &mchp_pdmc_pcm_new, }; /* PDMC interrupt handler */ @@ -962,7 +954,7 @@ static int mchp_pdmc_dt_init(struct mchp_pdmc *dd) /* used to clean the channel index found on RHR's MSB */ static int mchp_pdmc_process(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - void *buf, unsigned long bytes) + struct iov_iter *buf, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; u8 *dma_ptr = runtime->dma_area + hwoff + diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c index ff6aba143aee..33ce5e54482b 100644 --- a/sound/soc/atmel/mchp-spdifrx.c +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -503,11 +503,6 @@ unlock: return ret; } -static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = { - .trigger = mchp_spdifrx_trigger, - .hw_params = mchp_spdifrx_hw_params, -}; - #define MCHP_SPDIF_RATES SNDRV_PCM_RATE_8000_192000 #define MCHP_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -1009,10 +1004,15 @@ static int mchp_spdifrx_dai_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = { + .probe = mchp_spdifrx_dai_probe, + .remove = mchp_spdifrx_dai_remove, + .trigger = mchp_spdifrx_trigger, + .hw_params = mchp_spdifrx_hw_params, +}; + static struct snd_soc_dai_driver mchp_spdifrx_dai = { .name = "mchp-spdifrx", - .probe = mchp_spdifrx_dai_probe, - .remove = mchp_spdifrx_dai_remove, .capture = { .stream_name = "S/PDIF Capture", .channels_min = SPDIFRX_CHANNELS, @@ -1197,7 +1197,7 @@ static struct platform_driver mchp_spdifrx_driver = { .remove_new = mchp_spdifrx_remove, .driver = { .name = "mchp_spdifrx", - .of_match_table = of_match_ptr(mchp_spdifrx_dt_ids), + .of_match_table = mchp_spdifrx_dt_ids, .pm = pm_ptr(&mchp_spdifrx_pm_ops), }, }; diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c index 1d3e17119888..a201a96fa690 100644 --- a/sound/soc/atmel/mchp-spdiftx.c +++ b/sound/soc/atmel/mchp-spdiftx.c @@ -516,14 +516,6 @@ static int mchp_spdiftx_hw_free(struct snd_pcm_substream *substream, SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR); } -static const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = { - .startup = mchp_spdiftx_dai_startup, - .shutdown = mchp_spdiftx_dai_shutdown, - .trigger = mchp_spdiftx_trigger, - .hw_params = mchp_spdiftx_hw_params, - .hw_free = mchp_spdiftx_hw_free, -}; - #define MCHP_SPDIFTX_RATES SNDRV_PCM_RATE_8000_192000 #define MCHP_SPDIFTX_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ @@ -703,9 +695,17 @@ static int mchp_spdiftx_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = { + .probe = mchp_spdiftx_dai_probe, + .startup = mchp_spdiftx_dai_startup, + .shutdown = mchp_spdiftx_dai_shutdown, + .trigger = mchp_spdiftx_trigger, + .hw_params = mchp_spdiftx_hw_params, + .hw_free = mchp_spdiftx_hw_free, +}; + static struct snd_soc_dai_driver mchp_spdiftx_dai = { .name = "mchp-spdiftx", - .probe = mchp_spdiftx_dai_probe, .playback = { .stream_name = "S/PDIF Playback", .channels_min = 1, @@ -891,7 +891,7 @@ static struct platform_driver mchp_spdiftx_driver = { .remove_new = mchp_spdiftx_remove, .driver = { .name = "mchp_spdiftx", - .of_match_table = of_match_ptr(mchp_spdiftx_dt_ids), + .of_match_table = mchp_spdiftx_dt_ids, .pm = pm_ptr(&mchp_spdiftx_pm_ops) }, }; diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c index efead272d92b..c809b121037f 100644 --- a/sound/soc/atmel/tse850-pcm5142.c +++ b/sound/soc/atmel/tse850-pcm5142.c @@ -430,7 +430,7 @@ MODULE_DEVICE_TABLE(of, tse850_dt_ids); static struct platform_driver tse850_driver = { .driver = { .name = "axentia-tse850-pcm5142", - .of_match_table = of_match_ptr(tse850_dt_ids), + .of_match_table = tse850_dt_ids, }, .probe = tse850_probe, .remove_new = tse850_remove, diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c index a11d6841afc2..b0e1a1253e10 100644 --- a/sound/soc/au1x/ac97c.c +++ b/sound/soc/au1x/ac97c.c @@ -195,18 +195,18 @@ static int alchemy_ac97c_startup(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops alchemy_ac97c_ops = { - .startup = alchemy_ac97c_startup, -}; - static int au1xac97c_dai_probe(struct snd_soc_dai *dai) { return ac97c_workdata ? 0 : -ENODEV; } +static const struct snd_soc_dai_ops alchemy_ac97c_ops = { + .probe = au1xac97c_dai_probe, + .startup = alchemy_ac97c_startup, +}; + static struct snd_soc_dai_driver au1xac97c_dai_driver = { .name = "alchemy-ac97c", - .probe = au1xac97c_dai_probe, .playback = { .rates = AC97_RATES, .formats = AC97_FMTS, diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 9fd91aea7d1a..5d50ebc2bdd5 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -333,13 +333,13 @@ static int au1xpsc_ac97_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { + .probe = au1xpsc_ac97_probe, .startup = au1xpsc_ac97_startup, .trigger = au1xpsc_ac97_trigger, .hw_params = au1xpsc_ac97_hw_params, }; static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = { - .probe = au1xpsc_ac97_probe, .playback = { .rates = AC97_RATES, .formats = AC97_FMTS, diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 85f705afcdbb..9bda6499e66e 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -737,7 +737,19 @@ static void bcm2835_i2s_shutdown(struct snd_pcm_substream *substream, bcm2835_i2s_stop_clock(dev); } +static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, + &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK], + &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]); + + return 0; +} + static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { + .probe = bcm2835_i2s_dai_probe, .startup = bcm2835_i2s_startup, .shutdown = bcm2835_i2s_shutdown, .prepare = bcm2835_i2s_prepare, @@ -748,20 +760,8 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, }; -static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) -{ - struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - - snd_soc_dai_init_dma_data(dai, - &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK], - &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]); - - return 0; -} - static struct snd_soc_dai_driver bcm2835_i2s_dai = { .name = "bcm2835-i2s", - .probe = bcm2835_i2s_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/bcm/bcm63xx-i2s-whistler.c b/sound/soc/bcm/bcm63xx-i2s-whistler.c index 18c51dbbc8dc..c64609718738 100644 --- a/sound/soc/bcm/bcm63xx-i2s-whistler.c +++ b/sound/soc/bcm/bcm63xx-i2s-whistler.c @@ -225,7 +225,6 @@ static int bcm63xx_i2s_dev_probe(struct platform_device *pdev) { int ret = 0; void __iomem *regs; - struct resource *r_mem, *region; struct bcm_i2s_priv *i2s_priv; struct regmap *regmap_i2s; struct clk *i2s_clk; @@ -241,20 +240,7 @@ static int bcm63xx_i2s_dev_probe(struct platform_device *pdev) return PTR_ERR(i2s_clk); } - r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r_mem) { - dev_err(&pdev->dev, "Unable to get register resource.\n"); - return -ENODEV; - } - - region = devm_request_mem_region(&pdev->dev, r_mem->start, - resource_size(r_mem), DRV_NAME); - if (!region) { - dev_err(&pdev->dev, "Memory region already claimed\n"); - return -EBUSY; - } - - regs = devm_ioremap_resource(&pdev->dev, r_mem); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) { ret = PTR_ERR(regs); return ret; diff --git a/sound/soc/bcm/cygnus-ssp.h b/sound/soc/bcm/cygnus-ssp.h index 74152b2d770d..4925e03c3c30 100644 --- a/sound/soc/bcm/cygnus-ssp.h +++ b/sound/soc/bcm/cygnus-ssp.h @@ -117,8 +117,6 @@ struct cygnus_audio { unsigned long vco_rate; }; -extern int cygnus_ssp_get_mode(struct snd_soc_dai *cpu_dai); -extern int cygnus_ssp_add_pll_tweak_controls(struct snd_soc_pcm_runtime *rtd); extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len); extern int cygnus_soc_platform_register(struct device *dev, diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index afc6b5b570ea..522de4b80293 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -407,6 +407,7 @@ static int ep93xx_i2s_resume(struct snd_soc_component *component) #endif static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { + .probe = ep93xx_i2s_dai_probe, .startup = ep93xx_i2s_startup, .shutdown = ep93xx_i2s_shutdown, .hw_params = ep93xx_i2s_hw_params, @@ -418,7 +419,6 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { static struct snd_soc_dai_driver ep93xx_i2s_dai = { .symmetric_rate = 1, - .probe = ep93xx_i2s_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 3574c68e0dda..d99b674d574b 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -143,7 +143,7 @@ struct pm860x_priv { struct pm860x_det det; int irq[4]; - unsigned char name[4][MAX_NAME_LEN+1]; + unsigned char name[4][MAX_NAME_LEN]; }; /* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */ @@ -1373,7 +1373,7 @@ static int pm860x_codec_probe(struct platform_device *pdev) return -EINVAL; } pm860x->irq[i] = res->start + chip->irq_base; - strncpy(pm860x->name[i], res->name, MAX_NAME_LEN); + strscpy(pm860x->name[i], res->name, MAX_NAME_LEN); } ret = devm_snd_soc_register_component(&pdev->dev, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c2de4ee72183..95b5bd883215 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -15,7 +15,6 @@ config SND_SOC_ALL_CODECS tristate "Build all ASoC CODEC drivers" depends on COMPILE_TEST imply SND_SOC_88PM860X - imply SND_SOC_L3 imply SND_SOC_AB8500_CODEC imply SND_SOC_AC97_CODEC imply SND_SOC_AD1836 @@ -53,8 +52,10 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AK5558 imply SND_SOC_ALC5623 imply SND_SOC_ALC5632 + imply SND_SOC_AUDIO_IIO_AUX imply SND_SOC_AW8738 imply SND_SOC_AW88395 + imply SND_SOC_AW88261 imply SND_SOC_BT_SCO imply SND_SOC_BD28623 imply SND_SOC_CHV3_CODEC @@ -74,6 +75,8 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS35L56_SDW imply SND_SOC_CS42L42 imply SND_SOC_CS42L42_SDW + imply SND_SOC_CS42L43 + imply SND_SOC_CS42L43_SDW imply SND_SOC_CS42L51_I2C imply SND_SOC_CS42L52 imply SND_SOC_CS42L56 @@ -183,6 +186,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT1015 imply SND_SOC_RT1015P imply SND_SOC_RT1016 + imply SND_SOC_RT1017_SDCA_SDW imply SND_SOC_RT1019 imply SND_SOC_RT1305 imply SND_SOC_RT1308 @@ -265,7 +269,6 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TWL4030 imply SND_SOC_TWL6040 imply SND_SOC_UDA1334 - imply SND_SOC_UDA134X imply SND_SOC_UDA1380 imply SND_SOC_WCD9335 imply SND_SOC_WCD934X @@ -614,6 +617,17 @@ config SND_SOC_ALC5632 tristate depends on I2C +config SND_SOC_AUDIO_IIO_AUX + tristate "Audio IIO Auxiliary device" + depends on IIO + help + Enable support for Industrial I/O devices as audio auxiliary devices. + This allows to have an IIO device present in the audio path and + controlled using mixer controls. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-audio-iio-aux. + config SND_SOC_AW8738 tristate "Awinic AW8738 Audio Amplifier" select GPIOLIB @@ -640,6 +654,20 @@ config SND_SOC_AW88395 digital Smart K audio amplifier with an integrated 10V smart boost convert. +config SND_SOC_AW88261 + tristate "Soc Audio for awinic aw88261" + depends on I2C + select CRC8 + select REGMAP_I2C + select GPIOLIB + select SND_SOC_AW88395_LIB + help + This option enables support for aw88261 Smart PA. + The awinic AW88261 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier. The output voltage of + boost converter can be adjusted smartly according to + the input amplitude. + config SND_SOC_BD28623 tristate "ROHM BD28623 CODEC" help @@ -789,6 +817,20 @@ config SND_SOC_CS42L42_SDW help Enable support for Cirrus Logic CS42L42 codec with Soundwire control +config SND_SOC_CS42L43 + tristate "Cirrus Logic CS42L43 CODEC" + depends on MFD_CS42L43 + help + Select this to support the audio functions of the Cirrus Logic + CS42L43 PC CODEC. + +config SND_SOC_CS42L43_SDW + tristate "Cirrus Logic CS42L43 CODEC (SoundWire)" + depends on SND_SOC_CS42L43 && MFD_CS42L43_SDW + help + Select this to support the audio functions of the Cirrus Logic + CS42L43 PC CODEC over SoundWire. + config SND_SOC_CS42L51 tristate @@ -966,9 +1008,6 @@ config SND_SOC_JZ4770_CODEC This driver can also be built as a module. If so, the module will be called snd-soc-jz4770-codec. -config SND_SOC_L3 - tristate - config SND_SOC_DA7210 tristate depends on SND_SOC_I2C_AND_SPI @@ -1416,6 +1455,11 @@ config SND_SOC_RT1016 tristate depends on I2C +config SND_SOC_RT1017_SDCA_SDW + tristate "Realtek RT1017 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + config SND_SOC_RT1019 tristate depends on I2C @@ -1708,6 +1752,7 @@ config SND_SOC_STA529 config SND_SOC_STAC9766 tristate depends on SND_SOC_AC97_BUS + select REGMAP_AC97 config SND_SOC_STI_SAS tristate "codec Audio support for STI SAS codec" @@ -1919,9 +1964,6 @@ config SND_SOC_UDA1334 and has basic features such as de-emphasis (at 44.1 kHz sampling rate) and mute. -config SND_SOC_UDA134X - tristate - config SND_SOC_UDA1380 tristate depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b48a9a323b84..c8502a49b40a 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -45,10 +45,12 @@ snd-soc-ak4671-objs := ak4671.o snd-soc-ak5386-objs := ak5386.o snd-soc-ak5558-objs := ak5558.o snd-soc-arizona-objs := arizona.o arizona-jack.o +snd-soc-audio-iio-aux-objs := audio-iio-aux.o snd-soc-aw8738-objs := aw8738.o snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o snd-soc-aw88395-objs := aw88395/aw88395.o \ aw88395/aw88395_device.o +snd-soc-aw88261-objs := aw88261.o snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-chv3-codec-objs := chv3-codec.o @@ -75,6 +77,8 @@ snd-soc-cs35l56-sdw-objs := cs35l56-sdw.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l42-i2c-objs := cs42l42-i2c.o snd-soc-cs42l42-sdw-objs := cs42l42-sdw.o +snd-soc-cs42l43-objs := cs42l43.o cs42l43-jack.o +snd-soc-cs42l43-sdw-objs := cs42l43-sdw.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o @@ -127,7 +131,6 @@ snd-soc-jz4740-codec-objs := jz4740.o snd-soc-jz4725b-codec-objs := jz4725b.o snd-soc-jz4760-codec-objs := jz4760.o snd-soc-jz4770-codec-objs := jz4770.o -snd-soc-l3-objs := l3.o snd-soc-lm4857-objs := lm4857.o snd-soc-lm49453-objs := lm49453.o snd-soc-lochnagar-sc-objs := lochnagar-sc.o @@ -204,6 +207,7 @@ snd-soc-rt1011-objs := rt1011.o snd-soc-rt1015-objs := rt1015.o snd-soc-rt1015p-objs := rt1015p.o snd-soc-rt1016-objs := rt1016.o +snd-soc-rt1017-sdca-objs := rt1017-sdca-sdw.o snd-soc-rt1019-objs := rt1019.o snd-soc-rt1305-objs := rt1305.o snd-soc-rt1308-objs := rt1308.o @@ -298,7 +302,6 @@ snd-soc-ts3a227e-objs := ts3a227e.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o snd-soc-uda1334-objs := uda1334.o -snd-soc-uda134x-objs := uda134x.o snd-soc-uda1380-objs := uda1380.o snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o @@ -428,9 +431,11 @@ obj-$(CONFIG_SND_SOC_AK5558) += snd-soc-ak5558.o obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o +obj-$(CONFIG_SND_SOC_AUDIO_IIO_AUX) += snd-soc-audio-iio-aux.o obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o +obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o @@ -457,6 +462,8 @@ obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o obj-$(CONFIG_SND_SOC_CS42L42_SDW) += snd-soc-cs42l42-sdw.o +obj-$(CONFIG_SND_SOC_CS42L43) += snd-soc-cs42l43.o +obj-$(CONFIG_SND_SOC_CS42L43_SDW) += snd-soc-cs42l43-sdw.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o @@ -509,7 +516,6 @@ obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o obj-$(CONFIG_SND_SOC_JZ4760_CODEC) += snd-soc-jz4760-codec.o obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o -obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o @@ -581,6 +587,7 @@ obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o obj-$(CONFIG_SND_SOC_RT1015P) += snd-soc-rt1015p.o obj-$(CONFIG_SND_SOC_RT1016) += snd-soc-rt1016.o +obj-$(CONFIG_SND_SOC_RT1017_SDCA_SDW) += snd-soc-rt1017-sdca.o obj-$(CONFIG_SND_SOC_RT1019) += snd-soc-rt1019.o obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o @@ -677,7 +684,6 @@ obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o -obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 2c64df96b5ce..949077108bef 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -358,7 +358,7 @@ static const struct regmap_config ad1836_regmap_config = { .max_register = AD1836_ADC_CTRL3, .reg_defaults = ad1836_reg_defaults, .num_reg_defaults = ARRAY_SIZE(ad1836_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int ad1836_spi_probe(struct spi_device *spi) diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 5e777d7fd5d9..3c1ae13c1aae 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -92,7 +92,7 @@ static const struct regmap_config ad1980_regmap_config = { .reg_stride = 2, .val_bits = 16, .max_register = 0x7e, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = regmap_ac97_default_volatile, .readable_reg = ad1980_readable_reg, diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c index d9bde7eb043a..98380a7ce64d 100644 --- a/sound/soc/codecs/adau1372.c +++ b/sound/soc/codecs/adau1372.c @@ -1056,7 +1056,7 @@ const struct regmap_config adau1372_regmap_config = { .reg_defaults = adau1372_reg_defaults, .num_reg_defaults = ARRAY_SIZE(adau1372_reg_defaults), .volatile_reg = adau1372_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(adau1372_regmap_config); diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index c5b087b8fffc..b0ab0a69b207 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1451,7 +1451,7 @@ static const struct regmap_config adau1373_regmap_config = { .volatile_reg = adau1373_register_volatile, .max_register = ADAU1373_SOFT_RESET, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = adau1373_reg_defaults, .num_reg_defaults = ARRAY_SIZE(adau1373_reg_defaults), }; diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 8c8de3b3c901..94831aad7ac6 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -778,7 +778,7 @@ static const struct regmap_config adau1701_regmap = { .reg_bits = 16, .val_bits = 32, .max_register = ADAU1701_MAX_REGISTER, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = adau1701_volatile_reg, .reg_write = adau1701_reg_write, .reg_read = adau1701_reg_read, diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index 3ccc7acac205..1f09ea385f8a 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -1014,7 +1014,7 @@ const struct regmap_config adau1761_regmap_config = { .readable_reg = adau1761_readable_register, .volatile_reg = adau17x1_volatile_register, .precious_reg = adau17x1_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(adau1761_regmap_config); diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index ff6be24863bf..faad2f9f8dd2 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -472,7 +472,7 @@ const struct regmap_config adau1781_regmap_config = { .readable_reg = adau1781_readable_register, .volatile_reg = adau17x1_volatile_register, .precious_reg = adau17x1_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(adau1781_regmap_config); diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index 7a9672f94fc6..ae59efb38f26 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -991,7 +991,7 @@ const struct regmap_config adau1977_regmap_config = { .max_register = ADAU1977_REG_DC_HPF_CAL, .volatile_reg = adau1977_register_volatile, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = adau1977_reg_defaults, .num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults), }; diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c index 73f181f7757e..b302b28eca7c 100644 --- a/sound/soc/codecs/adau7118-i2c.c +++ b/sound/soc/codecs/adau7118-i2c.c @@ -43,7 +43,7 @@ static const struct regmap_config adau7118_regmap_config = { .val_bits = 8, .reg_defaults = adau7118_reg_defaults, .num_reg_defaults = ARRAY_SIZE(adau7118_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .max_register = ADAU7118_REG_RESET, .volatile_reg = adau7118_volatile, }; diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index fcff35f26cec..bb08969c5917 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -870,7 +870,7 @@ const struct regmap_config adav80x_regmap_config = { .max_register = ADAV80X_PLL_OUTE, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = adav80x_reg_defaults, .num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults), }; diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index ad56caec9dac..619a817ee91c 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -880,20 +880,11 @@ static void ak4613_parse_of(struct ak4613_priv *priv, static int ak4613_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; - struct device_node *np = dev->of_node; const struct regmap_config *regmap_cfg; struct regmap *regmap; struct ak4613_priv *priv; - regmap_cfg = NULL; - if (np) - regmap_cfg = of_device_get_match_data(dev); - else { - const struct i2c_device_id *id = - i2c_match_id(ak4613_i2c_id, i2c); - regmap_cfg = (const struct regmap_config *)id->driver_data; - } - + regmap_cfg = i2c_get_match_data(i2c); if (!regmap_cfg) return -EINVAL; diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c new file mode 100644 index 000000000000..a8bf14239bd7 --- /dev/null +++ b/sound/soc/codecs/audio-iio-aux.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// ALSA SoC glue to use IIO devices as audio components +// +// Copyright 2023 CS GROUP France +// +// Author: Herve Codina <herve.codina@bootlin.com> + +#include <linux/iio/consumer.h> +#include <linux/minmax.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string_helpers.h> + +#include <sound/soc.h> +#include <sound/tlv.h> + +struct audio_iio_aux_chan { + struct iio_channel *iio_chan; + const char *name; + int max; + int min; + bool is_invert_range; +}; + +struct audio_iio_aux { + struct device *dev; + struct audio_iio_aux_chan *chans; + unsigned int num_chans; +}; + +static int audio_iio_aux_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value; + + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = chan->max - chan->min; + uinfo->type = (uinfo->value.integer.max == 1) ? + SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + return 0; +} + +static int audio_iio_aux_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value; + int max = chan->max; + int min = chan->min; + bool invert_range = chan->is_invert_range; + int ret; + int val; + + ret = iio_read_channel_raw(chan->iio_chan, &val); + if (ret < 0) + return ret; + + ucontrol->value.integer.value[0] = val - min; + if (invert_range) + ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; + + return 0; +} + +static int audio_iio_aux_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value; + int max = chan->max; + int min = chan->min; + bool invert_range = chan->is_invert_range; + int val; + int ret; + int tmp; + + val = ucontrol->value.integer.value[0]; + if (val < 0) + return -EINVAL; + if (val > max - min) + return -EINVAL; + + val = val + min; + if (invert_range) + val = max - val; + + ret = iio_read_channel_raw(chan->iio_chan, &tmp); + if (ret < 0) + return ret; + + if (tmp == val) + return 0; + + ret = iio_write_channel_raw(chan->iio_chan, val); + if (ret) + return ret; + + return 1; /* The value changed */ +} + +static int audio_iio_aux_add_controls(struct snd_soc_component *component, + struct audio_iio_aux_chan *chan) +{ + struct snd_kcontrol_new control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = chan->name, + .info = audio_iio_aux_info_volsw, + .get = audio_iio_aux_get_volsw, + .put = audio_iio_aux_put_volsw, + .private_value = (unsigned long)chan, + }; + + return snd_soc_add_component_controls(component, &control, 1); +} + +/* + * These data could be on stack but they are pretty big. + * As ASoC internally copy them and protect them against concurrent accesses + * (snd_soc_bind_card() protects using client_mutex), keep them in the global + * data area. + */ +static struct snd_soc_dapm_widget widgets[3]; +static struct snd_soc_dapm_route routes[2]; + +/* Be sure sizes are correct (need 3 widgets and 2 routes) */ +static_assert(ARRAY_SIZE(widgets) >= 3, "3 widgets are needed"); +static_assert(ARRAY_SIZE(routes) >= 2, "2 routes are needed"); + +static int audio_iio_aux_add_dapms(struct snd_soc_component *component, + struct audio_iio_aux_chan *chan) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + char *output_name; + char *input_name; + char *pga_name; + int ret; + + input_name = kasprintf(GFP_KERNEL, "%s IN", chan->name); + if (!input_name) + return -ENOMEM; + + output_name = kasprintf(GFP_KERNEL, "%s OUT", chan->name); + if (!output_name) { + ret = -ENOMEM; + goto out_free_input_name; + } + + pga_name = kasprintf(GFP_KERNEL, "%s PGA", chan->name); + if (!pga_name) { + ret = -ENOMEM; + goto out_free_output_name; + } + + widgets[0] = SND_SOC_DAPM_INPUT(input_name); + widgets[1] = SND_SOC_DAPM_OUTPUT(output_name); + widgets[2] = SND_SOC_DAPM_PGA(pga_name, SND_SOC_NOPM, 0, 0, NULL, 0); + ret = snd_soc_dapm_new_controls(dapm, widgets, 3); + if (ret) + goto out_free_pga_name; + + routes[0].sink = pga_name; + routes[0].control = NULL; + routes[0].source = input_name; + routes[1].sink = output_name; + routes[1].control = NULL; + routes[1].source = pga_name; + ret = snd_soc_dapm_add_routes(dapm, routes, 2); + + /* Allocated names are no more needed (duplicated in ASoC internals) */ + +out_free_pga_name: + kfree(pga_name); +out_free_output_name: + kfree(output_name); +out_free_input_name: + kfree(input_name); + return ret; +} + +static int audio_iio_aux_component_probe(struct snd_soc_component *component) +{ + struct audio_iio_aux *iio_aux = snd_soc_component_get_drvdata(component); + struct audio_iio_aux_chan *chan; + int ret; + int i; + + for (i = 0; i < iio_aux->num_chans; i++) { + chan = iio_aux->chans + i; + + ret = iio_read_max_channel_raw(chan->iio_chan, &chan->max); + if (ret) + return dev_err_probe(component->dev, ret, + "chan[%d] %s: Cannot get max raw value\n", + i, chan->name); + + ret = iio_read_min_channel_raw(chan->iio_chan, &chan->min); + if (ret) + return dev_err_probe(component->dev, ret, + "chan[%d] %s: Cannot get min raw value\n", + i, chan->name); + + if (chan->min > chan->max) { + /* + * This should never happen but to avoid any check + * later, just swap values here to ensure that the + * minimum value is lower than the maximum value. + */ + dev_dbg(component->dev, "chan[%d] %s: Swap min and max\n", + i, chan->name); + swap(chan->min, chan->max); + } + + /* Set initial value */ + ret = iio_write_channel_raw(chan->iio_chan, + chan->is_invert_range ? chan->max : chan->min); + if (ret) + return dev_err_probe(component->dev, ret, + "chan[%d] %s: Cannot set initial value\n", + i, chan->name); + + ret = audio_iio_aux_add_controls(component, chan); + if (ret) + return ret; + + ret = audio_iio_aux_add_dapms(component, chan); + if (ret) + return ret; + + dev_dbg(component->dev, "chan[%d]: Added %s (min=%d, max=%d, invert=%s)\n", + i, chan->name, chan->min, chan->max, + str_on_off(chan->is_invert_range)); + } + + return 0; +} + +static const struct snd_soc_component_driver audio_iio_aux_component_driver = { + .probe = audio_iio_aux_component_probe, +}; + +static int audio_iio_aux_probe(struct platform_device *pdev) +{ + struct audio_iio_aux_chan *iio_aux_chan; + struct device *dev = &pdev->dev; + struct audio_iio_aux *iio_aux; + const char **names; + u32 *invert_ranges; + int count; + int ret; + int i; + + iio_aux = devm_kzalloc(dev, sizeof(*iio_aux), GFP_KERNEL); + if (!iio_aux) + return -ENOMEM; + + iio_aux->dev = dev; + + count = device_property_string_array_count(dev, "io-channel-names"); + if (count < 0) + return dev_err_probe(dev, count, "failed to count io-channel-names\n"); + + iio_aux->num_chans = count; + + iio_aux->chans = devm_kmalloc_array(dev, iio_aux->num_chans, + sizeof(*iio_aux->chans), GFP_KERNEL); + if (!iio_aux->chans) + return -ENOMEM; + + names = kcalloc(iio_aux->num_chans, sizeof(*names), GFP_KERNEL); + if (!names) + return -ENOMEM; + + invert_ranges = kcalloc(iio_aux->num_chans, sizeof(*invert_ranges), GFP_KERNEL); + if (!invert_ranges) { + ret = -ENOMEM; + goto out_free_names; + } + + ret = device_property_read_string_array(dev, "io-channel-names", + names, iio_aux->num_chans); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to read io-channel-names\n"); + goto out_free_invert_ranges; + } + + /* + * snd-control-invert-range is optional and can contain fewer items + * than the number of channels. Unset values default to 0. + */ + count = device_property_count_u32(dev, "snd-control-invert-range"); + if (count > 0) { + count = min_t(unsigned int, count, iio_aux->num_chans); + ret = device_property_read_u32_array(dev, "snd-control-invert-range", + invert_ranges, count); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to read snd-control-invert-range\n"); + goto out_free_invert_ranges; + } + } + + for (i = 0; i < iio_aux->num_chans; i++) { + iio_aux_chan = iio_aux->chans + i; + iio_aux_chan->name = names[i]; + iio_aux_chan->is_invert_range = invert_ranges[i]; + + iio_aux_chan->iio_chan = devm_iio_channel_get(dev, iio_aux_chan->name); + if (IS_ERR(iio_aux_chan->iio_chan)) { + ret = PTR_ERR(iio_aux_chan->iio_chan); + dev_err_probe(dev, ret, "get IIO channel '%s' failed\n", + iio_aux_chan->name); + goto out_free_invert_ranges; + } + } + + platform_set_drvdata(pdev, iio_aux); + + ret = devm_snd_soc_register_component(dev, &audio_iio_aux_component_driver, + NULL, 0); +out_free_invert_ranges: + kfree(invert_ranges); +out_free_names: + kfree(names); + return ret; +} + +static const struct of_device_id audio_iio_aux_ids[] = { + { .compatible = "audio-iio-aux" }, + { } +}; +MODULE_DEVICE_TABLE(of, audio_iio_aux_ids); + +static struct platform_driver audio_iio_aux_driver = { + .driver = { + .name = "audio-iio-aux", + .of_match_table = audio_iio_aux_ids, + }, + .probe = audio_iio_aux_probe, +}; +module_platform_driver(audio_iio_aux_driver); + +MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); +MODULE_DESCRIPTION("IIO ALSA SoC aux driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c new file mode 100644 index 000000000000..a697b5006b45 --- /dev/null +++ b/sound/soc/codecs/aw88261.c @@ -0,0 +1,1300 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88261.c -- AW88261 ALSA SoC Audio driver +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Jimmy Zhang <zhangjianming@awinic.com> +// Author: Weidong Wang <wangweidong.a@awinic.com> +// + +#include <linux/i2c.h> +#include <linux/firmware.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "aw88261.h" +#include "aw88395/aw88395_data_type.h" +#include "aw88395/aw88395_device.h" + +static const struct regmap_config aw88261_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88261_REG_MAX - 1, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int real_value, volume; + unsigned int reg_value; + + volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL); + real_value = DB_TO_REG_VAL(volume); + + regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, ®_value); + + real_value = (real_value | (reg_value & AW88261_VOL_START_MASK)); + + dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value); + + regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value); +} + +static void aw88261_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_in_vol = desc->ctl_volume; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw88261_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw88261_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, + aw_dev->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw88261_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw88261_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) { + aw88261_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i != AW88261_MUTE_VOL) { + aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + if (flag) + regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG, + ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG, + ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE); +} + +static void aw88261_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + if (pwd) + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_PWDN_MASK, AW88261_PWDN_POWER_DOWN_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_PWDN_MASK, AW88261_PWDN_WORKING_VALUE); +} + +static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + if (amppd) + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_AMPPD_MASK, AW88261_AMPPD_POWER_DOWN_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_AMPPD_MASK, AW88261_AMPPD_WORKING_VALUE); +} + +static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + if (is_mute) { + aw88261_dev_fade_out(aw_dev); + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE); + } else { + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE); + aw88261_dev_fade_in(aw_dev); + } +} + +static void aw88261_dev_clear_int_status(struct aw_device *aw_dev) +{ + unsigned int int_status; + + /* read int status and clear */ + regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status); + /* make sure int status is clear */ + regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status); + + dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status); +} + +static int aw88261_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, ®_val); + if (ret) + return ret; + if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) { + dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return ret; +} + +static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = aw88261_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + return ret; + } + } + + return -EPERM; +} + +static int aw88261_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, ®_val); + if (ret) + return ret; + + reg_val &= (~AW88261_CCO_MUX_MASK); + if (reg_val == AW88261_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG, + ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = aw88261_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG, + ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE); + if (ret == 0) { + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = aw88261_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +static int aw88261_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret = aw88261_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw88261_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return ret; +} + +static int aw88261_dev_check_sysst(struct aw_device *aw_dev) +{ + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, ®_val); + if (ret) + return ret; + + check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK) + & AW88261_BIT_SYSST_CHECK; + if (check_val != AW88261_BIT_SYSST_CHECK) { + dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x", + reg_val, AW88261_BIT_SYSST_CHECK); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static void aw88261_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute) +{ + if (uls_hmute) + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_ULS_HMUTE_MASK, + AW88261_ULS_HMUTE_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_ULS_HMUTE_MASK, + AW88261_ULS_HMUTE_DISABLE_VALUE); +} + +static void aw88261_reg_force_set(struct aw88261 *aw88261) +{ + if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) { + /* set FORCE_PWM */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG, + AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); + /* set BOOST_OS_WIDTH */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG, + AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); + /* set BURST_LOOPR */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG, + AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); + /* set RSQN_DLY */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG, + AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); + /* set BURST_SSMODE */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG, + AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); + /* set BST_BURST */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG, + AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); + } else { + dev_dbg(aw88261->aw_pa->dev, "needn't set reg value"); + } +} + +static int aw88261_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + u16 reg_icalk, reg_icalkl; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88261_EFRH4_REG, ®_val); + if (ret) + return ret; + + reg_icalk = reg_val & (~AW88261_EF_ISN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88261_EFRL4_REG, ®_val); + if (ret) + return ret; + + reg_icalkl = reg_val & (~AW88261_EF_ISN_GESLP_L_MASK); + + reg_icalk = (reg_icalk >> AW88261_ICALK_SHIFT) & (reg_icalkl >> AW88261_ICALKL_SHIFT); + + if (reg_icalk & (~AW88261_EF_ISN_GESLP_SIGN_MASK)) + reg_icalk = reg_icalk | ~AW88261_EF_ISN_GESLP_NEG; + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw88261_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + u16 reg_vcalk, reg_vcalkl; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88261_EFRH3_REG, ®_val); + if (ret) + return ret; + + reg_vcalk = (u16)reg_val & (~AW88261_EF_VSN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88261_EFRL3_REG, ®_val); + if (ret) + return ret; + + reg_vcalkl = (u16)reg_val & (~AW88261_EF_VSN_GESLP_L_MASK); + + reg_vcalk = (reg_vcalk >> AW88261_VCALK_SHIFT) & (reg_vcalkl >> AW88261_VCALKL_SHIFT); + + if (reg_vcalk & AW88261_EF_VSN_GESLP_SIGN_MASK) + reg_vcalk = reg_vcalk | (~AW88261_EF_VSN_GESLP_NEG); + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw88261_dev_set_vcalb(struct aw_device *aw_dev) +{ + int16_t icalk_val, vcalk_val; + int icalk, vcalk, vcalb; + u32 reg_val; + int ret; + + ret = aw88261_dev_get_icalk(aw_dev, &icalk_val); + if (ret) + return ret; + + ret = aw88261_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret) + return ret; + + icalk = AW88261_CABL_BASE_VALUE + AW88261_ICABLK_FACTOR * icalk_val; + vcalk = AW88261_CABL_BASE_VALUE + AW88261_VCABLK_FACTOR * vcalk_val; + if (!vcalk) + return -EINVAL; + + vcalb = AW88261_VCAL_FACTOR * icalk / vcalk; + reg_val = (unsigned int)vcalb; + + dev_dbg(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x", + icalk, vcalk, vcalb, reg_val); + ret = regmap_write(aw_dev->regmap, AW88261_VSNTM1_REG, reg_val); + + return ret; +} + +static int aw88261_dev_reg_update(struct aw88261 *aw88261, + unsigned char *data, unsigned int len) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int read_val, efcheck_val, read_vol; + int data_len, i, ret; + int16_t *reg_data; + u16 reg_val; + u8 reg_addr; + + if (!len || !data) { + dev_err(aw_dev->dev, "reg data is null or len is 0"); + return -EINVAL; + } + + reg_data = (int16_t *)data; + data_len = len >> 1; + + if (data_len & 0x1) { + dev_err(aw_dev->dev, "data len:%d unsupported", data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + reg_addr = reg_data[i]; + reg_val = reg_data[i + 1]; + + if (reg_addr == AW88261_SYSCTRL_REG) { + aw88261->amppd_st = reg_val & (~AW88261_AMPPD_MASK); + ret = regmap_read(aw_dev->regmap, reg_addr, &read_val); + if (ret) + break; + + read_val &= (~AW88261_AMPPD_MASK) | (~AW88261_PWDN_MASK) | + (~AW88261_HMUTE_MASK); + reg_val &= (AW88261_AMPPD_MASK | AW88261_PWDN_MASK | AW88261_HMUTE_MASK); + reg_val |= read_val; + + /* enable uls hmute */ + reg_val &= AW88261_ULS_HMUTE_MASK; + reg_val |= AW88261_ULS_HMUTE_ENABLE_VALUE; + } + + if (reg_addr == AW88261_DBGCTRL_REG) { + efcheck_val = reg_val & (~AW88261_EF_DBMD_MASK); + if (efcheck_val == AW88261_OR_VALUE) + aw88261->efuse_check = AW88261_EF_OR_CHECK; + else + aw88261->efuse_check = AW88261_EF_AND_CHECK; + } + + /* i2stxen */ + if (reg_addr == AW88261_I2SCTRL3_REG) { + /* close tx */ + reg_val &= AW88261_I2STXEN_MASK; + reg_val |= AW88261_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr == AW88261_SYSCTRL2_REG) { + read_vol = (reg_val & (~AW88261_VOL_MASK)) >> + AW88261_VOL_START_BIT; + aw_dev->volume_desc.init_volume = + REG_VAL_TO_DB(read_vol); + } + + if (reg_addr == AW88261_VSNTM1_REG) + continue; + + ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + break; + } + + ret = aw88261_dev_set_vcalb(aw_dev); + if (ret) + return ret; + + if (aw_dev->prof_cur != aw_dev->prof_index) + vol_desc->ctl_volume = 0; + + /* keep min volume */ + aw88261_dev_set_volume(aw_dev, vol_desc->mute_volume); + + return ret; +} + +static char *aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return prof_info->prof_name_list[prof_desc->id]; +} + +static int aw88261_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} + +static int aw88261_dev_fw_update(struct aw88261 *aw88261) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + prof_name = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index); + if (!prof_name) { + dev_err(aw_dev->dev, "get prof name failed"); + return -EINVAL; + } + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw88261_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) + return ret; + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw88261_dev_reg_update(aw88261, sec_desc[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw_dev->prof_cur = aw_dev->prof_index; + + return ret; +} + +static int aw88261_dev_start(struct aw88261 *aw88261) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + int ret; + + if (aw_dev->status == AW88261_DEV_PW_ON) { + dev_info(aw_dev->dev, "already power on"); + return 0; + } + + /* power on */ + aw88261_dev_pwd(aw_dev, false); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + + ret = aw88261_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start"); + goto pll_check_fail; + } + + /* amppd on */ + aw88261_dev_amppd(aw_dev, false); + usleep_range(AW88261_1000_US, AW88261_1000_US + 50); + + /* check i2s status */ + ret = aw88261_dev_check_sysst(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "sysst check failed"); + goto sysst_check_fail; + } + + /* enable tx feedback */ + aw88261_dev_i2s_tx_enable(aw_dev, true); + + if (aw88261->amppd_st) + aw88261_dev_amppd(aw_dev, true); + + aw88261_reg_force_set(aw88261); + + /* close uls mute */ + aw88261_dev_uls_hmute(aw_dev, false); + + /* close mute */ + if (!aw88261->mute_st) + aw88261_dev_mute(aw_dev, false); + + /* clear inturrupt */ + aw88261_dev_clear_int_status(aw_dev); + aw_dev->status = AW88261_DEV_PW_ON; + + return 0; + +sysst_check_fail: + aw88261_dev_i2s_tx_enable(aw_dev, false); + aw88261_dev_clear_int_status(aw_dev); + aw88261_dev_amppd(aw_dev, true); +pll_check_fail: + aw88261_dev_pwd(aw_dev, true); + aw_dev->status = AW88261_DEV_PW_OFF; + + return ret; +} + +static int aw88261_dev_stop(struct aw_device *aw_dev) +{ + if (aw_dev->status == AW88261_DEV_PW_OFF) { + dev_info(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88261_DEV_PW_OFF; + + /* clear inturrupt */ + aw88261_dev_clear_int_status(aw_dev); + + aw88261_dev_uls_hmute(aw_dev, true); + /* set mute */ + aw88261_dev_mute(aw_dev, true); + + /* close tx feedback */ + aw88261_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88261_1000_US, AW88261_1000_US + 100); + + /* enable amppd */ + aw88261_dev_amppd(aw_dev, true); + + /* set power down */ + aw88261_dev_pwd(aw_dev, true); + + return 0; +} + +static int aw88261_reg_update(struct aw88261 *aw88261, bool force) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + int ret; + + if (force) { + ret = regmap_write(aw_dev->regmap, + AW88261_ID_REG, AW88261_SOFT_RESET_VALUE); + if (ret) + return ret; + + ret = aw88261_dev_fw_update(aw88261); + if (ret) + return ret; + } else { + if (aw_dev->prof_cur != aw_dev->prof_index) { + ret = aw88261_dev_fw_update(aw88261); + if (ret) + return ret; + } else { + ret = 0; + } + } + + aw_dev->prof_cur = aw_dev->prof_index; + + return ret; +} + +static void aw88261_start_pa(struct aw88261 *aw88261) +{ + int ret, i; + + for (i = 0; i < AW88261_START_RETRIES; i++) { + ret = aw88261_reg_update(aw88261, aw88261->phase_sync); + if (ret) { + dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i); + continue; + } + ret = aw88261_dev_start(aw88261); + if (ret) { + dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i); + continue; + } else { + dev_info(aw88261->aw_pa->dev, "start success\n"); + break; + } + } +} + +static void aw88261_startup_work(struct work_struct *work) +{ + struct aw88261 *aw88261 = + container_of(work, struct aw88261, start_work.work); + + mutex_lock(&aw88261->lock); + aw88261_start_pa(aw88261); + mutex_unlock(&aw88261->lock); +} + +static void aw88261_start(struct aw88261 *aw88261, bool sync_start) +{ + if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK) + return; + + if (aw88261->aw_pa->status == AW88261_DEV_PW_ON) + return; + + if (sync_start == AW88261_SYNC_START) + aw88261_start_pa(aw88261); + else + queue_delayed_work(system_wq, + &aw88261->start_work, + AW88261_START_WORK_DELAY_MS); +} + +static struct snd_soc_dai_driver aw88261_dai[] = { + { + .name = "aw88261-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW88261_RATES, + .formats = AW88261_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW88261_RATES, + .formats = AW88261_FORMATS, + }, + }, +}; + +static int aw88261_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88261->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_in_time; + + return 0; +} + +static int aw88261_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88261->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_in_time) { + aw_dev->fade_in_time = time; + return 1; + } + + return 0; +} + +static int aw88261_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88261->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_out_time; + + return 0; +} + +static int aw88261_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88261->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_out_time) { + aw_dev->fade_out_time = time; + return 1; + } + + return 0; +} + +static int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + /* check the index whether is valid */ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + /* check the index whether change */ + if (aw_dev->prof_index == index) + return -EPERM; + + aw_dev->prof_index = index; + + return 0; +} + +static int aw88261_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + const char *prof_name; + char *name; + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw88261->aw_pa->prof_info.count; + if (count <= 0) { + uinfo->value.enumerated.items = 0; + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + + prof_name = aw88261_dev_get_prof_name(aw88261->aw_pa, count); + if (!prof_name) { + strscpy(uinfo->value.enumerated.name, "null", + strlen("null") + 1); + return 0; + } + + strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw88261_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88261->aw_pa->prof_index; + + return 0; +} + +static int aw88261_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + int ret; + + /* pa stop or stopping just set profile */ + mutex_lock(&aw88261->lock); + ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); + mutex_unlock(&aw88261->lock); + return 0; + } + + if (aw88261->aw_pa->status) { + aw88261_dev_stop(aw88261->aw_pa); + aw88261_start(aw88261, AW88261_SYNC_START); + } + + mutex_unlock(&aw88261->lock); + + return 1; +} + +static int aw88261_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc; + + ucontrol->value.integer.value[0] = vol_desc->ctl_volume; + + return 0; +} + +static int aw88261_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (vol_desc->ctl_volume != value) { + vol_desc->ctl_volume = value; + aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume); + + return 1; + } + + return 0; +} + +static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step; + + return 0; +} + +static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw88261->aw_pa->fade_step != value) { + aw88261->aw_pa->fade_step = value; + return 1; + } + + return 0; +} + +static const struct snd_kcontrol_new aw88261_controls[] = { + SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG, + 6, AW88261_MUTE_VOL, 0, aw88261_volume_get, + aw88261_volume_set), + SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0, + aw88261_get_fade_step, aw88261_set_fade_step), + SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88261_get_fade_in_time, aw88261_set_fade_in_time), + SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88261_get_fade_out_time, aw88261_set_fade_out_time), + AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info, + aw88261_profile_get, aw88261_profile_set), +}; + +static int aw88261_playback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + + mutex_lock(&aw88261->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88261_start(aw88261, AW88261_ASYNC_START); + break; + case SND_SOC_DAPM_POST_PMD: + aw88261_dev_stop(aw88261->aw_pa); + break; + default: + break; + } + mutex_unlock(&aw88261->lock); + + return 0; +} + +static const struct snd_soc_dapm_widget aw88261_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0, + aw88261_playback_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("DAC Output"), + + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("ADC Input"), +}; + +static const struct snd_soc_dapm_route aw88261_audio_map[] = { + {"DAC Output", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "ADC Input"}, +}; + +static int aw88261_frcset_check(struct aw88261 *aw88261) +{ + unsigned int reg_val; + u16 temh, teml, tem; + int ret; + + ret = regmap_read(aw88261->regmap, AW88261_EFRH3_REG, ®_val); + if (ret) + return ret; + temh = ((u16)reg_val & (~AW88261_TEMH_MASK)); + + ret = regmap_read(aw88261->regmap, AW88261_EFRL3_REG, ®_val); + if (ret) + return ret; + teml = ((u16)reg_val & (~AW88261_TEML_MASK)); + + if (aw88261->efuse_check == AW88261_EF_OR_CHECK) + tem = (temh | teml); + else + tem = (temh & teml); + + if (tem == AW88261_DEFAULT_CFG) + aw88261->frcset_en = AW88261_FRCSET_ENABLE; + else + aw88261->frcset_en = AW88261_FRCSET_DISABLE; + + dev_dbg(aw88261->aw_pa->dev, "tem is 0x%04x, frcset_en is %d", + tem, aw88261->frcset_en); + + return ret; +} + +static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + int ret; + + ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + + ret = regmap_write(aw_dev->regmap, AW88261_ID_REG, AW88261_SOFT_RESET_VALUE); + if (ret) + return ret; + + aw_dev->fade_in_time = AW88261_500_US; + aw_dev->fade_out_time = AW88261_500_US; + aw_dev->prof_cur = AW88261_INIT_PROFILE; + aw_dev->prof_index = AW88261_INIT_PROFILE; + + ret = aw88261_dev_fw_update(aw88261); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); + return ret; + } + + ret = aw88261_frcset_check(aw88261); + if (ret) { + dev_err(aw_dev->dev, "aw88261_frcset_check ret = %d\n", ret); + return ret; + } + + aw88261_dev_clear_int_status(aw_dev); + + aw88261_dev_uls_hmute(aw_dev, true); + + aw88261_dev_mute(aw_dev, true); + + aw88261_dev_i2s_tx_enable(aw_dev, false); + + usleep_range(AW88261_1000_US, AW88261_1000_US + 100); + + aw88261_dev_amppd(aw_dev, true); + + aw88261_dev_pwd(aw_dev, true); + + return 0; +} + +static int aw88261_request_firmware_file(struct aw88261 *aw88261) +{ + const struct firmware *cont = NULL; + int ret; + + aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev); + if (ret) + return dev_err_probe(aw88261->aw_pa->dev, ret, + "load [%s] failed!", AW88261_ACF_FILE); + + dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n", + AW88261_ACF_FILE, cont ? cont->size : 0); + + aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); + if (!aw88261->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw88261->aw_cfg->len = (int)cont->size; + memcpy(aw88261->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg); + if (ret) { + dev_err(aw88261->aw_pa->dev, "load [%s] failed !", AW88261_ACF_FILE); + return ret; + } + + mutex_lock(&aw88261->lock); + /* aw device init */ + ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); + if (ret) + dev_err(aw88261->aw_pa->dev, "dev init failed"); + mutex_unlock(&aw88261->lock); + + return ret; +} + +static int aw88261_codec_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + int ret; + + INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work); + + ret = aw88261_request_firmware_file(aw88261); + if (ret) + return dev_err_probe(aw88261->aw_pa->dev, ret, + "aw88261_request_firmware_file failed\n"); + + /* add widgets */ + ret = snd_soc_dapm_new_controls(dapm, aw88261_dapm_widgets, + ARRAY_SIZE(aw88261_dapm_widgets)); + if (ret) + return ret; + + /* add route */ + ret = snd_soc_dapm_add_routes(dapm, aw88261_audio_map, + ARRAY_SIZE(aw88261_audio_map)); + if (ret) + return ret; + + ret = snd_soc_add_component_controls(component, aw88261_controls, + ARRAY_SIZE(aw88261_controls)); + + return ret; +} + +static void aw88261_codec_remove(struct snd_soc_component *aw_codec) +{ + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec); + + cancel_delayed_work_sync(&aw88261->start_work); +} + +static const struct snd_soc_component_driver soc_codec_dev_aw88261 = { + .probe = aw88261_codec_probe, + .remove = aw88261_codec_remove, +}; + +static void aw88261_hw_reset(struct aw88261 *aw88261) +{ + gpiod_set_value_cansleep(aw88261->reset_gpio, 0); + usleep_range(AW88261_1000_US, AW88261_1000_US + 10); + gpiod_set_value_cansleep(aw88261->reset_gpio, 1); + usleep_range(AW88261_1000_US, AW88261_1000_US + 10); +} + +static void aw88261_parse_channel_dt(struct aw88261 *aw88261) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value = AW88261_DEV_DEFAULT_CH; + u32 sync_enable = false; + + of_property_read_u32(np, "sound-channel", &channel_value); + of_property_read_u32(np, "sync-flag", &sync_enable); + + aw_dev->channel = channel_value; + aw88261->phase_sync = sync_enable; +} + +static int aw88261_init(struct aw88261 **aw88261, struct i2c_client *i2c, struct regmap *regmap) +{ + struct aw_device *aw_dev; + unsigned int chip_id; + int ret; + + /* read chip id */ + ret = regmap_read(regmap, AW88261_ID_REG, &chip_id); + if (ret) { + dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret); + return ret; + } + if (chip_id != AW88261_CHIP_ID) { + dev_err(&i2c->dev, "unsupported device"); + return -ENXIO; + } + + dev_info(&i2c->dev, "chip id = %x\n", chip_id); + + aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); + if (!aw_dev) + return -ENOMEM; + + (*aw88261)->aw_pa = aw_dev; + aw_dev->i2c = i2c; + aw_dev->regmap = regmap; + aw_dev->dev = &i2c->dev; + aw_dev->chip_id = AW88261_CHIP_ID; + aw_dev->acf = NULL; + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = 0; + aw_dev->fw_status = AW88261_DEV_FW_FAILED; + aw_dev->fade_step = AW88261_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE; + aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL; + aw88261_parse_channel_dt(*aw88261); + + return ret; +} + +static int aw88261_i2c_probe(struct i2c_client *i2c) +{ + struct aw88261 *aw88261; + int ret; + + ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C); + if (!ret) + return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed"); + + aw88261 = devm_kzalloc(&i2c->dev, sizeof(*aw88261), GFP_KERNEL); + if (!aw88261) + return -ENOMEM; + + mutex_init(&aw88261->lock); + + i2c_set_clientdata(i2c, aw88261); + + aw88261->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(aw88261->reset_gpio)) + dev_info(&i2c->dev, "reset gpio not defined\n"); + else + aw88261_hw_reset(aw88261); + + aw88261->regmap = devm_regmap_init_i2c(i2c, &aw88261_remap_config); + if (IS_ERR(aw88261->regmap)) { + ret = PTR_ERR(aw88261->regmap); + return dev_err_probe(&i2c->dev, ret, "failed to init regmap: %d\n", ret); + } + + /* aw pa init */ + ret = aw88261_init(&aw88261, i2c, aw88261->regmap); + if (ret) + return ret; + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw88261, + aw88261_dai, ARRAY_SIZE(aw88261_dai)); + if (ret) + dev_err(&i2c->dev, "failed to register aw88261: %d", ret); + + return ret; +} + +static const struct i2c_device_id aw88261_i2c_id[] = { + { AW88261_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id); + +static struct i2c_driver aw88261_i2c_driver = { + .driver = { + .name = AW88261_I2C_NAME, + }, + .probe = aw88261_i2c_probe, + .id_table = aw88261_i2c_id, +}; +module_i2c_driver(aw88261_i2c_driver); + +MODULE_DESCRIPTION("ASoC AW88261 Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h new file mode 100644 index 000000000000..4f3dbf438510 --- /dev/null +++ b/sound/soc/codecs/aw88261.h @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88261.h -- AW88261 ALSA SoC Audio driver +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Jimmy Zhang <zhangjianming@awinic.com> +// Author: Weidong Wang <wangweidong.a@awinic.com> +// + +#ifndef __AW88261_H__ +#define __AW88261_H__ + +#define AW88261_ID_REG (0x00) +#define AW88261_SYSST_REG (0x01) +#define AW88261_SYSINT_REG (0x02) +#define AW88261_SYSINTM_REG (0x03) +#define AW88261_SYSCTRL_REG (0x04) +#define AW88261_SYSCTRL2_REG (0x05) +#define AW88261_I2SCTRL1_REG (0x06) +#define AW88261_I2SCTRL2_REG (0x07) +#define AW88261_I2SCTRL3_REG (0x08) +#define AW88261_DACCFG1_REG (0x09) +#define AW88261_DACCFG2_REG (0x0A) +#define AW88261_DACCFG3_REG (0x0B) +#define AW88261_DACCFG4_REG (0x0C) +#define AW88261_DACCFG5_REG (0x0D) +#define AW88261_DACCFG6_REG (0x0E) +#define AW88261_DACCFG7_REG (0x0F) +#define AW88261_DACCFG8_REG (0x10) +#define AW88261_PWMCTRL1_REG (0x11) +#define AW88261_PWMCTRL2_REG (0x12) +#define AW88261_I2SCFG1_REG (0x13) +#define AW88261_DBGCTRL_REG (0x14) +#define AW88261_DACCFG9_REG (0x15) +#define AW88261_DACCFG10_REG (0x16) +#define AW88261_DACST_REG (0x20) +#define AW88261_VBAT_REG (0x21) +#define AW88261_TEMP_REG (0x22) +#define AW88261_PVDD_REG (0x23) +#define AW88261_ISNDAT_REG (0x24) +#define AW88261_VSNDAT_REG (0x25) +#define AW88261_I2SINT_REG (0x26) +#define AW88261_I2SCAPCNT_REG (0x27) +#define AW88261_ANASTA1_REG (0x28) +#define AW88261_ANASTA2_REG (0x29) +#define AW88261_ANASTA3_REG (0x2A) +#define AW88261_TESTDET_REG (0x2B) +#define AW88261_DSMCFG1_REG (0x30) +#define AW88261_DSMCFG2_REG (0x31) +#define AW88261_DSMCFG3_REG (0x32) +#define AW88261_DSMCFG4_REG (0x33) +#define AW88261_DSMCFG5_REG (0x34) +#define AW88261_DSMCFG6_REG (0x35) +#define AW88261_DSMCFG7_REG (0x36) +#define AW88261_DSMCFG8_REG (0x37) +#define AW88261_TESTIN_REG (0x38) +#define AW88261_TESTOUT_REG (0x39) +#define AW88261_SADCCTRL1_REG (0x3A) +#define AW88261_SADCCTRL2_REG (0x3B) +#define AW88261_SADCCTRL3_REG (0x3C) +#define AW88261_SADCCTRL4_REG (0x3D) +#define AW88261_SADCCTRL5_REG (0x3E) +#define AW88261_SADCCTRL6_REG (0x3F) +#define AW88261_SADCCTRL7_REG (0x40) +#define AW88261_VSNTM1_REG (0x50) +#define AW88261_VSNTM2_REG (0x51) +#define AW88261_ISNCTRL1_REG (0x52) +#define AW88261_ISNCTRL2_REG (0x53) +#define AW88261_PLLCTRL1_REG (0x54) +#define AW88261_PLLCTRL2_REG (0x55) +#define AW88261_PLLCTRL3_REG (0x56) +#define AW88261_CDACTRL1_REG (0x57) +#define AW88261_CDACTRL2_REG (0x58) +#define AW88261_DITHERCFG1_REG (0x59) +#define AW88261_DITHERCFG2_REG (0x5A) +#define AW88261_DITHERCFG3_REG (0x5B) +#define AW88261_CPCTRL_REG (0x5C) +#define AW88261_BSTCTRL1_REG (0x60) +#define AW88261_BSTCTRL2_REG (0x61) +#define AW88261_BSTCTRL3_REG (0x62) +#define AW88261_BSTCTRL4_REG (0x63) +#define AW88261_BSTCTRL5_REG (0x64) +#define AW88261_BSTCTRL6_REG (0x65) +#define AW88261_BSTCTRL7_REG (0x66) +#define AW88261_BSTCTRL8_REG (0x67) +#define AW88261_BSTCTRL9_REG (0x68) +#define AW88261_TM_REG (0x6F) +#define AW88261_TESTCTRL1_REG (0x70) +#define AW88261_TESTCTRL2_REG (0x71) +#define AW88261_EFCTRL1_REG (0x72) +#define AW88261_EFCTRL2_REG (0x73) +#define AW88261_EFWH_REG (0x74) +#define AW88261_EFWM2_REG (0x75) +#define AW88261_EFWM1_REG (0x76) +#define AW88261_EFWL_REG (0x77) +#define AW88261_EFRH4_REG (0x78) +#define AW88261_EFRH3_REG (0x79) +#define AW88261_EFRH2_REG (0x7A) +#define AW88261_EFRH1_REG (0x7B) +#define AW88261_EFRL4_REG (0x7C) +#define AW88261_EFRL3_REG (0x7D) +#define AW88261_EFRL2_REG (0x7E) +#define AW88261_EFRL1_REG (0x7F) + +#define AW88261_REG_MAX (0x80) +#define AW88261_EF_DBMD_MASK (0xfff7) +#define AW88261_OR_VALUE (0x0008) + +#define AW88261_TEMH_MASK (0x83ff) +#define AW88261_TEML_MASK (0x83ff) +#define AW88261_DEFAULT_CFG (0x0000) + +#define AW88261_ICALK_SHIFT (0) +#define AW88261_ICALKL_SHIFT (0) +#define AW88261_VCALK_SHIFT (0) +#define AW88261_VCALKL_SHIFT (0) + +#define AW88261_AMPPD_START_BIT (1) +#define AW88261_AMPPD_BITS_LEN (1) +#define AW88261_AMPPD_MASK \ + (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT)) + +#define AW88261_UVLS_START_BIT (14) +#define AW88261_UVLS_NORMAL (0) +#define AW88261_UVLS_NORMAL_VALUE \ + (AW88261_UVLS_NORMAL << AW88261_UVLS_START_BIT) + +#define AW88261_BSTOCS_START_BIT (11) +#define AW88261_BSTOCS_OVER_CURRENT (1) +#define AW88261_BSTOCS_OVER_CURRENT_VALUE \ + (AW88261_BSTOCS_OVER_CURRENT << AW88261_BSTOCS_START_BIT) + +#define AW88261_BSTS_START_BIT (9) +#define AW88261_BSTS_FINISHED (1) +#define AW88261_BSTS_FINISHED_VALUE \ + (AW88261_BSTS_FINISHED << AW88261_BSTS_START_BIT) + +#define AW88261_SWS_START_BIT (8) +#define AW88261_SWS_SWITCHING (1) +#define AW88261_SWS_SWITCHING_VALUE \ + (AW88261_SWS_SWITCHING << AW88261_SWS_START_BIT) + +#define AW88261_NOCLKS_START_BIT (5) +#define AW88261_NOCLKS_NO_CLOCK (1) +#define AW88261_NOCLKS_NO_CLOCK_VALUE \ + (AW88261_NOCLKS_NO_CLOCK << AW88261_NOCLKS_START_BIT) + +#define AW88261_CLKS_START_BIT (4) +#define AW88261_CLKS_STABLE (1) +#define AW88261_CLKS_STABLE_VALUE \ + (AW88261_CLKS_STABLE << AW88261_CLKS_START_BIT) + +#define AW88261_OCDS_START_BIT (3) +#define AW88261_OCDS_OC (1) +#define AW88261_OCDS_OC_VALUE \ + (AW88261_OCDS_OC << AW88261_OCDS_START_BIT) + +#define AW88261_OTHS_START_BIT (1) +#define AW88261_OTHS_OT (1) +#define AW88261_OTHS_OT_VALUE \ + (AW88261_OTHS_OT << AW88261_OTHS_START_BIT) + +#define AW88261_PLLS_START_BIT (0) +#define AW88261_PLLS_LOCKED (1) +#define AW88261_PLLS_LOCKED_VALUE \ + (AW88261_PLLS_LOCKED << AW88261_PLLS_START_BIT) + +#define AW88261_BIT_PLL_CHECK \ + (AW88261_CLKS_STABLE_VALUE | \ + AW88261_PLLS_LOCKED_VALUE) + +#define AW88261_BIT_SYSST_CHECK_MASK \ + (~(AW88261_UVLS_NORMAL_VALUE | \ + AW88261_BSTOCS_OVER_CURRENT_VALUE | \ + AW88261_BSTS_FINISHED_VALUE | \ + AW88261_SWS_SWITCHING_VALUE | \ + AW88261_NOCLKS_NO_CLOCK_VALUE | \ + AW88261_CLKS_STABLE_VALUE | \ + AW88261_OCDS_OC_VALUE | \ + AW88261_OTHS_OT_VALUE | \ + AW88261_PLLS_LOCKED_VALUE)) + +#define AW88261_BIT_SYSST_CHECK \ + (AW88261_BSTS_FINISHED_VALUE | \ + AW88261_SWS_SWITCHING_VALUE | \ + AW88261_CLKS_STABLE_VALUE | \ + AW88261_PLLS_LOCKED_VALUE) + +#define AW88261_ULS_HMUTE_START_BIT (14) +#define AW88261_ULS_HMUTE_BITS_LEN (1) +#define AW88261_ULS_HMUTE_MASK \ + (~(((1<<AW88261_ULS_HMUTE_BITS_LEN)-1) << AW88261_ULS_HMUTE_START_BIT)) + +#define AW88261_ULS_HMUTE_DISABLE (0) +#define AW88261_ULS_HMUTE_DISABLE_VALUE \ + (AW88261_ULS_HMUTE_DISABLE << AW88261_ULS_HMUTE_START_BIT) + +#define AW88261_ULS_HMUTE_ENABLE (1) +#define AW88261_ULS_HMUTE_ENABLE_VALUE \ + (AW88261_ULS_HMUTE_ENABLE << AW88261_ULS_HMUTE_START_BIT) + +#define AW88261_HMUTE_START_BIT (8) +#define AW88261_HMUTE_BITS_LEN (1) +#define AW88261_HMUTE_MASK \ + (~(((1<<AW88261_HMUTE_BITS_LEN)-1) << AW88261_HMUTE_START_BIT)) + +#define AW88261_HMUTE_DISABLE (0) +#define AW88261_HMUTE_DISABLE_VALUE \ + (AW88261_HMUTE_DISABLE << AW88261_HMUTE_START_BIT) + +#define AW88261_HMUTE_ENABLE (1) +#define AW88261_HMUTE_ENABLE_VALUE \ + (AW88261_HMUTE_ENABLE << AW88261_HMUTE_START_BIT) + +#define AW88261_AMPPD_START_BIT (1) +#define AW88261_AMPPD_BITS_LEN (1) +#define AW88261_AMPPD_MASK \ + (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT)) + +#define AW88261_AMPPD_WORKING (0) +#define AW88261_AMPPD_WORKING_VALUE \ + (AW88261_AMPPD_WORKING << AW88261_AMPPD_START_BIT) + +#define AW88261_AMPPD_POWER_DOWN (1) +#define AW88261_AMPPD_POWER_DOWN_VALUE \ + (AW88261_AMPPD_POWER_DOWN << AW88261_AMPPD_START_BIT) + +#define AW88261_PWDN_START_BIT (0) +#define AW88261_PWDN_BITS_LEN (1) +#define AW88261_PWDN_MASK \ + (~(((1<<AW88261_PWDN_BITS_LEN)-1) << AW88261_PWDN_START_BIT)) + +#define AW88261_PWDN_WORKING (0) +#define AW88261_PWDN_WORKING_VALUE \ + (AW88261_PWDN_WORKING << AW88261_PWDN_START_BIT) + +#define AW88261_PWDN_POWER_DOWN (1) +#define AW88261_PWDN_POWER_DOWN_VALUE \ + (AW88261_PWDN_POWER_DOWN << AW88261_PWDN_START_BIT) + +#define AW88261_MUTE_VOL (90 * 8) +#define AW88261_VOLUME_STEP_DB (6 * 8) + +#define AW88261_VOL_6DB_START (6) + +#define AW88261_VOL_START_BIT (0) +#define AW88261_VOL_BITS_LEN (10) +#define AW88261_VOL_MASK \ + (~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT)) + +#define AW88261_VOL_DEFAULT_VALUE (0) + +#define AW88261_I2STXEN_START_BIT (6) +#define AW88261_I2STXEN_BITS_LEN (1) +#define AW88261_I2STXEN_MASK \ + (~(((1<<AW88261_I2STXEN_BITS_LEN)-1) << AW88261_I2STXEN_START_BIT)) + +#define AW88261_I2STXEN_DISABLE (0) +#define AW88261_I2STXEN_DISABLE_VALUE \ + (AW88261_I2STXEN_DISABLE << AW88261_I2STXEN_START_BIT) + +#define AW88261_I2STXEN_ENABLE (1) +#define AW88261_I2STXEN_ENABLE_VALUE \ + (AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT) + +#define AW88261_CCO_MUX_START_BIT (14) +#define AW88261_CCO_MUX_BITS_LEN (1) +#define AW88261_CCO_MUX_MASK \ + (~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT)) + +#define AW88261_CCO_MUX_DIVIDED (0) +#define AW88261_CCO_MUX_DIVIDED_VALUE \ + (AW88261_CCO_MUX_DIVIDED << AW88261_CCO_MUX_START_BIT) + +#define AW88261_CCO_MUX_BYPASS (1) +#define AW88261_CCO_MUX_BYPASS_VALUE \ + (AW88261_CCO_MUX_BYPASS << AW88261_CCO_MUX_START_BIT) + +#define AW88261_EF_VSN_GESLP_H_START_BIT (0) +#define AW88261_EF_VSN_GESLP_H_BITS_LEN (10) +#define AW88261_EF_VSN_GESLP_H_MASK \ + (~(((1<<AW88261_EF_VSN_GESLP_H_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_H_START_BIT)) + +#define AW88261_EF_VSN_GESLP_L_START_BIT (0) +#define AW88261_EF_VSN_GESLP_L_BITS_LEN (10) +#define AW88261_EF_VSN_GESLP_L_MASK \ + (~(((1<<AW88261_EF_VSN_GESLP_L_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_L_START_BIT)) + +#define AW88261_FORCE_PWM_START_BIT (12) +#define AW88261_FORCE_PWM_BITS_LEN (1) +#define AW88261_FORCE_PWM_MASK \ + (~(((1<<AW88261_FORCE_PWM_BITS_LEN)-1) << AW88261_FORCE_PWM_START_BIT)) + +#define AW88261_FORCE_PWM_FORCEMINUS_PWM (1) +#define AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE \ + (AW88261_FORCE_PWM_FORCEMINUS_PWM << AW88261_FORCE_PWM_START_BIT) + +#define AW88261_BST_OS_WIDTH_START_BIT (0) +#define AW88261_BST_OS_WIDTH_BITS_LEN (3) +#define AW88261_BST_OS_WIDTH_MASK \ + (~(((1<<AW88261_BST_OS_WIDTH_BITS_LEN)-1) << AW88261_BST_OS_WIDTH_START_BIT)) + +#define AW88261_BST_OS_WIDTH_50NS (4) +#define AW88261_BST_OS_WIDTH_50NS_VALUE \ + (AW88261_BST_OS_WIDTH_50NS << AW88261_BST_OS_WIDTH_START_BIT) + +/* BST_LOOPR bit 1:0 (BSTCTRL6 0x65) */ +#define AW88261_BST_LOOPR_START_BIT (0) +#define AW88261_BST_LOOPR_BITS_LEN (2) +#define AW88261_BST_LOOPR_MASK \ + (~(((1<<AW88261_BST_LOOPR_BITS_LEN)-1) << AW88261_BST_LOOPR_START_BIT)) + +#define AW88261_BST_LOOPR_340K (2) +#define AW88261_BST_LOOPR_340K_VALUE \ + (AW88261_BST_LOOPR_340K << AW88261_BST_LOOPR_START_BIT) + +/* RSQN_DLY bit 15:14 (BSTCTRL7 0x66) */ +#define AW88261_RSQN_DLY_START_BIT (14) +#define AW88261_RSQN_DLY_BITS_LEN (2) +#define AW88261_RSQN_DLY_MASK \ + (~(((1<<AW88261_RSQN_DLY_BITS_LEN)-1) << AW88261_RSQN_DLY_START_BIT)) + +#define AW88261_RSQN_DLY_35NS (2) +#define AW88261_RSQN_DLY_35NS_VALUE \ + (AW88261_RSQN_DLY_35NS << AW88261_RSQN_DLY_START_BIT) + +/* BURST_SSMODE bit 3 (BSTCTRL8 0x67) */ +#define AW88261_BURST_SSMODE_START_BIT (3) +#define AW88261_BURST_SSMODE_BITS_LEN (1) +#define AW88261_BURST_SSMODE_MASK \ + (~(((1<<AW88261_BURST_SSMODE_BITS_LEN)-1) << AW88261_BURST_SSMODE_START_BIT)) + +#define AW88261_BURST_SSMODE_FAST (0) +#define AW88261_BURST_SSMODE_FAST_VALUE \ + (AW88261_BURST_SSMODE_FAST << AW88261_BURST_SSMODE_START_BIT) + +/* BST_BURST bit 9:7 (BSTCTRL9 0x68) */ +#define AW88261_BST_BURST_START_BIT (7) +#define AW88261_BST_BURST_BITS_LEN (3) +#define AW88261_BST_BURST_MASK \ + (~(((1<<AW88261_BST_BURST_BITS_LEN)-1) << AW88261_BST_BURST_START_BIT)) + +#define AW88261_BST_BURST_30MA (2) +#define AW88261_BST_BURST_30MA_VALUE \ + (AW88261_BST_BURST_30MA << AW88261_BST_BURST_START_BIT) + +#define AW88261_EF_VSN_GESLP_SIGN_MASK (~0x0200) +#define AW88261_EF_VSN_GESLP_NEG (~0xfc00) + +#define AW88261_EF_ISN_GESLP_SIGN_MASK (~0x0200) +#define AW88261_EF_ISN_GESLP_NEG (~0xfc00) + +#define AW88261_EF_ISN_GESLP_H_START_BIT (0) +#define AW88261_EF_ISN_GESLP_H_BITS_LEN (10) +#define AW88261_EF_ISN_GESLP_H_MASK \ + (~(((1<<AW88261_EF_ISN_GESLP_H_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_H_START_BIT)) + +#define AW88261_EF_ISN_GESLP_L_START_BIT (0) +#define AW88261_EF_ISN_GESLP_L_BITS_LEN (10) +#define AW88261_EF_ISN_GESLP_L_MASK \ + (~(((1<<AW88261_EF_ISN_GESLP_L_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_L_START_BIT)) + +#define AW88261_CABL_BASE_VALUE (1000) +#define AW88261_ICABLK_FACTOR (1) +#define AW88261_VCABLK_FACTOR (1) + +#define AW88261_VCAL_FACTOR (1<<13) + +#define AW88261_START_RETRIES (5) +#define AW88261_START_WORK_DELAY_MS (0) + +#define AW88261_I2C_NAME "aw88261_smartpa" + +#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \ + SNDRV_PCM_RATE_96000) +#define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define FADE_TIME_MAX 100000 +#define FADE_TIME_MIN 0 + +#define AW88261_DEV_DEFAULT_CH (0) +#define AW88261_ACF_FILE "aw88261_acf.bin" +#define AW88261_DEV_SYSST_CHECK_MAX (10) +#define AW88261_SOFT_RESET_VALUE (0x55aa) +#define AW88261_REG_TO_DB (0x3f) +#define AW88261_VOL_START_MASK (0xfc00) +#define AW88261_INIT_PROFILE (0) + +#define REG_VAL_TO_DB(value) ((((value) >> AW88261_VOL_6DB_START) * \ + AW88261_VOLUME_STEP_DB) + \ + ((value) & AW88261_REG_TO_DB)) +#define DB_TO_REG_VAL(value) ((((value) / AW88261_VOLUME_STEP_DB) << \ + AW88261_VOL_6DB_START) + \ + ((value) % AW88261_VOLUME_STEP_DB)) + +#define AW88261_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = profile_info, \ + .get = profile_get, \ + .put = profile_set, \ +} + +enum { + AW88261_SYNC_START = 0, + AW88261_ASYNC_START, +}; + +enum aw88261_id { + AW88261_CHIP_ID = 0x2113, +}; + +enum { + AW88261_500_US = 500, + AW88261_1000_US = 1000, + AW88261_2000_US = 2000, +}; + +enum { + AW88261_DEV_PW_OFF = 0, + AW88261_DEV_PW_ON, +}; + +enum { + AW88261_DEV_FW_FAILED = 0, + AW88261_DEV_FW_OK, +}; + +enum { + AW88261_EF_AND_CHECK = 0, + AW88261_EF_OR_CHECK, +}; + +enum { + AW88261_FRCSET_DISABLE = 0, + AW88261_FRCSET_ENABLE, +}; + +struct aw88261 { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; + + int efuse_check; + int frcset_en; + unsigned int mute_st; + unsigned int amppd_st; + + unsigned char phase_sync; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c index 05bcf49da857..8ee1baa03269 100644 --- a/sound/soc/codecs/aw88395/aw88395_lib.c +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -11,6 +11,7 @@ #include <linux/i2c.h> #include "aw88395_lib.h" #include "aw88395_device.h" +#include "aw88395_reg.h" #define AW88395_CRC8_POLYNOMIAL 0x8C DECLARE_CRC8_TABLE(aw_crc8_table); @@ -429,6 +430,53 @@ parse_bin_failed: return ret; } +static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin; + int ret; + + aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(*aw_bin), GFP_KERNEL); + if (!aw_bin) + return -ENOMEM; + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw_parsing_bin_file(aw_dev, aw_bin); + if (ret < 0) { + dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + if ((aw_bin->all_bin_parse_num != 1) || + (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { + dev_err(aw_dev->dev, "bin num or type error"); + goto parse_bin_failed; + } + + if (aw_bin->header_info[0].valid_data_len % 4) { + dev_err(aw_dev->dev, "bin data len get error!"); + goto parse_bin_failed; + } + + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = + data + aw_bin->header_info[0].valid_data_addr; + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = + aw_bin->header_info[0].valid_data_len; + prof_desc->prof_st = AW88395_PROFILE_OK; + + devm_kfree(aw_dev->dev, aw_bin); + aw_bin = NULL; + + return 0; + +parse_bin_failed: + devm_kfree(aw_dev->dev, aw_bin); + aw_bin = NULL; + return ret; +} + static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc) { @@ -447,6 +495,9 @@ static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg return aw_dev_prof_parse_multi_bin( aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset, cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_HDR_REG: + return aw_dev_parse_reg_bin_with_hdr(aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); default: dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type); break; @@ -527,7 +578,49 @@ static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, return 0; } -static int aw_dev_cfg_get_valid_prof(struct aw_device *aw_dev, +static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev, + struct aw_all_prof_info all_prof_info) +{ + struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + int num = 0; + int i; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) + prof_info->count++; + } + + dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count); + + if (!prof_info->count) { + dev_err(aw_dev->dev, "no profile data"); + return -EPERM; + } + + prof_info->prof_desc = devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { + if (num >= prof_info->count) { + dev_err(aw_dev->dev, "overflow count[%d]", + prof_info->count); + return -EINVAL; + } + prof_info->prof_desc[num] = prof_desc[i]; + prof_info->prof_desc[num].id = i; + num++; + } + } + + return 0; +} + +static int aw88395_dev_cfg_get_valid_prof(struct aw_device *aw_dev, struct aw_all_prof_info all_prof_info) { struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; @@ -606,9 +699,22 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, goto exit; } - ret = aw_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); - if (ret < 0) - goto exit; + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + ret = aw88395_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + break; + case AW88261_CHIP_ID: + ret = aw88261_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + break; + default: + dev_err(aw_dev->dev, "valid prof unsupported"); + ret = -EINVAL; + break; + } aw_dev->prof_info.prof_name_list = profile_name; @@ -679,16 +785,37 @@ static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_contain struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); unsigned int i; + int ret; - for (i = 0; i < cfg_hdr->ddt_num; ++i) { - if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && - (aw_dev->chip_id == cfg_dde[i].chip_id) && - (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && - (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) - (*scene_num)++; + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) + (*scene_num)++; + } + ret = 0; + break; + case AW88261_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || + (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) + (*scene_num)++; + } + ret = 0; + break; + default: + dev_err(aw_dev->dev, "unsupported device"); + ret = -EINVAL; + break; } - return 0; + return ret; } static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, @@ -699,15 +826,35 @@ static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); unsigned int i; + int ret; - for (i = 0; i < cfg_hdr->ddt_num; ++i) { - if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && - (aw_dev->chip_id == cfg_dde[i].chip_id) && - (aw_dev->channel == cfg_dde[i].dev_index)) - (*scene_num)++; + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; + } + ret = 0; + break; + case AW88261_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || + (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; + } + ret = 0; + break; + default: + dev_err(aw_dev->dev, "unsupported device"); + ret = -EINVAL; + break; } - return 0; + return ret; } static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev, @@ -756,6 +903,18 @@ static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev, prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; (*cur_scene_id)++; break; + case ACF_SEC_TYPE_HDR_REG: + ret = aw_dev_parse_reg_bin_with_hdr(aw_dev, + (uint8_t *)prof_hdr + cfg_dde->data_offset, + cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse reg bin with hdr failed"); + return ret; + } + prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str; + prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; + (*cur_scene_id)++; + break; default: dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type); return -EINVAL; diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h index e64f24e97150..e7a7c02efaf3 100644 --- a/sound/soc/codecs/aw88395/aw88395_reg.h +++ b/sound/soc/codecs/aw88395/aw88395_reg.h @@ -96,6 +96,7 @@ enum aw88395_id { AW88395_CHIP_ID = 0x2049, + AW88261_CHIP_ID = 0x2113, }; #define AW88395_REG_MAX (0x7D) diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index 04ba7f25012e..20084c7d3acb 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -1312,7 +1312,7 @@ static struct regmap_config cs35l36_regmap = { .precious_reg = cs35l36_precious_reg, .volatile_reg = cs35l36_volatile_reg, .readable_reg = cs35l36_readable_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static irqreturn_t cs35l36_irq(int irq, void *data) diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c index 1e4205295a0d..4ec306cd2f47 100644 --- a/sound/soc/codecs/cs35l41-lib.c +++ b/sound/soc/codecs/cs35l41-lib.c @@ -743,7 +743,7 @@ struct regmap_config cs35l41_regmap_i2c = { .volatile_reg = cs35l41_volatile_reg, .readable_reg = cs35l41_readable_reg, .precious_reg = cs35l41_precious_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c); @@ -760,7 +760,7 @@ struct regmap_config cs35l41_regmap_spi = { .volatile_reg = cs35l41_volatile_reg, .readable_reg = cs35l41_readable_reg, .precious_reg = cs35l41_precious_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs35l41_regmap_spi); @@ -1080,28 +1080,32 @@ static const struct reg_sequence cs35l41_safe_to_reset[] = { { 0x00000040, 0x00000033 }, }; -static const struct reg_sequence cs35l41_active_to_safe[] = { +static const struct reg_sequence cs35l41_active_to_safe_start[] = { { 0x00000040, 0x00000055 }, { 0x00000040, 0x000000AA }, { 0x00007438, 0x00585941 }, { CS35L41_PWR_CTRL1, 0x00000000 }, - { 0x0000742C, 0x00000009, 3000 }, + { 0x0000742C, 0x00000009 }, +}; + +static const struct reg_sequence cs35l41_active_to_safe_end[] = { { 0x00007438, 0x00580941 }, { 0x00000040, 0x000000CC }, { 0x00000040, 0x00000033 }, }; -static const struct reg_sequence cs35l41_safe_to_active[] = { +static const struct reg_sequence cs35l41_safe_to_active_start[] = { { 0x00000040, 0x00000055 }, { 0x00000040, 0x000000AA }, { 0x0000742C, 0x0000000F }, { 0x0000742C, 0x00000079 }, { 0x00007438, 0x00585941 }, - { CS35L41_PWR_CTRL1, 0x00000001, 3000 }, // GLOBAL_EN = 1 + { CS35L41_PWR_CTRL1, 0x00000001 }, // GLOBAL_EN = 1 +}; + +static const struct reg_sequence cs35l41_safe_to_active_en_spk[] = { { 0x0000742C, 0x000000F9 }, { 0x00007438, 0x00580941 }, - { 0x00000040, 0x000000CC }, - { 0x00000040, 0x00000033 }, }; static const struct reg_sequence cs35l41_reset_to_safe[] = { @@ -1188,11 +1192,12 @@ bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type) } EXPORT_SYMBOL_GPL(cs35l41_safe_reset); -int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable, - struct completion *pll_lock) +int cs35l41_global_enable(struct device *dev, struct regmap *regmap, enum cs35l41_boost_type b_type, + int enable, struct completion *pll_lock, bool firmware_running) { int ret; - unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3; + unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3, int_status, pup_pdn_mask; + unsigned int pwr_ctl1_val; struct reg_sequence cs35l41_mdsync_down_seq[] = { {CS35L41_PWR_CTRL3, 0}, {CS35L41_GPIO_PAD_CONTROL, 0}, @@ -1204,6 +1209,20 @@ int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, {CS35L41_PWR_CTRL1, 0x00000001, 3000}, }; + pup_pdn_mask = enable ? CS35L41_PUP_DONE_MASK : CS35L41_PDN_DONE_MASK; + + ret = regmap_read(regmap, CS35L41_PWR_CTRL1, &pwr_ctl1_val); + if (ret) + return ret; + + if ((pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && enable) { + dev_dbg(dev, "Cannot set Global Enable - already set.\n"); + return 0; + } else if (!(pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && !enable) { + dev_dbg(dev, "Cannot unset Global Enable - not set.\n"); + return 0; + } + switch (b_type) { case CS35L41_SHD_BOOST_ACTV: case CS35L41_SHD_BOOST_PASS: @@ -1240,20 +1259,85 @@ int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_up_seq, ARRAY_SIZE(cs35l41_mdsync_up_seq)); } + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, + int_status, int_status & pup_pdn_mask, + 1000, 100000); + if (ret) + dev_err(dev, "Enable(%d) failed: %d\n", enable, ret); + + // Clear PUP/PDN status + regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask); break; case CS35L41_INT_BOOST: ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK, enable << CS35L41_GLOBAL_EN_SHIFT); - usleep_range(3000, 3100); + if (ret) { + dev_err(dev, "CS35L41_PWR_CTRL1 set failed: %d\n", ret); + return ret; + } + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, + int_status, int_status & pup_pdn_mask, + 1000, 100000); + if (ret) + dev_err(dev, "Enable(%d) failed: %d\n", enable, ret); + + /* Clear PUP/PDN status */ + regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask); break; case CS35L41_EXT_BOOST: case CS35L41_EXT_BOOST_NO_VSPK_SWITCH: - if (enable) - ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active, - ARRAY_SIZE(cs35l41_safe_to_active)); - else - ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe, - ARRAY_SIZE(cs35l41_active_to_safe)); + if (enable) { + /* Test Key is unlocked here */ + ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_start, + ARRAY_SIZE(cs35l41_safe_to_active_start)); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status, + int_status & CS35L41_PUP_DONE_MASK, 1000, 100000); + if (ret) { + dev_err(dev, "Failed waiting for CS35L41_PUP_DONE_MASK: %d\n", ret); + /* Lock the test key, it was unlocked during the multi_reg_write */ + cs35l41_test_key_lock(dev, regmap); + return ret; + } + regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PUP_DONE_MASK); + + if (firmware_running) + ret = cs35l41_set_cspl_mbox_cmd(dev, regmap, + CSPL_MBOX_CMD_SPK_OUT_ENABLE); + else + ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_en_spk, + ARRAY_SIZE(cs35l41_safe_to_active_en_spk)); + + /* Lock the test key, it was unlocked during the multi_reg_write */ + cs35l41_test_key_lock(dev, regmap); + } else { + /* Test Key is unlocked here */ + ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_start, + ARRAY_SIZE(cs35l41_active_to_safe_start)); + if (ret) { + /* Lock the test key, it was unlocked during the multi_reg_write */ + cs35l41_test_key_lock(dev, regmap); + return ret; + } + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status, + int_status & CS35L41_PDN_DONE_MASK, 1000, 100000); + if (ret) { + dev_err(dev, "Failed waiting for CS35L41_PDN_DONE_MASK: %d\n", ret); + /* Lock the test key, it was unlocked during the multi_reg_write */ + cs35l41_test_key_lock(dev, regmap); + return ret; + } + regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PDN_DONE_MASK); + + /* Test Key is locked here */ + ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_end, + ARRAY_SIZE(cs35l41_active_to_safe_end)); + } break; default: ret = -EINVAL; @@ -1344,6 +1428,8 @@ static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd, return (sts == CSPL_MBOX_STS_RUNNING); case CSPL_MBOX_CMD_STOP_PRE_REINIT: return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT); + case CSPL_MBOX_CMD_SPK_OUT_ENABLE: + return (sts == CSPL_MBOX_STS_RUNNING); default: return false; } diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 8a879b6f4829..722b69a6de26 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -491,7 +491,6 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); - unsigned int val; int ret = 0; switch (event) { @@ -500,21 +499,12 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, cs35l41_pup_patch, ARRAY_SIZE(cs35l41_pup_patch)); - cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1, - &cs35l41->pll_lock); + ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type, + 1, &cs35l41->pll_lock, cs35l41->dsp.cs_dsp.running); break; case SND_SOC_DAPM_POST_PMD: - cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0, - &cs35l41->pll_lock); - - ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1, - val, val & CS35L41_PDN_DONE_MASK, - 1000, 100000); - if (ret) - dev_warn(cs35l41->dev, "PDN failed: %d\n", ret); - - regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1, - CS35L41_PDN_DONE_MASK); + ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type, + 0, &cs35l41->pll_lock, cs35l41->dsp.cs_dsp.running); regmap_multi_reg_write_bypassed(cs35l41->regmap, cs35l41_pdn_patch, diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c index 066f83c0c7ac..621af1785979 100644 --- a/sound/soc/codecs/cs35l45-tables.c +++ b/sound/soc/codecs/cs35l45-tables.c @@ -255,7 +255,7 @@ const struct regmap_config cs35l45_i2c_regmap = { .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults), .volatile_reg = cs35l45_volatile_reg, .readable_reg = cs35l45_readable_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45); @@ -271,7 +271,7 @@ const struct regmap_config cs35l45_spi_regmap = { .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults), .volatile_reg = cs35l45_volatile_reg, .readable_reg = cs35l45_readable_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 40666e6698ba..9f4f2f4f23f5 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -26,14 +26,14 @@ static int cs35l56_i2c_probe(struct i2c_client *client) if (!cs35l56) return -ENOMEM; - cs35l56->dev = dev; - cs35l56->can_hibernate = true; + cs35l56->base.dev = dev; + cs35l56->base.can_hibernate = true; i2c_set_clientdata(client, cs35l56); - cs35l56->regmap = devm_regmap_init_i2c(client, regmap_config); - if (IS_ERR(cs35l56->regmap)) { - ret = PTR_ERR(cs35l56->regmap); - return dev_err_probe(cs35l56->dev, ret, "Failed to allocate register map\n"); + cs35l56->base.regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(cs35l56->base.regmap)) { + ret = PTR_ERR(cs35l56->base.regmap); + return dev_err_probe(cs35l56->base.dev, ret, "Failed to allocate register map\n"); } ret = cs35l56_common_probe(cs35l56); @@ -42,7 +42,7 @@ static int cs35l56_i2c_probe(struct i2c_client *client) ret = cs35l56_init(cs35l56); if (ret == 0) - ret = cs35l56_irq_request(cs35l56, client->irq); + ret = cs35l56_irq_request(&cs35l56->base, client->irq); if (ret < 0) cs35l56_remove(cs35l56); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 2cde78605ba9..b433266b7844 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -166,13 +166,13 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); int ret; - pm_runtime_get_noresume(cs35l56->dev); + pm_runtime_get_noresume(cs35l56->base.dev); - regcache_cache_only(cs35l56->regmap, false); + regcache_cache_only(cs35l56->base.regmap, false); ret = cs35l56_init(cs35l56); if (ret < 0) { - regcache_cache_only(cs35l56->regmap, true); + regcache_cache_only(cs35l56->base.regmap, true); goto out; } @@ -180,15 +180,15 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) * cs35l56_init can return with !init_done if it triggered * a soft reset. */ - if (cs35l56->init_done) { + if (cs35l56->base.init_done) { /* Enable SoundWire interrupts */ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, CS35L56_SDW_INT_MASK_CODEC_IRQ); } out: - pm_runtime_mark_last_busy(cs35l56->dev); - pm_runtime_put_autosuspend(cs35l56->dev); + pm_runtime_mark_last_busy(cs35l56->base.dev); + pm_runtime_put_autosuspend(cs35l56->base.dev); } static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, @@ -198,7 +198,7 @@ static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, /* SoundWire core holds our pm_runtime when calling this function. */ - dev_dbg(cs35l56->dev, "int control_port=%#x\n", status->control_port); + dev_dbg(cs35l56->base.dev, "int control_port=%#x\n", status->control_port); if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0) return 0; @@ -207,7 +207,7 @@ static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, * Prevent bus manager suspending and possibly issuing a * bus-reset before the queued work has run. */ - pm_runtime_get_noresume(cs35l56->dev); + pm_runtime_get_noresume(cs35l56->base.dev); /* * Mask and clear until it has been handled. The read of GEN_INT_STAT_1 @@ -230,14 +230,14 @@ static void cs35l56_sdw_irq_work(struct work_struct *work) struct cs35l56_private, sdw_irq_work); - cs35l56_irq(-1, cs35l56); + cs35l56_irq(-1, &cs35l56->base); /* unmask interrupts */ if (!cs35l56->sdw_irq_no_unmask) sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, CS35L56_SDW_INT_MASK_CODEC_IRQ); - pm_runtime_put_autosuspend(cs35l56->dev); + pm_runtime_put_autosuspend(cs35l56->base.dev); } static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) @@ -246,7 +246,7 @@ static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) struct sdw_slave_prop *prop = &peripheral->prop; struct sdw_dpn_prop *ports; - ports = devm_kcalloc(cs35l56->dev, 2, sizeof(*ports), GFP_KERNEL); + ports = devm_kcalloc(cs35l56->base.dev, 2, sizeof(*ports), GFP_KERNEL); if (!ports) return -ENOMEM; @@ -279,17 +279,17 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral, switch (status) { case SDW_SLAVE_ATTACHED: - dev_dbg(cs35l56->dev, "%s: ATTACHED\n", __func__); + dev_dbg(cs35l56->base.dev, "%s: ATTACHED\n", __func__); if (cs35l56->sdw_attached) break; - if (!cs35l56->init_done || cs35l56->soft_resetting) + if (!cs35l56->base.init_done || cs35l56->soft_resetting) cs35l56_sdw_init(peripheral); cs35l56->sdw_attached = true; break; case SDW_SLAVE_UNATTACHED: - dev_dbg(cs35l56->dev, "%s: UNATTACHED\n", __func__); + dev_dbg(cs35l56->base.dev, "%s: UNATTACHED\n", __func__); cs35l56->sdw_attached = false; break; default: @@ -305,7 +305,7 @@ static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56, unsigned int curr_scale_reg, next_scale_reg; int curr_scale, next_scale, ret; - if (!cs35l56->init_done) + if (!cs35l56->base.init_done) return 0; if (peripheral->bus->params.curr_bank) { @@ -324,13 +324,13 @@ static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56, */ curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg); if (curr_scale < 0) { - dev_err(cs35l56->dev, "Failed to read current clock scale: %d\n", curr_scale); + dev_err(cs35l56->base.dev, "Failed to read current clock scale: %d\n", curr_scale); return curr_scale; } next_scale = sdw_read_no_pm(peripheral, next_scale_reg); if (next_scale < 0) { - dev_err(cs35l56->dev, "Failed to read next clock scale: %d\n", next_scale); + dev_err(cs35l56->base.dev, "Failed to read next clock scale: %d\n", next_scale); return next_scale; } @@ -338,7 +338,8 @@ static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56, next_scale = cs35l56->old_sdw_clock_scale; ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale); if (ret < 0) { - dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret); + dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", + ret); return ret; } } @@ -346,11 +347,11 @@ static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56, cs35l56->old_sdw_clock_scale = curr_scale; ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE); if (ret < 0) { - dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret); + dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", ret); return ret; } - dev_dbg(cs35l56->dev, "Next bus scale: %#x\n", next_scale); + dev_dbg(cs35l56->base.dev, "Next bus scale: %#x\n", next_scale); return 0; } @@ -362,9 +363,10 @@ static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral, int sclk; sclk = params->curr_dr_freq / 2; - dev_dbg(cs35l56->dev, "%s: sclk=%u c=%u r=%u\n", __func__, sclk, params->col, params->row); + dev_dbg(cs35l56->base.dev, "%s: sclk=%u c=%u r=%u\n", + __func__, sclk, params->col, params->row); - if (cs35l56->rev < 0xb0) + if (cs35l56->base.rev < 0xb0) return cs35l56_a1_kick_divider(cs35l56, peripheral); return 0; @@ -376,7 +378,7 @@ static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral, { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - dev_dbg(cs35l56->dev, "%s: mode:%d type:%d\n", __func__, mode, type); + dev_dbg(cs35l56->base.dev, "%s: mode:%d type:%d\n", __func__, mode, type); return 0; } @@ -397,10 +399,10 @@ static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs if (peripheral->unattach_request) { /* Cannot access registers until bus is re-initialized. */ - dev_dbg(cs35l56->dev, "Wait for initialization_complete\n"); + dev_dbg(cs35l56->base.dev, "Wait for initialization_complete\n"); if (!wait_for_completion_timeout(&peripheral->initialization_complete, msecs_to_jiffies(5000))) { - dev_err(cs35l56->dev, "initialization_complete timed out\n"); + dev_err(cs35l56->base.dev, "initialization_complete timed out\n"); return -ETIMEDOUT; } @@ -419,10 +421,10 @@ static int __maybe_unused cs35l56_sdw_runtime_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); - if (!cs35l56->init_done) + if (!cs35l56->base.init_done) return 0; - return cs35l56_runtime_suspend(dev); + return cs35l56_runtime_suspend_common(&cs35l56->base); } static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) @@ -432,14 +434,14 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) dev_dbg(dev, "Runtime resume\n"); - if (!cs35l56->init_done) + if (!cs35l56->base.init_done) return 0; ret = cs35l56_sdw_handle_unattach(cs35l56); if (ret < 0) return ret; - ret = cs35l56_runtime_resume_common(cs35l56); + ret = cs35l56_runtime_resume_common(&cs35l56->base, true); if (ret) return ret; @@ -454,7 +456,7 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); - if (!cs35l56->init_done) + if (!cs35l56->base.init_done) return 0; /* @@ -493,21 +495,21 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi if (!cs35l56) return -ENOMEM; - cs35l56->dev = dev; + cs35l56->base.dev = dev; cs35l56->sdw_peripheral = peripheral; INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work); dev_set_drvdata(dev, cs35l56); - cs35l56->regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw, + cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw, peripheral, &cs35l56_regmap_sdw); - if (IS_ERR(cs35l56->regmap)) { - ret = PTR_ERR(cs35l56->regmap); + if (IS_ERR(cs35l56->base.regmap)) { + ret = PTR_ERR(cs35l56->base.regmap); return dev_err_probe(dev, ret, "Failed to allocate register map\n"); } /* Start in cache-only until device is enumerated */ - regcache_cache_only(cs35l56->regmap, true); + regcache_cache_only(cs35l56->base.regmap, true); ret = cs35l56_common_probe(cs35l56); if (ret != 0) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 60da8c75b7b9..ae373f335ea8 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -11,6 +11,20 @@ #include "cs35l56.h" +static const struct reg_sequence cs35l56_patch[] = { + /* These are not reset by a soft-reset, so patch to defaults. */ + { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 }, + { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 }, + { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 }, +}; + +int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) +{ + return regmap_register_patch(cs35l56_base->regmap, cs35l56_patch, + ARRAY_SIZE(cs35l56_patch)); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED); + static const struct reg_default cs35l56_reg_defaults[] = { { CS35L56_ASP1_ENABLES1, 0x00000000 }, { CS35L56_ASP1_CONTROL1, 0x00000028 }, @@ -35,9 +49,9 @@ static const struct reg_default cs35l56_reg_defaults[] = { { CS35L56_IRQ1_MASK_8, 0xfc000fff }, { CS35L56_IRQ1_MASK_18, 0x1f7df0ff }, { CS35L56_IRQ1_MASK_20, 0x15c00000 }, - /* CS35L56_MAIN_RENDER_USER_MUTE - soft register, no default */ - /* CS35L56_MAIN_RENDER_USER_VOLUME - soft register, no default */ - /* CS35L56_MAIN_POSTURE_NUMBER - soft register, no default */ + { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 }, + { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 }, + { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 }, }; static bool cs35l56_is_dsp_memory(unsigned int reg) @@ -181,33 +195,463 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) } } -static const u32 cs35l56_firmware_registers[] = { - CS35L56_MAIN_RENDER_USER_MUTE, - CS35L56_MAIN_RENDER_USER_VOLUME, - CS35L56_MAIN_POSTURE_NUMBER, +int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command) +{ + unsigned int val; + int ret; + + regmap_write(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command); + ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + val, (val == 0), + CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US); + if (ret) { + dev_warn(cs35l56_base->dev, "MBOX command %#x failed: %d\n", command, ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_mbox_send, SND_SOC_CS35L56_SHARED); + +int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base) +{ + int ret; + unsigned int reg; + unsigned int val; + + ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_SHUTDOWN); + if (ret) + return ret; + + if (cs35l56_base->rev < CS35L56_REVID_B0) + reg = CS35L56_DSP1_PM_CUR_STATE_A1; + else + reg = CS35L56_DSP1_PM_CUR_STATE; + + ret = regmap_read_poll_timeout(cs35l56_base->regmap, reg, + val, (val == CS35L56_HALO_STATE_SHUTDOWN), + CS35L56_HALO_STATE_POLL_US, + CS35L56_HALO_STATE_TIMEOUT_US); + if (ret < 0) + dev_err(cs35l56_base->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n", + val, ret); + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_firmware_shutdown, SND_SOC_CS35L56_SHARED); + +int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base) +{ + unsigned int reg; + unsigned int val; + int ret; + + if (cs35l56_base->rev < CS35L56_REVID_B0) + reg = CS35L56_DSP1_HALO_STATE_A1; + else + reg = CS35L56_DSP1_HALO_STATE; + + ret = regmap_read_poll_timeout(cs35l56_base->regmap, reg, + val, + (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE), + CS35L56_HALO_STATE_POLL_US, + CS35L56_HALO_STATE_TIMEOUT_US); + + if ((ret < 0) && (ret != -ETIMEDOUT)) { + dev_err(cs35l56_base->dev, "Failed to read HALO_STATE: %d\n", ret); + return ret; + } + + if ((ret == -ETIMEDOUT) || (val != CS35L56_HALO_STATE_BOOT_DONE)) { + dev_err(cs35l56_base->dev, "Firmware boot fail: HALO_STATE=%#x\n", val); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_wait_for_firmware_boot, SND_SOC_CS35L56_SHARED); + +void cs35l56_wait_control_port_ready(void) +{ + /* Wait for control port to be ready (datasheet tIRS). */ + usleep_range(CS35L56_CONTROL_PORT_READY_US, 2 * CS35L56_CONTROL_PORT_READY_US); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_wait_control_port_ready, SND_SOC_CS35L56_SHARED); + +void cs35l56_wait_min_reset_pulse(void) +{ + /* Satisfy minimum reset pulse width spec */ + usleep_range(CS35L56_RESET_PULSE_MIN_US, 2 * CS35L56_RESET_PULSE_MIN_US); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_wait_min_reset_pulse, SND_SOC_CS35L56_SHARED); + +static const struct reg_sequence cs35l56_system_reset_seq[] = { + REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET), }; -void cs35l56_reread_firmware_registers(struct device *dev, struct regmap *regmap) +void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) +{ + /* + * Must enter cache-only first so there can't be any more register + * accesses other than the controlled system reset sequence below. + */ + regcache_cache_only(cs35l56_base->regmap, true); + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l56_system_reset_seq, + ARRAY_SIZE(cs35l56_system_reset_seq)); + + /* On SoundWire the registers won't be accessible until it re-enumerates. */ + if (is_soundwire) + return; + + cs35l56_wait_control_port_ready(); + regcache_cache_only(cs35l56_base->regmap, false); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, SND_SOC_CS35L56_SHARED); + +int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) +{ + int ret; + + if (!irq) + return 0; + + ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "cs35l56", cs35l56_base); + if (!ret) + cs35l56_base->irq = irq; + else + dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, SND_SOC_CS35L56_SHARED); + +irqreturn_t cs35l56_irq(int irq, void *data) +{ + struct cs35l56_base *cs35l56_base = data; + unsigned int status1 = 0, status8 = 0, status20 = 0; + unsigned int mask1, mask8, mask20; + unsigned int val; + int rv; + + irqreturn_t ret = IRQ_NONE; + + if (!cs35l56_base->init_done) + return IRQ_NONE; + + mutex_lock(&cs35l56_base->irq_lock); + + rv = pm_runtime_resume_and_get(cs35l56_base->dev); + if (rv < 0) { + dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv); + goto err_unlock; + } + + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val); + if ((val & CS35L56_IRQ1_STS_MASK) == 0) { + dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n"); + goto err; + } + + /* Ack interrupts */ + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, &status1); + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1, &mask1); + status1 &= ~mask1; + regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, status1); + + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, &status8); + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8, &mask8); + status8 &= ~mask8; + regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, status8); + + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_20, &status20); + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, &mask20); + status20 &= ~mask20; + /* We don't want EINT20 but they default to unmasked: force mask */ + regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); + + dev_dbg(cs35l56_base->dev, "%s: %#x %#x\n", __func__, status1, status8); + + /* Check to see if unmasked bits are active */ + if (!status1 && !status8 && !status20) + goto err; + + if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK) + dev_crit(cs35l56_base->dev, "Amp short error\n"); + + if (status8 & CS35L56_TEMP_ERR_EINT1_MASK) + dev_crit(cs35l56_base->dev, "Overtemp error\n"); + + ret = IRQ_HANDLED; + +err: + pm_runtime_put(cs35l56_base->dev); +err_unlock: + mutex_unlock(&cs35l56_base->irq_lock); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_irq, SND_SOC_CS35L56_SHARED); + +int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base) +{ + unsigned int val; + int ret; + + /* Nothing to re-patch if we haven't done any patching yet. */ + if (!cs35l56_base->fw_patched) + return false; + + /* + * If we have control of RESET we will have asserted it so the firmware + * will need re-patching. + */ + if (cs35l56_base->reset_gpio) + return true; + + /* + * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so + * can't be used here to test for memory retention. + * Assume that tuning must be re-loaded. + */ + if (cs35l56_base->secured) + return true; + + ret = pm_runtime_resume_and_get(cs35l56_base->dev); + if (ret) { + dev_err(cs35l56_base->dev, "Failed to runtime_get: %d\n", ret); + return ret; + } + + ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &val); + if (ret) + dev_err(cs35l56_base->dev, "Failed to read PROTECTION_STATUS: %d\n", ret); + else + ret = !!(val & CS35L56_FIRMWARE_MISSING); + + pm_runtime_put_autosuspend(cs35l56_base->dev); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_is_fw_reload_needed, SND_SOC_CS35L56_SHARED); + +static const struct reg_sequence cs35l56_hibernate_seq[] = { + /* This must be the last register access */ + REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_HIBERNATE_NOW), +}; + +static const struct reg_sequence cs35l56_hibernate_wake_seq[] = { + REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_WAKEUP), +}; + +int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base) { - int i; unsigned int val; + int ret; + + if (!cs35l56_base->init_done) + return 0; - for (i = 0; i < ARRAY_SIZE(cs35l56_firmware_registers); i++) { - regmap_read(regmap, cs35l56_firmware_registers[i], &val); - dev_dbg(dev, "%s: %d: %#x: %#x\n", __func__, - i, cs35l56_firmware_registers[i], val); + /* Firmware must have entered a power-save state */ + ret = regmap_read_poll_timeout(cs35l56_base->regmap, + CS35L56_TRANSDUCER_ACTUAL_PS, + val, (val >= CS35L56_PS3), + CS35L56_PS3_POLL_US, + CS35L56_PS3_TIMEOUT_US); + if (ret) + dev_warn(cs35l56_base->dev, "PS3 wait failed: %d\n", ret); + + /* Clear BOOT_DONE so it can be used to detect a reboot */ + regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK); + + if (!cs35l56_base->can_hibernate) { + regcache_cache_only(cs35l56_base->regmap, true); + dev_dbg(cs35l56_base->dev, "Suspended: no hibernate"); + + return 0; } + + /* + * Enable auto-hibernate. If it is woken by some other wake source + * it will automatically return to hibernate. + */ + cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE); + + /* + * Must enter cache-only first so there can't be any more register + * accesses other than the controlled hibernate sequence below. + */ + regcache_cache_only(cs35l56_base->regmap, true); + + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l56_hibernate_seq, + ARRAY_SIZE(cs35l56_hibernate_seq)); + + dev_dbg(cs35l56_base->dev, "Suspended: hibernate"); + + return 0; } -EXPORT_SYMBOL_NS_GPL(cs35l56_reread_firmware_registers, SND_SOC_CS35L56_SHARED); +EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend_common, SND_SOC_CS35L56_SHARED); + +int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire) +{ + unsigned int val; + int ret; + + if (!cs35l56_base->init_done) + return 0; + + if (!cs35l56_base->can_hibernate) + goto out_sync; + + if (!is_soundwire) { + /* + * Dummy transaction to trigger I2C/SPI auto-wake. This will NAK on I2C. + * Must be done before releasing cache-only. + */ + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l56_hibernate_wake_seq, + ARRAY_SIZE(cs35l56_hibernate_wake_seq)); -const struct cs_dsp_region cs35l56_dsp1_regions[] = { + cs35l56_wait_control_port_ready(); + } + +out_sync: + regcache_cache_only(cs35l56_base->regmap, false); + + ret = cs35l56_wait_for_firmware_boot(cs35l56_base); + if (ret) { + dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); + goto err; + } + + ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); + if (ret) + goto err; + + /* BOOT_DONE will be 1 if the amp reset */ + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, &val); + if (val & CS35L56_OTP_BOOT_DONE_MASK) { + dev_dbg(cs35l56_base->dev, "Registers reset in suspend\n"); + regcache_mark_dirty(cs35l56_base->regmap); + } + + regcache_sync(cs35l56_base->regmap); + + dev_dbg(cs35l56_base->dev, "Resumed"); + + return 0; + +err: + regmap_write(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + CS35L56_MBOX_CMD_HIBERNATE_NOW); + + regcache_cache_only(cs35l56_base->regmap, true); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_SHARED); + +static const struct cs_dsp_region cs35l56_dsp1_regions[] = { { .type = WMFW_HALO_PM_PACKED, .base = CS35L56_DSP1_PMEM_0 }, { .type = WMFW_HALO_XM_PACKED, .base = CS35L56_DSP1_XMEM_PACKED_0 }, { .type = WMFW_HALO_YM_PACKED, .base = CS35L56_DSP1_YMEM_PACKED_0 }, { .type = WMFW_ADSP2_XM, .base = CS35L56_DSP1_XMEM_UNPACKED24_0 }, { .type = WMFW_ADSP2_YM, .base = CS35L56_DSP1_YMEM_UNPACKED24_0 }, }; -EXPORT_SYMBOL_NS_GPL(cs35l56_dsp1_regions, SND_SOC_CS35L56_SHARED); + +void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp) +{ + cs_dsp->num = 1; + cs_dsp->type = WMFW_HALO; + cs_dsp->rev = 0; + cs_dsp->dev = cs35l56_base->dev; + cs_dsp->regmap = cs35l56_base->regmap; + cs_dsp->base = CS35L56_DSP1_CORE_BASE; + cs_dsp->base_sysinfo = CS35L56_DSP1_SYS_INFO_ID; + cs_dsp->mem = cs35l56_dsp1_regions; + cs_dsp->num_mems = ARRAY_SIZE(cs35l56_dsp1_regions); + cs_dsp->no_core_startstop = true; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED); + +int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) +{ + int ret; + unsigned int devid, revid, otpid, secured; + + /* + * If the system is not using a reset_gpio then issue a + * dummy read to force a wakeup. + */ + if (!cs35l56_base->reset_gpio) + regmap_read(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, &devid); + + cs35l56_wait_control_port_ready(); + + /* + * The HALO_STATE register is in different locations on Ax and B0 + * devices so the REVID needs to be determined before waiting for the + * firmware to boot. + */ + ret = regmap_read(cs35l56_base->regmap, CS35L56_REVID, &revid); + if (ret < 0) { + dev_err(cs35l56_base->dev, "Get Revision ID failed\n"); + return ret; + } + cs35l56_base->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK); + + ret = cs35l56_wait_for_firmware_boot(cs35l56_base); + if (ret) + return ret; + + ret = regmap_read(cs35l56_base->regmap, CS35L56_DEVID, &devid); + if (ret < 0) { + dev_err(cs35l56_base->dev, "Get Device ID failed\n"); + return ret; + } + devid &= CS35L56_DEVID_MASK; + + switch (devid) { + case 0x35A56: + break; + default: + dev_err(cs35l56_base->dev, "Unknown device %x\n", devid); + return ret; + } + + ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured); + if (ret) { + dev_err(cs35l56_base->dev, "Get Secure status failed\n"); + return ret; + } + + /* When any bus is restricted treat the device as secured */ + if (secured & CS35L56_RESTRICTED_MASK) + cs35l56_base->secured = true; + + ret = regmap_read(cs35l56_base->regmap, CS35L56_OTPID, &otpid); + if (ret < 0) { + dev_err(cs35l56_base->dev, "Get OTP ID failed\n"); + return ret; + } + + dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n", + cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid); + + /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ + regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); + regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1, + CS35L56_AMP_SHORT_ERR_EINT1_MASK, + 0); + regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8, + CS35L56_TEMP_ERR_EINT1_MASK, + 0); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_hw_init, SND_SOC_CS35L56_SHARED); static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = { [0x0C] = 128000, @@ -319,7 +763,7 @@ struct regmap_config cs35l56_regmap_i2c = { .volatile_reg = cs35l56_volatile_reg, .readable_reg = cs35l56_readable_reg, .precious_reg = cs35l56_precious_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_i2c, SND_SOC_CS35L56_SHARED); @@ -336,7 +780,7 @@ struct regmap_config cs35l56_regmap_spi = { .volatile_reg = cs35l56_volatile_reg, .readable_reg = cs35l56_readable_reg, .precious_reg = cs35l56_precious_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, SND_SOC_CS35L56_SHARED); @@ -352,7 +796,7 @@ struct regmap_config cs35l56_regmap_sdw = { .volatile_reg = cs35l56_volatile_reg, .readable_reg = cs35l56_readable_reg, .precious_reg = cs35l56_precious_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, SND_SOC_CS35L56_SHARED); diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c index 302f9c47407a..9962703915e1 100644 --- a/sound/soc/codecs/cs35l56-spi.c +++ b/sound/soc/codecs/cs35l56-spi.c @@ -25,13 +25,13 @@ static int cs35l56_spi_probe(struct spi_device *spi) return -ENOMEM; spi_set_drvdata(spi, cs35l56); - cs35l56->regmap = devm_regmap_init_spi(spi, regmap_config); - if (IS_ERR(cs35l56->regmap)) { - ret = PTR_ERR(cs35l56->regmap); + cs35l56->base.regmap = devm_regmap_init_spi(spi, regmap_config); + if (IS_ERR(cs35l56->base.regmap)) { + ret = PTR_ERR(cs35l56->base.regmap); return dev_err_probe(&spi->dev, ret, "Failed to allocate register map\n"); } - cs35l56->dev = &spi->dev; + cs35l56->base.dev = &spi->dev; ret = cs35l56_common_probe(cs35l56); if (ret != 0) @@ -39,7 +39,7 @@ static int cs35l56_spi_probe(struct spi_device *spi) ret = cs35l56_init(cs35l56); if (ret == 0) - ret = cs35l56_irq_request(cs35l56, spi->irq); + ret = cs35l56_irq_request(&cs35l56->base, spi->irq); if (ret < 0) cs35l56_remove(cs35l56); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index fd06b9f9d496..600b79c62ec4 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -33,23 +33,6 @@ static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -static int cs35l56_mbox_send(struct cs35l56_private *cs35l56, unsigned int command) -{ - unsigned int val; - int ret; - - regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command); - ret = regmap_read_poll_timeout(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, - val, (val == 0), - CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US); - if (ret) { - dev_warn(cs35l56->dev, "MBOX command %#x failed: %d\n", command, ret); - return ret; - } - - return 0; -} - static void cs35l56_wait_dsp_ready(struct cs35l56_private *cs35l56) { /* Wait for patching to complete */ @@ -173,25 +156,25 @@ static int cs35l56_play_event(struct snd_soc_dapm_widget *w, unsigned int val; int ret; - dev_dbg(cs35l56->dev, "play: %d\n", event); + dev_dbg(cs35l56->base.dev, "play: %d\n", event); switch (event) { case SND_SOC_DAPM_PRE_PMU: /* Don't wait for ACK, we check in POST_PMU that it completed */ - return regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + return regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_AUDIO_PLAY); case SND_SOC_DAPM_POST_PMU: /* Wait for firmware to enter PS0 power state */ - ret = regmap_read_poll_timeout(cs35l56->regmap, + ret = regmap_read_poll_timeout(cs35l56->base.regmap, CS35L56_TRANSDUCER_ACTUAL_PS, val, (val == CS35L56_PS0), CS35L56_PS0_POLL_US, CS35L56_PS0_TIMEOUT_US); if (ret) - dev_err(cs35l56->dev, "PS0 wait failed: %d\n", ret); + dev_err(cs35l56->base.dev, "PS0 wait failed: %d\n", ret); return ret; case SND_SOC_DAPM_POST_PMD: - return cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_PAUSE); + return cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE); default: return 0; } @@ -309,109 +292,23 @@ static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - dev_dbg(cs35l56->dev, "%s: %d\n", __func__, event); + dev_dbg(cs35l56->base.dev, "%s: %d\n", __func__, event); return wm_adsp_event(w, kcontrol, event); } -irqreturn_t cs35l56_irq(int irq, void *data) -{ - struct cs35l56_private *cs35l56 = data; - unsigned int status1 = 0, status8 = 0, status20 = 0; - unsigned int mask1, mask8, mask20; - unsigned int val; - int rv; - - irqreturn_t ret = IRQ_NONE; - - if (!cs35l56->init_done) - return IRQ_NONE; - - mutex_lock(&cs35l56->irq_lock); - - rv = pm_runtime_resume_and_get(cs35l56->dev); - if (rv < 0) { - dev_err(cs35l56->dev, "irq: failed to get pm_runtime: %d\n", rv); - goto err_unlock; - } - - regmap_read(cs35l56->regmap, CS35L56_IRQ1_STATUS, &val); - if ((val & CS35L56_IRQ1_STS_MASK) == 0) { - dev_dbg(cs35l56->dev, "Spurious IRQ: no pending interrupt\n"); - goto err; - } - - /* Ack interrupts */ - regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_1, &status1); - regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_1, &mask1); - status1 &= ~mask1; - regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_1, status1); - - regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_8, &status8); - regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_8, &mask8); - status8 &= ~mask8; - regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_8, status8); - - regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_20, &status20); - regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_20, &mask20); - status20 &= ~mask20; - /* We don't want EINT20 but they default to unmasked: force mask */ - regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); - - dev_dbg(cs35l56->dev, "%s: %#x %#x\n", __func__, status1, status8); - - /* Check to see if unmasked bits are active */ - if (!status1 && !status8 && !status20) - goto err; - - if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK) - dev_crit(cs35l56->dev, "Amp short error\n"); - - if (status8 & CS35L56_TEMP_ERR_EINT1_MASK) - dev_crit(cs35l56->dev, "Overtemp error\n"); - - ret = IRQ_HANDLED; - -err: - pm_runtime_put(cs35l56->dev); -err_unlock: - mutex_unlock(&cs35l56->irq_lock); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(cs35l56_irq, SND_SOC_CS35L56_CORE); - -int cs35l56_irq_request(struct cs35l56_private *cs35l56, int irq) -{ - int ret; - - if (!irq) - return 0; - - ret = devm_request_threaded_irq(cs35l56->dev, irq, NULL, cs35l56_irq, - IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, - "cs35l56", cs35l56); - if (!ret) - cs35l56->irq = irq; - else - dev_err(cs35l56->dev, "Failed to get IRQ: %d\n", ret); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, SND_SOC_CS35L56_CORE); - static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component); unsigned int val; - dev_dbg(cs35l56->dev, "%s: %#x\n", __func__, fmt); + dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt); switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBC_CFC: break; default: - dev_err(cs35l56->dev, "Unsupported clock source mode\n"); + dev_err(cs35l56->base.dev, "Unsupported clock source mode\n"); return -EINVAL; } @@ -425,7 +322,7 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f cs35l56->tdm_mode = false; break; default: - dev_err(cs35l56->dev, "Unsupported DAI format\n"); + dev_err(cs35l56->base.dev, "Unsupported DAI format\n"); return -EINVAL; } @@ -442,40 +339,29 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f case SND_SOC_DAIFMT_NB_NF: break; default: - dev_err(cs35l56->dev, "Invalid clock invert\n"); + dev_err(cs35l56->base.dev, "Invalid clock invert\n"); return -EINVAL; } - regmap_update_bits(cs35l56->regmap, + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL2, CS35L56_ASP_FMT_MASK | CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK, val); /* Hi-Z DOUT in unused slots and when all TX are disabled */ - regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL3, + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL3, CS35L56_ASP1_DOUT_HIZ_CTRL_MASK, CS35L56_ASP_UNUSED_HIZ_OFF_HIZ); return 0; } -static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56, - unsigned int reg, unsigned long mask) +static unsigned int cs35l56_make_tdm_config_word(unsigned int reg_val, unsigned long mask) { - unsigned int reg_val, channel_shift; + unsigned int channel_shift; int bit_num; - /* Init all slots to 63 */ - switch (reg) { - case CS35L56_ASP1_FRAME_CONTROL1: - reg_val = 0x3f3f3f3f; - break; - case CS35L56_ASP1_FRAME_CONTROL5: - reg_val = 0x3f3f3f; - break; - } - /* Enable consecutive TX1..TXn for each of the slots set in mask */ channel_shift = 0; for_each_set_bit(bit_num, &mask, 32) { @@ -484,7 +370,7 @@ static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56, channel_shift += 8; } - regmap_write(cs35l56->regmap, reg, reg_val); + return reg_val; } static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, @@ -493,20 +379,20 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); if ((slots == 0) || (slot_width == 0)) { - dev_dbg(cs35l56->dev, "tdm config cleared\n"); + dev_dbg(cs35l56->base.dev, "tdm config cleared\n"); cs35l56->asp_slot_width = 0; cs35l56->asp_slot_count = 0; return 0; } if (slot_width > (CS35L56_ASP_RX_WIDTH_MASK >> CS35L56_ASP_RX_WIDTH_SHIFT)) { - dev_err(cs35l56->dev, "tdm invalid slot width %d\n", slot_width); + dev_err(cs35l56->base.dev, "tdm invalid slot width %d\n", slot_width); return -EINVAL; } /* More than 32 slots would give an unsupportable BCLK frequency */ if (slots > 32) { - dev_err(cs35l56->dev, "tdm invalid slot count %d\n", slots); + dev_err(cs35l56->base.dev, "tdm invalid slot count %d\n", slots); return -EINVAL; } @@ -520,10 +406,13 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx if (rx_mask == 0) rx_mask = 0xf; // ASPTX1..TX4 in slots 0..3 - cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL1, rx_mask); - cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL5, tx_mask); + /* Default unused slots to 63 */ + regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL1, + cs35l56_make_tdm_config_word(0x3f3f3f3f, rx_mask)); + regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL5, + cs35l56_make_tdm_config_word(0x3f3f3f, tx_mask)); - dev_dbg(cs35l56->dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n", + dev_dbg(cs35l56->base.dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n", cs35l56->asp_slot_width, cs35l56->asp_slot_count, tx_mask, rx_mask); return 0; @@ -543,7 +432,8 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream, else asp_width = asp_wl; - dev_dbg(cs35l56->dev, "%s: wl=%d, width=%d, rate=%d", __func__, asp_wl, asp_width, rate); + dev_dbg(cs35l56->base.dev, "%s: wl=%d, width=%d, rate=%d", + __func__, asp_wl, asp_width, rate); if (!cs35l56->sysclk_set) { unsigned int slots = cs35l56->asp_slot_count; @@ -561,26 +451,26 @@ static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream, bclk_freq = asp_width * slots * rate; freq_id = cs35l56_get_bclk_freq_id(bclk_freq); if (freq_id < 0) { - dev_err(cs35l56->dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq); + dev_err(cs35l56->base.dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq); return -EINVAL; } - regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1, + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL1, CS35L56_ASP_BCLK_FREQ_MASK, freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT); } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2, + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL2, CS35L56_ASP_RX_WIDTH_MASK, asp_width << CS35L56_ASP_RX_WIDTH_SHIFT); - regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL5, + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_DATA_CONTROL5, CS35L56_ASP_RX_WL_MASK, asp_wl); } else { - regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2, + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL2, CS35L56_ASP_TX_WIDTH_MASK, asp_width << CS35L56_ASP_TX_WIDTH_SHIFT); - regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL1, + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_DATA_CONTROL1, CS35L56_ASP_TX_WL_MASK, asp_wl); } @@ -602,7 +492,7 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai, if (freq_id < 0) return freq_id; - regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1, + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL1, CS35L56_ASP_BCLK_FREQ_MASK, freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT); cs35l56->sysclk_set = true; @@ -645,9 +535,9 @@ static int cs35l56_sdw_dai_hw_params(struct snd_pcm_substream *substream, struct sdw_port_config pconfig; int ret; - dev_dbg(cs35l56->dev, "%s: rate %d\n", __func__, params_rate(params)); + dev_dbg(cs35l56->base.dev, "%s: rate %d\n", __func__, params_rate(params)); - if (!cs35l56->init_done) + if (!cs35l56->base.init_done) return -ENODEV; if (!sdw_stream) @@ -760,85 +650,31 @@ static struct snd_soc_dai_driver cs35l56_dai[] = { } }; -static int cs35l56_wait_for_firmware_boot(struct cs35l56_private *cs35l56) -{ - unsigned int reg; - unsigned int val; - int ret; - - if (cs35l56->rev < CS35L56_REVID_B0) - reg = CS35L56_DSP1_HALO_STATE_A1; - else - reg = CS35L56_DSP1_HALO_STATE; - - ret = regmap_read_poll_timeout(cs35l56->regmap, reg, - val, - (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE), - CS35L56_HALO_STATE_POLL_US, - CS35L56_HALO_STATE_TIMEOUT_US); - - if ((ret < 0) && (ret != -ETIMEDOUT)) { - dev_err(cs35l56->dev, "Failed to read HALO_STATE: %d\n", ret); - return ret; - } - - if ((ret == -ETIMEDOUT) || (val != CS35L56_HALO_STATE_BOOT_DONE)) { - dev_err(cs35l56->dev, "Firmware boot fail: HALO_STATE=%#x\n", val); - return -EIO; - } - - return 0; -} - -static inline void cs35l56_wait_min_reset_pulse(void) -{ - /* Satisfy minimum reset pulse width spec */ - usleep_range(CS35L56_RESET_PULSE_MIN_US, 2 * CS35L56_RESET_PULSE_MIN_US); -} - -static const struct reg_sequence cs35l56_system_reset_seq[] = { - REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET), -}; - -static void cs35l56_system_reset(struct cs35l56_private *cs35l56) -{ - cs35l56->soft_resetting = true; - - /* - * Must enter cache-only first so there can't be any more register - * accesses other than the controlled system reset sequence below. - */ - regcache_cache_only(cs35l56->regmap, true); - regmap_multi_reg_write_bypassed(cs35l56->regmap, - cs35l56_system_reset_seq, - ARRAY_SIZE(cs35l56_system_reset_seq)); - - /* On SoundWire the registers won't be accessible until it re-enumerates. */ - if (cs35l56->sdw_peripheral) - return; - - usleep_range(CS35L56_CONTROL_PORT_READY_US, CS35L56_CONTROL_PORT_READY_US + 400); - regcache_cache_only(cs35l56->regmap, false); -} - static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) { int ret; /* Use wm_adsp to load and apply the firmware patch and coefficient files */ - ret = wm_adsp_power_up(&cs35l56->dsp); + ret = wm_adsp_power_up(&cs35l56->dsp, true); if (ret) - dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); + dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); else - cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_REINIT); + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); } static void cs35l56_patch(struct cs35l56_private *cs35l56) { - unsigned int reg; - unsigned int val; + unsigned int firmware_missing; int ret; + ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing); + if (ret) { + dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret); + return; + } + + firmware_missing &= CS35L56_FIRMWARE_MISSING; + /* * Disable SoundWire interrupts to prevent race with IRQ work. * Setting sdw_irq_no_unmask prevents the handler re-enabling @@ -853,35 +689,27 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56) flush_work(&cs35l56->sdw_irq_work); } - ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_SHUTDOWN); + ret = cs35l56_firmware_shutdown(&cs35l56->base); if (ret) goto err; - if (cs35l56->rev < CS35L56_REVID_B0) - reg = CS35L56_DSP1_PM_CUR_STATE_A1; - else - reg = CS35L56_DSP1_PM_CUR_STATE; - - ret = regmap_read_poll_timeout(cs35l56->regmap, reg, - val, (val == CS35L56_HALO_STATE_SHUTDOWN), - CS35L56_HALO_STATE_POLL_US, - CS35L56_HALO_STATE_TIMEOUT_US); - if (ret < 0) - dev_err(cs35l56->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n", - val, ret); - - /* Use wm_adsp to load and apply the firmware patch and coefficient files */ - ret = wm_adsp_power_up(&cs35l56->dsp); + /* + * Use wm_adsp to load and apply the firmware patch and coefficient files, + * but only if firmware is missing. If firmware is already patched just + * power-up wm_adsp without downloading firmware. + */ + ret = wm_adsp_power_up(&cs35l56->dsp, !!firmware_missing); if (ret) { - dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); + dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); goto err; } - mutex_lock(&cs35l56->irq_lock); + mutex_lock(&cs35l56->base.irq_lock); init_completion(&cs35l56->init_completion); - cs35l56_system_reset(cs35l56); + cs35l56->soft_resetting = true; + cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral); if (cs35l56->sdw_peripheral) { /* @@ -891,18 +719,20 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56) */ if (!wait_for_completion_timeout(&cs35l56->init_completion, msecs_to_jiffies(5000))) { - dev_err(cs35l56->dev, "%s: init_completion timed out (SDW)\n", __func__); + dev_err(cs35l56->base.dev, "%s: init_completion timed out (SDW)\n", + __func__); goto err_unlock; } } else if (cs35l56_init(cs35l56)) { goto err_unlock; } - regmap_clear_bits(cs35l56->regmap, CS35L56_PROTECTION_STATUS, CS35L56_FIRMWARE_MISSING); - cs35l56->fw_patched = true; + regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, + CS35L56_FIRMWARE_MISSING); + cs35l56->base.fw_patched = true; err_unlock: - mutex_unlock(&cs35l56->irq_lock); + mutex_unlock(&cs35l56->base.irq_lock); err: /* Re-enable SoundWire interrupts */ if (cs35l56->sdw_peripheral) { @@ -918,10 +748,10 @@ static void cs35l56_dsp_work(struct work_struct *work) struct cs35l56_private, dsp_work); - if (!cs35l56->init_done) + if (!cs35l56->base.init_done) return; - pm_runtime_get_sync(cs35l56->dev); + pm_runtime_get_sync(cs35l56->base.dev); /* * When the device is running in secure mode the firmware files can @@ -929,13 +759,13 @@ static void cs35l56_dsp_work(struct work_struct *work) * shutdown the firmware to apply them and can use the lower cost * reinit sequence instead. */ - if (cs35l56->secured) + if (cs35l56->base.secured) cs35l56_secure_patch(cs35l56); else cs35l56_patch(cs35l56); - pm_runtime_mark_last_busy(cs35l56->dev); - pm_runtime_put_autosuspend(cs35l56->dev); + pm_runtime_mark_last_busy(cs35l56->base.dev); + pm_runtime_put_autosuspend(cs35l56->base.dev); } static int cs35l56_component_probe(struct snd_soc_component *component) @@ -947,16 +777,16 @@ static int cs35l56_component_probe(struct snd_soc_component *component) if (!wait_for_completion_timeout(&cs35l56->init_completion, msecs_to_jiffies(5000))) { - dev_err(cs35l56->dev, "%s: init_completion timed out\n", __func__); + dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__); return -ENODEV; } cs35l56->component = component; wm_adsp2_component_probe(&cs35l56->dsp, component); - debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->init_done); - debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->can_hibernate); - debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->fw_patched); + debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->base.init_done); + debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate); + debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched); queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); @@ -1008,171 +838,18 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l56 = { .suspend_bias_off = 1, /* see cs35l56_system_resume() */ }; -static const struct reg_sequence cs35l56_hibernate_seq[] = { - /* This must be the last register access */ - REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_HIBERNATE_NOW), -}; - -static const struct reg_sequence cs35l56_hibernate_wake_seq[] = { - REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_WAKEUP), -}; - -int cs35l56_runtime_suspend(struct device *dev) +static int __maybe_unused cs35l56_runtime_suspend_i2c_spi(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); - unsigned int val; - int ret; - - if (!cs35l56->init_done) - return 0; - - /* Firmware must have entered a power-save state */ - ret = regmap_read_poll_timeout(cs35l56->regmap, - CS35L56_TRANSDUCER_ACTUAL_PS, - val, (val >= CS35L56_PS3), - CS35L56_PS3_POLL_US, - CS35L56_PS3_TIMEOUT_US); - if (ret) - dev_warn(cs35l56->dev, "PS3 wait failed: %d\n", ret); - - /* Clear BOOT_DONE so it can be used to detect a reboot */ - regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK); - - if (!cs35l56->can_hibernate) { - regcache_cache_only(cs35l56->regmap, true); - dev_dbg(dev, "Suspended: no hibernate"); - - return 0; - } - - /* - * Enable auto-hibernate. If it is woken by some other wake source - * it will automatically return to hibernate. - */ - cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE); - - /* - * Must enter cache-only first so there can't be any more register - * accesses other than the controlled hibernate sequence below. - */ - regcache_cache_only(cs35l56->regmap, true); - - regmap_multi_reg_write_bypassed(cs35l56->regmap, - cs35l56_hibernate_seq, - ARRAY_SIZE(cs35l56_hibernate_seq)); - - dev_dbg(dev, "Suspended: hibernate"); - return 0; + return cs35l56_runtime_suspend_common(&cs35l56->base); } -EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend, SND_SOC_CS35L56_CORE); static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); - if (!cs35l56->init_done) - return 0; - - return cs35l56_runtime_resume_common(cs35l56); -} - -int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56) -{ - unsigned int val; - int ret; - - if (!cs35l56->can_hibernate) - goto out_sync; - - if (!cs35l56->sdw_peripheral) { - /* - * Dummy transaction to trigger I2C/SPI auto-wake. This will NAK on I2C. - * Must be done before releasing cache-only. - */ - regmap_multi_reg_write_bypassed(cs35l56->regmap, - cs35l56_hibernate_wake_seq, - ARRAY_SIZE(cs35l56_hibernate_wake_seq)); - - usleep_range(CS35L56_CONTROL_PORT_READY_US, - CS35L56_CONTROL_PORT_READY_US + 400); - } - -out_sync: - regcache_cache_only(cs35l56->regmap, false); - - ret = cs35l56_wait_for_firmware_boot(cs35l56); - if (ret) { - dev_err(cs35l56->dev, "Hibernate wake failed: %d\n", ret); - goto err; - } - - ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); - if (ret) - goto err; - - /* BOOT_DONE will be 1 if the amp reset */ - regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_4, &val); - if (val & CS35L56_OTP_BOOT_DONE_MASK) { - dev_dbg(cs35l56->dev, "Registers reset in suspend\n"); - regcache_mark_dirty(cs35l56->regmap); - } - - regcache_sync(cs35l56->regmap); - - dev_dbg(cs35l56->dev, "Resumed"); - - return 0; - -err: - regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, - CS35L56_MBOX_CMD_HIBERNATE_NOW); - - regcache_cache_only(cs35l56->regmap, true); - - return ret; -} -EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_CORE); - -static int cs35l56_is_fw_reload_needed(struct cs35l56_private *cs35l56) -{ - unsigned int val; - int ret; - - /* Nothing to re-patch if we haven't done any patching yet. */ - if (!cs35l56->fw_patched) - return false; - - /* - * If we have control of RESET we will have asserted it so the firmware - * will need re-patching. - */ - if (cs35l56->reset_gpio) - return true; - - /* - * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so - * can't be used here to test for memory retention. - * Assume that tuning must be re-loaded. - */ - if (cs35l56->secured) - return true; - - ret = pm_runtime_resume_and_get(cs35l56->dev); - if (ret) { - dev_err(cs35l56->dev, "Failed to runtime_get: %d\n", ret); - return ret; - } - - ret = regmap_read(cs35l56->regmap, CS35L56_PROTECTION_STATUS, &val); - if (ret) - dev_err(cs35l56->dev, "Failed to read PROTECTION_STATUS: %d\n", ret); - else - ret = !!(val & CS35L56_FIRMWARE_MISSING); - - pm_runtime_put_autosuspend(cs35l56->dev); - - return ret; + return cs35l56_runtime_resume_common(&cs35l56->base, false); } int cs35l56_system_suspend(struct device *dev) @@ -1190,8 +867,8 @@ int cs35l56_system_suspend(struct device *dev) * clear it. Prevent this race by temporarily disabling the parent irq * until we reach _no_irq. */ - if (cs35l56->irq) - disable_irq(cs35l56->irq); + if (cs35l56->base.irq) + disable_irq(cs35l56->base.irq); return pm_runtime_force_suspend(dev); } @@ -1208,8 +885,8 @@ int cs35l56_system_suspend_late(struct device *dev) * RESET is usually shared by all amps so it must not be asserted until * all driver instances have done their suspend() stage. */ - if (cs35l56->reset_gpio) { - gpiod_set_value_cansleep(cs35l56->reset_gpio, 0); + if (cs35l56->base.reset_gpio) { + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); cs35l56_wait_min_reset_pulse(); } @@ -1226,8 +903,8 @@ int cs35l56_system_suspend_no_irq(struct device *dev) dev_dbg(dev, "system_suspend_no_irq\n"); /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */ - if (cs35l56->irq) - enable_irq(cs35l56->irq); + if (cs35l56->base.irq) + enable_irq(cs35l56->base.irq); return 0; } @@ -1246,8 +923,8 @@ int cs35l56_system_resume_no_irq(struct device *dev) * clear it, until it has fully resumed. Prevent this race by temporarily * disabling the parent irq until we complete resume(). */ - if (cs35l56->irq) - disable_irq(cs35l56->irq); + if (cs35l56->base.irq) + disable_irq(cs35l56->base.irq); return 0; } @@ -1261,8 +938,8 @@ int cs35l56_system_resume_early(struct device *dev) dev_dbg(dev, "system_resume_early\n"); /* Ensure a spec-compliant RESET pulse. */ - if (cs35l56->reset_gpio) { - gpiod_set_value_cansleep(cs35l56->reset_gpio, 0); + if (cs35l56->base.reset_gpio) { + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); cs35l56_wait_min_reset_pulse(); } @@ -1274,7 +951,7 @@ int cs35l56_system_resume_early(struct device *dev) } /* Release shared RESET before drivers start resume(). */ - gpiod_set_value_cansleep(cs35l56->reset_gpio, 1); + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); return 0; } @@ -1287,10 +964,16 @@ int cs35l56_system_resume(struct device *dev) dev_dbg(dev, "system_resume\n"); + /* + * We might have done a hard reset or the CS35L56 was power-cycled + * so wait for control port to be ready. + */ + cs35l56_wait_control_port_ready(); + /* Undo pm_runtime_force_suspend() before re-enabling the irq */ ret = pm_runtime_force_resume(dev); - if (cs35l56->irq) - enable_irq(cs35l56->irq); + if (cs35l56->base.irq) + enable_irq(cs35l56->base.irq); if (ret) return ret; @@ -1299,12 +982,13 @@ int cs35l56_system_resume(struct device *dev) if (!cs35l56->component) return 0; - ret = cs35l56_is_fw_reload_needed(cs35l56); - dev_dbg(cs35l56->dev, "fw_reload_needed: %d\n", ret); + ret = cs35l56_is_fw_reload_needed(&cs35l56->base); + dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret); if (ret < 1) return ret; - cs35l56->fw_patched = false; + cs35l56->base.fw_patched = false; + wm_adsp_power_down(&cs35l56->dsp); queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); /* @@ -1328,25 +1012,16 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56) INIT_WORK(&cs35l56->dsp_work, cs35l56_dsp_work); dsp = &cs35l56->dsp; + cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp); dsp->part = "cs35l56"; - dsp->cs_dsp.num = 1; - dsp->cs_dsp.type = WMFW_HALO; - dsp->cs_dsp.rev = 0; dsp->fw = 12; - dsp->cs_dsp.dev = cs35l56->dev; - dsp->cs_dsp.regmap = cs35l56->regmap; - dsp->cs_dsp.base = CS35L56_DSP1_CORE_BASE; - dsp->cs_dsp.base_sysinfo = CS35L56_DSP1_SYS_INFO_ID; - dsp->cs_dsp.mem = cs35l56_dsp1_regions; - dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l56_dsp1_regions); - dsp->cs_dsp.no_core_startstop = true; dsp->wmfw_optional = true; - dev_dbg(cs35l56->dev, "DSP system name: '%s'\n", dsp->system_name); + dev_dbg(cs35l56->base.dev, "DSP system name: '%s'\n", dsp->system_name); ret = wm_halo_init(dsp); if (ret != 0) { - dev_err(cs35l56->dev, "wm_halo_init failed\n"); + dev_err(cs35l56->base.dev, "wm_halo_init failed\n"); return ret; } @@ -1355,7 +1030,7 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56) static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56) { - struct device *dev = cs35l56->dev; + struct device *dev = cs35l56->base.dev; const char *prop; int ret; @@ -1378,38 +1053,41 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) int ret; init_completion(&cs35l56->init_completion); - mutex_init(&cs35l56->irq_lock); + mutex_init(&cs35l56->base.irq_lock); - dev_set_drvdata(cs35l56->dev, cs35l56); + dev_set_drvdata(cs35l56->base.dev, cs35l56); cs35l56_fill_supply_names(cs35l56->supplies); - ret = devm_regulator_bulk_get(cs35l56->dev, ARRAY_SIZE(cs35l56->supplies), + ret = devm_regulator_bulk_get(cs35l56->base.dev, ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); if (ret != 0) - return dev_err_probe(cs35l56->dev, ret, "Failed to request supplies\n"); + return dev_err_probe(cs35l56->base.dev, ret, "Failed to request supplies\n"); /* Reset could be controlled by the BIOS or shared by multiple amps */ - cs35l56->reset_gpio = devm_gpiod_get_optional(cs35l56->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(cs35l56->reset_gpio)) { - ret = PTR_ERR(cs35l56->reset_gpio); + cs35l56->base.reset_gpio = devm_gpiod_get_optional(cs35l56->base.dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs35l56->base.reset_gpio)) { + ret = PTR_ERR(cs35l56->base.reset_gpio); /* * If RESET is shared the first amp to probe will grab the reset * line and reset all the amps */ if (ret != -EBUSY) - return dev_err_probe(cs35l56->dev, ret, "Failed to get reset GPIO\n"); + return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n"); - dev_info(cs35l56->dev, "Reset GPIO busy, assume shared reset\n"); - cs35l56->reset_gpio = NULL; + dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n"); + cs35l56->base.reset_gpio = NULL; } ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); if (ret != 0) - return dev_err_probe(cs35l56->dev, ret, "Failed to enable supplies\n"); + return dev_err_probe(cs35l56->base.dev, ret, "Failed to enable supplies\n"); - if (cs35l56->reset_gpio) { + if (cs35l56->base.reset_gpio) { + /* ACPI can override GPIOD_OUT_LOW flag so force it to start low */ + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); cs35l56_wait_min_reset_pulse(); - gpiod_set_value_cansleep(cs35l56->reset_gpio, 1); + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); } ret = cs35l56_get_firmware_uid(cs35l56); @@ -1418,22 +1096,22 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) ret = cs35l56_dsp_init(cs35l56); if (ret < 0) { - dev_err_probe(cs35l56->dev, ret, "DSP init failed\n"); + dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n"); goto err; } - ret = devm_snd_soc_register_component(cs35l56->dev, + ret = devm_snd_soc_register_component(cs35l56->base.dev, &soc_component_dev_cs35l56, cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); if (ret < 0) { - dev_err_probe(cs35l56->dev, ret, "Register codec failed\n"); + dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); goto err; } return 0; err: - gpiod_set_value_cansleep(cs35l56->reset_gpio, 0); + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); return ret; @@ -1443,7 +1121,6 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, SND_SOC_CS35L56_CORE); int cs35l56_init(struct cs35l56_private *cs35l56) { int ret; - unsigned int devid, revid, otpid, secured; /* * Check whether the actions associated with soft reset or one time @@ -1452,96 +1129,31 @@ int cs35l56_init(struct cs35l56_private *cs35l56) if (cs35l56->soft_resetting) goto post_soft_reset; - if (cs35l56->init_done) + if (cs35l56->base.init_done) return 0; - pm_runtime_set_autosuspend_delay(cs35l56->dev, 100); - pm_runtime_use_autosuspend(cs35l56->dev); - pm_runtime_set_active(cs35l56->dev); - pm_runtime_enable(cs35l56->dev); - - /* - * If the system is not using a reset_gpio then issue a - * dummy read to force a wakeup. - */ - if (!cs35l56->reset_gpio) - regmap_read(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, &devid); - - /* Wait for control port to be ready (datasheet tIRS). */ - usleep_range(CS35L56_CONTROL_PORT_READY_US, - CS35L56_CONTROL_PORT_READY_US + 400); - - /* - * The HALO_STATE register is in different locations on Ax and B0 - * devices so the REVID needs to be determined before waiting for the - * firmware to boot. - */ - ret = regmap_read(cs35l56->regmap, CS35L56_REVID, &revid); - if (ret < 0) { - dev_err(cs35l56->dev, "Get Revision ID failed\n"); - return ret; - } - cs35l56->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK); - - ret = cs35l56_wait_for_firmware_boot(cs35l56); - if (ret) - return ret; - - ret = regmap_read(cs35l56->regmap, CS35L56_DEVID, &devid); - if (ret < 0) { - dev_err(cs35l56->dev, "Get Device ID failed\n"); - return ret; - } - devid &= CS35L56_DEVID_MASK; - - switch (devid) { - case 0x35A56: - break; - default: - dev_err(cs35l56->dev, "Unknown device %x\n", devid); - return ret; - } - - ret = regmap_read(cs35l56->regmap, CS35L56_DSP_RESTRICT_STS1, &secured); - if (ret) { - dev_err(cs35l56->dev, "Get Secure status failed\n"); - return ret; - } - - /* When any bus is restricted treat the device as secured */ - if (secured & CS35L56_RESTRICTED_MASK) - cs35l56->secured = true; + pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 100); + pm_runtime_use_autosuspend(cs35l56->base.dev); + pm_runtime_set_active(cs35l56->base.dev); + pm_runtime_enable(cs35l56->base.dev); - ret = regmap_read(cs35l56->regmap, CS35L56_OTPID, &otpid); - if (ret < 0) { - dev_err(cs35l56->dev, "Get OTP ID failed\n"); + ret = cs35l56_hw_init(&cs35l56->base); + if (ret < 0) return ret; - } - - dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n", - cs35l56->secured ? "s" : "", cs35l56->rev, otpid); /* Populate the DSP information with the revision and security state */ - cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x", - cs35l56->secured ? "s" : "", cs35l56->rev); + cs35l56->dsp.part = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "cs35l56%s-%02x", + cs35l56->base.secured ? "s" : "", cs35l56->base.rev); if (!cs35l56->dsp.part) return -ENOMEM; - /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ - regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); - regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_1, - CS35L56_AMP_SHORT_ERR_EINT1_MASK, - 0); - regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_8, - CS35L56_TEMP_ERR_EINT1_MASK, - 0); - - if (!cs35l56->reset_gpio) { - dev_dbg(cs35l56->dev, "No reset gpio: using soft reset\n"); - cs35l56_system_reset(cs35l56); + if (!cs35l56->base.reset_gpio) { + dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n"); + cs35l56->soft_resetting = true; + cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral); if (cs35l56->sdw_peripheral) { /* Keep alive while we wait for re-enumeration */ - pm_runtime_get_noresume(cs35l56->dev); + pm_runtime_get_noresume(cs35l56->base.dev); return 0; } } @@ -1551,29 +1163,30 @@ post_soft_reset: cs35l56->soft_resetting = false; /* Done re-enumerating after one-time init so release the keep-alive */ - if (cs35l56->sdw_peripheral && !cs35l56->init_done) - pm_runtime_put_noidle(cs35l56->dev); + if (cs35l56->sdw_peripheral && !cs35l56->base.init_done) + pm_runtime_put_noidle(cs35l56->base.dev); - regcache_mark_dirty(cs35l56->regmap); - ret = cs35l56_wait_for_firmware_boot(cs35l56); + regcache_mark_dirty(cs35l56->base.regmap); + ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); if (ret) return ret; - dev_dbg(cs35l56->dev, "Firmware rebooted after soft reset\n"); + dev_dbg(cs35l56->base.dev, "Firmware rebooted after soft reset\n"); } /* Disable auto-hibernate so that runtime_pm has control */ - ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); + ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); if (ret) return ret; - /* Populate soft registers in the regmap cache */ - cs35l56_reread_firmware_registers(cs35l56->dev, cs35l56->regmap); + ret = cs35l56_set_patch(&cs35l56->base); + if (ret) + return ret; /* Registers could be dirty after soft reset or SoundWire enumeration */ - regcache_sync(cs35l56->regmap); + regcache_sync(cs35l56->base.regmap); - cs35l56->init_done = true; + cs35l56->base.init_done = true; complete(&cs35l56->init_completion); return 0; @@ -1582,30 +1195,30 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_init, SND_SOC_CS35L56_CORE); void cs35l56_remove(struct cs35l56_private *cs35l56) { - cs35l56->init_done = false; + cs35l56->base.init_done = false; /* * WAKE IRQs unmask if CS35L56 hibernates so free the handler to * prevent it racing with remove(). */ - if (cs35l56->irq) - devm_free_irq(cs35l56->dev, cs35l56->irq, cs35l56); + if (cs35l56->base.irq) + devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base); flush_workqueue(cs35l56->dsp_wq); destroy_workqueue(cs35l56->dsp_wq); - pm_runtime_suspend(cs35l56->dev); - pm_runtime_disable(cs35l56->dev); + pm_runtime_suspend(cs35l56->base.dev); + pm_runtime_disable(cs35l56->base.dev); - regcache_cache_only(cs35l56->regmap, true); + regcache_cache_only(cs35l56->base.regmap, true); - gpiod_set_value_cansleep(cs35l56->reset_gpio, 0); + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); } EXPORT_SYMBOL_NS_GPL(cs35l56_remove, SND_SOC_CS35L56_CORE); const struct dev_pm_ops cs35l56_pm_ops_i2c_spi = { - SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend, cs35l56_runtime_resume_i2c_spi, NULL) + SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend_i2c_spi, cs35l56_runtime_resume_i2c_spi, NULL) SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend, cs35l56_system_resume) LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_no_irq, cs35l56_system_resume_no_irq) diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index 1f7894662fcb..8159c3e217d9 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -32,26 +32,17 @@ struct sdw_slave; struct cs35l56_private { struct wm_adsp dsp; /* must be first member */ + struct cs35l56_base base; struct work_struct dsp_work; struct workqueue_struct *dsp_wq; - struct mutex irq_lock; struct snd_soc_component *component; - struct device *dev; - struct regmap *regmap; struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES]; - int irq; struct sdw_slave *sdw_peripheral; - u8 rev; struct work_struct sdw_irq_work; - bool secured; bool sdw_irq_no_unmask; bool soft_resetting; - bool init_done; bool sdw_attached; - bool fw_patched; - bool can_hibernate; struct completion init_completion; - struct gpio_desc *reset_gpio; u32 rx_mask; u32 tx_mask; @@ -64,8 +55,6 @@ struct cs35l56_private { extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; -int cs35l56_runtime_suspend(struct device *dev); -int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56); int cs35l56_system_suspend(struct device *dev); int cs35l56_system_suspend_late(struct device *dev); int cs35l56_system_suspend_no_irq(struct device *dev); @@ -73,7 +62,7 @@ int cs35l56_system_resume_no_irq(struct device *dev); int cs35l56_system_resume_early(struct device *dev); int cs35l56_system_resume(struct device *dev); irqreturn_t cs35l56_irq(int irq, void *data); -int cs35l56_irq_request(struct cs35l56_private *cs35l56, int irq); +int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); int cs35l56_common_probe(struct cs35l56_private *cs35l56); int cs35l56_init(struct cs35l56_private *cs35l56); void cs35l56_remove(struct cs35l56_private *cs35l56); diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 0cfc5ab36a13..1ed1e60d8e53 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -564,7 +564,7 @@ static const struct regmap_config cs4265_regmap = { .num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults), .readable_reg = cs4265_readable_register, .volatile_reg = cs4265_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int cs4265_i2c_probe(struct i2c_client *i2c_client) diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index ab32f15e3b44..3df567214952 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -636,7 +636,7 @@ static const struct regmap_config cs4270_regmap = { .max_register = CS4270_LASTREG, .reg_defaults = cs4270_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .write_flag_mask = CS4270_I2C_INCR, .readable_reg = cs4270_reg_is_readable, diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c new file mode 100644 index 000000000000..92e37bc1df9d --- /dev/null +++ b/sound/soc/codecs/cs42l43-jack.c @@ -0,0 +1,946 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS42L43 CODEC driver jack handling +// +// Copyright (C) 2022-2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/build_bug.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/irq.h> +#include <linux/jiffies.h> +#include <linux/mfd/cs42l43.h> +#include <linux/mfd/cs42l43-regs.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <sound/control.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-component.h> +#include <sound/soc.h> + +#include "cs42l43.h" + +static const unsigned int cs42l43_accdet_us[] = { + 20, 100, 1000, 10000, 50000, 75000, 100000, 200000 +}; + +static const unsigned int cs42l43_accdet_db_ms[] = { + 0, 125, 250, 500, 750, 1000, 1250, 1500 +}; + +static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 }; + +static const unsigned int cs42l43_accdet_bias_sense[] = { + 14, 23, 41, 50, 60, 68, 86, 95, 0, +}; + +static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const prop, + unsigned int defval, unsigned int *val, + const unsigned int *values, const int nvalues) +{ + struct cs42l43 *cs42l43 = priv->core; + int i, ret; + + ret = device_property_read_u32(cs42l43->dev, prop, &defval); + if (ret != -EINVAL && ret < 0) { + dev_err(priv->dev, "Property %s malformed: %d\n", prop, ret); + return ret; + } + + if (val) + *val = defval; + + for (i = 0; i < nvalues; i++) + if (defval == values[i]) + return i; + + dev_err(priv->dev, "Invalid value for property %s: %d\n", prop, defval); + return -EINVAL; +} + +int cs42l43_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *d) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + /* This tip sense invert is always set, HW wants an inverted signal */ + unsigned int tip_deb = CS42L43_TIPSENSE_INV_MASK; + unsigned int hs2 = 0x2 << CS42L43_HSDET_MODE_SHIFT; + unsigned int autocontrol = 0, pdncntl = 0; + int ret; + + dev_dbg(priv->dev, "Configure accessory detect\n"); + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret); + return ret; + } + + mutex_lock(&priv->jack_lock); + + priv->jack_hp = jack; + + if (!jack) + goto done; + + ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms"); + if (ret != -EINVAL) { + if (ret < 0) { + dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n", + ret); + goto error; + } + + if (ret > CS42L43_N_BUTTONS) { + ret = -EINVAL; + dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n"); + goto error; + } + + device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", + priv->buttons, ret); + } else { + priv->buttons[0] = 70; + priv->buttons[1] = 185; + priv->buttons[2] = 355; + priv->buttons[3] = 735; + } + + ret = cs42l43_find_index(priv, "cirrus,detect-us", 10000, &priv->detect_us, + cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); + if (ret < 0) + goto error; + + hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT; + + priv->bias_low = device_property_read_bool(cs42l43->dev, "cirrus,bias-low"); + + ret = cs42l43_find_index(priv, "cirrus,bias-ramp-ms", 170, + &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms, + ARRAY_SIZE(cs42l43_accdet_ramp_ms)); + if (ret < 0) + goto error; + + hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,bias-sense-microamp", 0, + &priv->bias_sense_ua, cs42l43_accdet_bias_sense, + ARRAY_SIZE(cs42l43_accdet_bias_sense)); + if (ret < 0) + goto error; + + if (priv->bias_sense_ua) + autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT; + + if (!device_property_read_bool(cs42l43->dev, "cirrus,button-automute")) + autocontrol |= CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK; + + ret = device_property_read_u32(cs42l43->dev, "cirrus,tip-debounce-ms", + &priv->tip_debounce_ms); + if (ret < 0 && ret != -EINVAL) { + dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret); + goto error; + } + + /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */ + if (device_property_read_bool(cs42l43->dev, "cirrus,tip-invert")) + autocontrol |= 0x1 << CS42L43_JACKDET_INV_SHIFT; + + if (device_property_read_bool(cs42l43->dev, "cirrus,tip-disable-pullup")) + autocontrol |= 0x1 << CS42L43_JACKDET_MODE_SHIFT; + else + autocontrol |= 0x3 << CS42L43_JACKDET_MODE_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,tip-fall-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,tip-rise-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT; + + if (device_property_read_bool(cs42l43->dev, "cirrus,use-ring-sense")) { + unsigned int ring_deb = 0; + + priv->use_ring_sense = true; + + /* HW wants an inverted signal, so invert the invert */ + if (!device_property_read_bool(cs42l43->dev, "cirrus,ring-invert")) + ring_deb |= CS42L43_RINGSENSE_INV_MASK; + + if (!device_property_read_bool(cs42l43->dev, + "cirrus,ring-disable-pullup")) + ring_deb |= CS42L43_RINGSENSE_PULLUP_PDNB_MASK; + + ret = cs42l43_find_index(priv, "cirrus,ring-fall-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,ring-rise-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT; + pdncntl |= CS42L43_RING_SENSE_EN_MASK; + + regmap_update_bits(cs42l43->regmap, CS42L43_RINGSENSE_DEB_CTRL, + CS42L43_RINGSENSE_INV_MASK | + CS42L43_RINGSENSE_PULLUP_PDNB_MASK | + CS42L43_RINGSENSE_FALLING_DB_TIME_MASK | + CS42L43_RINGSENSE_RISING_DB_TIME_MASK, + ring_deb); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_TIPSENSE_DEB_CTRL, + CS42L43_TIPSENSE_INV_MASK | + CS42L43_TIPSENSE_FALLING_DB_TIME_MASK | + CS42L43_TIPSENSE_RISING_DB_TIME_MASK, tip_deb); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK | + CS42L43_AUTO_HSDET_TIME_MASK, hs2); + +done: + ret = 0; + + regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | + CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); + regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, + CS42L43_RING_SENSE_EN_MASK, pdncntl); + + dev_dbg(priv->dev, "Successfully configured accessory detect\n"); + +error: + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return ret; +} + +static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool force_high) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val = 0x3 << CS42L43_HSBIAS_MODE_SHIFT; + + dev_dbg(priv->dev, "Start headset bias\n"); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK); + + if (!force_high && priv->bias_low) + val = 0x2 << CS42L43_HSBIAS_MODE_SHIFT; + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, val); + + msleep(priv->bias_ramp_ms); +} + +static void cs42l43_stop_hs_bias(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Stop headset bias\n"); + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, 0); +} + +irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + queue_delayed_work(system_wq, &priv->bias_sense_timeout, + msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +#define CS42L43_JACK_PRESENT 0x3 +#define CS42L43_JACK_ABSENT 0x0 + +#define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT) +#define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE) +#define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET) +#define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT) +#define CS42L43_JACK_LINEIN (SND_JACK_MECHANICAL | SND_JACK_LINEIN) +#define CS42L43_JACK_EXTENSION (SND_JACK_MECHANICAL) +#define CS42L43_JACK_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | \ + SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5) + +static inline bool cs42l43_jack_present(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int sts = 0; + + regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts); + + sts = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; + + return sts == CS42L43_JACK_PRESENT; +} + +static void cs42l43_start_button_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val = 0x3 << CS42L43_BUTTON_DETECT_MODE_SHIFT; + + dev_dbg(priv->dev, "Start button detect\n"); + + priv->button_detect_running = true; + + if (priv->bias_low) + val = 0x1 << CS42L43_BUTTON_DETECT_MODE_SHIFT; + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_BUTTON_DETECT_MODE_MASK | + CS42L43_MIC_LVL_DET_DISABLE_MASK, val); + + if (priv->bias_sense_ua) { + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); + } +} + +static void cs42l43_stop_button_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Stop button detect\n"); + + if (priv->bias_sense_ua) { + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_BUTTON_DETECT_MODE_MASK | + CS42L43_MIC_LVL_DET_DISABLE_MASK, + CS42L43_MIC_LVL_DET_DISABLE_MASK); + + priv->button_detect_running = false; +} + +#define CS42L43_BUTTON_COMB_MAX 512 +#define CS42L43_BUTTON_ROUT 2210 + +void cs42l43_button_press_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + button_press_work.work); + struct cs42l43 *cs42l43 = priv->core; + unsigned int buttons = 0; + unsigned int val = 0; + int i, ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for button press: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + if (!priv->button_detect_running) { + dev_dbg(priv->dev, "Spurious button press IRQ\n"); + goto error; + } + + regmap_read(cs42l43->regmap, CS42L43_DETECT_STATUS_1, &val); + + /* Bail if jack removed, the button is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) { + dev_dbg(priv->dev, "Button ignored due to removal\n"); + goto error; + } + + if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) { + dev_dbg(priv->dev, "Button ignored due to bias sense\n"); + goto error; + } + + val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT; + val = ((CS42L43_BUTTON_COMB_MAX << 20) / (val + 1)) - (1 << 20); + if (val) + val = (CS42L43_BUTTON_ROUT << 20) / val; + else + val = UINT_MAX; + + for (i = 0; i < CS42L43_N_BUTTONS; i++) { + if (val < priv->buttons[i]) { + buttons = SND_JACK_BTN_0 >> i; + dev_dbg(priv->dev, "Detected button %d at %d Ohms\n", i, val); + break; + } + } + + if (!buttons) + dev_dbg(priv->dev, "Unrecognised button: %d Ohms\n", val); + + snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS); + +error: + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +irqreturn_t cs42l43_button_press(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + // Wait for 2 full cycles of comb filter to ensure good reading + queue_delayed_work(system_wq, &priv->button_press_work, + msecs_to_jiffies(10)); + + return IRQ_HANDLED; +} + +void cs42l43_button_release_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + button_release_work); + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for button release: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + if (priv->button_detect_running) { + dev_dbg(priv->dev, "Button release IRQ\n"); + + snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); + } else { + dev_dbg(priv->dev, "Spurious button release IRQ\n"); + } + + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +irqreturn_t cs42l43_button_release(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + queue_work(system_wq, &priv->button_release_work); + + return IRQ_HANDLED; +} + +void cs42l43_bias_sense_timeout(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + bias_sense_timeout.work); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + if (cs42l43_jack_present(priv) && priv->button_detect_running) { + dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n"); + + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); + } + + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +static void cs42l43_start_load_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Start load detect\n"); + + snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component)); + + priv->load_detect_running = true; + + if (priv->hp_ena) { + unsigned long time_left; + + reinit_completion(&priv->hp_shutdown); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + CS42L43_HP_EN_MASK, 0); + + time_left = wait_for_completion_timeout(&priv->hp_shutdown, + msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS)); + if (!time_left) + dev_err(priv->dev, "Load detect HP power down timed out\n"); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3, + CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2, CS42L43_HP_HPF_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL, + CS42L43_ADPTPWR_MODE_MASK, 0x4 << CS42L43_ADPTPWR_MODE_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL, + CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, 0x6); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1, + CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, 0); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK); + + regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA, + CS42L43_HPLOAD_DET_EN_MASK, + CS42L43_HPLOAD_DET_EN_MASK); + + snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component)); +} + +static void cs42l43_stop_load_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Stop load detect\n"); + + snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component)); + + regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA, + CS42L43_HPLOAD_DET_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1, + CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, + CS42L43_HP_MSTR_VOL_CTRL_EN_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL, + CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, + 0x4 << CS42L43_HP_DIG_VOL_RAMP_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL, + CS42L43_ADPTPWR_MODE_MASK, 0x7 << CS42L43_ADPTPWR_MODE_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2, + CS42L43_HP_HPF_EN_MASK, CS42L43_HP_HPF_EN_MASK); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3, + CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, + priv->adc_ena); + + if (priv->hp_ena) { + unsigned long time_left; + + reinit_completion(&priv->hp_startup); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + CS42L43_HP_EN_MASK, priv->hp_ena); + + time_left = wait_for_completion_timeout(&priv->hp_startup, + msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS)); + if (!time_left) + dev_err(priv->dev, "Load detect HP restore timed out\n"); + } + + priv->load_detect_running = false; + + snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component)); +} + +static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val = 0; + unsigned long time_left; + + reinit_completion(&priv->load_detect); + + cs42l43_start_load_detect(priv); + time_left = wait_for_completion_timeout(&priv->load_detect, + msecs_to_jiffies(CS42L43_LOAD_TIMEOUT_MS)); + cs42l43_stop_load_detect(priv); + + if (!time_left) + return -ETIMEDOUT; + + regmap_read(cs42l43->regmap, CS42L43_LOADDETRESULTS, &val); + + dev_dbg(priv->dev, "Headphone load detect: 0x%x\n", val); + + /* Bail if jack removed, the load is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) + return -ENODEV; + + if (mic) { + cs42l43_start_hs_bias(priv, false); + cs42l43_start_button_detect(priv); + + return CS42L43_JACK_HEADSET; + } + + switch (val & CS42L43_AMP3_RES_DET_MASK) { + case 0x0: // low impedance + case 0x1: // high impedance + return CS42L43_JACK_HEADPHONE; + case 0x2: // lineout + case 0x3: // Open circuit + return CS42L43_JACK_LINEOUT; + default: + return -EINVAL; + } +} + +static int cs42l43_run_type_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + int timeout_ms = ((2 * priv->detect_us) / 1000) + 200; + unsigned int type = 0xff; + unsigned long time_left; + + reinit_completion(&priv->type_detect); + + cs42l43_start_hs_bias(priv, true); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK, 0x3 << CS42L43_HSDET_MODE_SHIFT); + + time_left = wait_for_completion_timeout(&priv->type_detect, + msecs_to_jiffies(timeout_ms)); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK, 0x2 << CS42L43_HSDET_MODE_SHIFT); + cs42l43_stop_hs_bias(priv); + + if (!time_left) + return -ETIMEDOUT; + + regmap_read(cs42l43->regmap, CS42L43_HS_STAT, &type); + + dev_dbg(priv->dev, "Type detect: 0x%x\n", type); + + /* Bail if jack removed, the type is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) + return -ENODEV; + + switch (type & CS42L43_HSDET_TYPE_STS_MASK) { + case 0x0: // CTIA + case 0x1: // OMTP + return cs42l43_run_load_detect(priv, true); + case 0x2: // 3-pole + return cs42l43_run_load_detect(priv, false); + case 0x3: // Open-circuit + return CS42L43_JACK_EXTENSION; + default: + return -EINVAL; + } +} + +static void cs42l43_clear_jack(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + cs42l43_stop_button_detect(priv); + cs42l43_stop_hs_bias(priv); + + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL, + CS42L43_JACK_STEREO_CONFIG_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK, + 0x2 << CS42L43_HSDET_MODE_SHIFT); + + snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF); +} + +void cs42l43_tip_sense_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + tip_sense_work.work); + struct cs42l43 *cs42l43 = priv->core; + unsigned int sts = 0; + unsigned int tip, ring; + int ret, report; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for tip work: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts); + + dev_dbg(priv->dev, "Tip sense: 0x%x\n", sts); + + tip = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; + ring = (sts >> CS42L43_RINGSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; + + if (tip == CS42L43_JACK_PRESENT) { + if (cs42l43->sdw && !priv->jack_present) { + priv->jack_present = true; + pm_runtime_get(priv->dev); + } + + if (priv->use_ring_sense && ring == CS42L43_JACK_ABSENT) { + report = CS42L43_JACK_OPTICAL; + } else { + report = cs42l43_run_type_detect(priv); + if (report < 0) { + dev_err(priv->dev, "Jack detect failed: %d\n", report); + goto error; + } + } + + snd_soc_jack_report(priv->jack_hp, report, report); + } else { + priv->jack_override = 0; + + cs42l43_clear_jack(priv); + + if (cs42l43->sdw && priv->jack_present) { + pm_runtime_put(priv->dev); + priv->jack_present = false; + } + } + +error: + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +irqreturn_t cs42l43_tip_sense(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + cancel_delayed_work(&priv->bias_sense_timeout); + cancel_delayed_work(&priv->tip_sense_work); + cancel_delayed_work(&priv->button_press_work); + cancel_work(&priv->button_release_work); + + queue_delayed_work(system_long_wq, &priv->tip_sense_work, + msecs_to_jiffies(priv->tip_debounce_ms)); + + return IRQ_HANDLED; +} + +enum cs42l43_raw_jack { + CS42L43_JACK_RAW_CTIA = 0, + CS42L43_JACK_RAW_OMTP, + CS42L43_JACK_RAW_HEADPHONE, + CS42L43_JACK_RAW_LINE_OUT, + CS42L43_JACK_RAW_LINE_IN, + CS42L43_JACK_RAW_MICROPHONE, + CS42L43_JACK_RAW_OPTICAL, +}; + +#define CS42L43_JACK_3_POLE_SWITCHES ((0x2 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | \ + CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | \ + CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | \ + CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | \ + CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | \ + CS42L43_HSGND_HS3_SEL_MASK | \ + CS42L43_HSGND_HS4_SEL_MASK) + +static const struct cs42l43_jack_override_mode { + unsigned int hsdet_mode; + unsigned int mic_ctrl; + unsigned int clamp_ctrl; + int report; +} cs42l43_jack_override_modes[] = { + [CS42L43_JACK_RAW_CTIA] = { + .hsdet_mode = CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | + CS42L43_HSBIAS_OUT_HS4_SEL_MASK | + CS42L43_HSGND_HS3_SEL_MASK, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_HEADSET, + }, + [CS42L43_JACK_RAW_OMTP] = { + .hsdet_mode = (0x1 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | + CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_OUT_HS3_SEL_MASK | + CS42L43_HSGND_HS4_SEL_MASK, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_HEADSET, + }, + [CS42L43_JACK_RAW_HEADPHONE] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_HEADPHONE, + }, + [CS42L43_JACK_RAW_LINE_OUT] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_LINEOUT, + }, + [CS42L43_JACK_RAW_LINE_IN] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .mic_ctrl = 0x2 << CS42L43_JACK_STEREO_CONFIG_SHIFT, + .report = CS42L43_JACK_LINEIN, + }, + [CS42L43_JACK_RAW_MICROPHONE] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) | + CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK, + .report = CS42L43_JACK_LINEIN, + }, + [CS42L43_JACK_RAW_OPTICAL] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_OPTICAL, + }, +}; + +static const char * const cs42l43_jack_text[] = { + "None", "CTIA", "OMTP", "Headphone", "Line-Out", + "Line-In", "Microphone", "Optical", +}; + +SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text); + +int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + mutex_lock(&priv->jack_lock); + ucontrol->value.integer.value[0] = priv->jack_override; + mutex_unlock(&priv->jack_lock); + + return 0; +} + +int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int override = ucontrol->value.integer.value[0]; + + BUILD_BUG_ON(ARRAY_SIZE(cs42l43_jack_override_modes) != + ARRAY_SIZE(cs42l43_jack_text) - 1); + + if (override >= e->items) + return -EINVAL; + + mutex_lock(&priv->jack_lock); + + if (!cs42l43_jack_present(priv)) { + mutex_unlock(&priv->jack_lock); + return -EBUSY; + } + + if (override == priv->jack_override) { + mutex_unlock(&priv->jack_lock); + return 0; + } + + priv->jack_override = override; + + cs42l43_clear_jack(priv); + + if (!override) { + queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0); + } else { + override--; + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK | + CS42L43_HSDET_MANUAL_MODE_MASK | + CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | + CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_OUT_HS3_SEL_MASK | + CS42L43_HSBIAS_OUT_HS4_SEL_MASK | + CS42L43_HSGND_HS3_SEL_MASK | + CS42L43_HSGND_HS4_SEL_MASK, + cs42l43_jack_override_modes[override].hsdet_mode); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL, + CS42L43_HS2_BIAS_EN_MASK | CS42L43_HS1_BIAS_EN_MASK | + CS42L43_JACK_STEREO_CONFIG_MASK, + cs42l43_jack_override_modes[override].mic_ctrl); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL, + CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + cs42l43_jack_override_modes[override].clamp_ctrl); + + switch (override) { + case CS42L43_JACK_RAW_CTIA: + case CS42L43_JACK_RAW_OMTP: + cs42l43_start_hs_bias(priv, false); + cs42l43_start_button_detect(priv); + break; + case CS42L43_JACK_RAW_LINE_IN: + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, + CS42L43_PGA_WIDESWING_MODE_EN_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, + CS42L43_PGA_WIDESWING_MODE_EN_MASK); + break; + case CS42L43_JACK_RAW_MICROPHONE: + cs42l43_start_hs_bias(priv, false); + break; + default: + break; + } + + snd_soc_jack_report(priv->jack_hp, + cs42l43_jack_override_modes[override].report, + cs42l43_jack_override_modes[override].report); + } + + mutex_unlock(&priv->jack_lock); + + return 1; +} diff --git a/sound/soc/codecs/cs42l43-sdw.c b/sound/soc/codecs/cs42l43-sdw.c new file mode 100644 index 000000000000..55ac5fe8c3db --- /dev/null +++ b/sound/soc/codecs/cs42l43-sdw.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS42L43 CODEC driver SoundWire handling +// +// Copyright (C) 2022-2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/errno.h> +#include <linux/mfd/cs42l43.h> +#include <linux/mfd/cs42l43-regs.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/sdw.h> +#include <sound/soc-component.h> +#include <sound/soc-dai.h> +#include <sound/soc.h> + +#include "cs42l43.h" + +int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent); + struct sdw_stream_config sconfig = {0}; + struct sdw_port_config pconfig = {0}; + int ret; + + if (!sdw_stream) + return -EINVAL; + + snd_sdw_params_to_config(substream, params, &sconfig, &pconfig); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pconfig.num = dai->id; + else + pconfig.num = dai->id; + + ret = sdw_stream_add_slave(sdw, &sconfig, &pconfig, 1, sdw_stream); + if (ret) { + dev_err(priv->dev, "Failed to add sdw stream: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_add_peripheral, SND_SOC_CS42L43); + +int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent); + + if (!sdw_stream) + return -EINVAL; + + return sdw_stream_remove_slave(sdw, sdw_stream); +} +EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_remove_peripheral, SND_SOC_CS42L43); + +int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_set_stream, SND_SOC_CS42L43); + +MODULE_DESCRIPTION("CS42L43 CODEC SoundWire Driver"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c new file mode 100644 index 000000000000..24e718e51174 --- /dev/null +++ b/sound/soc/codecs/cs42l43.c @@ -0,0 +1,2278 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS42L43 CODEC driver +// +// Copyright (C) 2022-2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/gcd.h> +#include <linux/irq.h> +#include <linux/jiffies.h> +#include <linux/mfd/cs42l43.h> +#include <linux/mfd/cs42l43-regs.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/string.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-component.h> +#include <sound/soc-dapm.h> +#include <sound/soc-dai.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "cs42l43.h" + +#define CS42L43_DECL_MUX(name, reg) \ +static SOC_VALUE_ENUM_SINGLE_DECL(cs42l43_##name##_enum, reg, \ + 0, CS42L43_MIXER_SRC_MASK, \ + cs42l43_mixer_texts, cs42l43_mixer_values); \ +static const struct snd_kcontrol_new cs42l43_##name##_mux = \ + SOC_DAPM_ENUM("Route", cs42l43_##name##_enum) + +#define CS42L43_DECL_MIXER(name, reg) \ + CS42L43_DECL_MUX(name##_in1, reg); \ + CS42L43_DECL_MUX(name##_in2, reg + 0x4); \ + CS42L43_DECL_MUX(name##_in3, reg + 0x8); \ + CS42L43_DECL_MUX(name##_in4, reg + 0xC) + +#define CS42L43_DAPM_MUX(name_str, name) \ + SND_SOC_DAPM_MUX(name_str " Input", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_mux) + +#define CS42L43_DAPM_MIXER(name_str, name) \ + SND_SOC_DAPM_MUX(name_str " Input 1", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in1_mux), \ + SND_SOC_DAPM_MUX(name_str " Input 2", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in2_mux), \ + SND_SOC_DAPM_MUX(name_str " Input 3", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in3_mux), \ + SND_SOC_DAPM_MUX(name_str " Input 4", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in4_mux), \ + SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0) + +#define CS42L43_BASE_ROUTES(name_str) \ + { name_str, "Tone Generator 1", "Tone 1" }, \ + { name_str, "Tone Generator 2", "Tone 2" }, \ + { name_str, "Decimator 1", "Decimator 1" }, \ + { name_str, "Decimator 2", "Decimator 2" }, \ + { name_str, "Decimator 3", "Decimator 3" }, \ + { name_str, "Decimator 4", "Decimator 4" }, \ + { name_str, "ASPRX1", "ASPRX1" }, \ + { name_str, "ASPRX2", "ASPRX2" }, \ + { name_str, "ASPRX3", "ASPRX3" }, \ + { name_str, "ASPRX4", "ASPRX4" }, \ + { name_str, "ASPRX5", "ASPRX5" }, \ + { name_str, "ASPRX6", "ASPRX6" }, \ + { name_str, "DP5RX1", "DP5RX1" }, \ + { name_str, "DP5RX2", "DP5RX2" }, \ + { name_str, "DP6RX1", "DP6RX1" }, \ + { name_str, "DP6RX2", "DP6RX2" }, \ + { name_str, "DP7RX1", "DP7RX1" }, \ + { name_str, "DP7RX2", "DP7RX2" }, \ + { name_str, "ASRC INT1", "ASRC_INT1" }, \ + { name_str, "ASRC INT2", "ASRC_INT2" }, \ + { name_str, "ASRC INT3", "ASRC_INT3" }, \ + { name_str, "ASRC INT4", "ASRC_INT4" }, \ + { name_str, "ASRC DEC1", "ASRC_DEC1" }, \ + { name_str, "ASRC DEC2", "ASRC_DEC2" }, \ + { name_str, "ASRC DEC3", "ASRC_DEC3" }, \ + { name_str, "ASRC DEC4", "ASRC_DEC4" }, \ + { name_str, "ISRC1 INT1", "ISRC1INT1" }, \ + { name_str, "ISRC1 INT2", "ISRC1INT2" }, \ + { name_str, "ISRC1 DEC1", "ISRC1DEC1" }, \ + { name_str, "ISRC1 DEC2", "ISRC1DEC2" }, \ + { name_str, "ISRC2 INT1", "ISRC2INT1" }, \ + { name_str, "ISRC2 INT2", "ISRC2INT2" }, \ + { name_str, "ISRC2 DEC1", "ISRC2DEC1" }, \ + { name_str, "ISRC2 DEC2", "ISRC2DEC2" }, \ + { name_str, "EQ1", "EQ" }, \ + { name_str, "EQ2", "EQ" } + +#define CS42L43_MUX_ROUTES(name_str, widget) \ + { widget, NULL, name_str " Input" }, \ + { name_str " Input", NULL, "Mixer Core" }, \ + CS42L43_BASE_ROUTES(name_str " Input") + +#define CS42L43_MIXER_ROUTES(name_str, widget) \ + { name_str " Mixer", NULL, name_str " Input 1" }, \ + { name_str " Mixer", NULL, name_str " Input 2" }, \ + { name_str " Mixer", NULL, name_str " Input 3" }, \ + { name_str " Mixer", NULL, name_str " Input 4" }, \ + { widget, NULL, name_str " Mixer" }, \ + { name_str " Mixer", NULL, "Mixer Core" }, \ + CS42L43_BASE_ROUTES(name_str " Input 1"), \ + CS42L43_BASE_ROUTES(name_str " Input 2"), \ + CS42L43_BASE_ROUTES(name_str " Input 3"), \ + CS42L43_BASE_ROUTES(name_str " Input 4") + +#define CS42L43_MIXER_VOLUMES(name_str, base) \ + SOC_SINGLE_RANGE_TLV(name_str " Input 1 Volume", base, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name_str " Input 2 Volume", base + 4, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name_str " Input 3 Volume", base + 8, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name_str " Input 4 Volume", base + 12, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv) + +#define CS42L43_IRQ_ERROR(name) \ +static irqreturn_t cs42l43_##name(int irq, void *data) \ +{ \ + struct cs42l43_codec *priv = data; \ + dev_err(priv->dev, "Error " #name " IRQ\n"); \ + return IRQ_HANDLED; \ +} + +CS42L43_IRQ_ERROR(pll_lost_lock) +CS42L43_IRQ_ERROR(spkr_clock_stop) +CS42L43_IRQ_ERROR(spkl_clock_stop) +CS42L43_IRQ_ERROR(spkr_brown_out) +CS42L43_IRQ_ERROR(spkl_brown_out) +CS42L43_IRQ_ERROR(spkr_therm_shutdown) +CS42L43_IRQ_ERROR(spkl_therm_shutdown) +CS42L43_IRQ_ERROR(spkr_therm_warm) +CS42L43_IRQ_ERROR(spkl_therm_warm) +CS42L43_IRQ_ERROR(spkr_sc_detect) +CS42L43_IRQ_ERROR(spkl_sc_detect) +CS42L43_IRQ_ERROR(hp_ilimit) + +#define CS42L43_IRQ_COMPLETE(name) \ +static irqreturn_t cs42l43_##name(int irq, void *data) \ +{ \ + struct cs42l43_codec *priv = data; \ + dev_dbg(priv->dev, #name " completed\n"); \ + complete(&priv->name); \ + return IRQ_HANDLED; \ +} + +CS42L43_IRQ_COMPLETE(pll_ready) +CS42L43_IRQ_COMPLETE(hp_startup) +CS42L43_IRQ_COMPLETE(hp_shutdown) +CS42L43_IRQ_COMPLETE(type_detect) +CS42L43_IRQ_COMPLETE(spkr_shutdown) +CS42L43_IRQ_COMPLETE(spkl_shutdown) +CS42L43_IRQ_COMPLETE(spkr_startup) +CS42L43_IRQ_COMPLETE(spkl_startup) +CS42L43_IRQ_COMPLETE(load_detect) + +static irqreturn_t cs42l43_mic_shutter(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + const char * const controls[] = { + "Decimator 1 Switch", + "Decimator 2 Switch", + "Decimator 3 Switch", + "Decimator 4 Switch", + }; + int i, ret; + + dev_dbg(priv->dev, "Microphone shutter changed\n"); + + if (!priv->component) + return IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(controls); i++) { + ret = snd_soc_component_notify_control(priv->component, + controls[i]); + if (ret) + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static irqreturn_t cs42l43_spk_shutter(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + int ret; + + dev_dbg(priv->dev, "Speaker shutter changed\n"); + + if (!priv->component) + return IRQ_NONE; + + ret = snd_soc_component_notify_control(priv->component, + "Speaker Digital Switch"); + if (ret) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static const unsigned int cs42l43_sample_rates[] = { + 8000, 16000, 24000, 32000, 44100, 48000, 96000, 192000, +}; + +#define CS42L43_CONSUMER_RATE_MASK 0xFF +#define CS42L43_PROVIDER_RATE_MASK 0xEF // 44.1k only supported as consumer + +static const struct snd_pcm_hw_constraint_list cs42l43_constraint = { + .count = ARRAY_SIZE(cs42l43_sample_rates), + .list = cs42l43_sample_rates, +}; + +static int cs42l43_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int provider = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK); + + if (provider) + priv->constraint.mask = CS42L43_PROVIDER_RATE_MASK; + else + priv->constraint.mask = CS42L43_CONSUMER_RATE_MASK; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &priv->constraint); +} + +static int cs42l43_convert_sample_rate(unsigned int rate) +{ + switch (rate) { + case 8000: + return 0x11; + case 16000: + return 0x12; + case 24000: + return 0x02; + case 32000: + return 0x13; + case 44100: + return 0x0B; + case 48000: + return 0x03; + case 96000: + return 0x04; + case 192000: + return 0x05; + default: + return -EINVAL; + } +} + +static int cs42l43_set_sample_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + ret = cs42l43_convert_sample_rate(params_rate(params)); + if (ret < 0) { + dev_err(priv->dev, "Failed to convert sample rate: %d\n", ret); + return ret; + } + + //FIXME: For now lets just set sample rate 1, this needs expanded in the future + regmap_update_bits(cs42l43->regmap, CS42L43_SAMPLE_RATE1, + CS42L43_SAMPLE_RATE_MASK, ret); + + return 0; +} + +static int cs42l43_asp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct cs42l43 *cs42l43 = priv->core; + int dsp_mode = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CTRL, + CS42L43_ASP_FSYNC_MODE_MASK); + int provider = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK); + int n_chans = params_channels(params); + int data_width = params_width(params); + int n_slots = n_chans; + int slot_width = data_width; + int frame, bclk_target, i; + unsigned int reg; + int *slots; + + if (priv->n_slots) { + n_slots = priv->n_slots; + slot_width = priv->slot_width; + } + + if (!dsp_mode && (n_slots & 0x1)) { + dev_dbg(priv->dev, "Forcing balanced channels on ASP\n"); + n_slots++; + } + + frame = n_slots * slot_width; + bclk_target = params_rate(params) * frame; + + if (provider) { + unsigned int gcd_nm = gcd(bclk_target, CS42L43_INTERNAL_SYSCLK); + int n = bclk_target / gcd_nm; + int m = CS42L43_INTERNAL_SYSCLK / gcd_nm; + + if (n > (CS42L43_ASP_BCLK_N_MASK >> CS42L43_ASP_BCLK_N_SHIFT) || + m > CS42L43_ASP_BCLK_M_MASK) { + dev_err(priv->dev, "Can't produce %dHz bclk\n", bclk_target); + return -EINVAL; + } + + dev_dbg(priv->dev, "bclk %d/%d = %dHz, with %dx%d frame\n", + n, m, bclk_target, n_slots, slot_width); + + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG1, + CS42L43_ASP_BCLK_N_MASK | CS42L43_ASP_BCLK_M_MASK, + n << CS42L43_ASP_BCLK_N_SHIFT | + m << CS42L43_ASP_BCLK_M_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL1, + CS42L43_ASP_FSYNC_M_MASK, frame); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL4, + CS42L43_ASP_NUM_BCLKS_PER_FSYNC_MASK, + frame << CS42L43_ASP_NUM_BCLKS_PER_FSYNC_SHIFT); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + reg = CS42L43_ASP_TX_CH1_CTRL; + slots = priv->tx_slots; + } else { + reg = CS42L43_ASP_RX_CH1_CTRL; + slots = priv->rx_slots; + } + + for (i = 0; i < n_chans; i++, reg += 4) { + int slot_phase = dsp_mode | (i & CS42L43_ASP_CH_SLOT_PHASE_MASK); + int slot_pos; + + if (dsp_mode) + slot_pos = slots[i] * slot_width; + else + slot_pos = (slots[i] / 2) * slot_width; + + dev_dbg(priv->dev, "Configure channel %d at slot %d (%d,%d)\n", + i, slots[i], slot_pos, slot_phase); + + regmap_update_bits(cs42l43->regmap, reg, + CS42L43_ASP_CH_WIDTH_MASK | + CS42L43_ASP_CH_SLOT_MASK | + CS42L43_ASP_CH_SLOT_PHASE_MASK, + ((data_width - 1) << CS42L43_ASP_CH_WIDTH_SHIFT) | + (slot_pos << CS42L43_ASP_CH_SLOT_SHIFT) | + slot_phase); + } + + return cs42l43_set_sample_rate(substream, params, dai); +} + +static int cs42l43_asp_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int provider = regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK); + struct snd_soc_dapm_route routes[] = { + { "BCLK", NULL, "FSYNC" }, + }; + unsigned int asp_ctrl = 0; + unsigned int data_ctrl = 0; + unsigned int fsync_ctrl = 0; + unsigned int clk_config = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT; + fallthrough; + case SND_SOC_DAIFMT_DSP_B: + asp_ctrl |= CS42L43_ASP_FSYNC_MODE_MASK; + data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK; + break; + case SND_SOC_DAIFMT_I2S: + data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT; + break; + case SND_SOC_DAIFMT_LEFT_J: + data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK; + break; + default: + dev_err(priv->dev, "Unsupported DAI format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + if (provider) + snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes)); + break; + case SND_SOC_DAIFMT_CBP_CFP: + if (!provider) + snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes)); + clk_config |= CS42L43_ASP_MASTER_MODE_MASK; + break; + default: + dev_err(priv->dev, "Unsupported ASP mode 0x%x\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + clk_config |= CS42L43_ASP_BCLK_INV_MASK; /* Yes BCLK_INV = NB */ + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + clk_config |= CS42L43_ASP_BCLK_INV_MASK; + fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK | + CS42L43_ASP_FSYNC_OUT_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK | + CS42L43_ASP_FSYNC_OUT_INV_MASK; + break; + default: + dev_err(priv->dev, "Unsupported invert mode 0x%x\n", + fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CTRL, + CS42L43_ASP_FSYNC_MODE_MASK, + asp_ctrl); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_DATA_CTRL, + CS42L43_ASP_FSYNC_FRAME_START_DLY_MASK | + CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK, + data_ctrl); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK | + CS42L43_ASP_BCLK_INV_MASK, + clk_config); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL3, + CS42L43_ASP_FSYNC_IN_INV_MASK | + CS42L43_ASP_FSYNC_OUT_INV_MASK, + fsync_ctrl); + + return 0; +} + +static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned int mask, int *slots) +{ + int i; + + for (i = 0; i < CS42L43_ASP_MAX_CHANNELS; ++i) { + int slot = ffs(mask) - 1; + + if (slot < 0) + return; + + slots[i] = slot; + + mask &= ~(1 << slot); + } + + if (mask) + dev_warn(priv->dev, "Too many channels in TDM mask\n"); +} + +static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + priv->n_slots = slots; + priv->slot_width = slot_width; + + if (!slots) { + tx_mask = CS42L43_DEFAULT_SLOTS; + rx_mask = CS42L43_DEFAULT_SLOTS; + } + + cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots); + cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots); + + return 0; +} + +static const struct snd_soc_dai_ops cs42l43_asp_ops = { + .startup = cs42l43_startup, + .hw_params = cs42l43_asp_hw_params, + .set_fmt = cs42l43_asp_set_fmt, + .set_tdm_slot = cs42l43_asp_set_tdm_slot, +}; + +static int cs42l43_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret; + + ret = cs42l43_sdw_add_peripheral(substream, params, dai); + if (ret) + return ret; + + return cs42l43_set_sample_rate(substream, params, dai); +}; + +static const struct snd_soc_dai_ops cs42l43_sdw_ops = { + .startup = cs42l43_startup, + .set_stream = cs42l43_sdw_set_stream, + .hw_params = cs42l43_sdw_hw_params, + .hw_free = cs42l43_sdw_remove_peripheral, +}; + +#define CS42L43_ASP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define CS42L43_SDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver cs42l43_dais[] = { + { + .name = "cs42l43-asp", + .ops = &cs42l43_asp_ops, + .symmetric_rate = 1, + .capture = { + .stream_name = "ASP Capture", + .channels_min = 1, + .channels_max = CS42L43_ASP_MAX_CHANNELS, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_ASP_FORMATS, + }, + .playback = { + .stream_name = "ASP Playback", + .channels_min = 1, + .channels_max = CS42L43_ASP_MAX_CHANNELS, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_ASP_FORMATS, + }, + }, + { + .name = "cs42l43-dp1", + .id = 1, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp2", + .id = 2, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp3", + .id = 3, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp4", + .id = 4, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp5", + .id = 5, + .ops = &cs42l43_sdw_ops, + .playback = { + .stream_name = "DP5 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp6", + .id = 6, + .ops = &cs42l43_sdw_ops, + .playback = { + .stream_name = "DP6 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp7", + .id = 7, + .ops = &cs42l43_sdw_ops, + .playback = { + .stream_name = "DP7 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, +}; + +static const DECLARE_TLV_DB_SCALE(cs42l43_mixer_tlv, -3200, 100, 0); + +static const char * const cs42l43_ramp_text[] = { + "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", + "15ms/6dB", "30ms/6dB", +}; + +static const char * const cs42l43_adc1_input_text[] = { "IN1", "IN2" }; + +static SOC_ENUM_SINGLE_DECL(cs42l43_adc1_input, CS42L43_ADC_B_CTRL1, + CS42L43_ADC_AIN_SEL_SHIFT, + cs42l43_adc1_input_text); + +static const struct snd_kcontrol_new cs42l43_adc1_input_ctl = + SOC_DAPM_ENUM("ADC1 Input", cs42l43_adc1_input); + +static const char * const cs42l43_dec_mode_text[] = { "ADC", "PDM" }; + +static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec1_mode, cs42l43_dec_mode_text); +static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec2_mode, cs42l43_dec_mode_text); + +static const struct snd_kcontrol_new cs42l43_dec_mode_ctl[] = { + SOC_DAPM_ENUM("Decimator 1 Mode", cs42l43_dec1_mode), + SOC_DAPM_ENUM("Decimator 2 Mode", cs42l43_dec2_mode), +}; + +static const char * const cs42l43_pdm_clk_text[] = { + "3.072MHz", "1.536MHz", "768kHz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_pdm1_clk, CS42L43_PDM_CONTROL, + CS42L43_PDM1_CLK_DIV_SHIFT, cs42l43_pdm_clk_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_pdm2_clk, CS42L43_PDM_CONTROL, + CS42L43_PDM2_CLK_DIV_SHIFT, cs42l43_pdm_clk_text); + +static DECLARE_TLV_DB_SCALE(cs42l43_adc_tlv, -600, 600, 0); +static DECLARE_TLV_DB_SCALE(cs42l43_dec_tlv, -6400, 50, 0); + +static const char * const cs42l43_wnf_corner_text[] = { + "160Hz", "180Hz", "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); + +static const char * const cs42l43_hpf_corner_text[] = { + "3Hz", "12Hz", "48Hz", "96Hz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); + +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VD_RAMP_SHIFT, cs42l43_ramp_text); + +static DECLARE_TLV_DB_SCALE(cs42l43_speaker_tlv, -6400, 50, 0); + +static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_up, CS42L43_AMP1_2_VOL_RAMP, + CS42L43_AMP1_2_VI_RAMP_SHIFT, cs42l43_ramp_text); + +static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_down, CS42L43_AMP1_2_VOL_RAMP, + CS42L43_AMP1_2_VD_RAMP_SHIFT, cs42l43_ramp_text); + +static DECLARE_TLV_DB_SCALE(cs42l43_headphone_tlv, -11450, 50, 1); + +static const char * const cs42l43_headphone_ramp_text[] = { + "1", "2", "4", "6", "8", "11", "12", "16", "22", "24", "33", "36", "44", + "48", "66", "72", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_headphone_ramp, CS42L43_PGAVOL, + CS42L43_HP_PATH_VOL_RAMP_SHIFT, + cs42l43_headphone_ramp_text); + +static const char * const cs42l43_tone_freq_text[] = { + "1kHz", "2kHz", "4kHz", "6kHz", "8kHz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_tone1_freq, CS42L43_TONE_CH1_CTRL, + CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text); + +static SOC_ENUM_SINGLE_DECL(cs42l43_tone2_freq, CS42L43_TONE_CH2_CTRL, + CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text); + +static const char * const cs42l43_mixer_texts[] = { + "None", + "Tone Generator 1", "Tone Generator 2", + "Decimator 1", "Decimator 2", "Decimator 3", "Decimator 4", + "ASPRX1", "ASPRX2", "ASPRX3", "ASPRX4", "ASPRX5", "ASPRX6", + "DP5RX1", "DP5RX2", "DP6RX1", "DP6RX2", "DP7RX1", "DP7RX2", + "ASRC INT1", "ASRC INT2", "ASRC INT3", "ASRC INT4", + "ASRC DEC1", "ASRC DEC2", "ASRC DEC3", "ASRC DEC4", + "ISRC1 INT1", "ISRC1 INT2", + "ISRC1 DEC1", "ISRC1 DEC2", + "ISRC2 INT1", "ISRC2 INT2", + "ISRC2 DEC1", "ISRC2 DEC2", + "EQ1", "EQ2", +}; + +static const unsigned int cs42l43_mixer_values[] = { + 0x00, // None + 0x04, 0x05, // Tone Generator 1, 2 + 0x10, 0x11, 0x12, 0x13, // Decimator 1, 2, 3, 4 + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, // ASPRX1,2,3,4,5,6 + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, // DP5, 6, 7RX1, 2 + 0x40, 0x41, 0x42, 0x43, // ASRC INT1, 2, 3, 4 + 0x44, 0x45, 0x46, 0x47, // ASRC DEC1, 2, 3, 4 + 0x50, 0x51, // ISRC1 INT1, 2 + 0x52, 0x53, // ISRC1 DEC1, 2 + 0x54, 0x55, // ISRC2 INT1, 2 + 0x56, 0x57, // ISRC2 DEC1, 2 + 0x58, 0x59, // EQ1, 2 +}; + +CS42L43_DECL_MUX(asptx1, CS42L43_ASPTX1_INPUT); +CS42L43_DECL_MUX(asptx2, CS42L43_ASPTX2_INPUT); +CS42L43_DECL_MUX(asptx3, CS42L43_ASPTX3_INPUT); +CS42L43_DECL_MUX(asptx4, CS42L43_ASPTX4_INPUT); +CS42L43_DECL_MUX(asptx5, CS42L43_ASPTX5_INPUT); +CS42L43_DECL_MUX(asptx6, CS42L43_ASPTX6_INPUT); + +CS42L43_DECL_MUX(dp1tx1, CS42L43_SWIRE_DP1_CH1_INPUT); +CS42L43_DECL_MUX(dp1tx2, CS42L43_SWIRE_DP1_CH2_INPUT); +CS42L43_DECL_MUX(dp1tx3, CS42L43_SWIRE_DP1_CH3_INPUT); +CS42L43_DECL_MUX(dp1tx4, CS42L43_SWIRE_DP1_CH4_INPUT); +CS42L43_DECL_MUX(dp2tx1, CS42L43_SWIRE_DP2_CH1_INPUT); +CS42L43_DECL_MUX(dp2tx2, CS42L43_SWIRE_DP2_CH2_INPUT); +CS42L43_DECL_MUX(dp3tx1, CS42L43_SWIRE_DP3_CH1_INPUT); +CS42L43_DECL_MUX(dp3tx2, CS42L43_SWIRE_DP3_CH2_INPUT); +CS42L43_DECL_MUX(dp4tx1, CS42L43_SWIRE_DP4_CH1_INPUT); +CS42L43_DECL_MUX(dp4tx2, CS42L43_SWIRE_DP4_CH2_INPUT); + +CS42L43_DECL_MUX(asrcint1, CS42L43_ASRC_INT1_INPUT1); +CS42L43_DECL_MUX(asrcint2, CS42L43_ASRC_INT2_INPUT1); +CS42L43_DECL_MUX(asrcint3, CS42L43_ASRC_INT3_INPUT1); +CS42L43_DECL_MUX(asrcint4, CS42L43_ASRC_INT4_INPUT1); +CS42L43_DECL_MUX(asrcdec1, CS42L43_ASRC_DEC1_INPUT1); +CS42L43_DECL_MUX(asrcdec2, CS42L43_ASRC_DEC2_INPUT1); +CS42L43_DECL_MUX(asrcdec3, CS42L43_ASRC_DEC3_INPUT1); +CS42L43_DECL_MUX(asrcdec4, CS42L43_ASRC_DEC4_INPUT1); + +CS42L43_DECL_MUX(isrc1int1, CS42L43_ISRC1INT1_INPUT1); +CS42L43_DECL_MUX(isrc1int2, CS42L43_ISRC1INT2_INPUT1); +CS42L43_DECL_MUX(isrc1dec1, CS42L43_ISRC1DEC1_INPUT1); +CS42L43_DECL_MUX(isrc1dec2, CS42L43_ISRC1DEC2_INPUT1); +CS42L43_DECL_MUX(isrc2int1, CS42L43_ISRC2INT1_INPUT1); +CS42L43_DECL_MUX(isrc2int2, CS42L43_ISRC2INT2_INPUT1); +CS42L43_DECL_MUX(isrc2dec1, CS42L43_ISRC2DEC1_INPUT1); +CS42L43_DECL_MUX(isrc2dec2, CS42L43_ISRC2DEC2_INPUT1); + +CS42L43_DECL_MUX(spdif1, CS42L43_SPDIF1_INPUT1); +CS42L43_DECL_MUX(spdif2, CS42L43_SPDIF2_INPUT1); + +CS42L43_DECL_MIXER(eq1, CS42L43_EQ1MIX_INPUT1); +CS42L43_DECL_MIXER(eq2, CS42L43_EQ2MIX_INPUT1); + +CS42L43_DECL_MIXER(amp1, CS42L43_AMP1MIX_INPUT1); +CS42L43_DECL_MIXER(amp2, CS42L43_AMP2MIX_INPUT1); + +CS42L43_DECL_MIXER(amp3, CS42L43_AMP3MIX_INPUT1); +CS42L43_DECL_MIXER(amp4, CS42L43_AMP4MIX_INPUT1); + +static int cs42l43_dapm_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_get_volsw(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_dapm_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_put_volsw(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_dapm_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_get_enum_double(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_dapm_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_eq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + memcpy(ucontrol->value.integer.value, priv->eq_coeffs, sizeof(priv->eq_coeffs)); + + return 0; +} + +static int cs42l43_eq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + snd_soc_dapm_mutex_lock(dapm); + + memcpy(priv->eq_coeffs, ucontrol->value.integer.value, sizeof(priv->eq_coeffs)); + + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + +static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + mutex_lock(&priv->spk_vu_lock); + + regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, + CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, + CS42L43_AMP1_2_VU_MASK, 0); + + mutex_unlock(&priv->spk_vu_lock); +} + +static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val; + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for shutters: %d\n", ret); + return ret; + } + + /* + * SHUTTER_CONTROL is a mix of volatile and non-volatile bits, so must + * be cached for the non-volatiles, so drop it from the cache here so + * we force a read. + */ + ret = regcache_drop_region(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, + CS42L43_SHUTTER_CONTROL); + if (ret) { + dev_err(priv->dev, "Failed to drop shutter from cache: %d\n", ret); + goto error; + } + + ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val); + if (ret) { + dev_err(priv->dev, "Failed to check shutter status: %d\n", ret); + goto error; + } + + ret = !(val & BIT(shift)); + + dev_dbg(priv->dev, "%s shutter is %s\n", + BIT(shift) == CS42L43_STATUS_MIC_SHUTTER_MUTE_MASK ? "Mic" : "Speaker", + ret ? "open" : "closed"); + +error: + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return ret; +} + +static int cs42l43_decim_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = cs42l43_shutter_get(priv, CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT); + if (ret < 0) + return ret; + else if (!ret) + ucontrol->value.integer.value[0] = ret; + else + ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol); + + return ret; +} + +static int cs42l43_spk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = cs42l43_shutter_get(priv, CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT); + if (ret < 0) + return ret; + else if (!ret) + ucontrol->value.integer.value[0] = ret; + else + ret = snd_soc_get_volsw(kcontrol, ucontrol); + + return ret; +} + +static int cs42l43_spk_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret > 0) + cs42l43_spk_vu_sync(priv); + + return ret; +} + +static const struct snd_kcontrol_new cs42l43_controls[] = { + SOC_ENUM_EXT("Jack Override", cs42l43_jack_enum, + cs42l43_jack_get, cs42l43_jack_put), + + SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L43_ADC_B_CTRL1, CS42L43_ADC_B_CTRL2, + CS42L43_ADC_PGA_GAIN_SHIFT, + 0xF, 5, cs42l43_adc_tlv), + + SOC_DOUBLE("PDM1 Invert Switch", CS42L43_DMIC_PDM_CTRL, + CS42L43_PDM1L_INV_SHIFT, CS42L43_PDM1R_INV_SHIFT, 1, 0), + SOC_DOUBLE("PDM2 Invert Switch", CS42L43_DMIC_PDM_CTRL, + CS42L43_PDM2L_INV_SHIFT, CS42L43_PDM2R_INV_SHIFT, 1, 0), + SOC_ENUM("PDM1 Clock", cs42l43_pdm1_clk), + SOC_ENUM("PDM2 Clock", cs42l43_pdm2_clk), + + SOC_SINGLE("Decimator 1 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 2 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 3 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 4 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + + SOC_ENUM("Decimator 1 WNF Corner Frequency", cs42l43_dec1_wnf_corner), + SOC_ENUM("Decimator 2 WNF Corner Frequency", cs42l43_dec2_wnf_corner), + SOC_ENUM("Decimator 3 WNF Corner Frequency", cs42l43_dec3_wnf_corner), + SOC_ENUM("Decimator 4 WNF Corner Frequency", cs42l43_dec4_wnf_corner), + + SOC_SINGLE("Decimator 1 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 2 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 3 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 4 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + + SOC_ENUM("Decimator 1 HPF Corner Frequency", cs42l43_dec1_hpf_corner), + SOC_ENUM("Decimator 2 HPF Corner Frequency", cs42l43_dec2_hpf_corner), + SOC_ENUM("Decimator 3 HPF Corner Frequency", cs42l43_dec3_hpf_corner), + SOC_ENUM("Decimator 4 HPF Corner Frequency", cs42l43_dec4_hpf_corner), + + SOC_SINGLE_TLV("Decimator 1 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 1 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 2 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 2 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 3 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 3 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 4 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 4 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + + SOC_ENUM_EXT("Decimator 1 Ramp Up", cs42l43_dec1_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 1 Ramp Down", cs42l43_dec1_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 2 Ramp Up", cs42l43_dec2_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 2 Ramp Down", cs42l43_dec2_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 3 Ramp Up", cs42l43_dec3_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 3 Ramp Down", cs42l43_dec3_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 4 Ramp Up", cs42l43_dec4_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 4 Ramp Down", cs42l43_dec4_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + + SOC_DOUBLE_R_EXT("Speaker Digital Switch", + CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2, + CS42L43_AMP_MUTE_SHIFT, 1, 1, + cs42l43_spk_get, cs42l43_spk_put), + + SOC_DOUBLE_R_EXT_TLV("Speaker Digital Volume", + CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2, + CS42L43_AMP_VOL_SHIFT, + 0xBF, 0, snd_soc_get_volsw, cs42l43_spk_put, + cs42l43_speaker_tlv), + + SOC_ENUM("Speaker Ramp Up", cs42l43_speaker_ramp_up), + SOC_ENUM("Speaker Ramp Down", cs42l43_speaker_ramp_down), + + CS42L43_MIXER_VOLUMES("Speaker L", CS42L43_AMP1MIX_INPUT1), + CS42L43_MIXER_VOLUMES("Speaker R", CS42L43_AMP2MIX_INPUT1), + + SOC_DOUBLE_SX_TLV("Headphone Digital Volume", CS42L43_HPPATHVOL, + CS42L43_AMP3_PATH_VOL_SHIFT, CS42L43_AMP4_PATH_VOL_SHIFT, + 0x11B, 229, cs42l43_headphone_tlv), + + SOC_DOUBLE("Headphone Invert Switch", CS42L43_DACCNFG1, + CS42L43_AMP3_INV_SHIFT, CS42L43_AMP4_INV_SHIFT, 1, 0), + + SOC_SINGLE("Headphone Zero Cross Switch", CS42L43_PGAVOL, + CS42L43_HP_PATH_VOL_ZC_SHIFT, 1, 0), + SOC_SINGLE("Headphone Ramp Switch", CS42L43_PGAVOL, + CS42L43_HP_PATH_VOL_SFT_SHIFT, 1, 0), + SOC_ENUM("Headphone Ramp Rate", cs42l43_headphone_ramp), + + CS42L43_MIXER_VOLUMES("Headphone L", CS42L43_AMP3MIX_INPUT1), + CS42L43_MIXER_VOLUMES("Headphone R", CS42L43_AMP4MIX_INPUT1), + + SOC_ENUM("Tone 1 Frequency", cs42l43_tone1_freq), + SOC_ENUM("Tone 2 Frequency", cs42l43_tone2_freq), + + SOC_DOUBLE_EXT("EQ Switch", + CS42L43_MUTE_EQ_IN0, CS42L43_MUTE_EQ_CH1_SHIFT, + CS42L43_MUTE_EQ_CH2_SHIFT, 1, 1, + cs42l43_dapm_get_volsw, cs42l43_dapm_put_volsw), + + SND_SOC_BYTES_E("EQ Coefficients", 0, CS42L43_N_EQ_COEFFS, + cs42l43_eq_get, cs42l43_eq_put), + + CS42L43_MIXER_VOLUMES("EQ1", CS42L43_EQ1MIX_INPUT1), + CS42L43_MIXER_VOLUMES("EQ2", CS42L43_EQ2MIX_INPUT1), +}; + +static int cs42l43_eq_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int val; + int i, ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0, + CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK, + CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK); + + regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0, + CS42L43_WRITE_MODE_MASK, CS42L43_WRITE_MODE_MASK); + + for (i = 0; i < CS42L43_N_EQ_COEFFS; i++) + regmap_write(cs42l43->regmap, CS42L43_COEFF_DATA_IN0, + priv->eq_coeffs[i]); + + regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0, + CS42L43_WRITE_MODE_MASK, 0); + + return 0; + case SND_SOC_DAPM_POST_PMU: + ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_INIT_DONE0, + val, (val & CS42L43_INITIALIZE_DONE_MASK), + 2000, 10000); + if (ret) + dev_err(priv->dev, "Failed to start EQs: %d\n", ret); + + regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0, + CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK, 0); + return ret; + default: + return 0; + } +} + +struct cs42l43_pll_config { + unsigned int freq; + + unsigned int div; + unsigned int mode; + unsigned int cal; +}; + +static const struct cs42l43_pll_config cs42l43_pll_configs[] = { + { 2400000, 0x50000000, 0x1, 0xA4 }, + { 3000000, 0x40000000, 0x1, 0x83 }, + { 3072000, 0x40000000, 0x3, 0x80 }, +}; + +static int cs42l43_set_pll(struct cs42l43_codec *priv, unsigned int src, + unsigned int freq) +{ + struct cs42l43 *cs42l43 = priv->core; + + lockdep_assert_held(&cs42l43->pll_lock); + + if (priv->refclk_src == src && priv->refclk_freq == freq) + return 0; + + if (regmap_test_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK)) { + dev_err(priv->dev, "PLL active, can't change configuration\n"); + return -EBUSY; + } + + switch (src) { + case CS42L43_SYSCLK_MCLK: + case CS42L43_SYSCLK_SDW: + dev_dbg(priv->dev, "Source PLL from %s at %uHz\n", + src ? "SoundWire" : "MCLK", freq); + + priv->refclk_src = src; + priv->refclk_freq = freq; + + return 0; + default: + dev_err(priv->dev, "Invalid PLL source: 0x%x\n", src); + return -EINVAL; + } +} + +static int cs42l43_enable_pll(struct cs42l43_codec *priv) +{ + static const struct reg_sequence enable_seq[] = { + { CS42L43_OSC_DIV_SEL, 0x0, }, + { CS42L43_MCLK_SRC_SEL, CS42L43_OSC_PLL_MCLK_SEL_MASK, 5, }, + }; + struct cs42l43 *cs42l43 = priv->core; + const struct cs42l43_pll_config *config = NULL; + unsigned int div = 0; + unsigned int freq = priv->refclk_freq; + unsigned long time_left; + + lockdep_assert_held(&cs42l43->pll_lock); + + if (priv->refclk_src == CS42L43_SYSCLK_SDW) { + if (!freq) + freq = cs42l43->sdw_freq; + else if (!cs42l43->sdw_freq) + cs42l43->sdw_freq = freq; + } + + dev_dbg(priv->dev, "Enabling PLL at %uHz\n", freq); + + while (freq > cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq) { + div++; + freq /= 2; + } + + if (div <= CS42L43_PLL_REFCLK_DIV_MASK) { + int i; + + for (i = 0; i < ARRAY_SIZE(cs42l43_pll_configs); i++) { + if (freq == cs42l43_pll_configs[i].freq) { + config = &cs42l43_pll_configs[i]; + break; + } + } + } + + if (!config) { + dev_err(priv->dev, "No suitable PLL config: 0x%x, %uHz\n", div, freq); + return -EINVAL; + } + + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_DIV_MASK | CS42L43_PLL_REFCLK_SRC_MASK, + div << CS42L43_PLL_REFCLK_DIV_SHIFT | + priv->refclk_src << CS42L43_PLL_REFCLK_SRC_SHIFT); + regmap_write(cs42l43->regmap, CS42L43_FDIV_FRAC, config->div); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, + CS42L43_PLL_MODE_BYPASS_500_MASK | + CS42L43_PLL_MODE_BYPASS_1029_MASK, + config->mode << CS42L43_PLL_MODE_BYPASS_1029_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_CAL_RATIO, + CS42L43_PLL_CAL_RATIO_MASK, config->cal); + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_EN_MASK, CS42L43_PLL_REFCLK_EN_MASK); + + reinit_completion(&priv->pll_ready); + + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, + CS42L43_PLL_EN_MASK, CS42L43_PLL_EN_MASK); + + time_left = wait_for_completion_timeout(&priv->pll_ready, + msecs_to_jiffies(CS42L43_PLL_TIMEOUT_MS)); + if (!time_left) { + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, + CS42L43_PLL_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_EN_MASK, 0); + + dev_err(priv->dev, "Timeout out waiting for PLL\n"); + return -ETIMEDOUT; + } + + if (priv->refclk_src == CS42L43_SYSCLK_SDW) + cs42l43->sdw_pll_active = true; + + dev_dbg(priv->dev, "PLL locked in %ums\n", 200 - jiffies_to_msecs(time_left)); + + /* + * Reads are not allowed over Soundwire without OSC_DIV2_EN or the PLL, + * but you can not change to PLL with OSC_DIV2_EN set. So ensure the whole + * change over happens under the regmap lock to prevent any reads. + */ + regmap_multi_reg_write(cs42l43->regmap, enable_seq, ARRAY_SIZE(enable_seq)); + + return 0; +} + +static int cs42l43_disable_pll(struct cs42l43_codec *priv) +{ + static const struct reg_sequence disable_seq[] = { + { CS42L43_MCLK_SRC_SEL, 0x0, 5, }, + { CS42L43_OSC_DIV_SEL, CS42L43_OSC_DIV2_EN_MASK, }, + }; + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Disabling PLL\n"); + + lockdep_assert_held(&cs42l43->pll_lock); + + regmap_multi_reg_write(cs42l43->regmap, disable_seq, ARRAY_SIZE(disable_seq)); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_EN_MASK, 0); + + cs42l43->sdw_pll_active = false; + + return 0; +} + +static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + mutex_lock(&cs42l43->pll_lock); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (priv->refclk_src == CS42L43_SYSCLK_MCLK) { + ret = clk_prepare_enable(priv->mclk); + if (ret) { + dev_err(priv->dev, "Failed to enable MCLK: %d\n", ret); + break; + } + } + + ret = cs42l43_enable_pll(priv); + break; + case SND_SOC_DAPM_POST_PMD: + ret = cs42l43_disable_pll(priv); + + if (priv->refclk_src == CS42L43_SYSCLK_MCLK) + clk_disable_unprepare(priv->mclk); + break; + default: + ret = 0; + break; + } + + mutex_unlock(&cs42l43->pll_lock); + + return ret; +} + +static int cs42l43_dapm_wait_completion(struct completion *pmu, struct completion *pmd, + int event, int timeout_ms) +{ + unsigned long time_left; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + reinit_completion(pmu); + return 0; + case SND_SOC_DAPM_PRE_PMD: + reinit_completion(pmd); + return 0; + case SND_SOC_DAPM_POST_PMU: + time_left = wait_for_completion_timeout(pmu, msecs_to_jiffies(timeout_ms)); + break; + case SND_SOC_DAPM_POST_PMD: + time_left = wait_for_completion_timeout(pmd, msecs_to_jiffies(timeout_ms)); + break; + default: + return 0; + } + + if (!time_left) + return -ETIMEDOUT; + else + return 0; +} + +static int cs42l43_spkr_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + return cs42l43_dapm_wait_completion(&priv->spkr_startup, + &priv->spkr_shutdown, event, + CS42L43_SPK_TIMEOUT_MS); +} + +static int cs42l43_spkl_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + return cs42l43_dapm_wait_completion(&priv->spkl_startup, + &priv->spkl_shutdown, event, + CS42L43_SPK_TIMEOUT_MS); +} + +static int cs42l43_hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int mask = 1 << w->shift; + unsigned int val = 0; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + val = mask; + fallthrough; + case SND_SOC_DAPM_PRE_PMD: + priv->hp_ena &= ~mask; + priv->hp_ena |= val; + + ret = cs42l43_dapm_wait_completion(&priv->hp_startup, + &priv->hp_shutdown, event, + CS42L43_HP_TIMEOUT_MS); + if (ret) + return ret; + + if (!priv->load_detect_running) + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + mask, val); + break; + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + if (priv->load_detect_running) + break; + + ret = cs42l43_dapm_wait_completion(&priv->hp_startup, + &priv->hp_shutdown, event, + CS42L43_HP_TIMEOUT_MS); + if (ret) + return ret; + break; + default: + break; + } + + return 0; +} + +static int cs42l43_mic_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int reg, ramp, mute; + unsigned int *val; + int ret; + + switch (w->shift) { + case CS42L43_ADC1_EN_SHIFT: + case CS42L43_PDM1_DIN_L_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2; + ramp = CS42L43_DECIM1_VD_RAMP_MASK; + mute = CS42L43_DECIM1_MUTE_MASK; + val = &priv->decim_cache[0]; + break; + case CS42L43_ADC2_EN_SHIFT: + case CS42L43_PDM1_DIN_R_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2; + ramp = CS42L43_DECIM2_VD_RAMP_MASK; + mute = CS42L43_DECIM2_MUTE_MASK; + val = &priv->decim_cache[1]; + break; + case CS42L43_PDM2_DIN_L_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4; + ramp = CS42L43_DECIM3_VD_RAMP_MASK; + mute = CS42L43_DECIM3_MUTE_MASK; + val = &priv->decim_cache[2]; + break; + case CS42L43_PDM2_DIN_R_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4; + ramp = CS42L43_DECIM4_VD_RAMP_MASK; + mute = CS42L43_DECIM4_MUTE_MASK; + val = &priv->decim_cache[3]; + break; + default: + dev_err(priv->dev, "Invalid microphone shift: %d\n", w->shift); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regmap_read(cs42l43->regmap, reg, val); + if (ret) { + dev_err(priv->dev, + "Failed to cache decimator settings: %d\n", + ret); + return ret; + } + + regmap_update_bits(cs42l43->regmap, reg, mute | ramp, mute); + break; + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(cs42l43->regmap, reg, mute | ramp, *val); + break; + default: + break; + } + + return 0; +} + +static int cs42l43_adc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int mask = 1 << w->shift; + unsigned int val = 0; + int ret; + + ret = cs42l43_mic_ev(w, kcontrol, event); + if (ret) + return ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + val = mask; + fallthrough; + case SND_SOC_DAPM_PRE_PMD: + priv->adc_ena &= ~mask; + priv->adc_ena |= val; + + if (!priv->load_detect_running) + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3, + mask, val); + fallthrough; + default: + return 0; + } +} + +static const struct snd_soc_dapm_widget cs42l43_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, cs42l43_pll_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("ADC1_IN1_P"), + SND_SOC_DAPM_INPUT("ADC1_IN1_N"), + SND_SOC_DAPM_INPUT("ADC1_IN2_P"), + SND_SOC_DAPM_INPUT("ADC1_IN2_N"), + SND_SOC_DAPM_INPUT("ADC2_IN_P"), + SND_SOC_DAPM_INPUT("ADC2_IN_N"), + + SND_SOC_DAPM_INPUT("PDM1_DIN"), + SND_SOC_DAPM_INPUT("PDM2_DIN"), + + SND_SOC_DAPM_MUX("ADC1 Input", SND_SOC_NOPM, 0, 0, &cs42l43_adc1_input_ctl), + + SND_SOC_DAPM_PGA_E("ADC1", SND_SOC_NOPM, CS42L43_ADC1_EN_SHIFT, 0, NULL, 0, + cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("ADC2", SND_SOC_NOPM, CS42L43_ADC2_EN_SHIFT, 0, NULL, 0, + cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA_E("PDM1L", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_L_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PDM1R", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_R_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PDM2L", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_L_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PDM2R", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_R_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("Decimator 1 Mode", SND_SOC_NOPM, 0, 0, + &cs42l43_dec_mode_ctl[0]), + SND_SOC_DAPM_MUX("Decimator 2 Mode", SND_SOC_NOPM, 0, 0, + &cs42l43_dec_mode_ctl[1]), + + SND_SOC_DAPM_PGA("Decimator 1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Decimator 2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Decimator 3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Decimator 4", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("FSYNC", 0, CS42L43_ASP_CTRL, CS42L43_ASP_FSYNC_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("BCLK", 1, CS42L43_ASP_CTRL, CS42L43_ASP_BCLK_EN_SHIFT, + 0, NULL, 0), + + SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH4_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH5_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH6_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 1, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX3", NULL, 2, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX4", NULL, 3, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH4_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX5", NULL, 4, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH5_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX6", NULL, 5, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH6_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_OUT("DP1TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP1TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP1TX3", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP1TX4", NULL, 3, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("DP2TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("DP3TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP3TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("DP4TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("DP5RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP5RX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("DP6RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP6RX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("DP7RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP7RX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-amp", 0, 0), + + SND_SOC_DAPM_PGA_E("AMP1", CS42L43_BLOCK_EN10, CS42L43_AMP1_EN_SHIFT, 0, NULL, 0, + cs42l43_spkl_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("AMP2", CS42L43_BLOCK_EN10, CS42L43_AMP2_EN_SHIFT, 0, NULL, 0, + cs42l43_spkr_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("AMP1_OUT_P"), + SND_SOC_DAPM_OUTPUT("AMP1_OUT_N"), + SND_SOC_DAPM_OUTPUT("AMP2_OUT_P"), + SND_SOC_DAPM_OUTPUT("AMP2_OUT_N"), + + SND_SOC_DAPM_PGA("SPDIF", CS42L43_BLOCK_EN11, CS42L43_SPDIF_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPDIF_TX"), + + SND_SOC_DAPM_PGA_E("HP", SND_SOC_NOPM, CS42L43_HP_EN_SHIFT, 0, NULL, 0, + cs42l43_hp_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("AMP3_OUT"), + SND_SOC_DAPM_OUTPUT("AMP4_OUT"), + + SND_SOC_DAPM_SIGGEN("Tone"), + SND_SOC_DAPM_SUPPLY("Tone Generator", CS42L43_BLOCK_EN9, CS42L43_TONE_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 1", CS42L43_TONE_CH1_CTRL, + CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 2", CS42L43_TONE_CH2_CTRL, + CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0), + + SND_SOC_DAPM_SUPPLY("ISRC1", CS42L43_BLOCK_EN5, CS42L43_ISRC1_BANK_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ISRC2", CS42L43_BLOCK_EN5, CS42L43_ISRC2_BANK_EN_SHIFT, + 0, NULL, 0), + + SND_SOC_DAPM_PGA("ISRC1INT2", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC1INT1", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC1DEC2", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC1DEC1", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("ISRC2INT2", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2INT1", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2DEC2", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2DEC1", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("ASRC_INT", CS42L43_BLOCK_EN4, + CS42L43_ASRC_INT_BANK_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASRC_DEC", CS42L43_BLOCK_EN4, + CS42L43_ASRC_DEC_BANK_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("ASRC_INT1", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_INT2", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_INT3", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT3_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_INT4", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT4_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC1", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC2", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC3", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC3_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC4", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC4_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("EQ Clock", CS42L43_BLOCK_EN7, CS42L43_EQ_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA_E("EQ", CS42L43_START_EQZ0, CS42L43_START_FILTER_SHIFT, + 0, NULL, 0, cs42l43_eq_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("Mixer Core", CS42L43_BLOCK_EN6, CS42L43_MIXER_EN_SHIFT, + 0, NULL, 0), + CS42L43_DAPM_MUX("ASPTX1", asptx1), + CS42L43_DAPM_MUX("ASPTX2", asptx2), + CS42L43_DAPM_MUX("ASPTX3", asptx3), + CS42L43_DAPM_MUX("ASPTX4", asptx4), + CS42L43_DAPM_MUX("ASPTX5", asptx5), + CS42L43_DAPM_MUX("ASPTX6", asptx6), + + CS42L43_DAPM_MUX("DP1TX1", dp1tx1), + CS42L43_DAPM_MUX("DP1TX2", dp1tx2), + CS42L43_DAPM_MUX("DP1TX3", dp1tx3), + CS42L43_DAPM_MUX("DP1TX4", dp1tx4), + CS42L43_DAPM_MUX("DP2TX1", dp2tx1), + CS42L43_DAPM_MUX("DP2TX2", dp2tx2), + CS42L43_DAPM_MUX("DP3TX1", dp3tx1), + CS42L43_DAPM_MUX("DP3TX2", dp3tx2), + CS42L43_DAPM_MUX("DP4TX1", dp4tx1), + CS42L43_DAPM_MUX("DP4TX2", dp4tx2), + + CS42L43_DAPM_MUX("ASRC INT1", asrcint1), + CS42L43_DAPM_MUX("ASRC INT2", asrcint2), + CS42L43_DAPM_MUX("ASRC INT3", asrcint3), + CS42L43_DAPM_MUX("ASRC INT4", asrcint4), + CS42L43_DAPM_MUX("ASRC DEC1", asrcdec1), + CS42L43_DAPM_MUX("ASRC DEC2", asrcdec2), + CS42L43_DAPM_MUX("ASRC DEC3", asrcdec3), + CS42L43_DAPM_MUX("ASRC DEC4", asrcdec4), + + CS42L43_DAPM_MUX("ISRC1INT1", isrc1int1), + CS42L43_DAPM_MUX("ISRC1INT2", isrc1int2), + CS42L43_DAPM_MUX("ISRC1DEC1", isrc1dec1), + CS42L43_DAPM_MUX("ISRC1DEC2", isrc1dec2), + CS42L43_DAPM_MUX("ISRC2INT1", isrc2int1), + CS42L43_DAPM_MUX("ISRC2INT2", isrc2int2), + CS42L43_DAPM_MUX("ISRC2DEC1", isrc2dec1), + CS42L43_DAPM_MUX("ISRC2DEC2", isrc2dec2), + + CS42L43_DAPM_MUX("SPDIF1", spdif1), + CS42L43_DAPM_MUX("SPDIF2", spdif2), + + CS42L43_DAPM_MIXER("EQ1", eq1), + CS42L43_DAPM_MIXER("EQ2", eq2), + + CS42L43_DAPM_MIXER("Speaker L", amp1), + CS42L43_DAPM_MIXER("Speaker R", amp2), + + CS42L43_DAPM_MIXER("Headphone L", amp3), + CS42L43_DAPM_MIXER("Headphone R", amp4), +}; + +static const struct snd_soc_dapm_route cs42l43_routes[] = { + { "ADC1_IN1_P", NULL, "PLL" }, + { "ADC1_IN1_N", NULL, "PLL" }, + { "ADC1_IN2_P", NULL, "PLL" }, + { "ADC1_IN2_N", NULL, "PLL" }, + { "ADC2_IN_P", NULL, "PLL" }, + { "ADC2_IN_N", NULL, "PLL" }, + { "PDM1_DIN", NULL, "PLL" }, + { "PDM2_DIN", NULL, "PLL" }, + { "AMP1_OUT_P", NULL, "PLL" }, + { "AMP1_OUT_N", NULL, "PLL" }, + { "AMP2_OUT_P", NULL, "PLL" }, + { "AMP2_OUT_N", NULL, "PLL" }, + { "SPDIF_TX", NULL, "PLL" }, + { "HP", NULL, "PLL" }, + { "AMP3_OUT", NULL, "PLL" }, + { "AMP4_OUT", NULL, "PLL" }, + { "Tone 1", NULL, "PLL" }, + { "Tone 2", NULL, "PLL" }, + { "ASP Playback", NULL, "PLL" }, + { "ASP Capture", NULL, "PLL" }, + { "DP1 Capture", NULL, "PLL" }, + { "DP2 Capture", NULL, "PLL" }, + { "DP3 Capture", NULL, "PLL" }, + { "DP4 Capture", NULL, "PLL" }, + { "DP5 Playback", NULL, "PLL" }, + { "DP6 Playback", NULL, "PLL" }, + { "DP7 Playback", NULL, "PLL" }, + + { "ADC1 Input", "IN1", "ADC1_IN1_P" }, + { "ADC1 Input", "IN1", "ADC1_IN1_N" }, + { "ADC1 Input", "IN2", "ADC1_IN2_P" }, + { "ADC1 Input", "IN2", "ADC1_IN2_N" }, + + { "ADC1", NULL, "ADC1 Input" }, + { "ADC2", NULL, "ADC2_IN_P" }, + { "ADC2", NULL, "ADC2_IN_N" }, + + { "PDM1L", NULL, "PDM1_DIN" }, + { "PDM1R", NULL, "PDM1_DIN" }, + { "PDM2L", NULL, "PDM2_DIN" }, + { "PDM2R", NULL, "PDM2_DIN" }, + + { "Decimator 1 Mode", "PDM", "PDM1L" }, + { "Decimator 1 Mode", "ADC", "ADC1" }, + { "Decimator 2 Mode", "PDM", "PDM1R" }, + { "Decimator 2 Mode", "ADC", "ADC2" }, + + { "Decimator 1", NULL, "Decimator 1 Mode" }, + { "Decimator 2", NULL, "Decimator 2 Mode" }, + { "Decimator 3", NULL, "PDM2L" }, + { "Decimator 4", NULL, "PDM2R" }, + + { "ASP Capture", NULL, "ASPTX1" }, + { "ASP Capture", NULL, "ASPTX2" }, + { "ASP Capture", NULL, "ASPTX3" }, + { "ASP Capture", NULL, "ASPTX4" }, + { "ASP Capture", NULL, "ASPTX5" }, + { "ASP Capture", NULL, "ASPTX6" }, + { "ASPTX1", NULL, "BCLK" }, + { "ASPTX2", NULL, "BCLK" }, + { "ASPTX3", NULL, "BCLK" }, + { "ASPTX4", NULL, "BCLK" }, + { "ASPTX5", NULL, "BCLK" }, + { "ASPTX6", NULL, "BCLK" }, + + { "ASPRX1", NULL, "ASP Playback" }, + { "ASPRX2", NULL, "ASP Playback" }, + { "ASPRX3", NULL, "ASP Playback" }, + { "ASPRX4", NULL, "ASP Playback" }, + { "ASPRX5", NULL, "ASP Playback" }, + { "ASPRX6", NULL, "ASP Playback" }, + { "ASPRX1", NULL, "BCLK" }, + { "ASPRX2", NULL, "BCLK" }, + { "ASPRX3", NULL, "BCLK" }, + { "ASPRX4", NULL, "BCLK" }, + { "ASPRX5", NULL, "BCLK" }, + { "ASPRX6", NULL, "BCLK" }, + + { "DP1 Capture", NULL, "DP1TX1" }, + { "DP1 Capture", NULL, "DP1TX2" }, + { "DP1 Capture", NULL, "DP1TX3" }, + { "DP1 Capture", NULL, "DP1TX4" }, + + { "DP2 Capture", NULL, "DP2TX1" }, + { "DP2 Capture", NULL, "DP2TX2" }, + + { "DP3 Capture", NULL, "DP3TX1" }, + { "DP3 Capture", NULL, "DP3TX2" }, + + { "DP4 Capture", NULL, "DP4TX1" }, + { "DP4 Capture", NULL, "DP4TX2" }, + + { "DP5RX1", NULL, "DP5 Playback" }, + { "DP5RX2", NULL, "DP5 Playback" }, + + { "DP6RX1", NULL, "DP6 Playback" }, + { "DP6RX2", NULL, "DP6 Playback" }, + + { "DP7RX1", NULL, "DP7 Playback" }, + { "DP7RX2", NULL, "DP7 Playback" }, + + { "AMP1", NULL, "vdd-amp" }, + { "AMP2", NULL, "vdd-amp" }, + + { "AMP1_OUT_P", NULL, "AMP1" }, + { "AMP1_OUT_N", NULL, "AMP1" }, + { "AMP2_OUT_P", NULL, "AMP2" }, + { "AMP2_OUT_N", NULL, "AMP2" }, + + { "SPDIF_TX", NULL, "SPDIF" }, + + { "AMP3_OUT", NULL, "HP" }, + { "AMP4_OUT", NULL, "HP" }, + + { "Tone 1", NULL, "Tone" }, + { "Tone 1", NULL, "Tone Generator" }, + { "Tone 2", NULL, "Tone" }, + { "Tone 2", NULL, "Tone Generator" }, + + { "ISRC1INT2", NULL, "ISRC1" }, + { "ISRC1INT1", NULL, "ISRC1" }, + { "ISRC1DEC2", NULL, "ISRC1" }, + { "ISRC1DEC1", NULL, "ISRC1" }, + + { "ISRC2INT2", NULL, "ISRC2" }, + { "ISRC2INT1", NULL, "ISRC2" }, + { "ISRC2DEC2", NULL, "ISRC2" }, + { "ISRC2DEC1", NULL, "ISRC2" }, + + { "ASRC_INT1", NULL, "ASRC_INT" }, + { "ASRC_INT2", NULL, "ASRC_INT" }, + { "ASRC_INT3", NULL, "ASRC_INT" }, + { "ASRC_INT4", NULL, "ASRC_INT" }, + { "ASRC_DEC1", NULL, "ASRC_DEC" }, + { "ASRC_DEC2", NULL, "ASRC_DEC" }, + { "ASRC_DEC3", NULL, "ASRC_DEC" }, + { "ASRC_DEC4", NULL, "ASRC_DEC" }, + + { "EQ", NULL, "EQ Clock" }, + + CS42L43_MUX_ROUTES("ASPTX1", "ASPTX1"), + CS42L43_MUX_ROUTES("ASPTX2", "ASPTX2"), + CS42L43_MUX_ROUTES("ASPTX3", "ASPTX3"), + CS42L43_MUX_ROUTES("ASPTX4", "ASPTX4"), + CS42L43_MUX_ROUTES("ASPTX5", "ASPTX5"), + CS42L43_MUX_ROUTES("ASPTX6", "ASPTX6"), + + CS42L43_MUX_ROUTES("DP1TX1", "DP1TX1"), + CS42L43_MUX_ROUTES("DP1TX2", "DP1TX2"), + CS42L43_MUX_ROUTES("DP1TX3", "DP1TX3"), + CS42L43_MUX_ROUTES("DP1TX4", "DP1TX4"), + CS42L43_MUX_ROUTES("DP2TX1", "DP2TX1"), + CS42L43_MUX_ROUTES("DP2TX2", "DP2TX2"), + CS42L43_MUX_ROUTES("DP3TX1", "DP3TX1"), + CS42L43_MUX_ROUTES("DP3TX2", "DP3TX2"), + CS42L43_MUX_ROUTES("DP4TX1", "DP4TX1"), + CS42L43_MUX_ROUTES("DP4TX2", "DP4TX2"), + + CS42L43_MUX_ROUTES("ASRC INT1", "ASRC_INT1"), + CS42L43_MUX_ROUTES("ASRC INT2", "ASRC_INT2"), + CS42L43_MUX_ROUTES("ASRC INT3", "ASRC_INT3"), + CS42L43_MUX_ROUTES("ASRC INT4", "ASRC_INT4"), + CS42L43_MUX_ROUTES("ASRC DEC1", "ASRC_DEC1"), + CS42L43_MUX_ROUTES("ASRC DEC2", "ASRC_DEC2"), + CS42L43_MUX_ROUTES("ASRC DEC3", "ASRC_DEC3"), + CS42L43_MUX_ROUTES("ASRC DEC4", "ASRC_DEC4"), + + CS42L43_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + CS42L43_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + CS42L43_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + CS42L43_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + CS42L43_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + CS42L43_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + CS42L43_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + CS42L43_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + CS42L43_MUX_ROUTES("SPDIF1", "SPDIF"), + CS42L43_MUX_ROUTES("SPDIF2", "SPDIF"), + + CS42L43_MIXER_ROUTES("EQ1", "EQ"), + CS42L43_MIXER_ROUTES("EQ2", "EQ"), + + CS42L43_MIXER_ROUTES("Speaker L", "AMP1"), + CS42L43_MIXER_ROUTES("Speaker R", "AMP2"), + + CS42L43_MIXER_ROUTES("Headphone L", "HP"), + CS42L43_MIXER_ROUTES("Headphone R", "HP"), +}; + +static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, + int src, unsigned int freq, int dir) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + mutex_lock(&cs42l43->pll_lock); + ret = cs42l43_set_pll(priv, src, freq); + mutex_unlock(&cs42l43->pll_lock); + + return ret; +} + +static int cs42l43_component_probe(struct snd_soc_component *component) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + + snd_soc_component_init_regmap(component, cs42l43->regmap); + + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots); + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots); + + priv->component = component; + priv->constraint = cs42l43_constraint; + + return 0; +} + +static const struct snd_soc_component_driver cs42l43_component_drv = { + .name = "cs42l43-codec", + + .probe = cs42l43_component_probe, + .set_sysclk = cs42l43_set_sysclk, + .set_jack = cs42l43_set_jack, + + .endianness = 1, + + .controls = cs42l43_controls, + .num_controls = ARRAY_SIZE(cs42l43_controls), + .dapm_widgets = cs42l43_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l43_widgets), + .dapm_routes = cs42l43_routes, + .num_dapm_routes = ARRAY_SIZE(cs42l43_routes), +}; + +struct cs42l43_irq { + unsigned int irq; + const char *name; + irq_handler_t handler; +}; + +static const struct cs42l43_irq cs42l43_irqs[] = { + { CS42L43_PLL_LOST_LOCK, "pll lost lock", cs42l43_pll_lost_lock }, + { CS42L43_PLL_READY, "pll ready", cs42l43_pll_ready }, + { CS42L43_HP_STARTUP_DONE, "hp startup", cs42l43_hp_startup }, + { CS42L43_HP_SHUTDOWN_DONE, "hp shutdown", cs42l43_hp_shutdown }, + { CS42L43_HSDET_DONE, "type detect", cs42l43_type_detect }, + { CS42L43_TIPSENSE_UNPLUG_PDET, "tip sense unplug", cs42l43_tip_sense }, + { CS42L43_TIPSENSE_PLUG_PDET, "tip sense plug", cs42l43_tip_sense }, + { CS42L43_DC_DETECT1_TRUE, "button press", cs42l43_button_press }, + { CS42L43_DC_DETECT1_FALSE, "button release", cs42l43_button_release }, + { CS42L43_HSBIAS_CLAMPED, "hsbias detect clamp", cs42l43_bias_detect_clamp }, + { CS42L43_AMP2_CLK_STOP_FAULT, "spkr clock stop", cs42l43_spkr_clock_stop }, + { CS42L43_AMP1_CLK_STOP_FAULT, "spkl clock stop", cs42l43_spkl_clock_stop }, + { CS42L43_AMP2_VDDSPK_FAULT, "spkr brown out", cs42l43_spkr_brown_out }, + { CS42L43_AMP1_VDDSPK_FAULT, "spkl brown out", cs42l43_spkl_brown_out }, + { CS42L43_AMP2_SHUTDOWN_DONE, "spkr shutdown", cs42l43_spkr_shutdown }, + { CS42L43_AMP1_SHUTDOWN_DONE, "spkl shutdown", cs42l43_spkl_shutdown }, + { CS42L43_AMP2_STARTUP_DONE, "spkr startup", cs42l43_spkr_startup }, + { CS42L43_AMP1_STARTUP_DONE, "spkl startup", cs42l43_spkl_startup }, + { CS42L43_AMP2_THERM_SHDN, "spkr thermal shutdown", cs42l43_spkr_therm_shutdown }, + { CS42L43_AMP1_THERM_SHDN, "spkl thermal shutdown", cs42l43_spkl_therm_shutdown }, + { CS42L43_AMP2_THERM_WARN, "spkr thermal warning", cs42l43_spkr_therm_warm }, + { CS42L43_AMP1_THERM_WARN, "spkl thermal warning", cs42l43_spkl_therm_warm }, + { CS42L43_AMP2_SCDET, "spkr short circuit", cs42l43_spkr_sc_detect }, + { CS42L43_AMP1_SCDET, "spkl short circuit", cs42l43_spkl_sc_detect }, + { CS42L43_HP_ILIMIT, "hp ilimit", cs42l43_hp_ilimit }, + { CS42L43_HP_LOADDET_DONE, "load detect done", cs42l43_load_detect }, +}; + +static int cs42l43_request_irq(struct cs42l43_codec *priv, + struct irq_domain *dom, const char * const name, + unsigned int irq, irq_handler_t handler) +{ + int ret; + + ret = irq_create_mapping(dom, irq); + if (ret < 0) + return dev_err_probe(priv->dev, ret, "Failed to map IRQ %s\n", name); + + dev_dbg(priv->dev, "Request IRQ %d for %s\n", ret, name); + + ret = devm_request_threaded_irq(priv->dev, ret, NULL, handler, IRQF_ONESHOT, + name, priv); + if (ret) + return dev_err_probe(priv->dev, ret, "Failed to request IRQ %s\n", name); + + return 0; +} + +static int cs42l43_shutter_irq(struct cs42l43_codec *priv, + struct irq_domain *dom, unsigned int shutter, + const char * const open_name, + const char * const close_name, + irq_handler_t handler) +{ + unsigned int open_irq, close_irq; + int ret; + + switch (shutter) { + case 0x1: + dev_warn(priv->dev, "Manual shutters, notifications not available\n"); + return 0; + case 0x2: + open_irq = CS42L43_GPIO1_RISE; + close_irq = CS42L43_GPIO1_FALL; + break; + case 0x4: + open_irq = CS42L43_GPIO2_RISE; + close_irq = CS42L43_GPIO2_FALL; + break; + case 0x8: + open_irq = CS42L43_GPIO3_RISE; + close_irq = CS42L43_GPIO3_FALL; + break; + default: + return 0; + } + + ret = cs42l43_request_irq(priv, dom, close_name, close_irq, handler); + if (ret) + return ret; + + return cs42l43_request_irq(priv, dom, open_name, open_irq, handler); +} + +static int cs42l43_codec_probe(struct platform_device *pdev) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent); + struct cs42l43_codec *priv; + struct irq_domain *dom; + unsigned int val; + int i, ret; + + dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY); + if (!dom) + return -EPROBE_DEFER; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->core = cs42l43; + + platform_set_drvdata(pdev, priv); + + mutex_init(&priv->jack_lock); + mutex_init(&priv->spk_vu_lock); + + init_completion(&priv->hp_startup); + init_completion(&priv->hp_shutdown); + init_completion(&priv->spkr_shutdown); + init_completion(&priv->spkl_shutdown); + init_completion(&priv->spkr_startup); + init_completion(&priv->spkl_startup); + init_completion(&priv->pll_ready); + init_completion(&priv->type_detect); + init_completion(&priv->load_detect); + + INIT_DELAYED_WORK(&priv->tip_sense_work, cs42l43_tip_sense_work); + INIT_DELAYED_WORK(&priv->bias_sense_timeout, cs42l43_bias_sense_timeout); + INIT_DELAYED_WORK(&priv->button_press_work, cs42l43_button_press_work); + INIT_WORK(&priv->button_release_work, cs42l43_button_release_work); + + pm_runtime_set_autosuspend_delay(priv->dev, 100); + pm_runtime_use_autosuspend(priv->dev); + pm_runtime_set_active(priv->dev); + pm_runtime_get_noresume(priv->dev); + devm_pm_runtime_enable(priv->dev); + + for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) { + ret = cs42l43_request_irq(priv, dom, cs42l43_irqs[i].name, + cs42l43_irqs[i].irq, cs42l43_irqs[i].handler); + if (ret) + goto err_pm; + } + + ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val); + if (ret) { + dev_err(priv->dev, "Failed to check shutter source: %d\n", ret); + goto err_pm; + } + + ret = cs42l43_shutter_irq(priv, dom, val & CS42L43_MIC_SHUTTER_CFG_MASK, + "mic shutter open", "mic shutter close", + cs42l43_mic_shutter); + if (ret) + goto err_pm; + + ret = cs42l43_shutter_irq(priv, dom, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >> + CS42L43_SPK_SHUTTER_CFG_SHIFT, + "spk shutter open", "spk shutter close", + cs42l43_spk_shutter); + if (ret) + goto err_pm; + + // Don't use devm as we need to get against the MFD device + priv->mclk = clk_get_optional(cs42l43->dev, "mclk"); + if (IS_ERR(priv->mclk)) { + dev_err_probe(priv->dev, PTR_ERR(priv->mclk), "Failed to get mclk\n"); + goto err_pm; + } + + ret = devm_snd_soc_register_component(priv->dev, &cs42l43_component_drv, + cs42l43_dais, ARRAY_SIZE(cs42l43_dais)); + if (ret) { + dev_err_probe(priv->dev, ret, "Failed to register component\n"); + goto err_clk; + } + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return 0; + +err_clk: + clk_put(priv->mclk); +err_pm: + pm_runtime_put_sync(priv->dev); + + return ret; +} + +static int cs42l43_codec_remove(struct platform_device *pdev) +{ + struct cs42l43_codec *priv = platform_get_drvdata(pdev); + + clk_put(priv->mclk); + + return 0; +} + +static int cs42l43_codec_runtime_resume(struct device *dev) +{ + struct cs42l43_codec *priv = dev_get_drvdata(dev); + + dev_dbg(priv->dev, "Runtime resume\n"); + + // Toggle the speaker volume update incase the speaker volume was synced + cs42l43_spk_vu_sync(priv); + + return 0; +} + +DEFINE_RUNTIME_DEV_PM_OPS(cs42l43_codec_pm_ops, NULL, + cs42l43_codec_runtime_resume, NULL); + +static const struct platform_device_id cs42l43_codec_id_table[] = { + { "cs42l43-codec", }, + {} +}; +MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); + +static struct platform_driver cs42l43_codec_driver = { + .driver = { + .name = "cs42l43-codec", + .pm = &cs42l43_codec_pm_ops, + }, + + .probe = cs42l43_codec_probe, + .remove = cs42l43_codec_remove, + .id_table = cs42l43_codec_id_table, +}; +module_platform_driver(cs42l43_codec_driver); + +MODULE_IMPORT_NS(SND_SOC_CS42L43); + +MODULE_DESCRIPTION("CS42L43 CODEC Driver"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h new file mode 100644 index 000000000000..bf4f728eea3e --- /dev/null +++ b/sound/soc/codecs/cs42l43.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CS42L43 CODEC driver internal data + * + * Copyright (C) 2022-2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/types.h> +#include <sound/cs42l43.h> +#include <sound/pcm.h> +#include <sound/soc-jack.h> + +#ifndef CS42L43_ASOC_INT_H +#define CS42L43_ASOC_INT_H + +#define CS42L43_INTERNAL_SYSCLK 24576000 +#define CS42L43_DEFAULT_SLOTS 0x3F + +#define CS42L43_PLL_TIMEOUT_MS 200 +#define CS42L43_SPK_TIMEOUT_MS 100 +#define CS42L43_HP_TIMEOUT_MS 2000 +#define CS42L43_LOAD_TIMEOUT_MS 1000 + +#define CS42L43_ASP_MAX_CHANNELS 6 +#define CS42L43_N_EQ_COEFFS 15 + +#define CS42L43_N_BUTTONS 6 + +struct cs42l43_codec { + struct device *dev; + struct cs42l43 *core; + struct snd_soc_component *component; + + struct clk *mclk; + + int n_slots; + int slot_width; + int tx_slots[CS42L43_ASP_MAX_CHANNELS]; + int rx_slots[CS42L43_ASP_MAX_CHANNELS]; + struct snd_pcm_hw_constraint_list constraint; + + u32 eq_coeffs[CS42L43_N_EQ_COEFFS]; + + unsigned int refclk_src; + unsigned int refclk_freq; + struct completion pll_ready; + + unsigned int decim_cache[4]; + unsigned int adc_ena; + unsigned int hp_ena; + + struct completion hp_startup; + struct completion hp_shutdown; + struct completion spkr_shutdown; + struct completion spkl_shutdown; + struct completion spkr_startup; + struct completion spkl_startup; + // Lock to ensure speaker VU updates don't clash + struct mutex spk_vu_lock; + + // Lock for all jack detect operations + struct mutex jack_lock; + struct snd_soc_jack *jack_hp; + + bool use_ring_sense; + unsigned int tip_debounce_ms; + unsigned int bias_low; + unsigned int bias_sense_ua; + unsigned int bias_ramp_ms; + unsigned int detect_us; + unsigned int buttons[CS42L43_N_BUTTONS]; + + struct delayed_work tip_sense_work; + struct delayed_work bias_sense_timeout; + struct delayed_work button_press_work; + struct work_struct button_release_work; + struct completion type_detect; + struct completion load_detect; + + bool load_detect_running; + bool button_detect_running; + bool jack_present; + int jack_override; +}; + +#if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW) + +int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction); + +#else + +static inline int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return -EINVAL; +} + +#define cs42l43_sdw_remove_peripheral NULL +#define cs42l43_sdw_set_stream NULL + +#endif + +int cs42l43_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *d); +void cs42l43_bias_sense_timeout(struct work_struct *work); +void cs42l43_tip_sense_work(struct work_struct *work); +void cs42l43_button_press_work(struct work_struct *work); +void cs42l43_button_release_work(struct work_struct *work); +irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data); +irqreturn_t cs42l43_button_press(int irq, void *data); +irqreturn_t cs42l43_button_release(int irq, void *data); +irqreturn_t cs42l43_tip_sense(int irq, void *data); +int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + +extern const struct soc_enum cs42l43_jack_enum; + +#endif /* CS42L43_ASOC_INT_H */ diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index e7db7bcd0296..5ed2ef83dcdb 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -19,7 +19,7 @@ static struct i2c_device_id cs42l51_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id); -const struct of_device_id cs42l51_of_match[] = { +static const struct of_device_id cs42l51_of_match[] = { { .compatible = "cirrus,cs42l51", }, { } }; diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index a7079ae0ca09..e4827b8c2bde 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -703,7 +703,7 @@ const struct regmap_config cs42l51_regmap = { .volatile_reg = cs42l51_volatile_reg, .writeable_reg = cs42l51_writeable_reg, .max_register = CS42L51_CHARGE_FREQ, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs42l51_regmap); diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 1f1ded0ff0ac..4fc8a6ae8d92 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1084,7 +1084,7 @@ static const struct regmap_config cs42l52_regmap = { .num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults), .readable_reg = cs42l52_readable_register, .volatile_reg = cs42l52_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int cs42l52_i2c_probe(struct i2c_client *i2c_client) diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 4c646e8d72aa..1714857594fb 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -1125,7 +1125,7 @@ static const struct regmap_config cs42l56_regmap = { .num_reg_defaults = ARRAY_SIZE(cs42l56_reg_defaults), .readable_reg = cs42l56_readable_register, .volatile_reg = cs42l56_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int cs42l56_handle_of_data(struct i2c_client *i2c_client, diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index 4558ec38a7ac..9c44b6283b8f 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -458,7 +458,7 @@ const struct regmap_config cs42xx8_regmap_config = { .num_reg_defaults = ARRAY_SIZE(cs42xx8_reg), .volatile_reg = cs42xx8_volatile_register, .writeable_reg = cs42xx8_writeable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs42xx8_regmap_config); diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h index 1dd893674313..90e8895275e7 100644 --- a/sound/soc/codecs/cs43130.h +++ b/sound/soc/codecs/cs43130.h @@ -381,88 +381,88 @@ struct cs43130_clk_gen { /* frm_size = 16 */ static const struct cs43130_clk_gen cs43130_16_clk_gen[] = { - { 22579200, 32000, .v = { 441, 10, }, }, - { 22579200, 44100, .v = { 32, 1, }, }, - { 22579200, 48000, .v = { 147, 5, }, }, - { 22579200, 88200, .v = { 16, 1, }, }, - { 22579200, 96000, .v = { 147, 10, }, }, - { 22579200, 176400, .v = { 8, 1, }, }, - { 22579200, 192000, .v = { 147, 20, }, }, - { 22579200, 352800, .v = { 4, 1, }, }, - { 22579200, 384000, .v = { 147, 40, }, }, - { 24576000, 32000, .v = { 48, 1, }, }, - { 24576000, 44100, .v = { 5120, 147, }, }, - { 24576000, 48000, .v = { 32, 1, }, }, - { 24576000, 88200, .v = { 2560, 147, }, }, - { 24576000, 96000, .v = { 16, 1, }, }, - { 24576000, 176400, .v = { 1280, 147, }, }, - { 24576000, 192000, .v = { 8, 1, }, }, - { 24576000, 352800, .v = { 640, 147, }, }, - { 24576000, 384000, .v = { 4, 1, }, }, + { 22579200, 32000, .v = { 10, 441, }, }, + { 22579200, 44100, .v = { 1, 32, }, }, + { 22579200, 48000, .v = { 5, 147, }, }, + { 22579200, 88200, .v = { 1, 16, }, }, + { 22579200, 96000, .v = { 10, 147, }, }, + { 22579200, 176400, .v = { 1, 8, }, }, + { 22579200, 192000, .v = { 20, 147, }, }, + { 22579200, 352800, .v = { 1, 4, }, }, + { 22579200, 384000, .v = { 40, 147, }, }, + { 24576000, 32000, .v = { 1, 48, }, }, + { 24576000, 44100, .v = { 147, 5120, }, }, + { 24576000, 48000, .v = { 1, 32, }, }, + { 24576000, 88200, .v = { 147, 2560, }, }, + { 24576000, 96000, .v = { 1, 16, }, }, + { 24576000, 176400, .v = { 147, 1280, }, }, + { 24576000, 192000, .v = { 1, 8, }, }, + { 24576000, 352800, .v = { 147, 640, }, }, + { 24576000, 384000, .v = { 1, 4, }, }, }; /* frm_size = 32 */ static const struct cs43130_clk_gen cs43130_32_clk_gen[] = { - { 22579200, 32000, .v = { 441, 20, }, }, - { 22579200, 44100, .v = { 16, 1, }, }, - { 22579200, 48000, .v = { 147, 10, }, }, - { 22579200, 88200, .v = { 8, 1, }, }, - { 22579200, 96000, .v = { 147, 20, }, }, - { 22579200, 176400, .v = { 4, 1, }, }, - { 22579200, 192000, .v = { 147, 40, }, }, - { 22579200, 352800, .v = { 2, 1, }, }, - { 22579200, 384000, .v = { 147, 80, }, }, - { 24576000, 32000, .v = { 24, 1, }, }, - { 24576000, 44100, .v = { 2560, 147, }, }, - { 24576000, 48000, .v = { 16, 1, }, }, - { 24576000, 88200, .v = { 1280, 147, }, }, - { 24576000, 96000, .v = { 8, 1, }, }, - { 24576000, 176400, .v = { 640, 147, }, }, - { 24576000, 192000, .v = { 4, 1, }, }, - { 24576000, 352800, .v = { 320, 147, }, }, - { 24576000, 384000, .v = { 2, 1, }, }, + { 22579200, 32000, .v = { 20, 441, }, }, + { 22579200, 44100, .v = { 1, 16, }, }, + { 22579200, 48000, .v = { 10, 147, }, }, + { 22579200, 88200, .v = { 1, 8, }, }, + { 22579200, 96000, .v = { 20, 147, }, }, + { 22579200, 176400, .v = { 1, 4, }, }, + { 22579200, 192000, .v = { 40, 147, }, }, + { 22579200, 352800, .v = { 1, 2, }, }, + { 22579200, 384000, .v = { 80, 147, }, }, + { 24576000, 32000, .v = { 1, 24, }, }, + { 24576000, 44100, .v = { 147, 2560, }, }, + { 24576000, 48000, .v = { 1, 16, }, }, + { 24576000, 88200, .v = { 147, 1280, }, }, + { 24576000, 96000, .v = { 1, 8, }, }, + { 24576000, 176400, .v = { 147, 640, }, }, + { 24576000, 192000, .v = { 1, 4, }, }, + { 24576000, 352800, .v = { 147, 320, }, }, + { 24576000, 384000, .v = { 1, 2, }, }, }; /* frm_size = 48 */ static const struct cs43130_clk_gen cs43130_48_clk_gen[] = { - { 22579200, 32000, .v = { 147, 100, }, }, - { 22579200, 44100, .v = { 32, 3, }, }, - { 22579200, 48000, .v = { 49, 5, }, }, - { 22579200, 88200, .v = { 16, 3, }, }, - { 22579200, 96000, .v = { 49, 10, }, }, - { 22579200, 176400, .v = { 8, 3, }, }, - { 22579200, 192000, .v = { 49, 20, }, }, - { 22579200, 352800, .v = { 4, 3, }, }, - { 22579200, 384000, .v = { 49, 40, }, }, - { 24576000, 32000, .v = { 16, 1, }, }, - { 24576000, 44100, .v = { 5120, 441, }, }, - { 24576000, 48000, .v = { 32, 3, }, }, - { 24576000, 88200, .v = { 2560, 441, }, }, - { 24576000, 96000, .v = { 16, 3, }, }, - { 24576000, 176400, .v = { 1280, 441, }, }, - { 24576000, 192000, .v = { 8, 3, }, }, - { 24576000, 352800, .v = { 640, 441, }, }, - { 24576000, 384000, .v = { 4, 3, }, }, + { 22579200, 32000, .v = { 100, 147, }, }, + { 22579200, 44100, .v = { 3, 32, }, }, + { 22579200, 48000, .v = { 5, 49, }, }, + { 22579200, 88200, .v = { 3, 16, }, }, + { 22579200, 96000, .v = { 10, 49, }, }, + { 22579200, 176400, .v = { 3, 8, }, }, + { 22579200, 192000, .v = { 20, 49, }, }, + { 22579200, 352800, .v = { 3, 4, }, }, + { 22579200, 384000, .v = { 40, 49, }, }, + { 24576000, 32000, .v = { 1, 16, }, }, + { 24576000, 44100, .v = { 441, 5120, }, }, + { 24576000, 48000, .v = { 3, 32, }, }, + { 24576000, 88200, .v = { 441, 2560, }, }, + { 24576000, 96000, .v = { 3, 16, }, }, + { 24576000, 176400, .v = { 441, 1280, }, }, + { 24576000, 192000, .v = { 3, 8, }, }, + { 24576000, 352800, .v = { 441, 640, }, }, + { 24576000, 384000, .v = { 3, 4, }, }, }; /* frm_size = 64 */ static const struct cs43130_clk_gen cs43130_64_clk_gen[] = { - { 22579200, 32000, .v = { 441, 40, }, }, - { 22579200, 44100, .v = { 8, 1, }, }, - { 22579200, 48000, .v = { 147, 20, }, }, - { 22579200, 88200, .v = { 4, 1, }, }, - { 22579200, 96000, .v = { 147, 40, }, }, - { 22579200, 176400, .v = { 2, 1, }, }, - { 22579200, 192000, .v = { 147, 80, }, }, + { 22579200, 32000, .v = { 40, 441, }, }, + { 22579200, 44100, .v = { 1, 8, }, }, + { 22579200, 48000, .v = { 20, 147, }, }, + { 22579200, 88200, .v = { 1, 4, }, }, + { 22579200, 96000, .v = { 40, 147, }, }, + { 22579200, 176400, .v = { 1, 2, }, }, + { 22579200, 192000, .v = { 80, 147, }, }, { 22579200, 352800, .v = { 1, 1, }, }, - { 24576000, 32000, .v = { 12, 1, }, }, - { 24576000, 44100, .v = { 1280, 147, }, }, - { 24576000, 48000, .v = { 8, 1, }, }, - { 24576000, 88200, .v = { 640, 147, }, }, - { 24576000, 96000, .v = { 4, 1, }, }, - { 24576000, 176400, .v = { 320, 147, }, }, - { 24576000, 192000, .v = { 2, 1, }, }, - { 24576000, 352800, .v = { 160, 147, }, }, + { 24576000, 32000, .v = { 1, 12, }, }, + { 24576000, 44100, .v = { 147, 1280, }, }, + { 24576000, 48000, .v = { 1, 8, }, }, + { 24576000, 88200, .v = { 147, 640, }, }, + { 24576000, 96000, .v = { 1, 4, }, }, + { 24576000, 176400, .v = { 147, 320, }, }, + { 24576000, 192000, .v = { 1, 2, }, }, + { 24576000, 352800, .v = { 147, 160, }, }, { 24576000, 384000, .v = { 1, 1, }, }, }; diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index 8365dd0ebe2a..ef08e51901b5 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -271,7 +271,7 @@ static const struct regmap_config cs4349_regmap = { .num_reg_defaults = ARRAY_SIZE(cs4349_reg_defaults), .readable_reg = cs4349_readable_register, .writeable_reg = cs4349_writeable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int cs4349_i2c_probe(struct i2c_client *client) diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c index a6538dab6639..1245e1a4f2a5 100644 --- a/sound/soc/codecs/cs47l15.c +++ b/sound/soc/codecs/cs47l15.c @@ -1143,6 +1143,10 @@ static int cs47l15_set_fll(struct snd_soc_component *component, int fll_id, } } +static const struct snd_soc_dai_ops cs47l15_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l15_dai[] = { { .name = "cs47l15-aif1", @@ -1219,7 +1223,7 @@ static struct snd_soc_dai_driver cs47l15_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &cs47l15_dai_ops, }, { .name = "cs47l15-dsp-trace", diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index a07b621d463e..cfa1d34f6ebd 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -957,6 +957,10 @@ static int cs47l24_set_fll(struct snd_soc_component *component, int fll_id, #define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +static const struct snd_soc_dai_ops cs47l24_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l24_dai[] = { { .name = "cs47l24-aif1", @@ -1033,7 +1037,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = { .rates = CS47L24_RATES, .formats = CS47L24_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &cs47l24_dai_ops, }, { .name = "cs47l24-dsp-voicectrl", @@ -1054,7 +1058,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = { .rates = CS47L24_RATES, .formats = CS47L24_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &cs47l24_dai_ops, }, { .name = "cs47l24-dsp-trace", diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c index c05c80c16c84..a953f2ede1ee 100644 --- a/sound/soc/codecs/cs47l35.c +++ b/sound/soc/codecs/cs47l35.c @@ -1348,6 +1348,10 @@ static int cs47l35_set_fll(struct snd_soc_component *component, int fll_id, } } +static const struct snd_soc_dai_ops cs47l35_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l35_dai[] = { { .name = "cs47l35-aif1", @@ -1462,7 +1466,7 @@ static struct snd_soc_dai_driver cs47l35_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l35_dai_ops, }, { .name = "cs47l35-dsp-voicectrl", @@ -1483,7 +1487,7 @@ static struct snd_soc_dai_driver cs47l35_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l35_dai_ops, }, { .name = "cs47l35-dsp-trace", diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c index dd7997a53e70..827685481859 100644 --- a/sound/soc/codecs/cs47l85.c +++ b/sound/soc/codecs/cs47l85.c @@ -2249,6 +2249,10 @@ static int cs47l85_set_fll(struct snd_soc_component *component, int fll_id, } } +static const struct snd_soc_dai_ops cs47l85_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l85_dai[] = { { .name = "cs47l85-aif1", @@ -2404,7 +2408,7 @@ static struct snd_soc_dai_driver cs47l85_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l85_dai_ops, }, { .name = "cs47l85-dsp-voicectrl", @@ -2425,7 +2429,7 @@ static struct snd_soc_dai_driver cs47l85_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l85_dai_ops, }, { .name = "cs47l85-dsp-trace", diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c index cdd5e7e20b5d..2c9a5372cf51 100644 --- a/sound/soc/codecs/cs47l90.c +++ b/sound/soc/codecs/cs47l90.c @@ -2168,6 +2168,10 @@ static int cs47l90_set_fll(struct snd_soc_component *component, int fll_id, } } +static const struct snd_soc_dai_ops cs47l90_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l90_dai[] = { { .name = "cs47l90-aif1", @@ -2323,7 +2327,7 @@ static struct snd_soc_dai_driver cs47l90_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l90_dai_ops, }, { .name = "cs47l90-dsp-voicectrl", @@ -2344,7 +2348,7 @@ static struct snd_soc_dai_driver cs47l90_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l90_dai_ops, }, { .name = "cs47l90-dsp-trace", diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c index bc4d311d4778..352deeaff1ca 100644 --- a/sound/soc/codecs/cs47l92.c +++ b/sound/soc/codecs/cs47l92.c @@ -1690,6 +1690,10 @@ static int cs47l92_set_fll(struct snd_soc_component *component, int fll_id, } } +static const struct snd_soc_dai_ops cs47l92_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l92_dai[] = { { .name = "cs47l92-aif1", @@ -1823,7 +1827,7 @@ static struct snd_soc_dai_driver cs47l92_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &cs47l92_dai_ops, }, { .name = "cs47l92-dsp-trace", diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 082231088a26..f8b128084015 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -1546,6 +1546,14 @@ static int cx2072x_dsp_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops cx2072x_dai_ops2 = { + .probe = cx2072x_dsp_dai_probe, + .set_sysclk = cx2072x_set_dai_sysclk, + .set_fmt = cx2072x_set_dai_fmt, + .hw_params = cx2072x_hw_params, + .set_bclk_ratio = cx2072x_set_dai_bclk_ratio, +}; + #define CX2072X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = { @@ -1572,7 +1580,6 @@ static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = { { /* plabayck only, return echo reference to Conexant DSP chip */ .name = "cx2072x-dsp", .id = CX2072X_DAI_DSP, - .probe = cx2072x_dsp_dai_probe, .playback = { .stream_name = "DSP Playback", .channels_min = 2, @@ -1580,7 +1587,7 @@ static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = { .rates = CX2072X_RATES_DSP, .formats = CX2072X_FORMATS, }, - .ops = &cx2072x_dai_ops, + .ops = &cx2072x_dai_ops2, }, { /* plabayck only, return echo reference through I2S TX */ .name = "cx2072x-aec", diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index 65e497b455d3..a8f347f1affb 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -27,9 +27,9 @@ * MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on * Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK). */ -#define NR_SUPPORTED_MCLK_LRCK_RATIOS 6 +#define NR_SUPPORTED_MCLK_LRCK_RATIOS ARRAY_SIZE(supported_mclk_lrck_ratios) static const unsigned int supported_mclk_lrck_ratios[] = { - 256, 384, 400, 512, 768, 1024 + 256, 384, 400, 500, 512, 768, 1024 }; struct es8316_priv { @@ -494,6 +494,7 @@ static int es8316_pcm_hw_params(struct snd_pcm_substream *substream, bclk_divider /= 20; break; case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: wordlen = ES8316_SERDATA2_LEN_24; bclk_divider /= 24; break; diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 7cb5b57ae655..6c263086c44d 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -38,6 +38,11 @@ struct es8326_priv { u8 interrupt_clk; bool jd_inverted; unsigned int sysclk; + + bool calibrated; + int version; + int hp; + int jack_remove_retry; }; static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0); @@ -121,33 +126,12 @@ static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { /* Analog Power Supply*/ SND_SOC_DAPM_DAC("Right DAC", NULL, ES8326_ANA_PDN, 0, 1), SND_SOC_DAPM_DAC("Left DAC", NULL, ES8326_ANA_PDN, 1, 1), - SND_SOC_DAPM_SUPPLY("Analog Power", ES8326_ANA_PDN, 7, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("IBias Power", ES8326_ANA_PDN, 6, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADC Vref", ES8326_ANA_PDN, 5, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("DAC Vref", ES8326_ANA_PDN, 4, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("Vref Power", ES8326_ANA_PDN, 3, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("MICBIAS1", ES8326_ANA_MICBIAS, 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("MICBIAS2", ES8326_ANA_MICBIAS, 3, 0, NULL, 0), SND_SOC_DAPM_PGA("LHPMIX", ES8326_DAC2HPMIX, 7, 0, NULL, 0), SND_SOC_DAPM_PGA("RHPMIX", ES8326_DAC2HPMIX, 3, 0, NULL, 0), - /* Headphone Charge Pump and Output */ - SND_SOC_DAPM_SUPPLY("HPOR Cal", ES8326_HP_CAL, 7, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("HPOL Cal", ES8326_HP_CAL, 3, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", ES8326_HP_DRIVER, - 3, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("Headphone Driver Bias", ES8326_HP_DRIVER, - 2, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("Headphone LDO", ES8326_HP_DRIVER, - 1, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("Headphone Reference", ES8326_HP_DRIVER, - 0, 1, NULL, 0), - SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOR Supply", ES8326_HP_CAL, - ES8326_HPOR_SHIFT, 7, 7, 0), - SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPOL Supply", ES8326_HP_CAL, - 0, 7, 7, 0), - SND_SOC_DAPM_OUTPUT("HPOL"), SND_SOC_DAPM_OUTPUT("HPOR"), }; @@ -166,52 +150,35 @@ static const struct snd_soc_dapm_route es8326_dapm_routes[] = { {"I2S OUT", NULL, "ADC L"}, {"I2S OUT", NULL, "ADC R"}, - {"I2S OUT", NULL, "Analog Power"}, - {"I2S OUT", NULL, "ADC Vref"}, - {"I2S OUT", NULL, "Vref Power"}, - {"I2S OUT", NULL, "IBias Power"}, - {"I2S IN", NULL, "Analog Power"}, - {"I2S IN", NULL, "DAC Vref"}, - {"I2S IN", NULL, "Vref Power"}, - {"I2S IN", NULL, "IBias Power"}, - {"Right DAC", NULL, "I2S IN"}, {"Left DAC", NULL, "I2S IN"}, {"LHPMIX", NULL, "Left DAC"}, {"RHPMIX", NULL, "Right DAC"}, - {"HPOR", NULL, "HPOR Cal"}, - {"HPOL", NULL, "HPOL Cal"}, - {"HPOR", NULL, "HPOR Supply"}, - {"HPOL", NULL, "HPOL Supply"}, - {"HPOL", NULL, "Headphone Charge Pump"}, - {"HPOR", NULL, "Headphone Charge Pump"}, - {"HPOL", NULL, "Headphone Driver Bias"}, - {"HPOR", NULL, "Headphone Driver Bias"}, - {"HPOL", NULL, "Headphone LDO"}, - {"HPOR", NULL, "Headphone LDO"}, - {"HPOL", NULL, "Headphone Reference"}, - {"HPOR", NULL, "Headphone Reference"}, - {"HPOL", NULL, "LHPMIX"}, {"HPOR", NULL, "RHPMIX"}, }; -static const struct regmap_range es8326_volatile_ranges[] = { - regmap_reg_range(ES8326_HP_DETECT, ES8326_HP_DETECT), -}; - -static const struct regmap_access_table es8326_volatile_table = { - .yes_ranges = es8326_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(es8326_volatile_ranges), -}; +static bool es8326_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ES8326_HPL_OFFSET_INI: + case ES8326_HPR_OFFSET_INI: + case ES8326_HPDET_STA: + case ES8326_CTIA_OMTP_STA: + case ES8326_CSM_MUTE_STA: + return true; + default: + return false; + } +} static const struct regmap_config es8326_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0xff, - .volatile_table = &es8326_volatile_table, + .volatile_reg = es8326_volatile_register, .cache_type = REGCACHE_RBTREE, }; @@ -419,6 +386,38 @@ static int es8326_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } +static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int offset_l, offset_r; + + if (mute) { + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, + ES8326_MUTE_MASK, ES8326_MUTE); + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xf0); + } else { + if (!es8326->calibrated) { + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL); + msleep(30); + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); + regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l); + regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r); + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); + regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l); + regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r); + es8326->calibrated = true; + } + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa0); + regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80); + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON); + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, + ES8326_MUTE_MASK, ~(ES8326_MUTE)); + } + return 0; +} + static int es8326_set_bias_level(struct snd_soc_component *codec, enum snd_soc_bias_level level) { @@ -430,15 +429,17 @@ static int es8326_set_bias_level(struct snd_soc_component *codec, ret = clk_prepare_enable(es8326->mclk); if (ret) return ret; - regmap_write(es8326->regmap, ES8326_RESET, ES8326_PWRUP_SEQ_EN); - regmap_write(es8326->regmap, ES8326_INTOUT_IO, 0x45); + + regmap_write(es8326->regmap, ES8326_RESET, 0x9f); + msleep(20); + regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00); + regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); - regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT); - regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05); - regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x02); + regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40); - regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0xAA); + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00); + regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20); regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON); break; case SND_SOC_BIAS_PREPARE: @@ -447,15 +448,10 @@ static int es8326_set_bias_level(struct snd_soc_component *codec, break; case SND_SOC_BIAS_OFF: clk_disable_unprepare(es8326->mclk); - regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x11); - regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_OFF); - regmap_write(es8326->regmap, ES8326_PGA_PDN, 0xF8); + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x00); - regmap_write(es8326->regmap, ES8326_INT_SOURCE, 0x08); + regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT); - regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT); - regmap_write(es8326->regmap, ES8326_RESET, - ES8326_CODEC_RESET | ES8326_PWRUP_SEQ_EN); break; } @@ -469,6 +465,8 @@ static const struct snd_soc_dai_ops es8326_ops = { .hw_params = es8326_pcm_hw_params, .set_fmt = es8326_set_dai_fmt, .set_sysclk = es8326_set_dai_sysclk, + .mute_stream = es8326_mute, + .no_capture_mute = 1, }; static struct snd_soc_dai_driver es8326_dai = { @@ -532,13 +530,14 @@ static void es8326_jack_button_handler(struct work_struct *work) return; mutex_lock(&es8326->lock); - iface = snd_soc_component_read(comp, ES8326_HP_DETECT); + iface = snd_soc_component_read(comp, ES8326_HPDET_STA); switch (iface) { case 0x93: /* pause button detected */ cur_button = SND_JACK_BTN_0; break; case 0x6f: + case 0x4b: /* button volume up */ cur_button = SND_JACK_BTN_1; break; @@ -547,6 +546,7 @@ static void es8326_jack_button_handler(struct work_struct *work) cur_button = SND_JACK_BTN_2; break; case 0x1e: + case 0xe2: /* button released or not pressed */ cur_button = 0; break; @@ -556,20 +556,20 @@ static void es8326_jack_button_handler(struct work_struct *work) if ((prev_button == cur_button) && (cur_button != 0)) { press_count++; - if (press_count > 10) { - /* report a press every 500ms */ + if (press_count > 3) { + /* report a press every 120ms */ snd_soc_jack_report(es8326->jack, cur_button, SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2); press_count = 0; } button_to_report = cur_button; queue_delayed_work(system_wq, &es8326->button_press_work, - msecs_to_jiffies(50)); + msecs_to_jiffies(35)); } else if (prev_button != cur_button) { /* mismatch, detect again */ prev_button = cur_button; queue_delayed_work(system_wq, &es8326->button_press_work, - msecs_to_jiffies(50)); + msecs_to_jiffies(35)); } else { /* released or no pressed */ if (button_to_report != 0) { @@ -591,34 +591,98 @@ static void es8326_jack_detect_handler(struct work_struct *work) unsigned int iface; mutex_lock(&es8326->lock); - iface = snd_soc_component_read(comp, ES8326_HP_DETECT); + iface = snd_soc_component_read(comp, ES8326_HPDET_STA); dev_dbg(comp->dev, "gpio flag %#04x", iface); + + if (es8326->jack_remove_retry == 1) { + if (iface & ES8326_HPINSERT_FLAG) + es8326->jack_remove_retry = 2; + else + es8326->jack_remove_retry = 0; + + dev_dbg(comp->dev, "remove event check, set HPJACK_POL normal, cnt = %d\n", + es8326->jack_remove_retry); + /* + * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event + */ + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? + ~es8326->jack_pol : es8326->jack_pol)); + goto exit; + } + if ((iface & ES8326_HPINSERT_FLAG) == 0) { /* Jack unplugged or spurious IRQ */ - dev_dbg(comp->dev, "No headset detected"); + dev_dbg(comp->dev, "No headset detected\n"); + es8326_disable_micbias(es8326->component); if (es8326->jack->status & SND_JACK_HEADPHONE) { + dev_dbg(comp->dev, "Report hp remove event\n"); snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); - snd_soc_component_write(comp, ES8326_ADC1_SRC, es8326->mic2_src); - es8326_disable_micbias(comp); + /* mute adc when mic path switch */ + regmap_write(es8326->regmap, ES8326_ADC_SCALE, 0x33); + regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x44); + regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x66); + es8326->hp = 0; + } + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + /* + * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event + */ + if (es8326->jack_remove_retry == 0) { + es8326->jack_remove_retry = 1; + dev_dbg(comp->dev, "remove event check, invert HPJACK_POL, cnt = %d\n", + es8326->jack_remove_retry); + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? + es8326->jack_pol : ~es8326->jack_pol)); + + } else { + es8326->jack_remove_retry = 0; } } else if ((iface & ES8326_HPINSERT_FLAG) == ES8326_HPINSERT_FLAG) { + es8326->jack_remove_retry = 0; + if (es8326->hp == 0) { + dev_dbg(comp->dev, "First insert, start OMTP/CTIA type check\n"); + /* + * set auto-check mode, then restart jack_detect_work after 100ms. + * Don't report jack status. + */ + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + usleep_range(50000, 70000); + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); + queue_delayed_work(system_wq, &es8326->jack_detect_work, + msecs_to_jiffies(100)); + es8326->hp = 1; + goto exit; + } if (es8326->jack->status & SND_JACK_HEADSET) { /* detect button */ + dev_dbg(comp->dev, "button pressed\n"); queue_delayed_work(system_wq, &es8326->button_press_work, 10); + goto exit; + } + if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) { + dev_dbg(comp->dev, "Headphone detected\n"); + snd_soc_jack_report(es8326->jack, + SND_JACK_HEADPHONE, SND_JACK_HEADSET); } else { - if ((iface & ES8326_HPBUTTON_FLAG) == 0x00) { - dev_dbg(comp->dev, "Headset detected"); - snd_soc_jack_report(es8326->jack, - SND_JACK_HEADSET, SND_JACK_HEADSET); - snd_soc_component_write(comp, - ES8326_ADC1_SRC, es8326->mic1_src); - } else { - dev_dbg(comp->dev, "Headphone detected"); - snd_soc_jack_report(es8326->jack, - SND_JACK_HEADPHONE, SND_JACK_HEADSET); - } + dev_dbg(comp->dev, "Headset detected\n"); + snd_soc_jack_report(es8326->jack, + SND_JACK_HEADSET, SND_JACK_HEADSET); + + regmap_write(es8326->regmap, ES8326_ADC_SCALE, 0x33); + regmap_update_bits(es8326->regmap, ES8326_PGA_PDN, + 0x08, 0x08); + regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, + 0x80, 0x80); + regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00); + regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00); + regmap_update_bits(es8326->regmap, ES8326_PGA_PDN, + 0x08, 0x00); + usleep_range(10000, 15000); } } +exit: mutex_unlock(&es8326->lock); } @@ -637,51 +701,138 @@ static irqreturn_t es8326_irq(int irq, void *dev_id) msecs_to_jiffies(10)); else queue_delayed_work(system_wq, &es8326->jack_detect_work, - msecs_to_jiffies(300)); + msecs_to_jiffies(600)); out: return IRQ_HANDLED; } -static int es8326_resume(struct snd_soc_component *component) +static int es8326_calibrate(struct snd_soc_component *component) { struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); unsigned int reg; + unsigned int offset_l, offset_r; + + regmap_read(es8326->regmap, ES8326_CHIP_VERSION, ®); + es8326->version = reg; + + if ((es8326->version == ES8326_VERSION_B) && (es8326->calibrated == false)) { + dev_dbg(component->dev, "ES8326_VERSION_B, calibrating\n"); + regmap_write(es8326->regmap, ES8326_CLK_INV, 0xc0); + regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x01); + regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30); + regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed); + regmap_write(es8326->regmap, ES8326_CLK_TRI, 0xc1); + regmap_write(es8326->regmap, ES8326_DAC_MUTE, 0x03); + regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7f); + regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x33); + regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88); + regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80); + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); + regmap_write(es8326->regmap, ES8326_RESET, 0xc0); + usleep_range(15000, 20000); + + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, ES8326_HP_OFF); + regmap_read(es8326->regmap, ES8326_CSM_MUTE_STA, ®); + if ((reg & 0xf0) != 0x40) + msleep(50); + + regmap_write(es8326->regmap, ES8326_HP_CAL, 0xd4); + msleep(200); + regmap_write(es8326->regmap, ES8326_HP_CAL, 0x4d); + msleep(200); + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); + regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l); + regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r); + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); + regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l); + regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r); + regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00); + + es8326->calibrated = true; + } + + return 0; +} + +static int es8326_resume(struct snd_soc_component *component) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); regcache_cache_only(es8326->regmap, false); regcache_sync(es8326->regmap); + /* reset internal clock state */ + regmap_write(es8326->regmap, ES8326_RESET, 0x1f); + regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); + usleep_range(10000, 15000); + regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88); + /* set headphone default type and detect pin */ + regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x81); + regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05); + + /* set internal oscillator as clock source of headpone cp */ + regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x84); regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON); - /* Two channel ADC */ - regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x02); + /* clock manager reset release */ + regmap_write(es8326->regmap, ES8326_RESET, 0x17); + /* set headphone detection as half scan mode */ + regmap_write(es8326->regmap, ES8326_HP_MISC, 0x08); + regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00); + + /* enable headphone driver */ + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7); + usleep_range(2000, 5000); + regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xab); + usleep_range(2000, 5000); + regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xbb); + usleep_range(2000, 5000); + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1); + regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00); - regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x1F); - regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xC8); - regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x88); - regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x20); + regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xc4); + regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x81); + regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x00); + /* calibrate for B version */ + es8326_calibrate(component); + /* turn off headphone out */ + regmap_write(es8326->regmap, ES8326_HP_CAL, 0x00); + /* set ADC and DAC in low power mode */ + regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0); + + /* force micbias on */ + regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0x4f); regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x08); - regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x22); - regmap_write(es8326->regmap, ES8326_ADC1_SRC, es8326->mic1_src); - regmap_write(es8326->regmap, ES8326_ADC2_SRC, es8326->mic2_src); - regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88); - regmap_write(es8326->regmap, ES8326_HP_DET, - ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol); - regmap_write(es8326->regmap, ES8326_INT_SOURCE, es8326->interrupt_src); - regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); + regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F); + /* select vdda as micbias source */ + regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23); + /* set dac dsmclip = 1 */ + regmap_write(es8326->regmap, ES8326_DAC_DSM, 0x08); + regmap_write(es8326->regmap, ES8326_DAC_VPPSCALE, 0x15); + + regmap_write(es8326->regmap, ES8326_INT_SOURCE, + (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); + regmap_write(es8326->regmap, ES8326_INTOUT_IO, + es8326->interrupt_clk); + regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, + (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); + regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT); + + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON); - snd_soc_component_update_bits(component, ES8326_PGAGAIN, - ES8326_MIC_SEL_MASK, ES8326_MIC1_SEL); + regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK, + ES8326_MIC1_SEL); - regmap_read(es8326->regmap, ES8326_CHIP_VERSION, ®); - if ((reg & ES8326_VERSION_B) == 1) { - regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xDD); - regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F); - regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x0F); - /* enable button detect */ - regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xA0); - } + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK, + ES8326_MUTE); - es8326_irq(es8326->irq, es8326); + regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 | + ((es8326->version == ES8326_VERSION_B) ? + (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) : + (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04))); + + es8326->jack_remove_retry = 0; + es8326->hp = 0; return 0; } @@ -691,11 +842,15 @@ static int es8326_suspend(struct snd_soc_component *component) cancel_delayed_work_sync(&es8326->jack_detect_work); es8326_disable_micbias(component); - + es8326->calibrated = false; regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_OFF); regcache_cache_only(es8326->regmap, true); regcache_mark_dirty(es8326->regmap); + /* reset register value to default */ + regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01); + usleep_range(1000, 3000); + regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00); return 0; } @@ -725,7 +880,7 @@ static int es8326_probe(struct snd_soc_component *component) ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol); if (ret != 0) { dev_dbg(component->dev, "jack-pol return %d", ret); - es8326->jack_pol = ES8326_HP_DET_BUTTON_POL | ES8326_HP_TYPE_OMTP; + es8326->jack_pol = ES8326_HP_TYPE_AUTO; } dev_dbg(component->dev, "jack-pol %x", es8326->jack_pol); @@ -756,7 +911,7 @@ static void es8326_enable_jack_detect(struct snd_soc_component *component, mutex_lock(&es8326->lock); if (es8326->jd_inverted) - snd_soc_component_update_bits(component, ES8326_HP_DET, + snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); es8326->jack = jack; diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index 8e5ffe5ee10d..90a08351d6ac 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -9,8 +9,6 @@ #ifndef _ES8326_H #define _ES8326_H -#define CONFIG_HHTECH_MINIPMP 1 - /* ES8326 register space */ #define ES8326_RESET 0x00 #define ES8326_CLK_CTL 0x01 @@ -75,15 +73,19 @@ #define ES8326_DRC_RECOVERY 0x53 #define ES8326_DRC_WINSIZE 0x54 #define ES8326_HPJACK_TIMER 0x56 -#define ES8326_HP_DET 0x57 +#define ES8326_HPDET_TYPE 0x57 #define ES8326_INT_SOURCE 0x58 #define ES8326_INTOUT_IO 0x59 #define ES8326_SDINOUT1_IO 0x5A #define ES8326_SDINOUT23_IO 0x5B #define ES8326_JACK_PULSE 0x5C +#define ES8326_HP_MISC 0xF7 +#define ES8326_CTIA_OMTP_STA 0xF8 #define ES8326_PULLUP_CTL 0xF9 -#define ES8326_HP_DETECT 0xFB +#define ES8326_CSM_I2C_STA 0xFA +#define ES8326_HPDET_STA 0xFB +#define ES8326_CSM_MUTE_STA 0xFC #define ES8326_CHIP_ID1 0xFD #define ES8326_CHIP_ID2 0xFE #define ES8326_CHIP_VERSION 0xFF @@ -94,6 +96,8 @@ #define ES8326_PWRUP_SEQ_EN (1 << 5) #define ES8326_CODEC_RESET (0x0f << 0) #define ES8326_CSM_OFF (0 << 7) +#define ES8326_MUTE_MASK (3 << 0) +#define ES8326_MUTE (3 << 0) /* ES8326_CLK_CTL */ #define ES8326_CLK_ON (0x7f << 0) @@ -122,7 +126,9 @@ #define ES8326_MIC2_SEL (1 << 5) /* ES8326_HP_CAL */ -#define ES8326_HPOR_SHIFT 4 +#define ES8326_HP_OFF 0 +#define ES8326_HP_FORCE_CAL ((1 << 7) | (1 << 3)) +#define ES8326_HP_ON ((7 << 4) | (7 << 0)) /* ES8326_ADC1_SRC */ #define ES8326_ADC1_SHIFT 0 @@ -144,7 +150,7 @@ #define ES8326_ADC3_SHIFT 0 #define ES8326_ADC4_SHIFT 3 -/* ES8326_HP_DET */ +/* ES8326_HPDET_TYPE */ #define ES8326_HP_DET_SRC_PIN27 (1 << 5) #define ES8326_HP_DET_SRC_PIN9 (1 << 4) #define ES8326_HP_DET_JACK_POL (1 << 3) @@ -154,6 +160,13 @@ #define ES8326_HP_TYPE_AUTO (1 << 0) #define ES8326_HP_TYPE_AUTO_INV (0 << 0) +/* ES8326_INT_SOURCE */ +#define ES8326_INT_SRC_DAC_MOZ (1 << 0) +#define ES8326_INT_SRC_ADC_MOZ (1 << 1) +#define ES8326_INT_SRC_BUTTON (1 << 2) +#define ES8326_INT_SRC_PIN9 (1 << 3) +#define ES8326_INT_SRC_PIN27 (1 << 4) + /* ES8326_SDINOUT1_IO */ #define ES8326_IO_INPUT (0 << 0) #define ES8326_IO_SDIN_SLOT0 (1 << 0) @@ -172,11 +185,12 @@ #define ES8326_SDINOUT2_SHIFT 4 #define ES8326_SDINOUT3_SHIFT 0 -/* ES8326_HP_DETECT */ +/* ES8326_HPDET_STA */ #define ES8326_HPINSERT_FLAG (1 << 1) #define ES8326_HPBUTTON_FLAG (1 << 0) /* ES8326_CHIP_VERSION 0xFF */ -#define ES8326_VERSION_B (1 << 0) +#define ES8326_VERSION (1 << 0) +#define ES8326_VERSION_B (3 << 0) #endif diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index d21f69f05342..13689e718d36 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -723,24 +723,6 @@ static u64 hdmi_codec_formats = SND_SOC_POSSIBLE_DAIFMT_LEFT_J | SND_SOC_POSSIBLE_DAIFMT_AC97; -static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = { - .startup = hdmi_codec_startup, - .shutdown = hdmi_codec_shutdown, - .hw_params = hdmi_codec_hw_params, - .prepare = hdmi_codec_prepare, - .set_fmt = hdmi_codec_i2s_set_fmt, - .mute_stream = hdmi_codec_mute, - .auto_selectable_formats = &hdmi_codec_formats, - .num_auto_selectable_formats = 1, -}; - -static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { - .startup = hdmi_codec_startup, - .shutdown = hdmi_codec_shutdown, - .hw_params = hdmi_codec_hw_params, - .mute_stream = hdmi_codec_mute, -}; - #define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ @@ -921,10 +903,31 @@ static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = { + .probe = hdmi_dai_probe, + .startup = hdmi_codec_startup, + .shutdown = hdmi_codec_shutdown, + .hw_params = hdmi_codec_hw_params, + .prepare = hdmi_codec_prepare, + .set_fmt = hdmi_codec_i2s_set_fmt, + .mute_stream = hdmi_codec_mute, + .pcm_new = hdmi_codec_pcm_new, + .auto_selectable_formats = &hdmi_codec_formats, + .num_auto_selectable_formats = 1, +}; + +static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { + .probe = hdmi_dai_spdif_probe, + .startup = hdmi_codec_startup, + .shutdown = hdmi_codec_shutdown, + .hw_params = hdmi_codec_hw_params, + .mute_stream = hdmi_codec_mute, + .pcm_new = hdmi_codec_pcm_new, +}; + static const struct snd_soc_dai_driver hdmi_i2s_dai = { .name = "i2s-hifi", .id = DAI_ID_I2S, - .probe = hdmi_dai_probe, .playback = { .stream_name = "I2S Playback", .channels_min = 2, @@ -942,13 +945,11 @@ static const struct snd_soc_dai_driver hdmi_i2s_dai = { .sig_bits = 24, }, .ops = &hdmi_codec_i2s_dai_ops, - .pcm_new = hdmi_codec_pcm_new, }; static const struct snd_soc_dai_driver hdmi_spdif_dai = { .name = "spdif-hifi", .id = DAI_ID_SPDIF, - .probe = hdmi_dai_spdif_probe, .playback = { .stream_name = "SPDIF Playback", .channels_min = 2, @@ -964,7 +965,6 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = { .formats = SPDIF_FORMATS, }, .ops = &hdmi_codec_spdif_dai_ops, - .pcm_new = hdmi_codec_pcm_new, }; static int hdmi_of_xlate_dai_id(struct snd_soc_component *component, diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index 7c25acf6ff0d..d1cea93bdb59 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -301,7 +301,7 @@ static const struct regmap_config jz4740_codec_regmap_config = { .reg_defaults = jz4740_codec_reg_defaults, .num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int jz4740_codec_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c deleted file mode 100644 index b84f6f1f6800..000000000000 --- a/sound/soc/codecs/l3.c +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * L3 code - * - * Copyright (C) 2008, Christian Pellegrin <chripell@evolware.org> - * - * based on: - * - * L3 bus algorithm module. - * - * Copyright (C) 2001 Russell King, All Rights Reserved. - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/gpio.h> - -#include <sound/l3.h> - -/* - * Send one byte of data to the chip. Data is latched into the chip on - * the rising edge of the clock. - */ -static void sendbyte(struct l3_pins *adap, unsigned int byte) -{ - int i; - - for (i = 0; i < 8; i++) { - adap->setclk(adap, 0); - udelay(adap->data_hold); - adap->setdat(adap, byte & 1); - udelay(adap->data_setup); - adap->setclk(adap, 1); - udelay(adap->clock_high); - byte >>= 1; - } -} - -/* - * Send a set of bytes to the chip. We need to pulse the MODE line - * between each byte, but never at the start nor at the end of the - * transfer. - */ -static void sendbytes(struct l3_pins *adap, const u8 *buf, - int len) -{ - int i; - - for (i = 0; i < len; i++) { - if (i) { - udelay(adap->mode_hold); - adap->setmode(adap, 0); - udelay(adap->mode); - } - adap->setmode(adap, 1); - udelay(adap->mode_setup); - sendbyte(adap, buf[i]); - } -} - -int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len) -{ - adap->setclk(adap, 1); - adap->setdat(adap, 1); - adap->setmode(adap, 1); - udelay(adap->mode); - - adap->setmode(adap, 0); - udelay(adap->mode_setup); - sendbyte(adap, addr); - udelay(adap->mode_hold); - - sendbytes(adap, data, len); - - adap->setclk(adap, 1); - adap->setdat(adap, 1); - adap->setmode(adap, 0); - - return len; -} -EXPORT_SYMBOL_GPL(l3_write); - - -static void l3_set_clk(struct l3_pins *adap, int val) -{ - gpio_set_value(adap->gpio_clk, val); -} - -static void l3_set_data(struct l3_pins *adap, int val) -{ - gpio_set_value(adap->gpio_data, val); -} - -static void l3_set_mode(struct l3_pins *adap, int val) -{ - gpio_set_value(adap->gpio_mode, val); -} - -int l3_set_gpio_ops(struct device *dev, struct l3_pins *adap) -{ - int ret; - - if (!adap->use_gpios) - return -EINVAL; - - ret = devm_gpio_request_one(dev, adap->gpio_data, - GPIOF_OUT_INIT_LOW, "l3_data"); - if (ret < 0) - return ret; - adap->setdat = l3_set_data; - - ret = devm_gpio_request_one(dev, adap->gpio_clk, - GPIOF_OUT_INIT_LOW, "l3_clk"); - if (ret < 0) - return ret; - adap->setclk = l3_set_clk; - - ret = devm_gpio_request_one(dev, adap->gpio_mode, - GPIOF_OUT_INIT_LOW, "l3_mode"); - if (ret < 0) - return ret; - adap->setmode = l3_set_mode; - - return 0; -} -EXPORT_SYMBOL_GPL(l3_set_gpio_ops); - -MODULE_DESCRIPTION("L3 bit-banging driver"); -MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 685ca95ef4a9..29197d34ec09 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3537,25 +3537,25 @@ static int rx_macro_probe(struct platform_device *pdev) rx->macro = devm_clk_get_optional(dev, "macro"); if (IS_ERR(rx->macro)) - return PTR_ERR(rx->macro); + return dev_err_probe(dev, PTR_ERR(rx->macro), "unable to get macro clock\n"); rx->dcodec = devm_clk_get_optional(dev, "dcodec"); if (IS_ERR(rx->dcodec)) - return PTR_ERR(rx->dcodec); + return dev_err_probe(dev, PTR_ERR(rx->dcodec), "unable to get dcodec clock\n"); rx->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(rx->mclk)) - return PTR_ERR(rx->mclk); + return dev_err_probe(dev, PTR_ERR(rx->mclk), "unable to get mclk clock\n"); if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { rx->npl = devm_clk_get(dev, "npl"); if (IS_ERR(rx->npl)) - return PTR_ERR(rx->npl); + return dev_err_probe(dev, PTR_ERR(rx->npl), "unable to get npl clock\n"); } rx->fsgen = devm_clk_get(dev, "fsgen"); if (IS_ERR(rx->fsgen)) - return PTR_ERR(rx->fsgen); + return dev_err_probe(dev, PTR_ERR(rx->fsgen), "unable to get fsgen clock\n"); rx->pds = lpass_macro_pds_init(dev); if (IS_ERR(rx->pds)) diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index de978c3d70b7..3e33418898e8 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -1967,25 +1967,25 @@ static int tx_macro_probe(struct platform_device *pdev) tx->macro = devm_clk_get_optional(dev, "macro"); if (IS_ERR(tx->macro)) - return PTR_ERR(tx->macro); + return dev_err_probe(dev, PTR_ERR(tx->macro), "unable to get macro clock\n"); tx->dcodec = devm_clk_get_optional(dev, "dcodec"); if (IS_ERR(tx->dcodec)) - return PTR_ERR(tx->dcodec); + return dev_err_probe(dev, PTR_ERR(tx->dcodec), "unable to get dcodec clock\n"); tx->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(tx->mclk)) - return PTR_ERR(tx->mclk); + return dev_err_probe(dev, PTR_ERR(tx->mclk), "unable to get mclk clock\n"); if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { tx->npl = devm_clk_get(dev, "npl"); if (IS_ERR(tx->npl)) - return PTR_ERR(tx->npl); + return dev_err_probe(dev, PTR_ERR(tx->npl), "unable to get npl clock\n"); } tx->fsgen = devm_clk_get(dev, "fsgen"); if (IS_ERR(tx->fsgen)) - return PTR_ERR(tx->fsgen); + return dev_err_probe(dev, PTR_ERR(tx->fsgen), "unable to get fsgen clock\n"); tx->pds = lpass_macro_pds_init(dev); if (IS_ERR(tx->pds)) diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 74724448da50..b71ef03c4aef 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -1457,15 +1457,15 @@ static int va_macro_probe(struct platform_device *pdev) va->macro = devm_clk_get_optional(dev, "macro"); if (IS_ERR(va->macro)) - return PTR_ERR(va->macro); + return dev_err_probe(dev, PTR_ERR(va->macro), "unable to get macro clock\n"); va->dcodec = devm_clk_get_optional(dev, "dcodec"); if (IS_ERR(va->dcodec)) - return PTR_ERR(va->dcodec); + return dev_err_probe(dev, PTR_ERR(va->dcodec), "unable to get dcodec clock\n"); va->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(va->mclk)) - return PTR_ERR(va->mclk); + return dev_err_probe(dev, PTR_ERR(va->mclk), "unable to get mclk clock\n"); va->pds = lpass_macro_pds_init(dev); if (IS_ERR(va->pds)) diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 8ba7dc89daaa..ec6859ec0d38 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2396,25 +2396,25 @@ static int wsa_macro_probe(struct platform_device *pdev) wsa->macro = devm_clk_get_optional(dev, "macro"); if (IS_ERR(wsa->macro)) - return PTR_ERR(wsa->macro); + return dev_err_probe(dev, PTR_ERR(wsa->macro), "unable to get macro clock\n"); wsa->dcodec = devm_clk_get_optional(dev, "dcodec"); if (IS_ERR(wsa->dcodec)) - return PTR_ERR(wsa->dcodec); + return dev_err_probe(dev, PTR_ERR(wsa->dcodec), "unable to get dcodec clock\n"); wsa->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(wsa->mclk)) - return PTR_ERR(wsa->mclk); + return dev_err_probe(dev, PTR_ERR(wsa->mclk), "unable to get mclk clock\n"); if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { wsa->npl = devm_clk_get(dev, "npl"); if (IS_ERR(wsa->npl)) - return PTR_ERR(wsa->npl); + return dev_err_probe(dev, PTR_ERR(wsa->npl), "unable to get npl clock\n"); } wsa->fsgen = devm_clk_get(dev, "fsgen"); if (IS_ERR(wsa->fsgen)) - return PTR_ERR(wsa->fsgen); + return dev_err_probe(dev, PTR_ERR(wsa->fsgen), "unable to get fsgen clock\n"); base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index 2dfaf4fcfbd3..a2cca3436c68 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -160,28 +160,17 @@ static int max98363_io_init(struct sdw_slave *slave) struct max98363_priv *max98363 = dev_get_drvdata(dev); int ret, reg; - if (max98363->first_hw_init) { - regcache_cache_only(max98363->regmap, false); + regcache_cache_only(max98363->regmap, false); + if (max98363->first_hw_init) regcache_cache_bypass(max98363->regmap, true); - } /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!max98363->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - + if (!max98363->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(dev); - - pm_runtime_enable(dev); - } - pm_runtime_get_noresume(dev); ret = regmap_read(max98363->regmap, MAX98363_R21FF_REV_ID, ®); @@ -410,6 +399,8 @@ static int max98363_init(struct sdw_slave *slave, struct regmap *regmap) max98363->regmap = regmap; max98363->slave = slave; + regcache_cache_only(max98363->regmap, true); + max98363->hw_init = false; max98363->first_hw_init = false; @@ -417,10 +408,26 @@ static int max98363_init(struct sdw_slave *slave, struct regmap *regmap) ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98363, max98363_dai, ARRAY_SIZE(max98363_dai)); - if (ret < 0) + if (ret < 0) { dev_err(dev, "Failed to register codec: %d\n", ret); + return ret; + } - return ret; + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + return 0; } static int max98363_sdw_probe(struct sdw_slave *slave, diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index df92242af960..b5cb951af570 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -361,28 +361,17 @@ static int max98373_io_init(struct sdw_slave *slave) struct device *dev = &slave->dev; struct max98373_priv *max98373 = dev_get_drvdata(dev); - if (max98373->first_hw_init) { - regcache_cache_only(max98373->regmap, false); + regcache_cache_only(max98373->regmap, false); + if (max98373->first_hw_init) regcache_cache_bypass(max98373->regmap, true); - } /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!max98373->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - + if (!max98373->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(dev); - - pm_runtime_enable(dev); - } - pm_runtime_get_noresume(dev); /* Software Reset */ @@ -753,6 +742,8 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap) max98373->regmap = regmap; max98373->slave = slave; + regcache_cache_only(max98373->regmap, true); + max98373->cache_num = ARRAY_SIZE(max98373_sdw_cache_reg); max98373->cache = devm_kcalloc(dev, max98373->cache_num, sizeof(*max98373->cache), @@ -773,10 +764,27 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap) ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98373_sdw, max98373_sdw_dai, ARRAY_SIZE(max98373_sdw_dai)); - if (ret < 0) + if (ret < 0) { dev_err(dev, "Failed to register codec: %d\n", ret); + return ret; + } - return ret; + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + return 0; } static int max98373_update_status(struct sdw_slave *slave, @@ -834,10 +842,7 @@ static int max98373_sdw_probe(struct sdw_slave *slave, static int max98373_sdw_remove(struct sdw_slave *slave) { - struct max98373_priv *max98373 = dev_get_drvdata(&slave->dev); - - if (max98373->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index bdc508e23e59..922ce0dc4e60 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -528,7 +528,8 @@ static int max98926_i2c_probe(struct i2c_client *i2c) "Failed to allocate regmap: %d\n", ret); goto err_out; } - if (of_property_read_bool(i2c->dev.of_node, "interleave-mode")) + if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode") || + of_property_read_bool(i2c->dev.of_node, "interleave-mode")) max98926->interleave_mode = true; if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 0aaf2e6ae78d..776f23d38ac5 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -162,10 +162,8 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } - regmap_update_bits(max98927->regmap, - MAX98927_R0021_PCM_MASTER_MODE, - MAX98927_PCM_MASTER_MODE_MASK, - mode); + regmap_update_bits(max98927->regmap, MAX98927_R0021_PCM_MASTER_MODE, + MAX98927_PCM_MASTER_MODE_MASK, mode); switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: @@ -178,10 +176,8 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } - regmap_update_bits(max98927->regmap, - MAX98927_R0020_PCM_MODE_CFG, - MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE, - invert); + regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE, invert); /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -207,36 +203,31 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) if (!use_pdm) { /* pcm channel configuration */ - regmap_update_bits(max98927->regmap, - MAX98927_R0018_PCM_RX_EN_A, - MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, - MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN); + regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN); regmap_update_bits(max98927->regmap, - MAX98927_R0020_PCM_MODE_CFG, - MAX98927_PCM_MODE_CFG_FORMAT_MASK, - format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT); + MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT); - regmap_update_bits(max98927->regmap, - MAX98927_R003B_SPK_SRC_SEL, - MAX98927_SPK_SRC_MASK, 0); + regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL, + MAX98927_SPK_SRC_MASK, 0); - regmap_update_bits(max98927->regmap, - MAX98927_R0035_PDM_RX_CTRL, - MAX98927_PDM_RX_EN_MASK, 0); + regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL, + MAX98927_PDM_RX_EN_MASK, 0); } else { /* pdm channel configuration */ - regmap_update_bits(max98927->regmap, - MAX98927_R0035_PDM_RX_CTRL, - MAX98927_PDM_RX_EN_MASK, 1); + regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL, + MAX98927_PDM_RX_EN_MASK, 1); - regmap_update_bits(max98927->regmap, - MAX98927_R003B_SPK_SRC_SEL, - MAX98927_SPK_SRC_MASK, 3); + regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL, + MAX98927_SPK_SRC_MASK, 3); - regmap_update_bits(max98927->regmap, - MAX98927_R0018_PCM_RX_EN_A, - MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0); + regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, + 0); } return 0; } @@ -283,9 +274,9 @@ static int max98927_set_clock(struct max98927_priv *max98927, return -EINVAL; } regmap_update_bits(max98927->regmap, - MAX98927_R0021_PCM_MASTER_MODE, - MAX98927_PCM_MASTER_MODE_MCLK_MASK, - i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT); + MAX98927_R0021_PCM_MASTER_MODE, + MAX98927_PCM_MASTER_MODE_MCLK_MASK, + i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT); } if (!max98927->tdm_mode) { @@ -298,9 +289,8 @@ static int max98927_set_clock(struct max98927_priv *max98927, } regmap_update_bits(max98927->regmap, - MAX98927_R0022_PCM_CLK_SETUP, - MAX98927_PCM_CLK_SETUP_BSEL_MASK, - value); + MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, value); } return 0; } @@ -333,9 +323,8 @@ static int max98927_dai_hw_params(struct snd_pcm_substream *substream, max98927->ch_size = snd_pcm_format_width(params_format(params)); - regmap_update_bits(max98927->regmap, - MAX98927_R0020_PCM_MODE_CFG, - MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); dev_dbg(component->dev, "format supported %d", params_format(params)); @@ -375,27 +364,24 @@ static int max98927_dai_hw_params(struct snd_pcm_substream *substream, goto err; } /* set DAI_SR to correct LRCLK frequency */ - regmap_update_bits(max98927->regmap, - MAX98927_R0023_PCM_SR_SETUP1, - MAX98927_PCM_SR_SET1_SR_MASK, - sampling_rate); - regmap_update_bits(max98927->regmap, - MAX98927_R0024_PCM_SR_SETUP2, - MAX98927_PCM_SR_SET2_SR_MASK, - sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT); + regmap_update_bits(max98927->regmap, MAX98927_R0023_PCM_SR_SETUP1, + MAX98927_PCM_SR_SET1_SR_MASK, sampling_rate); + regmap_update_bits(max98927->regmap, MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_SR_MASK, + sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT); /* set sampling rate of IV */ if (max98927->interleave_mode && sampling_rate > MAX98927_PCM_SR_SET1_SR_16000) regmap_update_bits(max98927->regmap, - MAX98927_R0024_PCM_SR_SETUP2, - MAX98927_PCM_SR_SET2_IVADC_SR_MASK, - sampling_rate - 3); + MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate - 3); else regmap_update_bits(max98927->regmap, - MAX98927_R0024_PCM_SR_SETUP2, - MAX98927_PCM_SR_SET2_IVADC_SR_MASK, - sampling_rate); + MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate); return max98927_set_clock(max98927, params); err: return -EINVAL; @@ -420,10 +406,8 @@ static int max98927_dai_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; } - regmap_update_bits(max98927->regmap, - MAX98927_R0022_PCM_CLK_SETUP, - MAX98927_PCM_CLK_SETUP_BSEL_MASK, - bsel); + regmap_update_bits(max98927->regmap, MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, bsel); /* Channel size configuration */ switch (slot_width) { @@ -442,33 +426,26 @@ static int max98927_dai_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; } - regmap_update_bits(max98927->regmap, - MAX98927_R0020_PCM_MODE_CFG, - MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); /* Rx slot configuration */ - regmap_write(max98927->regmap, - MAX98927_R0018_PCM_RX_EN_A, - rx_mask & 0xFF); - regmap_write(max98927->regmap, - MAX98927_R0019_PCM_RX_EN_B, - (rx_mask & 0xFF00) >> 8); + regmap_write(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, + rx_mask & 0xFF); + regmap_write(max98927->regmap, MAX98927_R0019_PCM_RX_EN_B, + (rx_mask & 0xFF00) >> 8); /* Tx slot configuration */ - regmap_write(max98927->regmap, - MAX98927_R001A_PCM_TX_EN_A, - tx_mask & 0xFF); - regmap_write(max98927->regmap, - MAX98927_R001B_PCM_TX_EN_B, - (tx_mask & 0xFF00) >> 8); + regmap_write(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A, + tx_mask & 0xFF); + regmap_write(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B, + (tx_mask & 0xFF00) >> 8); /* Tx slot Hi-Z configuration */ - regmap_write(max98927->regmap, - MAX98927_R001C_PCM_TX_HIZ_CTRL_A, - ~tx_mask & 0xFF); - regmap_write(max98927->regmap, - MAX98927_R001D_PCM_TX_HIZ_CTRL_B, - (~tx_mask & 0xFF00) >> 8); + regmap_write(max98927->regmap, MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + ~tx_mask & 0xFF); + regmap_write(max98927->regmap, MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + (~tx_mask & 0xFF00) >> 8); return 0; } @@ -506,20 +483,16 @@ static int max98927_dac_event(struct snd_soc_dapm_widget *w, max98927->tdm_mode = false; break; case SND_SOC_DAPM_POST_PMU: - regmap_update_bits(max98927->regmap, - MAX98927_R003A_AMP_EN, - MAX98927_AMP_EN_MASK, 1); - regmap_update_bits(max98927->regmap, - MAX98927_R00FF_GLOBAL_SHDN, - MAX98927_GLOBAL_EN_MASK, 1); + regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN, + MAX98927_AMP_EN_MASK, 1); + regmap_update_bits(max98927->regmap, MAX98927_R00FF_GLOBAL_SHDN, + MAX98927_GLOBAL_EN_MASK, 1); break; case SND_SOC_DAPM_POST_PMD: - regmap_update_bits(max98927->regmap, - MAX98927_R00FF_GLOBAL_SHDN, - MAX98927_GLOBAL_EN_MASK, 0); - regmap_update_bits(max98927->regmap, - MAX98927_R003A_AMP_EN, - MAX98927_AMP_EN_MASK, 0); + regmap_update_bits(max98927->regmap, MAX98927_R00FF_GLOBAL_SHDN, + MAX98927_GLOBAL_EN_MASK, 0); + regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN, + MAX98927_AMP_EN_MASK, 0); break; default: return 0; @@ -532,8 +505,8 @@ static const char * const max98927_switch_text[] = { static const struct soc_enum dai_sel_enum = SOC_ENUM_SINGLE(MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, - MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT, - 3, max98927_switch_text); + MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT, 3, + max98927_switch_text); static const struct snd_kcontrol_new max98927_dai_controls = SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); @@ -543,17 +516,17 @@ static const struct snd_kcontrol_new max98927_vi_control = static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = { SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_R003A_AMP_EN, - 0, 0, max98927_dac_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + 0, 0, max98927_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, - &max98927_dai_controls), + &max98927_dai_controls), SND_SOC_DAPM_OUTPUT("BE_OUT"), SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, - MAX98927_R003E_MEAS_EN, 0, 0), + MAX98927_R003E_MEAS_EN, 0, 0), SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, - MAX98927_R003E_MEAS_EN, 1, 0), + MAX98927_R003E_MEAS_EN, 1, 0), SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, - &max98927_vi_control), + &max98927_vi_control), SND_SOC_DAPM_SIGGEN("VMON"), SND_SOC_DAPM_SIGGEN("IMON"), }; @@ -623,20 +596,19 @@ static SOC_ENUM_SINGLE_DECL(max98927_current_limit, max98927_current_limit_text); static const struct snd_kcontrol_new max98927_snd_controls[] = { - SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN, - 0, 6, 0, - max98927_spk_tlv), + SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN, 0, 6, 0, + max98927_spk_tlv), SOC_SINGLE_TLV("Digital Volume", MAX98927_R0036_AMP_VOL_CTRL, - 0, (1<<MAX98927_AMP_VOL_WIDTH)-1, 0, - max98927_digital_tlv), + 0, (1 << MAX98927_AMP_VOL_WIDTH) - 1, 0, + max98927_digital_tlv), SOC_SINGLE("Amp DSP Switch", MAX98927_R0052_BROWNOUT_EN, - MAX98927_BROWNOUT_DSP_SHIFT, 1, 0), + MAX98927_BROWNOUT_DSP_SHIFT, 1, 0), SOC_SINGLE("Ramp Switch", MAX98927_R0037_AMP_DSP_CFG, - MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0), - SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL, - MAX98927_DRE_EN_SHIFT, 1, 0), + MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0), + SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL, MAX98927_DRE_EN_SHIFT, + 1, 0), SOC_SINGLE("Volume Location Switch", MAX98927_R0036_AMP_VOL_CTRL, - MAX98927_AMP_VOL_SEL_SHIFT, 1, 0), + MAX98927_AMP_VOL_SEL_SHIFT, 1, 0), SOC_ENUM("Boost Output Voltage", max98927_boost_voltage), SOC_ENUM("Current Limit", max98927_current_limit), }; @@ -682,117 +654,82 @@ static int max98927_probe(struct snd_soc_component *component) max98927->component = component; /* Software Reset */ - regmap_write(max98927->regmap, - MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET); + regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, + MAX98927_SOFT_RESET); /* IV default slot configuration */ - regmap_write(max98927->regmap, - MAX98927_R001C_PCM_TX_HIZ_CTRL_A, - 0xFF); - regmap_write(max98927->regmap, - MAX98927_R001D_PCM_TX_HIZ_CTRL_B, - 0xFF); - regmap_write(max98927->regmap, - MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, - 0x80); - regmap_write(max98927->regmap, - MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, - 0x1); + regmap_write(max98927->regmap, MAX98927_R001C_PCM_TX_HIZ_CTRL_A, 0xFF); + regmap_write(max98927->regmap, MAX98927_R001D_PCM_TX_HIZ_CTRL_B, 0xFF); + regmap_write(max98927->regmap, MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, + 0x80); + regmap_write(max98927->regmap, MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, + 0x1); /* Set inital volume (+13dB) */ - regmap_write(max98927->regmap, - MAX98927_R0036_AMP_VOL_CTRL, - 0x38); - regmap_write(max98927->regmap, - MAX98927_R003C_SPK_GAIN, - 0x05); + regmap_write(max98927->regmap, MAX98927_R0036_AMP_VOL_CTRL, 0x38); + regmap_write(max98927->regmap, MAX98927_R003C_SPK_GAIN, 0x05); /* Enable DC blocker */ - regmap_write(max98927->regmap, - MAX98927_R0037_AMP_DSP_CFG, - 0x03); + regmap_write(max98927->regmap, MAX98927_R0037_AMP_DSP_CFG, 0x03); /* Enable IMON VMON DC blocker */ - regmap_write(max98927->regmap, - MAX98927_R003F_MEAS_DSP_CFG, - 0xF7); + regmap_write(max98927->regmap, MAX98927_R003F_MEAS_DSP_CFG, 0xF7); /* Boost Output Voltage & Current limit */ - regmap_write(max98927->regmap, - MAX98927_R0040_BOOST_CTRL0, - 0x1C); - regmap_write(max98927->regmap, - MAX98927_R0042_BOOST_CTRL1, - 0x3E); + regmap_write(max98927->regmap, MAX98927_R0040_BOOST_CTRL0, 0x1C); + regmap_write(max98927->regmap, MAX98927_R0042_BOOST_CTRL1, 0x3E); /* Measurement ADC config */ - regmap_write(max98927->regmap, - MAX98927_R0043_MEAS_ADC_CFG, - 0x04); - regmap_write(max98927->regmap, - MAX98927_R0044_MEAS_ADC_BASE_MSB, - 0x00); - regmap_write(max98927->regmap, - MAX98927_R0045_MEAS_ADC_BASE_LSB, - 0x24); + regmap_write(max98927->regmap, MAX98927_R0043_MEAS_ADC_CFG, 0x04); + regmap_write(max98927->regmap, MAX98927_R0044_MEAS_ADC_BASE_MSB, 0x00); + regmap_write(max98927->regmap, MAX98927_R0045_MEAS_ADC_BASE_LSB, 0x24); /* Brownout Level */ - regmap_write(max98927->regmap, - MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, - 0x06); + regmap_write(max98927->regmap, MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, + 0x06); /* Envelope Tracking configuration */ - regmap_write(max98927->regmap, - MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, - 0x08); - regmap_write(max98927->regmap, - MAX98927_R0086_ENV_TRACK_CTRL, - 0x01); - regmap_write(max98927->regmap, - MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, - 0x10); + regmap_write(max98927->regmap, MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, + 0x08); + regmap_write(max98927->regmap, MAX98927_R0086_ENV_TRACK_CTRL, 0x01); + regmap_write(max98927->regmap, MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, + 0x10); /* voltage, current slot configuration */ - regmap_write(max98927->regmap, - MAX98927_R001E_PCM_TX_CH_SRC_A, - (max98927->i_l_slot<<MAX98927_PCM_TX_CH_SRC_A_I_SHIFT| - max98927->v_l_slot)&0xFF); + regmap_write(max98927->regmap, MAX98927_R001E_PCM_TX_CH_SRC_A, + (max98927->i_l_slot << MAX98927_PCM_TX_CH_SRC_A_I_SHIFT | max98927->v_l_slot) & 0xFF); if (max98927->v_l_slot < 8) { regmap_update_bits(max98927->regmap, - MAX98927_R001C_PCM_TX_HIZ_CTRL_A, - 1 << max98927->v_l_slot, 0); - regmap_update_bits(max98927->regmap, - MAX98927_R001A_PCM_TX_EN_A, - 1 << max98927->v_l_slot, - 1 << max98927->v_l_slot); + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + 1 << max98927->v_l_slot, 0); + regmap_update_bits(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A, + 1 << max98927->v_l_slot, + 1 << max98927->v_l_slot); } else { regmap_update_bits(max98927->regmap, - MAX98927_R001D_PCM_TX_HIZ_CTRL_B, - 1 << (max98927->v_l_slot - 8), 0); - regmap_update_bits(max98927->regmap, - MAX98927_R001B_PCM_TX_EN_B, - 1 << (max98927->v_l_slot - 8), - 1 << (max98927->v_l_slot - 8)); + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + 1 << (max98927->v_l_slot - 8), 0); + regmap_update_bits(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B, + 1 << (max98927->v_l_slot - 8), + 1 << (max98927->v_l_slot - 8)); } if (max98927->i_l_slot < 8) { regmap_update_bits(max98927->regmap, - MAX98927_R001C_PCM_TX_HIZ_CTRL_A, - 1 << max98927->i_l_slot, 0); - regmap_update_bits(max98927->regmap, - MAX98927_R001A_PCM_TX_EN_A, - 1 << max98927->i_l_slot, - 1 << max98927->i_l_slot); + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + 1 << max98927->i_l_slot, 0); + regmap_update_bits(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A, + 1 << max98927->i_l_slot, + 1 << max98927->i_l_slot); } else { regmap_update_bits(max98927->regmap, - MAX98927_R001D_PCM_TX_HIZ_CTRL_B, - 1 << (max98927->i_l_slot - 8), 0); - regmap_update_bits(max98927->regmap, - MAX98927_R001B_PCM_TX_EN_B, - 1 << (max98927->i_l_slot - 8), - 1 << (max98927->i_l_slot - 8)); + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + 1 << (max98927->i_l_slot - 8), 0); + regmap_update_bits(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B, + 1 << (max98927->i_l_slot - 8), + 1 << (max98927->i_l_slot - 8)); } /* Set interleave mode */ if (max98927->interleave_mode) regmap_update_bits(max98927->regmap, - MAX98927_R001F_PCM_TX_CH_SRC_B, - MAX98927_PCM_TX_CH_INTERLEAVE_MASK, - MAX98927_PCM_TX_CH_INTERLEAVE_MASK); + MAX98927_R001F_PCM_TX_CH_SRC_B, + MAX98927_PCM_TX_CH_INTERLEAVE_MASK, + MAX98927_PCM_TX_CH_INTERLEAVE_MASK); return 0; } @@ -809,8 +746,8 @@ static int max98927_resume(struct device *dev) { struct max98927_priv *max98927 = dev_get_drvdata(dev); - regmap_write(max98927->regmap, - MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET); + regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, + MAX98927_SOFT_RESET); regcache_cache_only(max98927->regmap, false); regcache_sync(max98927->regmap); return 0; @@ -869,9 +806,7 @@ static int max98927_i2c_probe(struct i2c_client *i2c) int reg = 0; struct max98927_priv *max98927 = NULL; - max98927 = devm_kzalloc(&i2c->dev, - sizeof(*max98927), GFP_KERNEL); - + max98927 = devm_kzalloc(&i2c->dev, sizeof(*max98927), GFP_KERNEL); if (!max98927) { ret = -ENOMEM; return ret; @@ -879,14 +814,14 @@ static int max98927_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, max98927); /* update interleave mode info */ - if (!of_property_read_u32(i2c->dev.of_node, - "interleave_mode", &value)) { - if (value > 0) - max98927->interleave_mode = true; - else - max98927->interleave_mode = false; - } else - max98927->interleave_mode = false; + if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode")) { + max98927->interleave_mode = true; + } else { + if (!of_property_read_u32(i2c->dev.of_node, "interleave_mode", + &value)) + if (value > 0) + max98927->interleave_mode = true; + } /* regmap initialization */ max98927->regmap @@ -897,9 +832,9 @@ static int max98927_i2c_probe(struct i2c_client *i2c) "Failed to allocate regmap: %d\n", ret); return ret; } - - max98927->reset_gpio - = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH); + + max98927->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", + GPIOD_OUT_HIGH); if (IS_ERR(max98927->reset_gpio)) { ret = PTR_ERR(max98927->reset_gpio); return dev_err_probe(&i2c->dev, ret, "failed to request GPIO reset pin"); @@ -912,8 +847,7 @@ static int max98927_i2c_probe(struct i2c_client *i2c) } /* Check Revision ID */ - ret = regmap_read(max98927->regmap, - MAX98927_R01FF_REV_ID, ®); + ret = regmap_read(max98927->regmap, MAX98927_R01FF_REV_ID, ®); if (ret < 0) { dev_err(&i2c->dev, "Failed to read: 0x%02X\n", MAX98927_R01FF_REV_ID); @@ -938,9 +872,8 @@ static void max98927_i2c_remove(struct i2c_client *i2c) { struct max98927_priv *max98927 = i2c_get_clientdata(i2c); - if (max98927->reset_gpio) { + if (max98927->reset_gpio) gpiod_set_value_cansleep(max98927->reset_gpio, 1); - } } static const struct i2c_device_id max98927_i2c_id[] = { diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index cec90cf920ff..9ca381812975 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -7,7 +7,6 @@ #include <linux/delay.h> #include <linux/regulator/consumer.h> #include <linux/types.h> -#include <linux/clk.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regmap.h> @@ -1198,12 +1197,6 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) if (ret < 0) return ret; - priv->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(priv->mclk)) { - dev_err(dev, "failed to get mclk\n"); - return PTR_ERR(priv->mclk); - } - for (i = 0; i < ARRAY_SIZE(supply_names); i++) priv->supplies[i].supply = supply_names[i]; @@ -1214,55 +1207,48 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) return ret; } - ret = clk_prepare_enable(priv->mclk); - if (ret < 0) { - dev_err(dev, "failed to enable mclk %d\n", ret); - return ret; - } - irq = platform_get_irq_byname(pdev, "mbhc_switch_int"); - if (irq < 0) { - ret = irq; - goto err_disable_clk; - } + if (irq < 0) + return irq; ret = devm_request_threaded_irq(dev, irq, NULL, pm8916_mbhc_switch_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mbhc switch irq", priv); - if (ret) + if (ret) { dev_err(dev, "cannot request mbhc switch irq\n"); + return ret; + } if (priv->mbhc_btn_enabled) { irq = platform_get_irq_byname(pdev, "mbhc_but_press_det"); - if (irq < 0) { - ret = irq; - goto err_disable_clk; - } + if (irq < 0) + return irq; ret = devm_request_threaded_irq(dev, irq, NULL, mbhc_btn_press_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mbhc btn press irq", priv); - if (ret) + if (ret) { dev_err(dev, "cannot request mbhc button press irq\n"); + return ret; + } irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det"); - if (irq < 0) { - ret = irq; - goto err_disable_clk; - } + if (irq < 0) + return irq; ret = devm_request_threaded_irq(dev, irq, NULL, mbhc_btn_release_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mbhc btn release irq", priv); - if (ret) + if (ret) { dev_err(dev, "cannot request mbhc button release irq\n"); - + return ret; + } } dev_set_drvdata(dev, priv); @@ -1270,17 +1256,6 @@ static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) return devm_snd_soc_register_component(dev, &pm8916_wcd_analog, pm8916_wcd_analog_dai, ARRAY_SIZE(pm8916_wcd_analog_dai)); - -err_disable_clk: - clk_disable_unprepare(priv->mclk); - return ret; -} - -static void pm8916_wcd_analog_spmi_remove(struct platform_device *pdev) -{ - struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(&pdev->dev); - - clk_disable_unprepare(priv->mclk); } static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { @@ -1296,7 +1271,6 @@ static struct platform_driver pm8916_wcd_analog_spmi_driver = { .of_match_table = pm8916_wcd_analog_spmi_match_table, }, .probe = pm8916_wcd_analog_spmi_probe, - .remove_new = pm8916_wcd_analog_spmi_remove, }; module_platform_driver(pm8916_wcd_analog_spmi_driver); diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index ca6beb2d2649..f307374834b5 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -624,6 +624,36 @@ static int system_clock_control(struct snd_soc_dapm_widget *w, return 0; } +static int nau8821_left_fepga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + if (!nau8821->left_input_single_end) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA, + NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK, + NAU8821_ACDC_VREF_MICN | NAU8821_FEPGA_MODEL_AAF); + regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST, + NAU8821_HP_BOOST_DISCHRG_EN, NAU8821_HP_BOOST_DISCHRG_EN); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA, + NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK, 0); + regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST, + NAU8821_HP_BOOST_DISCHRG_EN, 0); + break; + default: + break; + } + + return 0; +} + static const struct snd_soc_dapm_widget nau8821_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0, system_clock_control, SND_SOC_DAPM_POST_PMD), @@ -635,8 +665,10 @@ static const struct snd_soc_dapm_widget nau8821_dapm_widgets[] = { NAU8821_POWERUP_ADCL_SFT, 0), SND_SOC_DAPM_ADC("ADCR Power", NULL, NAU8821_R72_ANALOG_ADC_2, NAU8821_POWERUP_ADCR_SFT, 0), + /* single-ended design only on the left */ SND_SOC_DAPM_PGA_S("Frontend PGA L", 1, NAU8821_R7F_POWER_UP_CONTROL, - NAU8821_PUP_PGA_L_SFT, 0, NULL, 0), + NAU8821_PUP_PGA_L_SFT, 0, nau8821_left_fepga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_S("Frontend PGA R", 1, NAU8821_R7F_POWER_UP_CONTROL, NAU8821_PUP_PGA_R_SFT, 0, NULL, 0), SND_SOC_DAPM_PGA_S("ADCL Digital path", 0, NAU8821_R01_ENA_CTRL, @@ -1677,6 +1709,8 @@ static int nau8821_read_device_properties(struct device *dev, "nuvoton,jkdet-pull-up"); nau8821->key_enable = device_property_read_bool(dev, "nuvoton,key-enable"); + nau8821->left_input_single_end = device_property_read_bool(dev, + "nuvoton,left-input-single-end"); ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity", &nau8821->jkdet_polarity); if (ret) @@ -1760,6 +1794,12 @@ static void nau8821_init_regs(struct nau8821 *nau8821) NAU8821_ADC_SYNC_DOWN_MASK, NAU8821_ADC_SYNC_DOWN_64); regmap_update_bits(regmap, NAU8821_R2C_DAC_CTRL1, NAU8821_DAC_OVERSAMPLE_MASK, NAU8821_DAC_OVERSAMPLE_64); + if (nau8821->left_input_single_end) { + regmap_update_bits(regmap, NAU8821_R6B_PGA_MUTE, + NAU8821_MUTE_MICNL_EN, NAU8821_MUTE_MICNL_EN); + regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS, + NAU8821_MICBIAS_LOWNOISE_EN, NAU8821_MICBIAS_LOWNOISE_EN); + } } static int nau8821_setup_irq(struct nau8821 *nau8821) diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h index d962293c218e..00a888ed07ce 100644 --- a/sound/soc/codecs/nau8821.h +++ b/sound/soc/codecs/nau8821.h @@ -433,6 +433,14 @@ #define NAU8821_DAC_CAPACITOR_MSB (0x1 << 1) #define NAU8821_DAC_CAPACITOR_LSB 0x1 +/* MUTE_MIC_L_N (0x6b) */ +#define NAU8821_MUTE_MICNL_SFT 5 +#define NAU8821_MUTE_MICNL_EN (0x1 << NAU8821_MUTE_MICNL_SFT) +#define NAU8821_MUTE_MICNR_SFT 4 +#define NAU8821_MUTE_MICNR_EN (0x1 << NAU8821_MUTE_MICNR_SFT) +#define NAU8821_MUTE_MICRP_SFT 2 +#define NAU8821_MUTE_MICRP_EN (0x1 << NAU8821_MUTE_MICRP_SFT) + /* ANALOG_ADC_1 (0x71) */ #define NAU8821_MICDET_EN_SFT 0 #define NAU8821_MICDET_MASK 0x1 @@ -463,23 +471,39 @@ /* MIC_BIAS (0x74) */ #define NAU8821_MICBIAS_JKR2 (0x1 << 12) +#define NAU8821_MICBIAS_LOWNOISE_SFT 10 +#define NAU8821_MICBIAS_LOWNOISE_EN (0x1 << NAU8821_MICBIAS_LOWNOISE_SFT) #define NAU8821_MICBIAS_POWERUP_SFT 8 +#define NAU8821_MICBIAS_POWERUP_EN (0x1 << NAU8821_MICBIAS_POWERUP_SFT) #define NAU8821_MICBIAS_VOLTAGE_SFT 0 #define NAU8821_MICBIAS_VOLTAGE_MASK 0x7 /* BOOST (0x76) */ #define NAU8821_PRECHARGE_DIS (0x1 << 13) #define NAU8821_GLOBAL_BIAS_EN (0x1 << 12) +#define NAU8821_HP_BOOST_DISCHRG_SFT 11 +#define NAU8821_HP_BOOST_DISCHRG_EN (0x1 << NAU8821_HP_BOOST_DISCHRG_SFT) #define NAU8821_HP_BOOST_DIS_SFT 9 #define NAU8821_HP_BOOST_DIS (0x1 << NAU8821_HP_BOOST_DIS_SFT) #define NAU8821_HP_BOOST_G_DIS (0x1 << 8) #define NAU8821_SHORT_SHUTDOWN_EN (0x1 << 6) /* FEPGA (0x77) */ +#define NAU8821_ACDC_CTRL_SFT 14 +#define NAU8821_ACDC_CTRL_MASK (0x3 << NAU8821_ACDC_CTRL_SFT) +#define NAU8821_ACDC_VREF_MICP (0x1 << NAU8821_ACDC_CTRL_SFT) +#define NAU8821_ACDC_VREF_MICN (0x2 << NAU8821_ACDC_CTRL_SFT) #define NAU8821_FEPGA_MODEL_SFT 4 #define NAU8821_FEPGA_MODEL_MASK (0xf << NAU8821_FEPGA_MODEL_SFT) +#define NAU8821_FEPGA_MODEL_AAF (0x1 << NAU8821_FEPGA_MODEL_SFT) +#define NAU8821_FEPGA_MODEL_DIS (0x2 << NAU8821_FEPGA_MODEL_SFT) +#define NAU8821_FEPGA_MODEL_IMP12K (0x8 << NAU8821_FEPGA_MODEL_SFT) #define NAU8821_FEPGA_MODER_SFT 0 #define NAU8821_FEPGA_MODER_MASK 0xf +#define NAU8821_FEPGA_MODER_AAF 0x1 +#define NAU8821_FEPGA_MODER_DIS 0x2 +#define NAU8821_FEPGA_MODER_IMP12K 0x8 + /* PGA_GAIN (0x7e) */ #define NAU8821_PGA_GAIN_L_SFT 8 @@ -543,6 +567,7 @@ struct nau8821 { bool jkdet_enable; bool jkdet_pull_enable; bool jkdet_pull_up; + bool left_input_single_end; int jkdet_polarity; int jack_insert_debounce; int jack_eject_debounce; diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 9e0e4ddf128e..5cb0de648bd3 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/init.h> +#include <linux/int_log.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -38,7 +39,6 @@ #define NAU_FVCO_MIN 90000000 /* cross talk suppression detection */ -#define LOG10_MAGIC 646456993 #define GAIN_AUGMENT 22500 #define SIDETONE_BASE 207000 @@ -219,42 +219,6 @@ static const struct reg_sequence nau8825_regmap_patch[] = { { NAU8825_REG_MIC_BIAS, 0x0046 }, }; - -static const unsigned short logtable[256] = { - 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7, - 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508, - 0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6, - 0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37, - 0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f, - 0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41, - 0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1, - 0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142, - 0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68, - 0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355, - 0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c, - 0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490, - 0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3, - 0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507, - 0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe, - 0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca, - 0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c, - 0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7, - 0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c, - 0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c, - 0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a, - 0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065, - 0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730, - 0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc, - 0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469, - 0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9, - 0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c, - 0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765, - 0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83, - 0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387, - 0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973, - 0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47 -}; - /** * nau8825_sema_acquire - acquire the semaphore of nau88l25 * @nau8825: component to register the codec private data with @@ -368,65 +332,14 @@ static void nau8825_hpvol_ramp(struct nau8825 *nau8825, } /** - * nau8825_intlog10_dec3 - Computes log10 of a value - * the result is round off to 3 decimal. This function takes reference to - * dvb-math. The source code locates as the following. - * Linux/drivers/media/dvb-core/dvb_math.c + * nau8825_intlog10_dec3 - Computes log10 of a value, rounding the result to 3 decimal places. * @value: input for log10 * * return log10(value) * 1000 */ static u32 nau8825_intlog10_dec3(u32 value) { - u32 msb, logentry, significand, interpolation, log10val; - u64 log2val; - - /* first detect the msb (count begins at 0) */ - msb = fls(value) - 1; - /** - * now we use a logtable after the following method: - * - * log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24 - * where x = msb and therefore 1 <= y < 2 - * first y is determined by shifting the value left - * so that msb is bit 31 - * 0x00231f56 -> 0x8C7D5800 - * the result is y * 2^31 -> "significand" - * then the highest 9 bits are used for a table lookup - * the highest bit is discarded because it's always set - * the highest nine bits in our example are 100011000 - * so we would use the entry 0x18 - */ - significand = value << (31 - msb); - logentry = (significand >> 23) & 0xff; - /** - * last step we do is interpolation because of the - * limitations of the log table the error is that part of - * the significand which isn't used for lookup then we - * compute the ratio between the error and the next table entry - * and interpolate it between the log table entry used and the - * next one the biggest error possible is 0x7fffff - * (in our example it's 0x7D5800) - * needed value for next table entry is 0x800000 - * so the interpolation is - * (error / 0x800000) * (logtable_next - logtable_current) - * in the implementation the division is moved to the end for - * better accuracy there is also an overflow correction if - * logtable_next is 256 - */ - interpolation = ((significand & 0x7fffff) * - ((logtable[(logentry + 1) & 0xff] - - logtable[logentry]) & 0xffff)) >> 15; - - log2val = ((msb << 24) + (logtable[logentry] << 8) + interpolation); - /** - * log10(x) = log2(x) * log10(2) - */ - log10val = (log2val * LOG10_MAGIC) >> 31; - /** - * the result is round off to 3 decimal - */ - return log10val / ((1 << 24) / 1000); + return intlog10(value) / ((1 << 24) / 1000); } /** diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index 42bac8150f62..d5285baad53a 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -13,11 +13,9 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/acpi.h> #include <linux/regmap.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/firmware.h> #include <sound/core.h> diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 38d9f69b08d6..99ec0f9a8362 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -12,7 +12,6 @@ #include <linux/delay.h> #include <linux/firmware.h> #include <linux/fs.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/module.h> diff --git a/sound/soc/codecs/rt1015p.c b/sound/soc/codecs/rt1015p.c index 06800dad8798..44e7fe3c32da 100644 --- a/sound/soc/codecs/rt1015p.c +++ b/sound/soc/codecs/rt1015p.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c index b1e69fa290b2..919a1f25e584 100644 --- a/sound/soc/codecs/rt1016.c +++ b/sound/soc/codecs/rt1016.c @@ -16,7 +16,6 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/firmware.h> -#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c new file mode 100644 index 000000000000..7295f44c77eb --- /dev/null +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -0,0 +1,824 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1017-sdca-sdw.c -- rt1017 SDCA ALSA SoC amplifier audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/pm_runtime.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "rt1017-sdca-sdw.h" + +static bool rt1017_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f55: + case 0x3206: + case 0xc000: + case 0xc001: + case 0xc022: + case 0xc030: + case 0xc104: + case 0xc10b: + case 0xc10c: + case 0xc110: + case 0xc112: + case 0xc300: + case 0xc301: + case 0xc318: + case 0xc325 ... 0xc328: + case 0xc331: + case 0xc340: + case 0xc350 ... 0xc351: + case 0xc500: + case 0xc502: + case 0xc504: + case 0xc507: + case 0xc509: + case 0xc510: + case 0xc512: + case 0xc518: + case 0xc51b: + case 0xc51d: + case 0xc520: + case 0xc540 ... 0xc542: + case 0xc550 ... 0xc552: + case 0xc600: + case 0xc602: + case 0xc612: + case 0xc622: + case 0xc632: + case 0xc642: + case 0xc651: + case 0xca00: + case 0xca09 ... 0xca0c: + case 0xca0e ... 0xca0f: + case 0xca10 ... 0xca11: + case 0xca16 ... 0xca17: + case 0xcb00: + case 0xcc00: + case 0xcc02: + case 0xd017: + case 0xd01a ... 0xd01c: + case 0xd101: + case 0xd20c: + case 0xd300: + case 0xd370: + case 0xd500: + case 0xd545 ... 0xd548: + case 0xd5a5 ... 0xd5a8: + case 0xd5aa ... 0xd5ad: + case 0xda04 ... 0xda07: + case 0xda09 ... 0xda0a: + case 0xda0c ... 0xda0f: + case 0xda11 ... 0xda14: + case 0xda16 ... 0xda19: + case 0xdab6 ... 0xdabb: + case 0xdb09 ... 0xdb0a: + case 0xdb14: + + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, + RT1017_SDCA_CTL_FU_MUTE, 0x01): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22, + RT1017_SDCA_CTL_BYPASS, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29, + RT1017_SDCA_CTL_PROT_STAT, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0): + return true; + default: + return false; + } +} + +static bool rt1017_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f55: + case 0xc000: + case 0xc022: + case 0xc351: + case 0xc518: + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29, + RT1017_SDCA_CTL_PROT_STAT, 0): + return true; + default: + return false; + } +} + +static const struct reg_sequence rt1017_blind_write[] = { + { 0xc001, 0x43 }, + { 0x2f55, 0x02 }, + { 0x3206, 0x80 }, + { 0x005f, 0x7f }, + { 0xd101, 0xa0 }, + { 0xc112, 0xc0 }, + { 0xc104, 0xaa }, + { 0xc110, 0x59 }, + { 0xc112, 0xc0 }, + { 0xc340, 0x80 }, + { 0xd017, 0x2c }, + { 0xd01a, 0xc8 }, + { 0xd01b, 0xcf }, + { 0xd01c, 0x0c }, + { 0xd20c, 0x14 }, + { 0xdb09, 0x0f }, + { 0xdb0a, 0x7f }, + { 0xdb14, 0x03 }, + { 0xcb00, 0x31 }, + { 0xc318, 0x44 }, + { 0xc325, 0xce }, + { 0xc326, 0x13 }, + { 0xc327, 0x5f }, + { 0xc328, 0xf3 }, + { 0xc350, 0xe1 }, + { 0xc351, 0x88 }, + { 0xc030, 0x14 }, + { 0xc331, 0xf2 }, + { 0xc551, 0x0f }, + { 0xc552, 0xff }, + { 0xc651, 0xc0 }, + { 0xc550, 0xd0 }, + { 0xc612, 0x00 }, + { 0xc622, 0x00 }, + { 0xc632, 0x00 }, + { 0xc642, 0x00 }, + { 0xc602, 0xf0 }, + { 0xc600, 0xd0 }, + { 0xcc02, 0x78 }, + { 0xcc00, 0x90 }, + { 0xc300, 0x3f }, + { 0xc301, 0x1d }, + { 0xc10b, 0x2e }, + { 0xc10c, 0x36 }, + + { 0xd5a5, 0x00 }, + { 0xd5a6, 0x6a }, + { 0xd5a7, 0xaa }, + { 0xd5a8, 0xaa }, + { 0xd5aa, 0x00 }, + { 0xd5ab, 0x16 }, + { 0xd5ac, 0xdb }, + { 0xd5ad, 0x6d }, + { 0xd545, 0x09 }, + { 0xd546, 0x30 }, + { 0xd547, 0xf0 }, + { 0xd548, 0xf0 }, + { 0xd500, 0x20 }, + { 0xc504, 0x3f }, + { 0xc540, 0x00 }, + { 0xc541, 0x0a }, + { 0xc542, 0x1a }, + { 0xc512, 0x00 }, + { 0xc520, 0x40 }, + { 0xc51b, 0x7f }, + { 0xc51d, 0x0f }, + { 0xc500, 0x40 }, + { 0xc502, 0xde }, + { 0xc507, 0x05 }, + { 0xc509, 0x05 }, + { 0xc510, 0x40 }, + { 0xc518, 0xc0 }, + { 0xc500, 0xc0 }, + + { 0xda0c, 0x00 }, + { 0xda0d, 0x0b }, + { 0xda0e, 0x55 }, + { 0xda0f, 0x55 }, + { 0xda04, 0x00 }, + { 0xda05, 0x51 }, + { 0xda06, 0xeb }, + { 0xda07, 0x85 }, + { 0xca16, 0x0f }, + { 0xca17, 0x00 }, + { 0xda09, 0x5d }, + { 0xda0a, 0xc0 }, + { 0xda11, 0x26 }, + { 0xda12, 0x66 }, + { 0xda13, 0x66 }, + { 0xda14, 0x66 }, + { 0xda16, 0x79 }, + { 0xda17, 0x99 }, + { 0xda18, 0x99 }, + { 0xda19, 0x99 }, + { 0xca09, 0x00 }, + { 0xca0a, 0x07 }, + { 0xca0b, 0x89 }, + { 0xca0c, 0x61 }, + { 0xca0e, 0x00 }, + { 0xca0f, 0x03 }, + { 0xca10, 0xc4 }, + { 0xca11, 0xb0 }, + { 0xdab6, 0x00 }, + { 0xdab7, 0x01 }, + { 0xdab8, 0x00 }, + { 0xdab9, 0x00 }, + { 0xdaba, 0x00 }, + { 0xdabb, 0x00 }, + { 0xd017, 0x0e }, + { 0xca00, 0xcd }, + { 0xc022, 0x84 }, +}; + +#define RT1017_MAX_REG_NUM 0x4108ffff + +static const struct regmap_config rt1017_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1017_sdca_readable_register, + .volatile_reg = rt1017_sdca_volatile_register, + .max_register = RT1017_MAX_REG_NUM, + .reg_defaults = rt1017_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1017_sdca_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt1017_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists + * port = 1 for AMP playback + * port = 2 for IV capture + */ + prop->source_ports = BIT(2); /* BITMAP: 00000100 */ + prop->sink_ports = BIT(1); /* BITMAP: 00000010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 64; + + return 0; +} + +static int rt1017_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); + + if (rt1017->hw_init) + return 0; + + if (rt1017->first_hw_init) { + regcache_cache_only(rt1017->regmap, false); + regcache_cache_bypass(rt1017->regmap, true); + } else { + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + /* sw reset */ + regmap_write(rt1017->regmap, 0xc000, 0x02); + + /* initial settings - blind write */ + regmap_multi_reg_write(rt1017->regmap, rt1017_blind_write, + ARRAY_SIZE(rt1017_blind_write)); + + if (rt1017->first_hw_init) { + regcache_cache_bypass(rt1017->regmap, false); + regcache_mark_dirty(rt1017->regmap); + } else + rt1017->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt1017->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "hw_init complete\n"); + return 0; +} + +static int rt1017_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt1017->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt1017->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt1017_sdca_io_init(&slave->dev, slave); +} + +static const char * const rt1017_rx_data_ch_select[] = { + "Bypass", + "CN1", + "CN2", + "CN3", + "CN4", + "(1+2)/2", + "(1+3)/2", + "(1+4)/2", + "(2+3)/2", + "(2+4)/2", + "(3+4)/2", +}; + +static SOC_ENUM_SINGLE_DECL(rt1017_rx_data_ch_enum, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), + 0, rt1017_rx_data_ch_select); + +static const struct snd_kcontrol_new rt1017_sdca_controls[] = { + /* UDMPU Cluster Selection */ + SOC_ENUM("RX Channel Select", rt1017_rx_data_ch_enum), +}; + +static const struct snd_kcontrol_new rt1017_sto_dac = + SOC_DAPM_SINGLE("Switch", + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, RT1017_SDCA_CTL_FU_MUTE, 0x1), + 0, 1, 1); + +static int rt1017_sdca_pde23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt1017->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt1017->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + default: + break; + } + return 0; +} + +static int rt1017_sdca_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt1017->regmap, RT1017_PWM_TRIM_1, + RT1017_PWM_FREQ_CTL_SRC_SEL_MASK, RT1017_PWM_FREQ_CTL_SRC_SEL_REG); + regmap_write(rt1017->regmap, RT1017_CLASSD_INT_1, 0x10); + break; + default: + break; + } + + return 0; +} + +static int rt1017_sdca_feedback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x08); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x09); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt1017_sdca_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0, + rt1017_sdca_feedback_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Digital Interface */ + SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1017_sto_dac), + + /* Output Lines */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1017_sdca_classd_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPO"), + + SND_SOC_DAPM_SUPPLY("PDE23", SND_SOC_NOPM, 0, 0, + rt1017_sdca_pde23_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA("I Sense", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("V Sense", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SIGGEN("I Gen"), + SND_SOC_DAPM_SIGGEN("V Gen"), +}; + +static const struct snd_soc_dapm_route rt1017_sdca_dapm_routes[] = { + + { "DAC", "Switch", "DP1RX" }, + { "CLASS D", NULL, "DAC" }, + { "CLASS D", NULL, "PDE23" }, + { "SPO", NULL, "CLASS D" }, + + { "I Sense", NULL, "I Gen" }, + { "V Sense", NULL, "V Gen" }, + { "I Sense", NULL, "PDE23" }, + { "V Sense", NULL, "PDE23" }, + { "DP2TX", NULL, "I Sense" }, + { "DP2TX", NULL, "V Sense" }, +}; + +static struct sdw_slave_ops rt1017_sdca_slave_ops = { + .read_prop = rt1017_sdca_read_prop, + .update_status = rt1017_sdca_update_status, +}; + +static int rt1017_sdca_component_probe(struct snd_soc_component *component) +{ + int ret; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static void rt1017_sdca_component_remove(struct snd_soc_component *component) +{ + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1017->regmap, true); +} + +static const struct snd_soc_component_driver soc_sdca_component_rt1017 = { + .probe = rt1017_sdca_component_probe, + .remove = rt1017_sdca_component_remove, + .controls = rt1017_sdca_controls, + .num_controls = ARRAY_SIZE(rt1017_sdca_controls), + .dapm_widgets = rt1017_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1017_sdca_dapm_widgets), + .dapm_routes = rt1017_sdca_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1017_sdca_dapm_routes), + .endianness = 1, +}; + +static int rt1017_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt1017_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + int retval, port, num_channels, ch_mask; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt1017->sdw_slave) + return -EINVAL; + + /* SoundWire specific configuration */ + /* port 1 for playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + direction = SDW_DATA_DIR_TX; + port = 2; + } + + num_channels = params_channels(params); + ch_mask = (1 << num_channels) - 1; + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = num_channels; + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + port_config.ch_mask = ch_mask; + port_config.num = port; + + dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n", + params_rate(params), num_channels, snd_pcm_format_width(params_format(params)), + direction, ch_mask, port); + + retval = sdw_stream_add_slave(rt1017->sdw_slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT1017_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT1017_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT1017_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT1017_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + regmap_write(rt1017->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0), + sampling_rate); + + return 0; +} + +static int rt1017_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt1017->sdw_slave) + return -EINVAL; + + sdw_stream_remove_slave(rt1017->sdw_slave, sdw_stream); + return 0; +} + +static const struct snd_soc_dai_ops rt1017_sdca_ops = { + .hw_params = rt1017_sdca_pcm_hw_params, + .hw_free = rt1017_sdca_pcm_hw_free, + .set_stream = rt1017_sdca_set_sdw_stream, + .shutdown = rt1017_sdca_shutdown, +}; + +#define RT1017_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT1017_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver rt1017_sdca_dai[] = { + { + .name = "rt1017-aif", + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 1, + .rates = RT1017_STEREO_RATES, + .formats = RT1017_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 1, + .rates = RT1017_STEREO_RATES, + .formats = RT1017_FORMATS, + }, + .ops = &rt1017_sdca_ops, + }, +}; + +static int rt1017_sdca_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt1017_sdca_priv *rt1017; + int ret; + + rt1017 = devm_kzalloc(dev, sizeof(*rt1017), GFP_KERNEL); + if (!rt1017) + return -ENOMEM; + + dev_set_drvdata(dev, rt1017); + rt1017->sdw_slave = slave; + rt1017->regmap = regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt1017->hw_init = false; + rt1017->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_sdca_component_rt1017, + rt1017_sdca_dai, + ARRAY_SIZE(rt1017_sdca_dai)); + + return ret; +} + +static int rt1017_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt1017_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt1017_sdca_init(&slave->dev, regmap, slave); +} + +static int rt1017_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev); + + if (rt1017->first_hw_init) + pm_runtime_disable(&slave->dev); + + return 0; +} + +static const struct sdw_device_id rt1017_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1017, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt1017_sdca_id); + +static int __maybe_unused rt1017_sdca_dev_suspend(struct device *dev) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); + + if (!rt1017->hw_init) + return 0; + + regcache_cache_only(rt1017->regmap, true); + + return 0; +} + +#define RT1017_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt1017_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt1017->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT1017_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt1017->regmap, false); + regcache_sync(rt1017->regmap); + + return 0; +} + +static const struct dev_pm_ops rt1017_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt1017_sdca_sdw_driver = { + .driver = { + .name = "rt1017-sdca", + .owner = THIS_MODULE, + .pm = &rt1017_sdca_pm, + }, + .probe = rt1017_sdca_sdw_probe, + .remove = rt1017_sdca_sdw_remove, + .ops = &rt1017_sdca_slave_ops, + .id_table = rt1017_sdca_id, +}; +module_sdw_driver(rt1017_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT1017 driver SDCA SDW"); +MODULE_AUTHOR("Derek Fang <derek.fang@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1017-sdca-sdw.h b/sound/soc/codecs/rt1017-sdca-sdw.h new file mode 100644 index 000000000000..4932b5dbe3c0 --- /dev/null +++ b/sound/soc/codecs/rt1017-sdca-sdw.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1017-sdca-sdw.h -- RT1017 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT1017_SDW_H__ +#define __RT1017_SDW_H__ + +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/soundwire/sdw_registers.h> +#include <sound/soc.h> + +/* RT1017 SDCA Control - function number */ +#define FUNC_NUM_SMART_AMP 0x04 + +/* RT1017 SDCA entity */ +#define RT1017_SDCA_ENT_PDE23 0x31 +#define RT1017_SDCA_ENT_PDE22 0x33 +#define RT1017_SDCA_ENT_CS21 0x21 +#define RT1017_SDCA_ENT_SAPU29 0x29 +#define RT1017_SDCA_ENT_XU22 0x22 +#define RT1017_SDCA_ENT_FU 0x03 +#define RT1017_SDCA_ENT_UDMPU21 0x02 + +/* RT1017 SDCA control */ +#define RT1017_SDCA_CTL_FS_INDEX 0x10 +#define RT1017_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT1017_SDCA_CTL_PROT_STAT 0x11 +#define RT1017_SDCA_CTL_BYPASS 0x01 +#define RT1017_SDCA_CTL_FU_MUTE 0x01 +#define RT1017_SDCA_CTL_FU_VOLUME 0x02 +#define RT1017_SDCA_CTL_UDMPU_CLUSTER 0x10 + + +#define RT1017_CLASSD_INT_1 0xd300 +#define RT1017_PWM_TRIM_1 0xd370 + + +#define RT1017_PWM_FREQ_CTL_SRC_SEL_MASK (0x3 << 2) +#define RT1017_PWM_FREQ_CTL_SRC_SEL_EFUSE (0x2 << 2) +#define RT1017_PWM_FREQ_CTL_SRC_SEL_REG (0x0 << 2) + +enum { + RT1017_SDCA_RATE_44100HZ = 0x8, + RT1017_SDCA_RATE_48000HZ = 0x9, + RT1017_SDCA_RATE_96000HZ = 0xb, + RT1017_SDCA_RATE_192000HZ = 0xd, +}; + +struct rt1017_sdca_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct sdw_slave *sdw_slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; +}; + +static const struct reg_default rt1017_sdca_reg_defaults[] = { + { 0x3206, 0x00 }, + { 0xc001, 0x43 }, + { 0xc030, 0x54 }, + { 0xc104, 0x8a }, + { 0xc10b, 0x2f }, + { 0xc10c, 0x2f }, + { 0xc110, 0x49 }, + { 0xc112, 0x10 }, + { 0xc300, 0xff }, + { 0xc301, 0xdd }, + { 0xc318, 0x40 }, + { 0xc325, 0x00 }, + { 0xc326, 0x00 }, + { 0xc327, 0x00 }, + { 0xc328, 0x02 }, + { 0xc331, 0xb2 }, + { 0xc340, 0x02 }, + { 0xc350, 0x21 }, + { 0xc500, 0x00 }, + { 0xc502, 0x00 }, + { 0xc504, 0x3f }, + { 0xc507, 0x1f }, + { 0xc509, 0x1f }, + { 0xc510, 0x40 }, + { 0xc512, 0x00 }, + { 0xc518, 0x02 }, + { 0xc51b, 0x7f }, + { 0xc51d, 0x0f }, + { 0xc520, 0x00 }, + { 0xc540, 0x80 }, + { 0xc541, 0x00 }, + { 0xc542, 0x0a }, + { 0xc550, 0x80 }, + { 0xc551, 0x0f }, + { 0xc552, 0xff }, + { 0xc600, 0x10 }, + { 0xc602, 0x83 }, + { 0xc612, 0x40 }, + { 0xc622, 0x40 }, + { 0xc632, 0x40 }, + { 0xc642, 0x40 }, + { 0xc651, 0x00 }, + { 0xca00, 0xc1 }, + { 0xca09, 0x00 }, + { 0xca0a, 0x51 }, + { 0xca0b, 0xeb }, + { 0xca0c, 0x85 }, + { 0xca0e, 0x00 }, + { 0xca0f, 0x10 }, + { 0xca10, 0x62 }, + { 0xca11, 0x4d }, + { 0xca16, 0x0f }, + { 0xca17, 0x00 }, + { 0xcb00, 0x10 }, + { 0xcc00, 0x10 }, + { 0xcc02, 0x0b }, + { 0xd017, 0x09 }, + { 0xd01a, 0x00 }, + { 0xd01b, 0x00 }, + { 0xd01c, 0x00 }, + { 0xd101, 0xa0 }, + { 0xd20c, 0x14 }, + { 0xd300, 0x0f }, + { 0xd370, 0x18 }, + { 0xd500, 0x00 }, + { 0xd545, 0x0b }, + { 0xd546, 0xf9 }, + { 0xd547, 0xb2 }, + { 0xd548, 0xa9 }, + { 0xd5a5, 0x00 }, + { 0xd5a6, 0x00 }, + { 0xd5a7, 0x00 }, + { 0xd5a8, 0x00 }, + { 0xd5aa, 0x00 }, + { 0xd5ab, 0x00 }, + { 0xd5ac, 0x00 }, + { 0xd5ad, 0x00 }, + { 0xda04, 0x03 }, + { 0xda05, 0x33 }, + { 0xda06, 0x33 }, + { 0xda07, 0x33 }, + { 0xda09, 0x5d }, + { 0xda0a, 0xc0 }, + { 0xda0c, 0x00 }, + { 0xda0d, 0x01 }, + { 0xda0e, 0x5d }, + { 0xda0f, 0x86 }, + { 0xda11, 0x20 }, + { 0xda12, 0x00 }, + { 0xda13, 0x00 }, + { 0xda14, 0x00 }, + { 0xda16, 0x7f }, + { 0xda17, 0xff }, + { 0xda18, 0xff }, + { 0xda19, 0xff }, + { 0xdab6, 0x00 }, + { 0xdab7, 0x01 }, + { 0xdab8, 0x00 }, + { 0xdab9, 0x01 }, + { 0xdaba, 0x00 }, + { 0xdabb, 0x01 }, + { 0xdb09, 0x0f }, + { 0xdb0a, 0xff }, + { 0xdb14, 0x00 }, + + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, + RT1017_SDCA_CTL_FU_MUTE, 0x01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22, + RT1017_SDCA_CTL_BYPASS, 0), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, +}; + +#endif /* __RT1017_SDW_H__ */ diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c index fd55049920c1..ceb8baa6a20d 100644 --- a/sound/soc/codecs/rt1019.c +++ b/sound/soc/codecs/rt1019.c @@ -18,7 +18,6 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/firmware.h> -#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index 59895131e6e0..80888cbcf49c 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -12,10 +12,8 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/acpi.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/regmap.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/firmware.h> #include <sound/core.h> diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index e566c8ddd3e9..63d4abf964d4 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -219,28 +219,17 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) if (rt1308->hw_init) return 0; - if (rt1308->first_hw_init) { - regcache_cache_only(rt1308->regmap, false); + regcache_cache_only(rt1308->regmap, false); + if (rt1308->first_hw_init) regcache_cache_bypass(rt1308->regmap, true); - } /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!rt1308->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - + if (!rt1308->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - pm_runtime_get_noresume(&slave->dev); regmap_read(rt1308->regmap, 0xcf01, &hibernation_flag); @@ -637,6 +626,9 @@ static int rt1308_sdw_component_probe(struct snd_soc_component *component) rt1308->component = component; rt1308_sdw_parse_dt(rt1308, &rt1308->sdw_slave->dev); + if (!rt1308->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -699,6 +691,8 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, rt1308->sdw_slave = slave; rt1308->regmap = regmap; + regcache_cache_only(rt1308->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -710,10 +704,27 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, &soc_component_sdw_rt1308, rt1308_sdw_dai, ARRAY_SIZE(rt1308_sdw_dai)); + if (ret < 0) + return ret; - dev_dbg(&slave->dev, "%s\n", __func__); + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - return ret; + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } static int rt1308_sdw_probe(struct sdw_slave *slave, @@ -726,17 +737,12 @@ static int rt1308_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return PTR_ERR(regmap); - rt1308_sdw_init(&slave->dev, regmap, slave); - - return 0; + return rt1308_sdw_init(&slave->dev, regmap, slave); } static int rt1308_sdw_remove(struct sdw_slave *slave) { - struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); - - if (rt1308->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c index 9d07756cda40..86afb429d423 100644 --- a/sound/soc/codecs/rt1308.c +++ b/sound/soc/codecs/rt1308.c @@ -11,10 +11,8 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/regmap.h> -#include <linux/of_gpio.h> #include <linux/acpi.h> #include <linux/platform_device.h> #include <linux/firmware.h> diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 721821d9e9af..47511f70119a 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -272,25 +272,16 @@ static int rt1316_io_init(struct device *dev, struct sdw_slave *slave) if (rt1316->hw_init) return 0; + regcache_cache_only(rt1316->regmap, false); if (rt1316->first_hw_init) { - regcache_cache_only(rt1316->regmap, false); regcache_cache_bypass(rt1316->regmap, true); } else { /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); } pm_runtime_get_noresume(&slave->dev); @@ -424,6 +415,15 @@ static SOC_ENUM_SINGLE_DECL(rt1316_rx_data_ch_enum, SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0, rt1316_rx_data_ch_select); +static const char * const rt1316_dac_output_vol_select[] = { + "immediately", + "zero crossing", + "zero crossing with soft ramp", +}; + +static SOC_ENUM_SINGLE_DECL(rt1316_dac_vol_ctl_enum, + 0xc010, 6, rt1316_dac_output_vol_select); + static const struct snd_kcontrol_new rt1316_snd_controls[] = { /* I2S Data Channel Selection */ @@ -442,6 +442,9 @@ static const struct snd_kcontrol_new rt1316_snd_controls[] = { /* IV mixer Control */ SOC_DOUBLE("Isense Mixer Switch", 0xc605, 2, 0, 1, 1), SOC_DOUBLE("Vsense Mixer Switch", 0xc605, 3, 1, 1, 1), + + /* DAC Output Volume Control */ + SOC_ENUM("DAC Output Vol Control", rt1316_dac_vol_ctl_enum), }; static const struct snd_kcontrol_new rt1316_sto_dac = @@ -595,6 +598,9 @@ static int rt1316_sdw_component_probe(struct snd_soc_component *component) rt1316->component = component; rt1316_sdw_parse_dt(rt1316, &rt1316->sdw_slave->dev); + if (!rt1316->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -662,6 +668,8 @@ static int rt1316_sdw_init(struct device *dev, struct regmap *regmap, rt1316->sdw_slave = slave; rt1316->regmap = regmap; + regcache_cache_only(rt1316->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -673,10 +681,27 @@ static int rt1316_sdw_init(struct device *dev, struct regmap *regmap, &soc_component_sdw_rt1316, rt1316_sdw_dai, ARRAY_SIZE(rt1316_sdw_dai)); + if (ret < 0) + return ret; - dev_dbg(&slave->dev, "%s\n", __func__); + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - return ret; + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } static int rt1316_sdw_probe(struct sdw_slave *slave, @@ -694,10 +719,7 @@ static int rt1316_sdw_probe(struct sdw_slave *slave, static int rt1316_sdw_remove(struct sdw_slave *slave) { - struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev); - - if (rt1316->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index 16d750102c8c..ff364bde4a08 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -408,25 +408,15 @@ static int rt1318_io_init(struct device *dev, struct sdw_slave *slave) if (rt1318->hw_init) return 0; + regcache_cache_only(rt1318->regmap, false); if (rt1318->first_hw_init) { - regcache_cache_only(rt1318->regmap, false); regcache_cache_bypass(rt1318->regmap, true); } else { /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); } pm_runtime_get_noresume(&slave->dev); @@ -686,6 +676,9 @@ static int rt1318_sdw_component_probe(struct snd_soc_component *component) rt1318->component = component; + if (!rt1318->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); dev_dbg(&rt1318->sdw_slave->dev, "%s pm_runtime_resume, ret=%d", __func__, ret); if (ret < 0 && ret != -EACCES) @@ -752,6 +745,8 @@ static int rt1318_sdw_init(struct device *dev, struct regmap *regmap, rt1318->sdw_slave = slave; rt1318->regmap = regmap; + regcache_cache_only(rt1318->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -763,8 +758,25 @@ static int rt1318_sdw_init(struct device *dev, struct regmap *regmap, &soc_component_sdw_rt1318, rt1318_sdw_dai, ARRAY_SIZE(rt1318_sdw_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); - dev_dbg(&slave->dev, "%s\n", __func__); + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); return ret; } @@ -784,10 +796,7 @@ static int rt1318_sdw_probe(struct sdw_slave *slave, static int rt1318_sdw_remove(struct sdw_slave *slave) { - struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev); - - if (rt1318->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 362663abcb89..3ee6d85268ba 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -15,7 +15,6 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> -#include <linux/gpio.h> #include <linux/sched.h> #include <linux/uaccess.h> #include <linux/regulator/consumer.h> diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index b3aac2373357..43fc7814fdde 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -17,7 +17,6 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/firmware.h> -#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index eceed8209787..15e1a62b9e57 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -12,11 +12,10 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> @@ -2571,7 +2570,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5640", rt5640); if (ret) { - dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); + dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); rt5640_disable_jack_detect(component); return; } @@ -2626,7 +2625,7 @@ static void rt5640_enable_hda_jack_detect( NULL, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640); if (ret) { - dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); + dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); rt5640->irq = -ENXIO; return; } @@ -2812,8 +2811,8 @@ static int rt5640_suspend(struct snd_soc_component *component) rt5640_reset(component); regcache_cache_only(rt5640->regmap, true); regcache_mark_dirty(rt5640->regmap); - if (gpio_is_valid(rt5640->ldo1_en)) - gpio_set_value_cansleep(rt5640->ldo1_en, 0); + if (rt5640->ldo1_en) + gpiod_set_value_cansleep(rt5640->ldo1_en, 0); return 0; } @@ -2822,8 +2821,8 @@ static int rt5640_resume(struct snd_soc_component *component) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(rt5640->ldo1_en)) { - gpio_set_value_cansleep(rt5640->ldo1_en, 1); + if (rt5640->ldo1_en) { + gpiod_set_value_cansleep(rt5640->ldo1_en, 1); msleep(400); } @@ -2986,22 +2985,6 @@ static const struct acpi_device_id rt5640_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match); #endif -static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np) -{ - rt5640->ldo1_en = of_get_named_gpio(np, "realtek,ldo1-en-gpios", 0); - /* - * LDO1_EN is optional (it may be statically tied on the board). - * -ENOENT means that the property doesn't exist, i.e. there is no - * GPIO, so is not an error. Any other error code means the property - * exists, but could not be parsed. - */ - if (!gpio_is_valid(rt5640->ldo1_en) && - (rt5640->ldo1_en != -ENOENT)) - return rt5640->ldo1_en; - - return 0; -} - static int rt5640_i2c_probe(struct i2c_client *i2c) { struct rt5640_priv *rt5640; @@ -3015,12 +2998,16 @@ static int rt5640_i2c_probe(struct i2c_client *i2c) return -ENOMEM; i2c_set_clientdata(i2c, rt5640); - if (i2c->dev.of_node) { - ret = rt5640_parse_dt(rt5640, i2c->dev.of_node); - if (ret) - return ret; - } else - rt5640->ldo1_en = -EINVAL; + rt5640->ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5640->ldo1_en)) + return PTR_ERR(rt5640->ldo1_en); + + if (rt5640->ldo1_en) { + gpiod_set_consumer_name(rt5640->ldo1_en, "RT5640 LDO1_EN"); + msleep(400); + } rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap); if (IS_ERR(rt5640->regmap)) { @@ -3030,18 +3017,6 @@ static int rt5640_i2c_probe(struct i2c_client *i2c) return ret; } - if (gpio_is_valid(rt5640->ldo1_en)) { - ret = devm_gpio_request_one(&i2c->dev, rt5640->ldo1_en, - GPIOF_OUT_INIT_HIGH, - "RT5640 LDO1_EN"); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to request LDO1_EN %d: %d\n", - rt5640->ldo1_en, ret); - return ret; - } - msleep(400); - } - regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val); if (val != RT5640_DEVICE_ID) { dev_err(&i2c->dev, diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 9847a1ae01f4..94b9a502f7f9 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2138,7 +2138,7 @@ struct rt5640_priv { struct regmap *regmap; struct clk *mclk; - int ldo1_en; /* GPIO for LDO1_EN */ + struct gpio_desc *ldo1_en; /* GPIO for LDO1_EN */ int irq; int jd_gpio_irq; int sysclk; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index a506d940a2ea..038d93e20883 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -14,7 +14,6 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/acpi.h> #include <linux/dmi.h> @@ -81,6 +80,7 @@ static const struct reg_sequence init_list[] = { static const struct reg_sequence rt5650_init_list[] = { {0xf6, 0x0100}, + {RT5645_PWR_ANLG1, 0x02}, }; static const struct reg_default rt5645_reg[] = { @@ -1697,6 +1697,9 @@ static void hp_amp_power(struct snd_soc_component *component, int on) regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R, + RT5645_PWR_HP_L | RT5645_PWR_HP_R); msleep(90); } else { /* depop parameters */ @@ -1744,7 +1747,8 @@ static void hp_amp_power(struct snd_soc_component *component, int on) snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140); msleep(100); snd_soc_component_write(component, RT5645_DEPOP_M1, 0x0001); - + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R, 0); } else { snd_soc_component_update_bits(component, RT5645_DEPOP_M1, RT5645_HP_SG_MASK | @@ -3151,7 +3155,7 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse unsigned int val; if (jack_insert) { - regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06); + regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0206); /* for jack type detect */ snd_soc_dapm_force_enable_pin(dapm, "LDO2"); @@ -3196,6 +3200,8 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse if (rt5645->pdata.level_trigger_irq) regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR); + + regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06); } else { /* jack out */ rt5645->jack_type = 0; @@ -3258,6 +3264,22 @@ int rt5645_set_jack_detect(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(rt5645_set_jack_detect); +static int rt5645_component_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct snd_soc_jack *mic_jack = NULL; + struct snd_soc_jack *btn_jack = NULL; + int *type = (int *)data; + + if (*type & SND_JACK_MICROPHONE) + mic_jack = hs_jack; + if (*type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + btn_jack = hs_jack; + + return rt5645_set_jack_detect(component, hs_jack, mic_jack, btn_jack); +} + static void rt5645_jack_detect_work(struct work_struct *work) { struct rt5645_priv *rt5645 = @@ -3532,6 +3554,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt5645 = { .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets), .dapm_routes = rt5645_dapm_routes, .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes), + .set_jack = rt5645_component_set_jack, .use_pmdown_time = 1, .endianness = 1, }; @@ -3987,13 +4010,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c) regmap_write(rt5645->regmap, RT5645_AD_DA_MIXER, 0x8080); - ret = regmap_register_patch(rt5645->regmap, init_list, + ret = regmap_multi_reg_write(rt5645->regmap, init_list, ARRAY_SIZE(init_list)); if (ret != 0) dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); if (rt5645->codec_type == CODEC_TYPE_RT5650) { - ret = regmap_register_patch(rt5645->regmap, rt5650_init_list, + ret = regmap_multi_reg_write(rt5645->regmap, rt5650_init_list, ARRAY_SIZE(rt5650_init_list)); if (ret != 0) dev_warn(&i2c->dev, "Apply rt5650 patch failed: %d\n", @@ -4182,11 +4205,43 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c) regmap_write(rt5645->regmap, RT5645_RESET, 0); } +static int __maybe_unused rt5645_sys_suspend(struct device *dev) +{ + struct rt5645_priv *rt5645 = dev_get_drvdata(dev); + + del_timer_sync(&rt5645->btn_check_timer); + cancel_delayed_work_sync(&rt5645->jack_detect_work); + cancel_delayed_work_sync(&rt5645->rcclock_work); + + regcache_cache_only(rt5645->regmap, true); + regcache_mark_dirty(rt5645->regmap); + return 0; +} + +static int __maybe_unused rt5645_sys_resume(struct device *dev) +{ + struct rt5645_priv *rt5645 = dev_get_drvdata(dev); + + regcache_cache_only(rt5645->regmap, false); + regcache_sync(rt5645->regmap); + + if (rt5645->hp_jack) { + rt5645->jack_type = 0; + rt5645_jack_detect_work(&rt5645->jack_detect_work.work); + } + return 0; +} + +static const struct dev_pm_ops rt5645_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt5645_sys_suspend, rt5645_sys_resume) +}; + static struct i2c_driver rt5645_i2c_driver = { .driver = { .name = "rt5645", .of_match_table = of_match_ptr(rt5645_of_match), .acpi_match_table = ACPI_PTR(rt5645_acpi_match), + .pm = &rt5645_pm, }, .probe = rt5645_i2c_probe, .remove = rt5645_i2c_remove, diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index df6f0d769bbd..a061028a16d8 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -16,7 +16,6 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index eade087b2d8b..0cecfd602415 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -11,11 +11,9 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 525713c33d71..a39de4a7df00 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -15,8 +15,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <linux/mutex.h> #include <sound/core.h> @@ -4659,9 +4658,6 @@ static int rt5665_parse_dt(struct rt5665_priv *rt5665, struct device *dev) of_property_read_u32(dev->of_node, "realtek,jd-src", &rt5665->pdata.jd_src); - rt5665->pdata.ldo1_en = of_get_named_gpio(dev->of_node, - "realtek,ldo1-en-gpios", 0); - return 0; } @@ -4795,10 +4791,13 @@ static int rt5665_i2c_probe(struct i2c_client *i2c) return ret; } - if (gpio_is_valid(rt5665->pdata.ldo1_en)) { - if (devm_gpio_request_one(&i2c->dev, rt5665->pdata.ldo1_en, - GPIOF_OUT_INIT_HIGH, "rt5665")) - dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); + + rt5665->gpiod_ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5665->gpiod_ldo1_en)) { + dev_err(&i2c->dev, "Failed gpio request ldo1_en\n"); + return PTR_ERR(rt5665->gpiod_ldo1_en); } /* Sleep for 300 ms miniumum */ diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index f04c810fd710..4623b3e62487 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -15,8 +15,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <linux/mutex.h> #include <sound/core.h> @@ -43,6 +42,7 @@ static const char *rt5668_supply_names[RT5668_NUM_SUPPLIES] = { struct rt5668_priv { struct snd_soc_component *component; struct rt5668_platform_data pdata; + struct gpio_desc *ldo1_en; struct regmap *regmap; struct snd_soc_jack *hs_jack; struct regulator_bulk_data supplies[RT5668_NUM_SUPPLIES]; @@ -2393,9 +2393,6 @@ static int rt5668_parse_dt(struct rt5668_priv *rt5668, struct device *dev) of_property_read_u32(dev->of_node, "realtek,jd-src", &rt5668->pdata.jd_src); - rt5668->pdata.ldo1_en = of_get_named_gpio(dev->of_node, - "realtek,ldo1-en-gpios", 0); - return 0; } @@ -2497,10 +2494,12 @@ static int rt5668_i2c_probe(struct i2c_client *i2c) return ret; } - if (gpio_is_valid(rt5668->pdata.ldo1_en)) { - if (devm_gpio_request_one(&i2c->dev, rt5668->pdata.ldo1_en, - GPIOF_OUT_INIT_HIGH, "rt5668")) - dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); + rt5668->ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5668->ldo1_en)) { + dev_err(&i2c->dev, "Fail gpio request ldo1_en\n"); + return PTR_ERR(rt5668->ldo1_en); } /* Sleep for 300 ms miniumum */ diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index ad14d18860fc..0e70a3ab42b5 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -6,23 +6,21 @@ * Author: Oder Chiou <oder_chiou@realtek.com> */ -#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/firmware.h> #include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irqdomain.h> +#include <linux/irq.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/delay.h> +#include <linux/platform_device.h> #include <linux/pm.h> +#include <linux/property.h> #include <linux/regmap.h> -#include <linux/i2c.h> -#include <linux/platform_device.h> #include <linux/spi/spi.h> -#include <linux/firmware.h> -#include <linux/of_device.h> -#include <linux/property.h> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/irqdomain.h> #include <linux/workqueue.h> #include <sound/core.h> #include <sound/pcm.h> @@ -4717,50 +4715,34 @@ static int rt5677_set_bias_level(struct snd_soc_component *component, return 0; } +static int rt5677_update_gpio_bits(struct rt5677_priv *rt5677, unsigned offset, int m, int v) +{ + unsigned int bank = offset / 5; + unsigned int shift = (offset % 5) * 3; + unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2; + + return regmap_update_bits(rt5677->regmap, reg, m << shift, v << shift); +} + #ifdef CONFIG_GPIOLIB static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO; + int m = RT5677_GPIOx_OUT_MASK; - switch (offset) { - case RT5677_GPIO1 ... RT5677_GPIO5: - regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2, - 0x1 << (offset * 3 + 1), !!value << (offset * 3 + 1)); - break; - - case RT5677_GPIO6: - regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3, - RT5677_GPIO6_OUT_MASK, !!value << RT5677_GPIO6_OUT_SFT); - break; - - default: - break; - } + rt5677_update_gpio_bits(rt5677, offset, m, level); } static int rt5677_gpio_direction_out(struct gpio_chip *chip, unsigned offset, int value) { struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO; + int m = RT5677_GPIOx_DIR_MASK | RT5677_GPIOx_OUT_MASK; + int v = RT5677_GPIOx_DIR_OUT | level; - switch (offset) { - case RT5677_GPIO1 ... RT5677_GPIO5: - regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2, - 0x3 << (offset * 3 + 1), - (0x2 | !!value) << (offset * 3 + 1)); - break; - - case RT5677_GPIO6: - regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3, - RT5677_GPIO6_DIR_MASK | RT5677_GPIO6_OUT_MASK, - RT5677_GPIO6_DIR_OUT | !!value << RT5677_GPIO6_OUT_SFT); - break; - - default: - break; - } - - return 0; + return rt5677_update_gpio_bits(rt5677, offset, m, v); } static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -4778,26 +4760,14 @@ static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset) static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) { struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + int m = RT5677_GPIOx_DIR_MASK; + int v = RT5677_GPIOx_DIR_IN; - switch (offset) { - case RT5677_GPIO1 ... RT5677_GPIO5: - regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2, - 0x1 << (offset * 3 + 2), 0x0); - break; - - case RT5677_GPIO6: - regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3, - RT5677_GPIO6_DIR_MASK, RT5677_GPIO6_DIR_IN); - break; - - default: - break; - } - - return 0; + return rt5677_update_gpio_bits(rt5677, offset, m, v); } -/** Configures the gpio as +/* + * Configures the GPIO as * 0 - floating * 1 - pull down * 2 - pull up @@ -5539,7 +5509,7 @@ static int rt5677_init_irq(struct i2c_client *i2c) RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); /* Ready to listen for interrupts */ - rt5677->domain = irq_domain_add_linear(i2c->dev.of_node, + rt5677->domain = irq_domain_create_linear(dev_fwnode(&i2c->dev), RT5677_IRQ_NUM, &rt5677_domain_ops, rt5677); if (!rt5677->domain) { dev_err(&i2c->dev, "Failed to create IRQ domain\n"); @@ -5559,6 +5529,7 @@ static int rt5677_init_irq(struct i2c_client *i2c) static int rt5677_i2c_probe(struct i2c_client *i2c) { + struct device *dev = &i2c->dev; struct rt5677_priv *rt5677; int ret; unsigned int val; @@ -5573,21 +5544,9 @@ static int rt5677_i2c_probe(struct i2c_client *i2c) INIT_DELAYED_WORK(&rt5677->dsp_work, rt5677_dsp_work); i2c_set_clientdata(i2c, rt5677); - if (i2c->dev.of_node) { - const struct of_device_id *match_id; - - match_id = of_match_device(rt5677_of_match, &i2c->dev); - if (match_id) - rt5677->type = (enum rt5677_type)match_id->data; - } else if (ACPI_HANDLE(&i2c->dev)) { - const struct acpi_device_id *acpi_id; - - acpi_id = acpi_match_device(rt5677_acpi_match, &i2c->dev); - if (acpi_id) - rt5677->type = (enum rt5677_type)acpi_id->driver_data; - } else { + rt5677->type = (enum rt5677_type)(uintptr_t)device_get_match_data(dev); + if (rt5677->type == 0) return -EINVAL; - } rt5677_read_device_properties(rt5677, &i2c->dev); @@ -5673,9 +5632,9 @@ static int rt5677_i2c_probe(struct i2c_client *i2c) regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2, RT5677_GPIO5_FUNC_MASK, RT5677_GPIO5_FUNC_DMIC); - regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2, - RT5677_GPIO5_DIR_MASK, - RT5677_GPIO5_DIR_OUT); + rt5677_update_gpio_bits(rt5677, RT5677_GPIO5, + RT5677_GPIOx_DIR_MASK, + RT5677_GPIOx_DIR_OUT); } if (rt5677->pdata.micbias1_vdd_3v3) @@ -5702,7 +5661,7 @@ static struct i2c_driver rt5677_i2c_driver = { .driver = { .name = RT5677_DRV_NAME, .of_match_table = rt5677_of_match, - .acpi_match_table = ACPI_PTR(rt5677_acpi_match), + .acpi_match_table = rt5677_acpi_match, }, .probe = rt5677_i2c_probe, .remove = rt5677_i2c_remove, diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 944ae02aafc2..d67ebae067d9 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1587,81 +1587,19 @@ #define RT5677_FUNC_MODE_DMIC_GPIO (0x0 << 13) #define RT5677_FUNC_MODE_JTAG (0x1 << 13) -/* GPIO Control 2 (0xc1) */ -#define RT5677_GPIO5_DIR_MASK (0x1 << 14) -#define RT5677_GPIO5_DIR_SFT 14 -#define RT5677_GPIO5_DIR_IN (0x0 << 14) -#define RT5677_GPIO5_DIR_OUT (0x1 << 14) -#define RT5677_GPIO5_OUT_MASK (0x1 << 13) -#define RT5677_GPIO5_OUT_SFT 13 -#define RT5677_GPIO5_OUT_LO (0x0 << 13) -#define RT5677_GPIO5_OUT_HI (0x1 << 13) -#define RT5677_GPIO5_P_MASK (0x1 << 12) -#define RT5677_GPIO5_P_SFT 12 -#define RT5677_GPIO5_P_NOR (0x0 << 12) -#define RT5677_GPIO5_P_INV (0x1 << 12) -#define RT5677_GPIO4_DIR_MASK (0x1 << 11) -#define RT5677_GPIO4_DIR_SFT 11 -#define RT5677_GPIO4_DIR_IN (0x0 << 11) -#define RT5677_GPIO4_DIR_OUT (0x1 << 11) -#define RT5677_GPIO4_OUT_MASK (0x1 << 10) -#define RT5677_GPIO4_OUT_SFT 10 -#define RT5677_GPIO4_OUT_LO (0x0 << 10) -#define RT5677_GPIO4_OUT_HI (0x1 << 10) -#define RT5677_GPIO4_P_MASK (0x1 << 9) -#define RT5677_GPIO4_P_SFT 9 -#define RT5677_GPIO4_P_NOR (0x0 << 9) -#define RT5677_GPIO4_P_INV (0x1 << 9) -#define RT5677_GPIO3_DIR_MASK (0x1 << 8) -#define RT5677_GPIO3_DIR_SFT 8 -#define RT5677_GPIO3_DIR_IN (0x0 << 8) -#define RT5677_GPIO3_DIR_OUT (0x1 << 8) -#define RT5677_GPIO3_OUT_MASK (0x1 << 7) -#define RT5677_GPIO3_OUT_SFT 7 -#define RT5677_GPIO3_OUT_LO (0x0 << 7) -#define RT5677_GPIO3_OUT_HI (0x1 << 7) -#define RT5677_GPIO3_P_MASK (0x1 << 6) -#define RT5677_GPIO3_P_SFT 6 -#define RT5677_GPIO3_P_NOR (0x0 << 6) -#define RT5677_GPIO3_P_INV (0x1 << 6) -#define RT5677_GPIO2_DIR_MASK (0x1 << 5) -#define RT5677_GPIO2_DIR_SFT 5 -#define RT5677_GPIO2_DIR_IN (0x0 << 5) -#define RT5677_GPIO2_DIR_OUT (0x1 << 5) -#define RT5677_GPIO2_OUT_MASK (0x1 << 4) -#define RT5677_GPIO2_OUT_SFT 4 -#define RT5677_GPIO2_OUT_LO (0x0 << 4) -#define RT5677_GPIO2_OUT_HI (0x1 << 4) -#define RT5677_GPIO2_P_MASK (0x1 << 3) -#define RT5677_GPIO2_P_SFT 3 -#define RT5677_GPIO2_P_NOR (0x0 << 3) -#define RT5677_GPIO2_P_INV (0x1 << 3) -#define RT5677_GPIO1_DIR_MASK (0x1 << 2) -#define RT5677_GPIO1_DIR_SFT 2 -#define RT5677_GPIO1_DIR_IN (0x0 << 2) -#define RT5677_GPIO1_DIR_OUT (0x1 << 2) -#define RT5677_GPIO1_OUT_MASK (0x1 << 1) -#define RT5677_GPIO1_OUT_SFT 1 -#define RT5677_GPIO1_OUT_LO (0x0 << 1) -#define RT5677_GPIO1_OUT_HI (0x1 << 1) -#define RT5677_GPIO1_P_MASK (0x1 << 0) -#define RT5677_GPIO1_P_SFT 0 -#define RT5677_GPIO1_P_NOR (0x0 << 0) -#define RT5677_GPIO1_P_INV (0x1 << 0) - -/* GPIO Control 3 (0xc2) */ -#define RT5677_GPIO6_DIR_MASK (0x1 << 2) -#define RT5677_GPIO6_DIR_SFT 2 -#define RT5677_GPIO6_DIR_IN (0x0 << 2) -#define RT5677_GPIO6_DIR_OUT (0x1 << 2) -#define RT5677_GPIO6_OUT_MASK (0x1 << 1) -#define RT5677_GPIO6_OUT_SFT 1 -#define RT5677_GPIO6_OUT_LO (0x0 << 1) -#define RT5677_GPIO6_OUT_HI (0x1 << 1) -#define RT5677_GPIO6_P_MASK (0x1 << 0) -#define RT5677_GPIO6_P_SFT 0 -#define RT5677_GPIO6_P_NOR (0x0 << 0) -#define RT5677_GPIO6_P_INV (0x1 << 0) +/* GPIO Control 2 (0xc1) & 3 (0xc2) common bits */ +#define RT5677_GPIOx_DIR_MASK (0x1 << 2) +#define RT5677_GPIOx_DIR_SFT 2 +#define RT5677_GPIOx_DIR_IN (0x0 << 2) +#define RT5677_GPIOx_DIR_OUT (0x1 << 2) +#define RT5677_GPIOx_OUT_MASK (0x1 << 1) +#define RT5677_GPIOx_OUT_SFT 1 +#define RT5677_GPIOx_OUT_LO (0x0 << 1) +#define RT5677_GPIOx_OUT_HI (0x1 << 1) +#define RT5677_GPIOx_P_MASK (0x1 << 0) +#define RT5677_GPIOx_P_SFT 0 +#define RT5677_GPIOx_P_NOR (0x0 << 0) +#define RT5677_GPIOx_P_INV (0x1 << 0) /* General Control (0xfa) */ #define RT5677_IRQ_DEBOUNCE_SEL_MASK (0x3 << 3) @@ -1753,8 +1691,8 @@ enum { }; enum rt5677_type { - RT5677, - RT5676, + RT5677 = 1, + RT5676 = 2, }; /* ASRC clock source selection */ diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index fb8ffb5b2ff6..b05b4f73d8aa 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -15,8 +15,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/mutex.h> #include <sound/core.h> #include <sound/pcm.h> @@ -170,11 +169,9 @@ static int rt5682_i2c_probe(struct i2c_client *i2c) return ret; } - if (gpio_is_valid(rt5682->pdata.ldo1_en)) { - if (devm_gpio_request_one(&i2c->dev, rt5682->pdata.ldo1_en, - GPIOF_OUT_INIT_HIGH, "rt5682")) - dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); - } + ret = rt5682_get_ldo1(rt5682, &i2c->dev); + if (ret) + return ret; /* Sleep for 300 ms miniumum */ usleep_range(300000, 350000); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 4968a8c0064d..e67c2e19cb1a 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -12,8 +12,6 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/mutex.h> @@ -322,6 +320,14 @@ static int rt5682_sdw_init(struct device *dev, struct regmap *regmap, return ret; } + + ret = rt5682_get_ldo1(rt5682, dev); + if (ret) + return ret; + + regcache_cache_only(rt5682->sdw_regmap, true); + regcache_cache_only(rt5682->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -336,7 +342,25 @@ static int rt5682_sdw_init(struct device *dev, struct regmap *regmap, ret = devm_snd_soc_register_component(dev, &rt5682_soc_component_dev, rt5682_dai, ARRAY_SIZE(rt5682_dai)); - dev_dbg(&slave->dev, "%s\n", __func__); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); return ret; } @@ -352,30 +376,20 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) if (rt5682->hw_init) return 0; + regcache_cache_only(rt5682->sdw_regmap, false); + regcache_cache_only(rt5682->regmap, false); + if (rt5682->first_hw_init) + regcache_cache_bypass(rt5682->regmap, true); + /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!rt5682->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - + if (!rt5682->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - pm_runtime_get_noresume(&slave->dev); - if (rt5682->first_hw_init) { - regcache_cache_only(rt5682->regmap, false); - regcache_cache_bypass(rt5682->regmap, true); - } - while (loop > 0) { regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val); if (val == DEVICE_ID) @@ -674,9 +688,7 @@ static int rt5682_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return -EINVAL; - rt5682_sdw_init(&slave->dev, regmap, slave); - - return 0; + return rt5682_sdw_init(&slave->dev, regmap, slave); } static int rt5682_sdw_remove(struct sdw_slave *slave) @@ -686,8 +698,7 @@ static int rt5682_sdw_remove(struct sdw_slave *slave) if (rt5682->hw_init) cancel_delayed_work_sync(&rt5682->jack_detect_work); - if (rt5682->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } @@ -707,6 +718,7 @@ static int __maybe_unused rt5682_dev_suspend(struct device *dev) cancel_delayed_work_sync(&rt5682->jack_detect_work); + regcache_cache_only(rt5682->sdw_regmap, true); regcache_cache_only(rt5682->regmap, true); regcache_mark_dirty(rt5682->regmap); @@ -771,6 +783,7 @@ static int __maybe_unused rt5682_dev_resume(struct device *dev) regmap_sync: slave->unattach_request = 0; + regcache_cache_only(rt5682->sdw_regmap, false); regcache_cache_only(rt5682->regmap, false); regcache_sync(rt5682->regmap); diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 5d992543b791..e3aca9c785a0 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -15,8 +15,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/mutex.h> #include <sound/core.h> #include <sound/pcm.h> @@ -1017,6 +1016,9 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component, rt5682->hs_jack = hs_jack; + if (rt5682->is_sdw && !rt5682->first_hw_init) + return 0; + if (!hs_jack) { regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, RT5682_JD1_EN_MASK, RT5682_JD1_DIS); @@ -3091,9 +3093,6 @@ int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) device_property_read_u32(dev, "realtek,dmic-delay-ms", &rt5682->pdata.dmic_delay); - rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node, - "realtek,ldo1-en-gpios", 0); - if (device_property_read_string_array(dev, "clock-output-names", rt5682->pdata.dai_clk_names, RT5682_DAI_NUM_CLKS) < 0) @@ -3108,6 +3107,20 @@ int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) } EXPORT_SYMBOL_GPL(rt5682_parse_dt); +int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev) +{ + rt5682->ldo1_en = devm_gpiod_get_optional(dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5682->ldo1_en)) { + dev_err(dev, "Fail gpio request ldo1_en\n"); + return PTR_ERR(rt5682->ldo1_en); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5682_get_ldo1); + void rt5682_calibrate(struct rt5682_priv *rt5682) { int value, count; diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 1a43d595f341..b2d9e87af259 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -11,6 +11,7 @@ #include <sound/rt5682.h> #include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> #include <linux/clk.h> #include <linux/clkdev.h> #include <linux/clk-provider.h> @@ -1430,6 +1431,7 @@ struct rt5682_priv { struct snd_soc_component *component; struct device *i2c_dev; struct rt5682_platform_data pdata; + struct gpio_desc *ldo1_en; struct regmap *regmap; struct regmap *sdw_regmap; struct snd_soc_jack *hs_jack; @@ -1481,6 +1483,7 @@ int rt5682_register_component(struct device *dev); void rt5682_calibrate(struct rt5682_priv *rt5682); void rt5682_reset(struct rt5682_priv *rt5682); int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev); +int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev); int rt5682_register_dai_clks(struct rt5682_priv *rt5682); diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index c77c675bd5f5..68ac5ea50396 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -15,8 +15,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/mutex.h> #include <sound/core.h> #include <sound/pcm.h> @@ -2973,9 +2972,6 @@ static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev) device_property_read_u32(dev, "realtek,amic-delay-ms", &rt5682s->pdata.amic_delay); - rt5682s->pdata.ldo1_en = of_get_named_gpio(dev->of_node, - "realtek,ldo1-en-gpios", 0); - if (device_property_read_string_array(dev, "clock-output-names", rt5682s->pdata.dai_clk_names, RT5682S_DAI_NUM_CLKS) < 0) @@ -3172,10 +3168,12 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c) return ret; } - if (gpio_is_valid(rt5682s->pdata.ldo1_en)) { - if (devm_gpio_request_one(&i2c->dev, rt5682s->pdata.ldo1_en, - GPIOF_OUT_INIT_HIGH, "rt5682s")) - dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); + rt5682s->ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5682s->ldo1_en)) { + dev_err(&i2c->dev, "Fail gpio request ldo1_en\n"); + return PTR_ERR(rt5682s->ldo1_en); } /* Sleep for 50 ms minimum */ diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h index caa7733b430f..1d79d432d0d8 100644 --- a/sound/soc/codecs/rt5682s.h +++ b/sound/soc/codecs/rt5682s.h @@ -11,6 +11,7 @@ #include <sound/rt5682s.h> #include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> #include <linux/clk.h> #include <linux/clkdev.h> #include <linux/clk-provider.h> @@ -1446,6 +1447,7 @@ enum { struct rt5682s_priv { struct snd_soc_component *component; struct rt5682s_platform_data pdata; + struct gpio_desc *ldo1_en; struct regmap *regmap; struct snd_soc_jack *hs_jack; struct regulator_bulk_data supplies[RT5682S_NUM_SUPPLIES]; diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 8b28e47775cc..52c33d56b143 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -452,9 +452,7 @@ static int rt700_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return PTR_ERR(regmap); - rt700_init(&slave->dev, sdw_regmap, regmap, slave); - - return 0; + return rt700_init(&slave->dev, sdw_regmap, regmap, slave); } static int rt700_sdw_remove(struct sdw_slave *slave) @@ -466,8 +464,7 @@ static int rt700_sdw_remove(struct sdw_slave *slave) cancel_delayed_work_sync(&rt700->jack_btn_check_work); } - if (rt700->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index a04b9246256b..0ebf344a1b60 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -320,6 +320,10 @@ static int rt700_set_jack_detect(struct snd_soc_component *component, rt700->hs_jack = hs_jack; + /* we can only resume if the device was initialized at least once */ + if (!rt700->first_hw_init) + return 0; + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0) { if (ret != -EACCES) { @@ -823,6 +827,9 @@ static int rt700_probe(struct snd_soc_component *component) rt700->component = component; + if (!rt700->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -1099,6 +1106,8 @@ int rt700_init(struct device *dev, struct regmap *sdw_regmap, rt700->sdw_regmap = sdw_regmap; rt700->regmap = regmap; + regcache_cache_only(rt700->regmap, true); + mutex_init(&rt700->disable_irq_lock); INIT_DELAYED_WORK(&rt700->jack_detect_work, @@ -1117,10 +1126,26 @@ int rt700_init(struct device *dev, struct regmap *sdw_regmap, &soc_codec_dev_rt700, rt700_dai, ARRAY_SIZE(rt700_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ dev_dbg(&slave->dev, "%s\n", __func__); - return ret; + return 0; } int rt700_io_init(struct device *dev, struct sdw_slave *slave) @@ -1132,28 +1157,17 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave) if (rt700->hw_init) return 0; - if (rt700->first_hw_init) { - regcache_cache_only(rt700->regmap, false); + regcache_cache_only(rt700->regmap, false); + if (rt700->first_hw_init) regcache_cache_bypass(rt700->regmap, true); - } /* * PM runtime is only enabled when a Slave reports as Attached */ - if (!rt700->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - - /* update count of parent 'active' children */ + if (!rt700->first_hw_init) + /* PM runtime status is marked as 'active' only when a Slave reports as Attached */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - pm_runtime_get_noresume(&slave->dev); /* reset */ diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 23f23f714b39..935e597022d3 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -366,8 +366,7 @@ static int rt711_sdca_sdw_remove(struct sdw_slave *slave) cancel_delayed_work_sync(&rt711->jack_btn_check_work); } - if (rt711->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); mutex_destroy(&rt711->calibrate_mutex); mutex_destroy(&rt711->disable_irq_lock); diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 07640d2f6e56..447154cb6010 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -507,6 +507,10 @@ static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, rt711->hs_jack = hs_jack; + /* we can only resume if the device was initialized at least once */ + if (!rt711->first_hw_init) + return 0; + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0) { if (ret != -EACCES) { @@ -1215,6 +1219,9 @@ static int rt711_sdca_probe(struct snd_soc_component *component) rt711_sdca_parse_dt(rt711, &rt711->slave->dev); rt711->component = component; + if (!rt711->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -1406,6 +1413,9 @@ int rt711_sdca_init(struct device *dev, struct regmap *regmap, rt711->regmap = regmap; rt711->mbq_regmap = mbq_regmap; + regcache_cache_only(rt711->regmap, true); + regcache_cache_only(rt711->mbq_regmap, true); + mutex_init(&rt711->calibrate_mutex); mutex_init(&rt711->disable_irq_lock); @@ -1431,9 +1441,27 @@ int rt711_sdca_init(struct device *dev, struct regmap *regmap, rt711_sdca_dai, ARRAY_SIZE(rt711_sdca_dai)); - dev_dbg(&slave->dev, "%s\n", __func__); + if (ret < 0) + return ret; - return ret; + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } static void rt711_sdca_vd0_io_init(struct rt711_sdca_priv *rt711) @@ -1500,27 +1528,19 @@ int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt711->hw_init) return 0; + regcache_cache_only(rt711->regmap, false); + regcache_cache_only(rt711->mbq_regmap, false); + if (rt711->first_hw_init) { - regcache_cache_only(rt711->regmap, false); regcache_cache_bypass(rt711->regmap, true); - regcache_cache_only(rt711->mbq_regmap, false); regcache_cache_bypass(rt711->mbq_regmap, true); } else { /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); } pm_runtime_get_noresume(&slave->dev); diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 33dced388f9e..3f5773310ae8 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -453,9 +453,7 @@ static int rt711_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return PTR_ERR(regmap); - rt711_init(&slave->dev, sdw_regmap, regmap, slave); - - return 0; + return rt711_init(&slave->dev, sdw_regmap, regmap, slave); } static int rt711_sdw_remove(struct sdw_slave *slave) @@ -468,8 +466,7 @@ static int rt711_sdw_remove(struct sdw_slave *slave) cancel_work_sync(&rt711->calibration_work); } - if (rt711->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); mutex_destroy(&rt711->calibrate_mutex); mutex_destroy(&rt711->disable_irq_lock); diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index af53cbcc7bf2..66eaed13b0d6 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -462,6 +462,10 @@ static int rt711_set_jack_detect(struct snd_soc_component *component, rt711->hs_jack = hs_jack; + /* we can only resume if the device was initialized at least once */ + if (!rt711->first_hw_init) + return 0; + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0) { if (ret != -EACCES) { @@ -941,6 +945,9 @@ static int rt711_probe(struct snd_soc_component *component) rt711_parse_dt(rt711, &rt711->slave->dev); rt711->component = component; + if (!rt711->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -1183,6 +1190,8 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap, rt711->sdw_regmap = sdw_regmap; rt711->regmap = regmap; + regcache_cache_only(rt711->regmap, true); + mutex_init(&rt711->calibrate_mutex); mutex_init(&rt711->disable_irq_lock); @@ -1204,8 +1213,25 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap, &soc_codec_dev_rt711, rt711_dai, ARRAY_SIZE(rt711_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - dev_dbg(&slave->dev, "%s\n", __func__); + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); return ret; } @@ -1219,28 +1245,17 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave) if (rt711->hw_init) return 0; - if (rt711->first_hw_init) { - regcache_cache_only(rt711->regmap, false); + regcache_cache_only(rt711->regmap, false); + if (rt711->first_hw_init) regcache_cache_bypass(rt711->regmap, true); - } /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!rt711->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - + if (!rt711->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - pm_runtime_get_noresume(&slave->dev); rt711_reset(rt711->regmap); diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 869cc7bfd178..ba08d03e717c 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -182,27 +182,18 @@ static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave) if (rt712->hw_init) return 0; + regcache_cache_only(rt712->regmap, false); + regcache_cache_only(rt712->mbq_regmap, false); if (rt712->first_hw_init) { - regcache_cache_only(rt712->regmap, false); regcache_cache_bypass(rt712->regmap, true); - regcache_cache_only(rt712->mbq_regmap, false); regcache_cache_bypass(rt712->mbq_regmap, true); } else { /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); } pm_runtime_get_noresume(&slave->dev); @@ -608,6 +599,9 @@ static int rt712_sdca_dmic_probe(struct snd_soc_component *component) rt712->component = component; + if (!rt712->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -777,6 +771,9 @@ static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap, rt712->regmap = regmap; rt712->mbq_regmap = mbq_regmap; + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -791,10 +788,27 @@ static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap, &soc_sdca_dev_rt712_dmic, rt712_sdca_dmic_dai, ARRAY_SIZE(rt712_sdca_dmic_dai)); + if (ret < 0) + return ret; - dev_dbg(&slave->dev, "%s\n", __func__); + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - return ret; + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } @@ -954,10 +968,7 @@ static int rt712_sdca_dmic_sdw_probe(struct sdw_slave *slave, static int rt712_sdca_dmic_sdw_remove(struct sdw_slave *slave) { - struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev); - - if (rt712->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 6bc50396a0f6..6b644a89c589 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -363,8 +363,7 @@ static int rt712_sdca_sdw_remove(struct sdw_slave *slave) cancel_delayed_work_sync(&rt712->jack_btn_check_work); } - if (rt712->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); mutex_destroy(&rt712->calibrate_mutex); mutex_destroy(&rt712->disable_irq_lock); diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 89d245655ca4..7077ff6ba1f4 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -453,6 +453,9 @@ static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, rt712->hs_jack = hs_jack; + if (!rt712->first_hw_init) + return 0; + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0) { if (ret != -EACCES) { @@ -960,6 +963,9 @@ static int rt712_sdca_probe(struct snd_soc_component *component) rt712_sdca_parse_dt(rt712, &rt712->slave->dev); rt712->component = component; + if (!rt712->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -1183,6 +1189,9 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, rt712->regmap = regmap; rt712->mbq_regmap = mbq_regmap; + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + mutex_init(&rt712->calibrate_mutex); mutex_init(&rt712->disable_irq_lock); @@ -1207,10 +1216,27 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, else ret = devm_snd_soc_register_component(dev, &soc_sdca_dev_rt712, rt712_sdca_dai, 1); + if (ret < 0) + return ret; - dev_dbg(&slave->dev, "%s\n", __func__); + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - return ret; + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) @@ -1224,27 +1250,18 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt712->hw_init) return 0; + regcache_cache_only(rt712->regmap, false); + regcache_cache_only(rt712->mbq_regmap, false); if (rt712->first_hw_init) { - regcache_cache_only(rt712->regmap, false); regcache_cache_bypass(rt712->regmap, true); - regcache_cache_only(rt712->mbq_regmap, false); regcache_cache_bypass(rt712->mbq_regmap, true); } else { /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); } pm_runtime_get_noresume(&slave->dev); diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index df10916bab46..ab54a67a27eb 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -193,10 +193,7 @@ static int rt715_sdca_sdw_probe(struct sdw_slave *slave, static int rt715_sdca_sdw_remove(struct sdw_slave *slave) { - struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev); - - if (rt715->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index b989f907784b..9fa96fd83d4a 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -761,8 +761,12 @@ static const struct snd_soc_dapm_route rt715_sdca_audio_map[] = { static int rt715_sdca_probe(struct snd_soc_component *component) { + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); int ret; + if (!rt715->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -977,6 +981,10 @@ int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap, rt715->regmap = regmap; rt715->mbq_regmap = mbq_regmap; rt715->hw_sdw_ver = slave->id.sdw_version; + + regcache_cache_only(rt715->regmap, true); + regcache_cache_only(rt715->mbq_regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -988,6 +996,25 @@ int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap, &soc_codec_dev_rt715_sdca, rt715_sdca_dai, ARRAY_SIZE(rt715_sdca_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); return ret; } @@ -1000,22 +1027,16 @@ int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt715->hw_init) return 0; + regcache_cache_only(rt715->regmap, false); + regcache_cache_only(rt715->mbq_regmap, false); + /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ if (!rt715->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - rt715->first_hw_init = true; } diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 6db87442b783..21f37babd148 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -508,17 +508,12 @@ static int rt715_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return PTR_ERR(regmap); - rt715_init(&slave->dev, sdw_regmap, regmap, slave); - - return 0; + return rt715_init(&slave->dev, sdw_regmap, regmap, slave); } static int rt715_sdw_remove(struct sdw_slave *slave) { - struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); - - if (rt715->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 6c2e165dd621..b59230c8fd32 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -16,14 +16,11 @@ #include <linux/pm_runtime.h> #include <linux/pm.h> #include <linux/soundwire/sdw.h> -#include <linux/gpio.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> -#include <linux/gpio/consumer.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_device.h> #include <sound/core.h> #include <sound/pcm.h> @@ -52,6 +49,60 @@ static int rt715_index_write(struct regmap *regmap, unsigned int reg, return ret; } +static int rt715_index_write_nid(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int value) +{ + int ret; + unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + pr_err("Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt715_index_read_nid(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg; + + *value = 0; + ret = regmap_read(regmap, addr, value); + if (ret < 0) + pr_err("Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt715_index_update_bits(struct regmap *regmap, unsigned int nid, + unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp, orig; + int ret; + + ret = rt715_index_read_nid(regmap, nid, reg, &orig); + if (ret < 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + + return rt715_index_write_nid(regmap, nid, reg, tmp); +} + +static void rt715_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT715_FUNC_RESET, 0); + rt715_index_update_bits(regmap, RT715_VENDOR_REGISTERS, + RT715_VD_CLEAR_CTRL, RT715_CLEAR_HIDDEN_REG, + RT715_CLEAR_HIDDEN_REG); +} + + static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h, unsigned int addr_l, unsigned int val_h, unsigned int *r_val, unsigned int *l_val) @@ -740,8 +791,12 @@ static int rt715_set_bias_level(struct snd_soc_component *component, static int rt715_probe(struct snd_soc_component *component) { + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); int ret; + if (!rt715->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -984,6 +1039,8 @@ int rt715_init(struct device *dev, struct regmap *sdw_regmap, rt715->regmap = regmap; rt715->sdw_regmap = sdw_regmap; + regcache_cache_only(rt715->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -995,8 +1052,25 @@ int rt715_init(struct device *dev, struct regmap *sdw_regmap, &soc_codec_dev_rt715, rt715_dai, ARRAY_SIZE(rt715_dai)); + if (ret < 0) + return ret; - return ret; + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + return 0; } int rt715_io_init(struct device *dev, struct sdw_slave *slave) @@ -1006,25 +1080,19 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave) if (rt715->hw_init) return 0; + regcache_cache_only(rt715->regmap, false); + /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!rt715->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - + if (!rt715->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - pm_runtime_get_noresume(&slave->dev); + rt715_reset(rt715->regmap); + /* Mute nid=08h/09h */ regmap_write(rt715->regmap, RT715_SET_GAIN_LINE_ADC_H, 0xb080); regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC_H, 0xb080); diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h index 12a0ae656d09..6e37bf64e12f 100644 --- a/sound/soc/codecs/rt715.h +++ b/sound/soc/codecs/rt715.h @@ -48,6 +48,7 @@ struct rt715_priv { #define RT715_INLINE_CMD 0x55 /* Index (NID:20h) */ +#define RT715_VD_CLEAR_CTRL 0x01 #define RT715_SDW_INPUT_SEL 0x39 #define RT715_EXT_DMIC_CLK_CTRL2 0x54 @@ -71,6 +72,8 @@ struct rt715_priv { #define RT715_READ_HDA_0 0x2015 #define RT715_PRIV_INDEX_W_H 0x7520 #define RT715_PRIV_INDEX_W_L 0x85a0 +#define RT715_PRIV_INDEX_W_H_2 0x7500 +#define RT715_PRIV_INDEX_W_L_2 0x8580 #define RT715_PRIV_DATA_W_H 0x7420 #define RT715_PRIV_DATA_W_L 0x84a0 #define RT715_PRIV_INDEX_R_H 0x9d20 @@ -198,6 +201,10 @@ struct rt715_priv { #define RT715_SET_DMIC4_CONFIG_DEFAULT4\ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC4) +/* vendor register clear ctrl-1 (0x01)(NID:20h) */ +#define RT715_CLEAR_HIDDEN_REG (0x1 << 15) + + #define RT715_MUTE_SFT 7 #define RT715_DIR_IN_SFT 6 #define RT715_DIR_OUT_SFT 7 diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index e9103ffb3f50..a38ec5862214 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -175,7 +175,7 @@ static int rt722_sdca_update_status(struct sdw_slave *slave, * This also could sync with the cache value as the rt722_sdca_jack_init set. */ sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, - SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6); + SDW_SCP_SDCA_INTMASK_SDCA_6); sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); } diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 9c0d34366c9e..0e1c65a20392 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -191,8 +191,7 @@ static void rt722_sdca_jack_detect_handler(struct work_struct *work) return; /* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */ - if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6 || - rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) { + if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6) { ret = rt722_sdca_headset_detect(rt722); if (ret < 0) return; diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 3047a6fbb380..b93c078a8040 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -669,36 +669,19 @@ static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, struct sigmadsp_control *ctrl, unsigned int samplerate_mask) { struct snd_card *card = sigmadsp->component->card->snd_card; - struct snd_kcontrol_volatile *vd; - struct snd_ctl_elem_id id; bool active; - bool changed = false; + int changed; active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask); - - down_write(&card->controls_rwsem); - if (!ctrl->kcontrol) { - up_write(&card->controls_rwsem); + if (!ctrl->kcontrol) return; - } - - id = ctrl->kcontrol->id; - vd = &ctrl->kcontrol->vd[0]; - if (active == (bool)(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) { - vd->access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE; - changed = true; - } - up_write(&card->controls_rwsem); - - if (active && changed) { + changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active); + if (active && changed > 0) { mutex_lock(&sigmadsp->lock); if (ctrl->cached) sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); mutex_unlock(&sigmadsp->lock); } - - if (changed) - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &id); } /** diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c index 008cb3eb5758..8c6665677a17 100644 --- a/sound/soc/codecs/ssm3515.c +++ b/sound/soc/codecs/ssm3515.c @@ -437,7 +437,7 @@ MODULE_DEVICE_TABLE(of, ssm3515_of_match); static struct i2c_driver ssm3515_i2c_driver = { .driver = { .name = "ssm3515", - .of_match_table = of_match_ptr(ssm3515_of_match), + .of_match_table = ssm3515_of_match, }, .probe = ssm3515_i2c_probe, }; diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 4a694d0bfd68..34ffd32ab9dc 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -1022,7 +1022,7 @@ static const struct regmap_config sta32x_regmap = { .max_register = STA32X_FDRC2, .reg_defaults = sta32x_regs, .num_reg_defaults = ARRAY_SIZE(sta32x_regs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .wr_table = &sta32x_write_regs, .rd_table = &sta32x_read_regs, .volatile_table = &sta32x_volatile_regs, diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index d05f3fd57661..e4a9e9241c60 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1065,7 +1065,7 @@ static const struct regmap_config sta350_regmap = { .max_register = STA350_MISC2, .reg_defaults = sta350_regs, .num_reg_defaults = ARRAY_SIZE(sta350_regs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .wr_table = &sta350_write_regs, .rd_table = &sta350_read_regs, .volatile_table = &sta350_volatile_regs, diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 0ac08478ddac..eedafef775e5 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -331,7 +331,7 @@ static const struct regmap_config sta529_regmap = { .max_register = STA529_MAX_REGISTER, .readable_reg = sta529_readable, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = sta529_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults), }; diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 1824a71fe053..2f9f10a4dfed 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -67,7 +67,7 @@ static const struct regmap_config stac9766_regmap_config = { .reg_stride = 2, .val_bits = 16, .max_register = 0x78, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = regmap_ac97_default_volatile, diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c index 99545bcb2ba9..c421906a0694 100644 --- a/sound/soc/codecs/sti-sas.c +++ b/sound/soc/codecs/sti-sas.c @@ -316,7 +316,7 @@ static const struct regmap_config stih407_sas_regmap = { .reg_defaults = stih407_sas_reg_defaults, .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults), .volatile_reg = sti_sas_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_read = sti_sas_read_reg, .reg_write = sti_sas_write_reg, }; diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 4c59429a42b7..55cd5e3c23a5 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -743,7 +743,6 @@ MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match); static struct i2c_driver tasdevice_i2c_driver = { .driver = { .name = "tas2781-codec", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(tasdevice_of_match), #ifdef CONFIG_ACPI .acpi_match_table = ACPI_PTR(tasdevice_acpi_match), diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c index aca3756ffab6..3b53eba38a0b 100644 --- a/sound/soc/codecs/tas5805m.c +++ b/sound/soc/codecs/tas5805m.c @@ -520,12 +520,11 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) } tas5805m->dsp_cfg_len = fw->size; - tas5805m->dsp_cfg_data = devm_kmalloc(dev, fw->size, GFP_KERNEL); + tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL); if (!tas5805m->dsp_cfg_data) { release_firmware(fw); return -ENOMEM; } - memcpy(tas5805m->dsp_cfg_data, fw->data, fw->size); release_firmware(fw); diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c index c116e82f712d..5c0a76a4a106 100644 --- a/sound/soc/codecs/tlv320aic32x4-clk.c +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -321,7 +321,7 @@ static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, u8 divisor; divisor = DIV_ROUND_UP(parent_rate, rate); - if (divisor > 128) + if (divisor > AIC32X4_DIV_MAX) return -EINVAL; return regmap_update_bits(div->regmap, div->reg, @@ -334,7 +334,7 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, unsigned long divisor; divisor = DIV_ROUND_UP(req->best_parent_rate, req->rate); - if (divisor > 128) + if (divisor > AIC32X4_DIV_MAX) return -EINVAL; req->rate = DIV_ROUND_UP(req->best_parent_rate, divisor); @@ -345,12 +345,18 @@ static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_aic32x4 *div = to_clk_aic32x4(hw); - unsigned int val; + int err; + + err = regmap_read(div->regmap, div->reg, &val); + if (err) + return 0; - regmap_read(div->regmap, div->reg, &val); + val &= AIC32X4_DIV_MASK; + if (!val) + val = AIC32X4_DIV_MAX; - return DIV_ROUND_UP(parent_rate, val & AIC32X4_DIV_MASK); + return DIV_ROUND_UP(parent_rate, val); } static const struct clk_ops aic32x4_div_ops = { diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index ffe1828a4b7e..6829834a412f 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -1349,7 +1349,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) return -ENOMEM; aic32x4->dev = dev; - aic32x4->type = (enum aic32x4_type)dev_get_drvdata(dev); + aic32x4->type = (uintptr_t)dev_get_drvdata(dev); dev_set_drvdata(dev, aic32x4); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index 4de5bd9e8cc5..d6101ce73f80 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -223,8 +223,9 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); #define AIC32X4_REFPOWERUP_120MS 0x07 /* Common mask and enable for all of the dividers */ -#define AIC32X4_DIVEN BIT(7) -#define AIC32X4_DIV_MASK GENMASK(6, 0) +#define AIC32X4_DIVEN BIT(7) +#define AIC32X4_DIV_MASK GENMASK(6, 0) +#define AIC32X4_DIV_MAX 128 /* Clock Limits */ #define AIC32X4_MAX_DOSR_FREQ 6200000 diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c deleted file mode 100644 index 1a62bec94005..000000000000 --- a/sound/soc/codecs/uda134x.c +++ /dev/null @@ -1,587 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * uda134x.c -- UDA134X ALSA SoC Codec driver - * - * Modifications by Christian Pellegrin <chripell@evolware.org> - * - * Copyright 2007 Dension Audio Systems Ltd. - * Author: Zoltan Devai - * - * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> -#include <sound/initval.h> - -#include <sound/uda134x.h> -#include <sound/l3.h> - -#include "uda134x.h" - - -#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000 -#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE) - -struct uda134x_priv { - int sysclk; - int dai_fmt; - - struct snd_pcm_substream *master_substream; - struct snd_pcm_substream *slave_substream; - - struct regmap *regmap; - struct uda134x_platform_data *pd; -}; - -static const struct reg_default uda134x_reg_defaults[] = { - { UDA134X_EA000, 0x04 }, - { UDA134X_EA001, 0x04 }, - { UDA134X_EA010, 0x04 }, - { UDA134X_EA011, 0x00 }, - { UDA134X_EA100, 0x00 }, - { UDA134X_EA101, 0x00 }, - { UDA134X_EA110, 0x00 }, - { UDA134X_EA111, 0x00 }, - { UDA134X_STATUS0, 0x00 }, - { UDA134X_STATUS1, 0x03 }, - { UDA134X_DATA000, 0x00 }, - { UDA134X_DATA001, 0x00 }, - { UDA134X_DATA010, 0x00 }, - { UDA134X_DATA011, 0x00 }, - { UDA134X_DATA1, 0x00 }, -}; - -/* - * Write to the uda134x registers - * - */ -static int uda134x_regmap_write(void *context, unsigned int reg, - unsigned int value) -{ - struct uda134x_platform_data *pd = context; - int ret; - u8 addr; - u8 data = value; - - switch (reg) { - case UDA134X_STATUS0: - case UDA134X_STATUS1: - addr = UDA134X_STATUS_ADDR; - data |= (reg - UDA134X_STATUS0) << 7; - break; - case UDA134X_DATA000: - case UDA134X_DATA001: - case UDA134X_DATA010: - case UDA134X_DATA011: - addr = UDA134X_DATA0_ADDR; - data |= (reg - UDA134X_DATA000) << 6; - break; - case UDA134X_DATA1: - addr = UDA134X_DATA1_ADDR; - break; - default: - /* It's an extended address register */ - addr = (reg | UDA134X_EXTADDR_PREFIX); - - ret = l3_write(&pd->l3, - UDA134X_DATA0_ADDR, &addr, 1); - if (ret != 1) - return -EIO; - - addr = UDA134X_DATA0_ADDR; - data = (value | UDA134X_EXTDATA_PREFIX); - break; - } - - ret = l3_write(&pd->l3, - addr, &data, 1); - if (ret != 1) - return -EIO; - - return 0; -} - -static inline void uda134x_reset(struct snd_soc_component *component) -{ - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - unsigned int mask = 1<<6; - - regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, mask); - msleep(1); - regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, 0); -} - -static int uda134x_mute(struct snd_soc_dai *dai, int mute, int direction) -{ - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(dai->component); - unsigned int mask = 1<<2; - unsigned int val; - - pr_debug("%s mute: %d\n", __func__, mute); - - if (mute) - val = mask; - else - val = 0; - - return regmap_update_bits(uda134x->regmap, UDA134X_DATA010, mask, val); -} - -static int uda134x_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - struct snd_pcm_runtime *master_runtime; - - if (uda134x->master_substream) { - master_runtime = uda134x->master_substream->runtime; - - pr_debug("%s constraining to %d bits at %d\n", __func__, - master_runtime->sample_bits, - master_runtime->rate); - - snd_pcm_hw_constraint_single(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, - master_runtime->rate); - - snd_pcm_hw_constraint_single(substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - master_runtime->sample_bits); - - uda134x->slave_substream = substream; - } else - uda134x->master_substream = substream; - - return 0; -} - -static void uda134x_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - - if (uda134x->master_substream == substream) - uda134x->master_substream = uda134x->slave_substream; - - uda134x->slave_substream = NULL; -} - -static int uda134x_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - unsigned int hw_params = 0; - - if (substream == uda134x->slave_substream) { - pr_debug("%s ignoring hw_params for slave substream\n", - __func__); - return 0; - } - - pr_debug("%s sysclk: %d, rate:%d\n", __func__, - uda134x->sysclk, params_rate(params)); - - /* set SYSCLK / fs ratio */ - switch (uda134x->sysclk / params_rate(params)) { - case 512: - break; - case 384: - hw_params |= (1<<4); - break; - case 256: - hw_params |= (1<<5); - break; - default: - printk(KERN_ERR "%s unsupported fs\n", __func__); - return -EINVAL; - } - - pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__, - uda134x->dai_fmt, params_format(params)); - - /* set DAI format and word length */ - switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - break; - case SND_SOC_DAIFMT_RIGHT_J: - switch (params_width(params)) { - case 16: - hw_params |= (1<<1); - break; - case 18: - hw_params |= (1<<2); - break; - case 20: - hw_params |= ((1<<2) | (1<<1)); - break; - default: - printk(KERN_ERR "%s unsupported format (right)\n", - __func__); - return -EINVAL; - } - break; - case SND_SOC_DAIFMT_LEFT_J: - hw_params |= (1<<3); - break; - default: - printk(KERN_ERR "%s unsupported format\n", __func__); - return -EINVAL; - } - - return regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, - STATUS0_SYSCLK_MASK | STATUS0_DAIFMT_MASK, hw_params); -} - -static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir) -{ - struct snd_soc_component *component = codec_dai->component; - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - - pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__, - clk_id, freq, dir); - - /* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable - because the codec is slave. Of course limitations of the clock - master (the IIS controller) apply. - We'll error out on set_hw_params if it's not OK */ - if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { - uda134x->sysclk = freq; - return 0; - } - - printk(KERN_ERR "%s unsupported sysclk\n", __func__); - return -EINVAL; -} - -static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -{ - struct snd_soc_component *component = codec_dai->component; - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - - pr_debug("%s fmt: %08X\n", __func__, fmt); - - /* codec supports only full consumer mode */ - if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { - printk(KERN_ERR "%s unsupported clocking mode\n", __func__); - return -EINVAL; - } - - /* no support for clock inversion */ - if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) { - printk(KERN_ERR "%s unsupported clock inversion\n", __func__); - return -EINVAL; - } - - /* We can't setup DAI format here as it depends on the word bit num */ - /* so let's just store the value for later */ - uda134x->dai_fmt = fmt; - - return 0; -} - -static int uda134x_set_bias_level(struct snd_soc_component *component, - enum snd_soc_bias_level level) -{ - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - struct uda134x_platform_data *pd = uda134x->pd; - pr_debug("%s bias level %d\n", __func__, level); - - switch (level) { - case SND_SOC_BIAS_ON: - break; - case SND_SOC_BIAS_PREPARE: - /* power on */ - if (pd->power) { - pd->power(1); - regcache_sync(uda134x->regmap); - } - break; - case SND_SOC_BIAS_STANDBY: - break; - case SND_SOC_BIAS_OFF: - /* power off */ - if (pd->power) { - pd->power(0); - regcache_mark_dirty(uda134x->regmap); - } - break; - } - return 0; -} - -static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1", - "Minimum2", "Maximum"}; -static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; -static const char *uda134x_mixmode[] = {"Differential", "Analog1", - "Analog2", "Both"}; - -static const struct soc_enum uda134x_mixer_enum[] = { -SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting), -SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph), -SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode), -}; - -static const struct snd_kcontrol_new uda1341_snd_controls[] = { -SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1), -SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0), -SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1), -SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1), - -SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0), -SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0), - -SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0), -SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0), - -SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]), -SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), -SOC_ENUM("Input Mux", uda134x_mixer_enum[2]), - -SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0), -SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1), -SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0), - -SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0), -SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0), -SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0), -SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0), -SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0), -SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), -}; - -static const struct snd_kcontrol_new uda1340_snd_controls[] = { -SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1), - -SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0), -SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0), - -SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]), -SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), - -SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), -}; - -static const struct snd_kcontrol_new uda1345_snd_controls[] = { -SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1), - -SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), - -SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), -}; - -/* UDA1341 has the DAC/ADC power down in STATUS1 */ -static const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = { - SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0), - SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0), -}; - -/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */ -static const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = { - SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0), - SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0), -}; - -/* Common DAPM widgets */ -static const struct snd_soc_dapm_widget uda134x_dapm_widgets[] = { - SND_SOC_DAPM_INPUT("VINL1"), - SND_SOC_DAPM_INPUT("VINR1"), - SND_SOC_DAPM_INPUT("VINL2"), - SND_SOC_DAPM_INPUT("VINR2"), - SND_SOC_DAPM_OUTPUT("VOUTL"), - SND_SOC_DAPM_OUTPUT("VOUTR"), -}; - -static const struct snd_soc_dapm_route uda134x_dapm_routes[] = { - { "ADC", NULL, "VINL1" }, - { "ADC", NULL, "VINR1" }, - { "ADC", NULL, "VINL2" }, - { "ADC", NULL, "VINR2" }, - { "VOUTL", NULL, "DAC" }, - { "VOUTR", NULL, "DAC" }, -}; - -static const struct snd_soc_dai_ops uda134x_dai_ops = { - .startup = uda134x_startup, - .shutdown = uda134x_shutdown, - .hw_params = uda134x_hw_params, - .mute_stream = uda134x_mute, - .set_sysclk = uda134x_set_dai_sysclk, - .set_fmt = uda134x_set_dai_fmt, - .no_capture_mute = 1, -}; - -static struct snd_soc_dai_driver uda134x_dai = { - .name = "uda134x-hifi", - /* playback capabilities */ - .playback = { - .stream_name = "Playback", - .channels_min = 1, - .channels_max = 2, - .rates = UDA134X_RATES, - .formats = UDA134X_FORMATS, - }, - /* capture capabilities */ - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = UDA134X_RATES, - .formats = UDA134X_FORMATS, - }, - /* pcm operations */ - .ops = &uda134x_dai_ops, -}; - -static int uda134x_soc_probe(struct snd_soc_component *component) -{ - struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - struct uda134x_platform_data *pd = uda134x->pd; - const struct snd_soc_dapm_widget *widgets; - unsigned int num_widgets; - int ret; - - printk(KERN_INFO "UDA134X SoC Audio Codec\n"); - - switch (pd->model) { - case UDA134X_UDA1340: - case UDA134X_UDA1341: - case UDA134X_UDA1344: - case UDA134X_UDA1345: - break; - default: - printk(KERN_ERR "UDA134X SoC codec: " - "unsupported model %d\n", - pd->model); - return -EINVAL; - } - - if (pd->power) - pd->power(1); - - uda134x_reset(component); - - if (pd->model == UDA134X_UDA1341) { - widgets = uda1341_dapm_widgets; - num_widgets = ARRAY_SIZE(uda1341_dapm_widgets); - } else { - widgets = uda1340_dapm_widgets; - num_widgets = ARRAY_SIZE(uda1340_dapm_widgets); - } - - ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets); - if (ret) { - printk(KERN_ERR "%s failed to register dapm controls: %d", - __func__, ret); - return ret; - } - - switch (pd->model) { - case UDA134X_UDA1340: - case UDA134X_UDA1344: - ret = snd_soc_add_component_controls(component, uda1340_snd_controls, - ARRAY_SIZE(uda1340_snd_controls)); - break; - case UDA134X_UDA1341: - ret = snd_soc_add_component_controls(component, uda1341_snd_controls, - ARRAY_SIZE(uda1341_snd_controls)); - break; - case UDA134X_UDA1345: - ret = snd_soc_add_component_controls(component, uda1345_snd_controls, - ARRAY_SIZE(uda1345_snd_controls)); - break; - default: - printk(KERN_ERR "%s unknown codec type: %d", - __func__, pd->model); - return -EINVAL; - } - - if (ret < 0) { - printk(KERN_ERR "UDA134X: failed to register controls\n"); - return ret; - } - - return 0; -} - -static const struct snd_soc_component_driver soc_component_dev_uda134x = { - .probe = uda134x_soc_probe, - .set_bias_level = uda134x_set_bias_level, - .dapm_widgets = uda134x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets), - .dapm_routes = uda134x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes), - .suspend_bias_off = 1, - .idle_bias_on = 1, - .use_pmdown_time = 1, - .endianness = 1, -}; - -static const struct regmap_config uda134x_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - .max_register = UDA134X_DATA1, - .reg_defaults = uda134x_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(uda134x_reg_defaults), - .cache_type = REGCACHE_RBTREE, - - .reg_write = uda134x_regmap_write, -}; - -static int uda134x_codec_probe(struct platform_device *pdev) -{ - struct uda134x_platform_data *pd = pdev->dev.platform_data; - struct uda134x_priv *uda134x; - int ret; - - if (!pd) { - dev_err(&pdev->dev, "Missing L3 bitbang function\n"); - return -ENODEV; - } - - uda134x = devm_kzalloc(&pdev->dev, sizeof(*uda134x), GFP_KERNEL); - if (!uda134x) - return -ENOMEM; - - uda134x->pd = pd; - platform_set_drvdata(pdev, uda134x); - - if (pd->l3.use_gpios) { - ret = l3_set_gpio_ops(&pdev->dev, &uda134x->pd->l3); - if (ret < 0) - return ret; - } - - uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd, - &uda134x_regmap_config); - if (IS_ERR(uda134x->regmap)) - return PTR_ERR(uda134x->regmap); - - return devm_snd_soc_register_component(&pdev->dev, - &soc_component_dev_uda134x, &uda134x_dai, 1); -} - -static struct platform_driver uda134x_codec_driver = { - .driver = { - .name = "uda134x-codec", - }, - .probe = uda134x_codec_probe, -}; - -module_platform_driver(uda134x_codec_driver); - -MODULE_DESCRIPTION("UDA134X ALSA soc codec driver"); -MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h deleted file mode 100644 index 664618c2571c..000000000000 --- a/sound/soc/codecs/uda134x.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _UDA134X_CODEC_H -#define _UDA134X_CODEC_H - -#define UDA134X_L3ADDR 5 -#define UDA134X_DATA0_ADDR ((UDA134X_L3ADDR << 2) | 0) -#define UDA134X_DATA1_ADDR ((UDA134X_L3ADDR << 2) | 1) -#define UDA134X_STATUS_ADDR ((UDA134X_L3ADDR << 2) | 2) - -#define UDA134X_EXTADDR_PREFIX 0xC0 -#define UDA134X_EXTDATA_PREFIX 0xE0 - -/* UDA134X registers */ -#define UDA134X_EA000 0 -#define UDA134X_EA001 1 -#define UDA134X_EA010 2 -#define UDA134X_EA011 3 -#define UDA134X_EA100 4 -#define UDA134X_EA101 5 -#define UDA134X_EA110 6 -#define UDA134X_EA111 7 -#define UDA134X_STATUS0 8 -#define UDA134X_STATUS1 9 -#define UDA134X_DATA000 10 -#define UDA134X_DATA001 11 -#define UDA134X_DATA010 12 -#define UDA134X_DATA011 13 -#define UDA134X_DATA1 14 - -#define STATUS0_DAIFMT_MASK (~(7<<1)) -#define STATUS0_SYSCLK_MASK (~(3<<4)) - -#endif diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 8bf3510a3ea3..a05b553e6472 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -4968,7 +4968,7 @@ static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg) static struct regmap_config wcd9335_regmap_config = { .reg_bits = 16, .val_bits = 8, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .max_register = WCD9335_MAX_REGISTER, .can_multi_write = true, .ranges = wcd9335_ranges, diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index bd0e9fbc12eb..6951120057e5 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1183,7 +1183,7 @@ static const struct regmap_config wcd938x_regmap_config = { .name = "wcd938x_csr", .reg_bits = 32, .val_bits = 8, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wcd938x_defaults, .num_reg_defaults = ARRAY_SIZE(wcd938x_defaults), .max_register = WCD938X_MAX_REGISTER, diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 277b8c468c78..9679906c6bd5 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1770,11 +1770,6 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops wm2200_dai_ops = { - .set_fmt = wm2200_set_fmt, - .hw_params = wm2200_hw_params, -}; - static int wm2200_set_sysclk(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir) { @@ -2068,6 +2063,12 @@ static int wm2200_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops wm2200_dai_ops = { + .probe = wm2200_dai_probe, + .set_fmt = wm2200_set_fmt, + .hw_params = wm2200_hw_params, +}; + #define WM2200_RATES SNDRV_PCM_RATE_8000_48000 #define WM2200_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ @@ -2075,7 +2076,6 @@ static int wm2200_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver wm2200_dai = { .name = "wm2200", - .probe = wm2200_dai_probe, .playback = { .stream_name = "Playback", .channels_min = 2, @@ -2151,7 +2151,7 @@ static const struct regmap_config wm2200_regmap = { .num_reg_defaults = ARRAY_SIZE(wm2200_reg_defaults), .volatile_reg = wm2200_volatile_register, .readable_reg = wm2200_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .ranges = wm2200_ranges, .num_ranges = ARRAY_SIZE(wm2200_ranges), }; diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index a86eacb2a9bb..ff63723928a1 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2400,7 +2400,7 @@ static const struct regmap_config wm5100_regmap = { .num_reg_defaults = ARRAY_SIZE(wm5100_reg_defaults), .volatile_reg = wm5100_volatile_register, .readable_reg = wm5100_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static const unsigned int wm5100_mic_ctrl_reg[] = { diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 3bdbdf3770b5..4ecf07c7448c 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1773,6 +1773,10 @@ static int wm5102_set_fll(struct snd_soc_component *component, int fll_id, #define WM5102_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +static const struct snd_soc_dai_ops wm5102_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver wm5102_dai[] = { { .name = "wm5102-aif1", @@ -1906,7 +1910,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = { .rates = WM5102_RATES, .formats = WM5102_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &wm5102_dai_ops, }, { .name = "wm5102-dsp-trace", diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index ad670300de8d..ac1f2c850346 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2073,6 +2073,10 @@ static int wm5110_set_fll(struct snd_soc_component *component, int fll_id, #define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +static const struct snd_soc_dai_ops wm5110_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver wm5110_dai[] = { { .name = "wm5110-aif1", @@ -2206,7 +2210,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = { .rates = WM5110_RATES, .formats = WM5110_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &wm5110_dai_ops, }, { .name = "wm5110-dsp-voicectrl", @@ -2227,7 +2231,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = { .rates = WM5110_RATES, .formats = WM5110_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &wm5110_dai_ops, }, { .name = "wm5110-dsp-trace", diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index c0ed76d5b65f..6636a70f3895 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -607,7 +607,7 @@ static const struct regmap_config wm8510_regmap = { .reg_defaults = wm8510_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8510_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8510_volatile, }; diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 55c72c5ac845..ea87cd3cc0d6 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -437,7 +437,7 @@ static const struct regmap_config wm8523_regmap = { .reg_defaults = wm8523_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8523_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8523_volatile_register, }; diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 34ae7fe05398..6d22f7d40ec2 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -975,7 +975,7 @@ static const struct regmap_config wm8580_regmap = { .reg_defaults = wm8580_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8580_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8580_volatile, }; diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 903a0147d584..916f297164de 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -393,7 +393,7 @@ static const struct regmap_config wm8711_regmap = { .reg_defaults = wm8711_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8711_volatile, }; diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 5ea6d8fd10f6..0c943e7d4159 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -236,7 +236,7 @@ static const struct regmap_config wm8728_regmap = { .reg_defaults = wm8728_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8728_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index d5ab3ba126a6..efc160c75f40 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -642,7 +642,7 @@ const struct regmap_config wm8731_regmap = { .max_register = WM8731_RESET, .volatile_reg = wm8731_volatile, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8731_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults), }; diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index 9f4e372e90ea..0d231c289ef3 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -599,7 +599,7 @@ static const struct regmap_config wm8737_regmap = { .reg_defaults = wm8737_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8737_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8737_volatile, }; diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 787156b980a1..19e8fc4062c7 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -543,7 +543,7 @@ static const struct regmap_config wm8741_regmap = { .reg_defaults = wm8741_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 20dc9ff9fea9..2d2feaf95e49 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -735,7 +735,7 @@ static const struct regmap_config wm8750_regmap = { .reg_defaults = wm8750_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8750_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 5e8a8eb41b2b..b5d8290c37d9 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1507,7 +1507,7 @@ static const struct regmap_config wm8753_regmap = { .max_register = WM8753_ADCTL2, .volatile_reg = wm8753_volatile, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8753_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8753_reg_defaults), }; diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c index e03fee8869c3..2469f4f3bea3 100644 --- a/sound/soc/codecs/wm8770.c +++ b/sound/soc/codecs/wm8770.c @@ -632,7 +632,7 @@ static const struct regmap_config wm8770_regmap = { .reg_defaults = wm8770_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8770_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8770_volatile_reg, }; diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 212224a68006..0673bbd32bab 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -451,7 +451,7 @@ static const struct regmap_config wm8776_regmap = { .reg_defaults = wm8776_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8776_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8776_volatile, }; diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 0b234bae480e..bbb4b6e3b41c 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -555,7 +555,7 @@ const struct regmap_config wm8804_regmap_config = { .max_register = WM8804_MAX_REGISTER, .volatile_reg = wm8804_volatile, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8804_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults), }; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 320ccd92f318..84d06c190411 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1223,7 +1223,7 @@ static const struct regmap_config wm8900_regmap = { .reg_defaults = wm8900_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8900_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8900_volatile_register, }; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 901b65ef8de5..84ae1102ac88 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1902,7 +1902,7 @@ static const struct regmap_config wm8903_regmap = { .volatile_reg = wm8903_volatile_register, .readable_reg = wm8903_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8903_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8903_reg_defaults), }; diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index f2baee7c332e..829bf055622a 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -2148,7 +2148,7 @@ static const struct regmap_config wm8904_regmap = { .volatile_reg = wm8904_volatile_register, .readable_reg = wm8904_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8904_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8904_reg_defaults), }; @@ -2202,7 +2202,7 @@ static int wm8904_i2c_probe(struct i2c_client *i2c) match = of_match_node(wm8904_of_match, i2c->dev.of_node); if (match == NULL) return -EINVAL; - wm8904->devtype = (enum wm8904_type)match->data; + wm8904->devtype = (uintptr_t)match->data; } else { const struct i2c_device_id *id = i2c_match_id(wm8904_i2c_id, i2c); diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 53c27986d216..b9432f8b64e5 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -815,7 +815,7 @@ static const struct regmap_config wm8940_regmap = { .max_register = WM8940_MONOMIX, .reg_defaults = wm8940_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .readable_reg = wm8940_readable_register, .volatile_reg = wm8940_volatile_register, diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 78044f580a67..4f4338326438 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -962,7 +962,7 @@ static const struct regmap_config wm8955_regmap = { .volatile_reg = wm8955_volatile, .writeable_reg = wm8955_writeable, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8955_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8955_reg_defaults), }; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 366f5d769d6d..0a50180750e8 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -120,6 +120,15 @@ static bool wm8960_volatile(struct device *dev, unsigned int reg) } } +#define WM8960_NUM_SUPPLIES 5 +static const char *wm8960_supply_names[WM8960_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "AVDD", + "SPKVDD1", + "SPKVDD2", +}; + struct wm8960_priv { struct clk *mclk; struct regmap *regmap; @@ -137,6 +146,7 @@ struct wm8960_priv { bool is_stream_in_use[2]; struct wm8960_data pdata; ktime_t dsch_start; + struct regulator_bulk_data supplies[WM8960_NUM_SUPPLIES]; }; #define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0) @@ -155,6 +165,7 @@ static const char *wm8960_adc_data_output_sel[] = { "Left Data = Right ADC; Right Data = Left ADC", }; static const char *wm8960_dmonomix[] = {"Stereo", "Mono"}; +static const char *wm8960_dacslope[] = {"Normal", "Sloping"}; static const struct soc_enum wm8960_enum[] = { SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), @@ -165,6 +176,7 @@ static const struct soc_enum wm8960_enum[] = { SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), SOC_ENUM_SINGLE(WM8960_ADDCTL1, 2, 4, wm8960_adc_data_output_sel), SOC_ENUM_SINGLE(WM8960_ADDCTL1, 4, 2, wm8960_dmonomix), + SOC_ENUM_SINGLE(WM8960_DACCTL2, 1, 2, wm8960_dacslope), }; static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; @@ -307,6 +319,7 @@ SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume", SOC_ENUM("ADC Data Output Select", wm8960_enum[6]), SOC_ENUM("DAC Mono Mix", wm8960_enum[7]), +SOC_ENUM("DAC Filter Characteristics", wm8960_enum[8]), }; static const struct snd_kcontrol_new wm8960_lin_boost[] = { @@ -1387,7 +1400,7 @@ static const struct regmap_config wm8960_regmap = { .reg_defaults = wm8960_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8960_volatile, }; @@ -1414,7 +1427,9 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) { struct wm8960_data *pdata = dev_get_platdata(&i2c->dev); struct wm8960_priv *wm8960; + unsigned int i; int ret; + u8 val; wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv), GFP_KERNEL); @@ -1425,6 +1440,31 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) if (IS_ERR(wm8960->mclk)) { if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER) return -EPROBE_DEFER; + } else { + ret = clk_get_rate(wm8960->mclk); + if (ret >= 0) { + wm8960->freq_in = ret; + } else { + dev_err(&i2c->dev, "Failed to read MCLK rate: %d\n", + ret); + } + } + + for (i = 0; i < ARRAY_SIZE(wm8960->supplies); i++) + wm8960->supplies[i].supply = wm8960_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8960->supplies), + wm8960->supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8960->supplies), + wm8960->supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; } wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap); @@ -1436,6 +1476,12 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) else if (i2c->dev.of_node) wm8960_set_pdata_from_of(i2c, &wm8960->pdata); + ret = i2c_master_recv(i2c, &val, sizeof(val)); + if (ret >= 0) { + dev_err(&i2c->dev, "Not wm8960, wm8960 reg can not read by i2c\n"); + return -EINVAL; + } + ret = wm8960_reset(wm8960->regmap); if (ret != 0) { dev_err(&i2c->dev, "Failed to issue reset\n"); @@ -1487,7 +1533,11 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) } static void wm8960_i2c_remove(struct i2c_client *client) -{} +{ + struct wm8960_priv *wm8960 = i2c_get_clientdata(client); + + regulator_bulk_disable(ARRAY_SIZE(wm8960->supplies), wm8960->supplies); +} static const struct i2c_device_id wm8960_i2c_id[] = { { "wm8960", 0 }, diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h index 63ba6c03c488..e8ff33b188e9 100644 --- a/sound/soc/codecs/wm8960.h +++ b/sound/soc/codecs/wm8960.h @@ -77,9 +77,9 @@ #define WM8960_SYSCLK_DIV_1 (0 << 1) #define WM8960_SYSCLK_DIV_2 (2 << 1) -#define WM8960_SYSCLK_MCLK (0 << 0) +#define WM8960_SYSCLK_AUTO (0 << 0) #define WM8960_SYSCLK_PLL (1 << 0) -#define WM8960_SYSCLK_AUTO (2 << 0) +#define WM8960_SYSCLK_MCLK (2 << 0) #define WM8960_DAC_DIV_1 (0 << 3) #define WM8960_DAC_DIV_1_5 (1 << 3) diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index c076f78d04ce..8f8330efb341 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -904,7 +904,7 @@ static const struct regmap_config wm8961_regmap = { .reg_defaults = wm8961_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8961_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8961_volatile, .readable_reg = wm8961_readable, diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 68ea15be7330..83ce5dbecc45 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3573,7 +3573,7 @@ static const struct regmap_config wm8962_regmap = { .num_reg_defaults = ARRAY_SIZE(wm8962_reg), .volatile_reg = wm8962_volatile_register, .readable_reg = wm8962_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int wm8962_set_pdata_from_of(struct i2c_client *i2c, diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index b22d8f0b59be..e88f323d28b2 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -668,7 +668,7 @@ static const struct regmap_config wm8971_regmap = { .reg_defaults = wm8971_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8971_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int wm8971_i2c_probe(struct i2c_client *i2c) diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index 5c829301cf4c..718bfef302cc 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -1014,7 +1014,7 @@ static const struct regmap_config wm8978_regmap_config = { .max_register = WM8978_MAX_REGISTER, .volatile_reg = wm8978_volatile, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8978_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8978_reg_defaults), }; diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 2bd26e2478d9..b26d6a68e8d2 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -995,7 +995,7 @@ static const struct regmap_config wm8983_regmap = { .reg_defaults = wm8983_defaults, .num_reg_defaults = ARRAY_SIZE(wm8983_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .max_register = WM8983_MAX_REGISTER, .writeable_reg = wm8983_writeable, diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index c0816bcfa294..8606e0752a60 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -1125,7 +1125,7 @@ static const struct regmap_config wm8985_regmap = { .max_register = WM8985_MAX_REGISTER, .writeable_reg = wm8985_writeable, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8985_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8985_reg_defaults), }; diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index b440719cca7d..76f214f12ce0 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -832,7 +832,7 @@ static const struct regmap_config wm8988_regmap = { .max_register = WM8988_LPPB, .writeable_reg = wm8988_writeable, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8988_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8988_reg_defaults), }; diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 8cb2ae829699..590318aafaea 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -1253,7 +1253,7 @@ static const struct regmap_config wm8991_regmap = { .volatile_reg = wm8991_volatile, .reg_defaults = wm8991_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8991_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int wm8991_i2c_probe(struct i2c_client *i2c) diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index feb997c698e2..5b788f35e5e4 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1608,7 +1608,7 @@ static const struct regmap_config wm8993_regmap = { .volatile_reg = wm8993_volatile, .readable_reg = wm8993_readable, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8993_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8993_reg_defaults), }; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index bca3ebe0dac4..a48e904a9740 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3215,6 +3215,7 @@ static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = { }; static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = { + .probe = wm8994_aif2_probe, .set_sysclk = wm8994_set_dai_sysclk, .set_fmt = wm8994_set_dai_fmt, .hw_params = wm8994_hw_params, @@ -3269,7 +3270,6 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .formats = WM8994_FORMATS, .sig_bits = 24, }, - .probe = wm8994_aif2_probe, .ops = &wm8994_aif2_dai_ops, }, { diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 90588614edcc..4ffa1896faab 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2193,7 +2193,7 @@ static const struct regmap_config wm8995_regmap = { .num_reg_defaults = ARRAY_SIZE(wm8995_reg_defaults), .volatile_reg = wm8995_volatile, .readable_reg = wm8995_readable, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; #if defined(CONFIG_SPI_MASTER) diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 5d0eb0ae0475..df6195778c57 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2610,7 +2610,7 @@ static const struct regmap_config wm8996_regmap = { .num_reg_defaults = ARRAY_SIZE(wm8996_reg), .volatile_reg = wm8996_volatile_register, .readable_reg = wm8996_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int wm8996_probe(struct snd_soc_component *component) diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 34a07db7342a..e7ec799573d3 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1295,7 +1295,7 @@ static const struct regmap_config wm9081_regmap = { .num_reg_defaults = ARRAY_SIZE(wm9081_reg), .volatile_reg = wm9081_volatile_register, .readable_reg = wm9081_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int wm9081_i2c_probe(struct i2c_client *i2c) diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index 432729c753dd..50c1cbccfdb9 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -553,7 +553,7 @@ static const struct regmap_config wm9090_regmap = { .volatile_reg = wm9090_volatile, .readable_reg = wm9090_readable, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm9090_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm9090_reg_defaults), }; diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index d04902ef1d5f..5c6aebe29cf1 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -64,7 +64,7 @@ static const struct regmap_config wm9705_regmap_config = { .reg_stride = 2, .val_bits = 16, .max_register = 0x7e, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = regmap_ac97_default_volatile, diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index df9b7980706b..e63921de0c37 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -86,7 +86,7 @@ static const struct regmap_config wm9712_regmap_config = { .reg_stride = 2, .val_bits = 16, .max_register = 0x7e, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm9712_volatile_reg, diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 5d2e54e06e30..64b69316e4c7 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -727,7 +727,7 @@ static const struct regmap_config wm9713_regmap_config = { .reg_stride = 2, .val_bits = 16, .max_register = 0x7e, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm9713_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults), diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 5a89abfe8784..6fc34f41b175 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -998,7 +998,7 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); -int wm_adsp_power_up(struct wm_adsp *dsp) +int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware) { int ret = 0; char *wmfw_filename = NULL; @@ -1006,11 +1006,13 @@ int wm_adsp_power_up(struct wm_adsp *dsp) char *coeff_filename = NULL; const struct firmware *coeff_firmware = NULL; - ret = wm_adsp_request_firmware_files(dsp, - &wmfw_firmware, &wmfw_filename, - &coeff_firmware, &coeff_filename); - if (ret) - return ret; + if (load_firmware) { + ret = wm_adsp_request_firmware_files(dsp, + &wmfw_firmware, &wmfw_filename, + &coeff_firmware, &coeff_filename); + if (ret) + return ret; + } ret = cs_dsp_power_up(&dsp->cs_dsp, wmfw_firmware, wmfw_filename, @@ -1025,13 +1027,19 @@ int wm_adsp_power_up(struct wm_adsp *dsp) } EXPORT_SYMBOL_GPL(wm_adsp_power_up); +void wm_adsp_power_down(struct wm_adsp *dsp) +{ + cs_dsp_power_down(&dsp->cs_dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp_power_down); + static void wm_adsp_boot_work(struct work_struct *work) { struct wm_adsp *dsp = container_of(work, struct wm_adsp, boot_work); - wm_adsp_power_up(dsp); + wm_adsp_power_up(dsp, true); } int wm_adsp_early_event(struct snd_soc_dapm_widget *w, @@ -1046,7 +1054,7 @@ int wm_adsp_early_event(struct snd_soc_dapm_widget *w, queue_work(system_unbound_wq, &dsp->boot_work); break; case SND_SOC_DAPM_PRE_PMD: - cs_dsp_power_down(&dsp->cs_dsp); + wm_adsp_power_down(dsp); break; default: break; diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 769904d34a87..067d807a7ca8 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -91,7 +91,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, int wm_adsp_early_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -int wm_adsp_power_up(struct wm_adsp *dsp); +int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware); +void wm_adsp_power_down(struct wm_adsp *dsp); irqreturn_t wm_adsp2_bus_error(int irq, void *data); irqreturn_t wm_halo_bus_error(int irq, void *data); diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 97f6873a0a8c..3c025dabaf7a 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -637,7 +637,7 @@ static bool wsa881x_volatile_register(struct device *dev, unsigned int reg) static struct regmap_config wsa881x_regmap_config = { .reg_bits = 32, .val_bits = 8, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wsa881x_defaults, .max_register = WSA881X_SPKR_STATUS3, .num_reg_defaults = ARRAY_SIZE(wsa881x_defaults), diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index e40d583a1ce6..197fae23762f 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -938,7 +938,7 @@ static bool wsa883x_volatile_register(struct device *dev, unsigned int reg) static struct regmap_config wsa883x_regmap_config = { .reg_bits = 32, .val_bits = 8, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wsa883x_defaults, .max_register = WSA883X_MAX_REGISTER, .num_reg_defaults = ARRAY_SIZE(wsa883x_defaults), diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 97d652f0e84d..22c004179214 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/interrupt.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/reset.h> #include <linux/slab.h> @@ -183,14 +184,23 @@ static void i2s_start(struct dw_i2s_dev *dev, { struct i2s_clk_config_data *config = &dev->config; - i2s_write_reg(dev->i2s_base, IER, 1); + u32 reg = IER_IEN; + + if (dev->tdm_slots) { + reg |= (dev->tdm_slots - 1) << IER_TDM_SLOTS_SHIFT; + reg |= IER_INTF_TYPE; + reg |= dev->frame_offset << IER_FRAME_OFF_SHIFT; + } + + i2s_write_reg(dev->i2s_base, IER, reg); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) i2s_write_reg(dev->i2s_base, ITER, 1); else i2s_write_reg(dev->i2s_base, IRER, 1); - if (dev->use_pio) + /* I2S needs to enable IRQ to make a handshake with DMAC on the JH7110 SoC */ + if (dev->use_pio || dev->is_jh7110) i2s_enable_irqs(dev, substream->stream, config->chan_nr); else i2s_enable_dma(dev, substream->stream); @@ -208,7 +218,7 @@ static void i2s_stop(struct dw_i2s_dev *dev, else i2s_write_reg(dev->i2s_base, IRER, 0); - if (dev->use_pio) + if (dev->use_pio || dev->is_jh7110) i2s_disable_irqs(dev, substream->stream, 8); else i2s_disable_dma(dev, substream->stream); @@ -219,6 +229,21 @@ static void i2s_stop(struct dw_i2s_dev *dev, } } +static int dw_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + + if (dev->is_jh7110) { + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; + } + + return 0; +} + static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) { u32 ch_reg; @@ -233,13 +258,15 @@ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) dev->xfer_resolution); i2s_write_reg(dev->i2s_base, TFCR(ch_reg), dev->fifo_th - 1); - i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); + i2s_write_reg(dev->i2s_base, TER(ch_reg), TER_TXCHEN | + dev->tdm_mask << TER_TXSLOT_SHIFT); } else { i2s_write_reg(dev->i2s_base, RCR(ch_reg), dev->xfer_resolution); i2s_write_reg(dev->i2s_base, RFCR(ch_reg), dev->fifo_th - 1); - i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); + i2s_write_reg(dev->i2s_base, RER(ch_reg), RER_RXCHEN | + dev->tdm_mask << RER_RXSLOT_SHIFT); } } @@ -276,6 +303,9 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + if (dev->tdm_slots) + config->data_width = 32; + config->chan_nr = params_channels(params); switch (config->chan_nr) { @@ -384,14 +414,68 @@ static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) ret = -EINVAL; break; } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_DSP_A: + dev->frame_offset = 1; + break; + case SND_SOC_DAIFMT_DSP_B: + dev->frame_offset = 0; + break; + default: + dev_err(dev->dev, "DAI format unsupported"); + return -EINVAL; + } + return ret; } +static int dw_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + + if (slot_width != 32) + return -EINVAL; + + if (slots < 0 || slots > 16) + return -EINVAL; + + if (rx_mask != tx_mask) + return -EINVAL; + + if (!rx_mask) + return -EINVAL; + + dev->tdm_slots = slots; + dev->tdm_mask = rx_mask; + + dev->l_reg = RSLOT_TSLOT(ffs(rx_mask) - 1); + dev->r_reg = RSLOT_TSLOT(fls(rx_mask) - 1); + + return 0; +} + +static int dw_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, &dev->capture_dma_data); + return 0; +} + static const struct snd_soc_dai_ops dw_i2s_dai_ops = { + .probe = dw_i2s_dai_probe, + .startup = dw_i2s_startup, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, .set_fmt = dw_i2s_set_fmt, + .set_tdm_slot = dw_i2s_set_tdm_slot, }; #ifdef CONFIG_PM @@ -571,17 +655,39 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) idx = 1; - /* Set DMA slaves info */ - dev->play_dma_data.pd.data = pdata->play_dma_data; - dev->capture_dma_data.pd.data = pdata->capture_dma_data; - dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; - dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; - dev->play_dma_data.pd.max_burst = 16; - dev->capture_dma_data.pd.max_burst = 16; - dev->play_dma_data.pd.addr_width = bus_widths[idx]; - dev->capture_dma_data.pd.addr_width = bus_widths[idx]; - dev->play_dma_data.pd.filter = pdata->filter; - dev->capture_dma_data.pd.filter = pdata->filter; + + if (dev->is_jh7110) { + /* Use platform data and snd_dmaengine_dai_dma_data struct at the same time */ + u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); + u32 idx2; + + if (COMP1_TX_ENABLED(comp1)) { + idx2 = COMP1_TX_WORDSIZE_0(comp1); + dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; + dev->play_dma_data.dt.fifo_size = dev->fifo_th * 2 * + (fifo_width[idx2]) >> 8; + dev->play_dma_data.dt.maxburst = 16; + } + if (COMP1_RX_ENABLED(comp1)) { + idx2 = COMP2_RX_WORDSIZE_0(comp2); + dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; + dev->capture_dma_data.dt.fifo_size = dev->fifo_th * 2 * + (fifo_width[idx2] >> 8); + dev->capture_dma_data.dt.maxburst = 16; + } + } else { + /* Set DMA slaves info */ + dev->play_dma_data.pd.data = pdata->play_dma_data; + dev->capture_dma_data.pd.data = pdata->capture_dma_data; + dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; + dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; + dev->play_dma_data.pd.max_burst = 16; + dev->capture_dma_data.pd.max_burst = 16; + dev->play_dma_data.pd.addr_width = bus_widths[idx]; + dev->capture_dma_data.pd.addr_width = bus_widths[idx]; + dev->play_dma_data.pd.filter = pdata->filter; + dev->capture_dma_data.pd.filter = pdata->filter; + } return 0; } @@ -623,17 +729,195 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, } -static int dw_i2s_dai_probe(struct snd_soc_dai *dai) +#ifdef CONFIG_OF +/* clocks initialization with master mode on JH7110 SoC */ +static int jh7110_i2s_crg_master_init(struct dw_i2s_dev *dev) { - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + static struct clk_bulk_data clks[] = { + { .id = "mclk" }, + { .id = "mclk_ext" }, + { .id = "mclk_inner" }, + { .id = "apb" }, + { .id = "i2sclk" }, + }; + struct reset_control *resets = devm_reset_control_array_get_exclusive(dev->dev); + int ret; + struct clk *pclk; + struct clk *bclk_mst; + struct clk *mclk; + struct clk *mclk_ext; + struct clk *mclk_inner; + + if (IS_ERR(resets)) + return dev_err_probe(dev->dev, PTR_ERR(resets), "failed to get i2s resets\n"); + + ret = clk_bulk_get(dev->dev, ARRAY_SIZE(clks), clks); + if (ret) + return dev_err_probe(dev->dev, ret, "failed to get i2s clocks\n"); + + mclk = clks[0].clk; + mclk_ext = clks[1].clk; + mclk_inner = clks[2].clk; + pclk = clks[3].clk; + bclk_mst = clks[4].clk; + + ret = clk_prepare_enable(pclk); + if (ret) + goto exit; + + /* Use inner mclk first and avoid uninitialized gpio for external mclk */ + ret = clk_set_parent(mclk, mclk_inner); + if (ret) + goto err_dis_pclk; + + ret = clk_prepare_enable(bclk_mst); + if (ret) + goto err_dis_pclk; + + /* deassert resets before set clock parent */ + ret = reset_control_deassert(resets); + if (ret) + goto err_dis_all; + + /* external clock (12.288MHz) for Audio */ + ret = clk_set_parent(mclk, mclk_ext); + if (ret) + goto err_dis_all; + + /* i2sclk will be got and enabled repeatedly later and should be disabled now. */ + clk_disable_unprepare(bclk_mst); + clk_bulk_put(ARRAY_SIZE(clks), clks); + dev->is_jh7110 = true; - snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, &dev->capture_dma_data); return 0; + +err_dis_all: + clk_disable_unprepare(bclk_mst); +err_dis_pclk: + clk_disable_unprepare(pclk); +exit: + clk_bulk_put(ARRAY_SIZE(clks), clks); + return ret; } +/* clocks initialization with slave mode on JH7110 SoC */ +static int jh7110_i2s_crg_slave_init(struct dw_i2s_dev *dev) +{ + static struct clk_bulk_data clks[] = { + { .id = "mclk" }, + { .id = "mclk_ext" }, + { .id = "apb" }, + { .id = "bclk_ext" }, + { .id = "lrck_ext" }, + { .id = "bclk" }, + { .id = "lrck" }, + { .id = "mclk_inner" }, + { .id = "i2sclk" }, + }; + struct reset_control *resets = devm_reset_control_array_get_exclusive(dev->dev); + int ret; + struct clk *pclk; + struct clk *bclk_mst; + struct clk *bclk_ext; + struct clk *lrck_ext; + struct clk *bclk; + struct clk *lrck; + struct clk *mclk; + struct clk *mclk_ext; + struct clk *mclk_inner; + + if (IS_ERR(resets)) + return dev_err_probe(dev->dev, PTR_ERR(resets), "failed to get i2s resets\n"); + + ret = clk_bulk_get(dev->dev, ARRAY_SIZE(clks), clks); + if (ret) + return dev_err_probe(dev->dev, ret, "failed to get i2s clocks\n"); + + mclk = clks[0].clk; + mclk_ext = clks[1].clk; + pclk = clks[2].clk; + bclk_ext = clks[3].clk; + lrck_ext = clks[4].clk; + bclk = clks[5].clk; + lrck = clks[6].clk; + mclk_inner = clks[7].clk; + bclk_mst = clks[8].clk; + + ret = clk_prepare_enable(pclk); + if (ret) + goto exit; + + ret = clk_set_parent(mclk, mclk_inner); + if (ret) + goto err_dis_pclk; + + ret = clk_prepare_enable(bclk_mst); + if (ret) + goto err_dis_pclk; + + ret = reset_control_deassert(resets); + if (ret) + goto err_dis_all; + + /* The sources of BCLK and LRCK are the external codec. */ + ret = clk_set_parent(bclk, bclk_ext); + if (ret) + goto err_dis_all; + + ret = clk_set_parent(lrck, lrck_ext); + if (ret) + goto err_dis_all; + + ret = clk_set_parent(mclk, mclk_ext); + if (ret) + goto err_dis_all; + + /* The i2sclk will be got and enabled repeatedly later and should be disabled now. */ + clk_disable_unprepare(bclk_mst); + clk_bulk_put(ARRAY_SIZE(clks), clks); + dev->is_jh7110 = true; + + return 0; + +err_dis_all: + clk_disable_unprepare(bclk_mst); +err_dis_pclk: + clk_disable_unprepare(pclk); +exit: + clk_bulk_put(ARRAY_SIZE(clks), clks); + return ret; +} + +/* Special syscon initialization about RX channel with slave mode on JH7110 SoC */ +static int jh7110_i2srx_crg_init(struct dw_i2s_dev *dev) +{ + struct regmap *regmap; + unsigned int args[2]; + + regmap = syscon_regmap_lookup_by_phandle_args(dev->dev->of_node, + "starfive,syscon", + 2, args); + if (IS_ERR(regmap)) + return dev_err_probe(dev->dev, PTR_ERR(regmap), "getting the regmap failed\n"); + + /* Enable I2Srx with syscon register, args[0]: offset, args[1]: mask */ + regmap_update_bits(regmap, args[0], args[1], args[1]); + + return jh7110_i2s_crg_slave_init(dev); +} + +static int jh7110_i2stx0_clk_cfg(struct i2s_clk_config_data *config) +{ + struct dw_i2s_dev *dev = container_of(config, struct dw_i2s_dev, config); + u32 bclk_rate = config->sample_rate * 64; + + return clk_set_rate(dev->clk, bclk_rate); +} +#endif /* CONFIG_OF */ + static int dw_i2s_probe(struct platform_device *pdev) { - const struct i2s_platform_data *pdata = pdev->dev.platform_data; + const struct i2s_platform_data *pdata = of_device_get_match_data(&pdev->dev); struct dw_i2s_dev *dev; struct resource *res; int ret, irq; @@ -649,21 +933,30 @@ static int dw_i2s_probe(struct platform_device *pdev) return -ENOMEM; dw_i2s_dai->ops = &dw_i2s_dai_ops; - dw_i2s_dai->probe = dw_i2s_dai_probe; dev->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(dev->i2s_base)) return PTR_ERR(dev->i2s_base); - dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev); - if (IS_ERR(dev->reset)) - return PTR_ERR(dev->reset); + dev->dev = &pdev->dev; + dev->is_jh7110 = false; + if (pdata) { + if (pdata->i2s_pd_init) { + ret = pdata->i2s_pd_init(dev); + if (ret) + return ret; + } + } - ret = reset_control_deassert(dev->reset); - if (ret) - return ret; + if (!dev->is_jh7110) { + dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev); + if (IS_ERR(dev->reset)) + return PTR_ERR(dev->reset); - dev->dev = &pdev->dev; + ret = reset_control_deassert(dev->reset); + if (ret) + return ret; + } irq = platform_get_irq_optional(pdev, 0); if (irq >= 0) { @@ -722,10 +1015,12 @@ static int dw_i2s_probe(struct platform_device *pdev) goto err_clk_disable; } - if (!pdata) { + if (!pdata || dev->is_jh7110) { if (irq >= 0) { ret = dw_pcm_register(pdev); dev->use_pio = true; + dev->l_reg = LRBR_LTHR(0); + dev->r_reg = RRBR_RTHR(0); } else { ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); @@ -762,8 +1057,36 @@ static void dw_i2s_remove(struct platform_device *pdev) } #ifdef CONFIG_OF +static const struct i2s_platform_data jh7110_i2stx0_data = { + .cap = DWC_I2S_PLAY | DW_I2S_MASTER, + .channel = TWO_CHANNEL_SUPPORT, + .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .snd_rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .i2s_clk_cfg = jh7110_i2stx0_clk_cfg, + .i2s_pd_init = jh7110_i2s_crg_master_init, +}; + +static const struct i2s_platform_data jh7110_i2stx1_data = { + .cap = DWC_I2S_PLAY | DW_I2S_SLAVE, + .channel = TWO_CHANNEL_SUPPORT, + .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .snd_rates = SNDRV_PCM_RATE_8000_192000, + .i2s_pd_init = jh7110_i2s_crg_slave_init, +}; + +static const struct i2s_platform_data jh7110_i2srx_data = { + .cap = DWC_I2S_RECORD | DW_I2S_SLAVE, + .channel = TWO_CHANNEL_SUPPORT, + .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .snd_rates = SNDRV_PCM_RATE_8000_192000, + .i2s_pd_init = jh7110_i2srx_crg_init, +}; + static const struct of_device_id dw_i2s_of_match[] = { { .compatible = "snps,designware-i2s", }, + { .compatible = "starfive,jh7110-i2stx0", .data = &jh7110_i2stx0_data, }, + { .compatible = "starfive,jh7110-i2stx1", .data = &jh7110_i2stx1_data,}, + { .compatible = "starfive,jh7110-i2srx", .data = &jh7110_i2srx_data,}, {}, }; diff --git a/sound/soc/dwc/dwc-pcm.c b/sound/soc/dwc/dwc-pcm.c index 9f25631d43d3..f99262b89008 100644 --- a/sound/soc/dwc/dwc-pcm.c +++ b/sound/soc/dwc/dwc-pcm.c @@ -31,8 +31,8 @@ static unsigned int dw_pcm_tx_##sample_bits(struct dw_i2s_dev *dev, \ int i; \ \ for (i = 0; i < dev->fifo_th; i++) { \ - iowrite32(p[tx_ptr][0], dev->i2s_base + LRBR_LTHR(0)); \ - iowrite32(p[tx_ptr][1], dev->i2s_base + RRBR_RTHR(0)); \ + iowrite32(p[tx_ptr][0], dev->i2s_base + dev->l_reg); \ + iowrite32(p[tx_ptr][1], dev->i2s_base + dev->r_reg); \ period_pos++; \ if (++tx_ptr >= runtime->buffer_size) \ tx_ptr = 0; \ @@ -51,8 +51,8 @@ static unsigned int dw_pcm_rx_##sample_bits(struct dw_i2s_dev *dev, \ int i; \ \ for (i = 0; i < dev->fifo_th; i++) { \ - p[rx_ptr][0] = ioread32(dev->i2s_base + LRBR_LTHR(0)); \ - p[rx_ptr][1] = ioread32(dev->i2s_base + RRBR_RTHR(0)); \ + p[rx_ptr][0] = ioread32(dev->i2s_base + dev->l_reg); \ + p[rx_ptr][1] = ioread32(dev->i2s_base + dev->r_reg); \ period_pos++; \ if (++rx_ptr >= runtime->buffer_size) \ rx_ptr = 0; \ diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h index ba4e397099be..dce88c9ad5f3 100644 --- a/sound/soc/dwc/local.h +++ b/sound/soc/dwc/local.h @@ -25,6 +25,13 @@ #define RXFFR 0x014 #define TXFFR 0x018 +/* Enable register fields */ +#define IER_TDM_SLOTS_SHIFT 8 +#define IER_FRAME_OFF_SHIFT 5 +#define IER_FRAME_OFF BIT(5) +#define IER_INTF_TYPE BIT(1) +#define IER_IEN BIT(0) + /* Interrupt status register fields */ #define ISR_TXFO BIT(5) #define ISR_TXFE BIT(4) @@ -46,6 +53,15 @@ #define TFCR(x) (0x40 * x + 0x04C) #define RFF(x) (0x40 * x + 0x050) #define TFF(x) (0x40 * x + 0x054) +#define RSLOT_TSLOT(x) (0x4 * (x) + 0x224) + +/* Receive enable register fields */ +#define RER_RXSLOT_SHIFT 8 +#define RER_RXCHEN BIT(0) + +/* Transmit enable register fields */ +#define TER_TXSLOT_SHIFT 8 +#define TER_TXCHEN BIT(0) /* I2SCOMPRegisters */ #define I2S_COMP_PARAM_2 0x01F0 @@ -105,6 +121,9 @@ struct dw_i2s_dev { u32 ccr; u32 xfer_resolution; u32 fifo_th; + u32 l_reg; + u32 r_reg; + bool is_jh7110; /* Flag for StarFive JH7110 SoC */ /* data related to DMA transfers b/w i2s and DMAC */ union dw_i2s_snd_dma_data play_dma_data; @@ -114,6 +133,12 @@ struct dw_i2s_dev { /* data related to PIO transfers */ bool use_pio; + + /* data related to TDM mode */ + u32 tdm_slots; + u32 tdm_mask; + u32 frame_offset; + struct snd_pcm_substream __rcu *tx_substream; struct snd_pcm_substream __rcu *rx_substream; unsigned int (*tx_fn)(struct dw_i2s_dev *dev, diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index adb8a59de2bd..b793263291dc 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -780,13 +780,6 @@ static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static const struct snd_soc_dai_ops fsl_asrc_dai_ops = { - .startup = fsl_asrc_dai_startup, - .hw_params = fsl_asrc_dai_hw_params, - .hw_free = fsl_asrc_dai_hw_free, - .trigger = fsl_asrc_dai_trigger, -}; - static int fsl_asrc_dai_probe(struct snd_soc_dai *dai) { struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai); @@ -797,12 +790,19 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops fsl_asrc_dai_ops = { + .probe = fsl_asrc_dai_probe, + .startup = fsl_asrc_dai_startup, + .hw_params = fsl_asrc_dai_hw_params, + .hw_free = fsl_asrc_dai_hw_free, + .trigger = fsl_asrc_dai_trigger, +}; + #define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_3LE) static struct snd_soc_dai_driver fsl_asrc_dai = { - .probe = fsl_asrc_dai_probe, .playback = { .stream_name = "ASRC-Playback", .channels_min = 1, diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c index 46b0c5dcc4a5..fc56f6ade368 100644 --- a/sound/soc/fsl/fsl_aud2htx.c +++ b/sound/soc/fsl/fsl_aud2htx.c @@ -49,10 +49,6 @@ static int fsl_aud2htx_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = { - .trigger = fsl_aud2htx_trigger, -}; - static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_aud2htx *aud2htx = dev_get_drvdata(cpu_dai->dev); @@ -84,8 +80,12 @@ static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } +static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = { + .probe = fsl_aud2htx_dai_probe, + .trigger = fsl_aud2htx_trigger, +}; + static struct snd_soc_dai_driver fsl_aud2htx_dai = { - .probe = fsl_aud2htx_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 1, diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 670cbdb361b6..ba62995c909a 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1531,13 +1531,6 @@ static int fsl_easrc_hw_free(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops fsl_easrc_dai_ops = { - .startup = fsl_easrc_startup, - .trigger = fsl_easrc_trigger, - .hw_params = fsl_easrc_hw_params, - .hw_free = fsl_easrc_hw_free, -}; - static int fsl_easrc_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_asrc *easrc = dev_get_drvdata(cpu_dai->dev); @@ -1548,8 +1541,15 @@ static int fsl_easrc_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } +static const struct snd_soc_dai_ops fsl_easrc_dai_ops = { + .probe = fsl_easrc_dai_probe, + .startup = fsl_easrc_startup, + .trigger = fsl_easrc_trigger, + .hw_params = fsl_easrc_hw_params, + .hw_free = fsl_easrc_hw_free, +}; + static struct snd_soc_dai_driver fsl_easrc_dai = { - .probe = fsl_easrc_dai_probe, .playback = { .stream_name = "ASRC-Playback", .channels_min = 1, diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 936f0cd4b06d..d0d8a01da9bd 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -785,15 +785,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static const struct snd_soc_dai_ops fsl_esai_dai_ops = { - .startup = fsl_esai_startup, - .trigger = fsl_esai_trigger, - .hw_params = fsl_esai_hw_params, - .set_sysclk = fsl_esai_set_dai_sysclk, - .set_fmt = fsl_esai_set_dai_fmt, - .set_tdm_slot = fsl_esai_set_dai_tdm_slot, -}; - static int fsl_esai_dai_probe(struct snd_soc_dai *dai) { struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); @@ -804,8 +795,17 @@ static int fsl_esai_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops fsl_esai_dai_ops = { + .probe = fsl_esai_dai_probe, + .startup = fsl_esai_startup, + .trigger = fsl_esai_trigger, + .hw_params = fsl_esai_hw_params, + .set_sysclk = fsl_esai_set_dai_sysclk, + .set_fmt = fsl_esai_set_dai_fmt, + .set_tdm_slot = fsl_esai_set_dai_tdm_slot, +}; + static struct snd_soc_dai_driver fsl_esai_dai = { - .probe = fsl_esai_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 1, diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 9d01225dedd9..0d37edb70261 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -56,6 +56,8 @@ struct fsl_micfil { int vad_init_mode; int vad_enabled; int vad_detected; + struct fsl_micfil_verid verid; + struct fsl_micfil_param param; }; struct fsl_micfil_soc_data { @@ -64,6 +66,7 @@ struct fsl_micfil_soc_data { unsigned int dataline; bool imx; bool use_edma; + bool use_verid; u64 formats; }; @@ -90,6 +93,7 @@ static struct fsl_micfil_soc_data fsl_micfil_imx93 = { .dataline = 0xf, .formats = SNDRV_PCM_FMTBIT_S32_LE, .use_edma = true, + .use_verid = true, }; static const struct of_device_id fsl_micfil_dt_ids[] = { @@ -356,6 +360,49 @@ static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL), }; +static int fsl_micfil_use_verid(struct device *dev) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + unsigned int val; + int ret; + + if (!micfil->soc->use_verid) + return 0; + + ret = regmap_read(micfil->regmap, REG_MICFIL_VERID, &val); + if (ret < 0) + return ret; + + dev_dbg(dev, "VERID: 0x%016X\n", val); + + micfil->verid.version = val & + (MICFIL_VERID_MAJOR_MASK | MICFIL_VERID_MINOR_MASK); + micfil->verid.version >>= MICFIL_VERID_MINOR_SHIFT; + micfil->verid.feature = val & MICFIL_VERID_FEATURE_MASK; + + ret = regmap_read(micfil->regmap, REG_MICFIL_PARAM, &val); + if (ret < 0) + return ret; + + dev_dbg(dev, "PARAM: 0x%016X\n", val); + + micfil->param.hwvad_num = (val & MICFIL_PARAM_NUM_HWVAD_MASK) >> + MICFIL_PARAM_NUM_HWVAD_SHIFT; + micfil->param.hwvad_zcd = val & MICFIL_PARAM_HWVAD_ZCD; + micfil->param.hwvad_energy_mode = val & MICFIL_PARAM_HWVAD_ENERGY_MODE; + micfil->param.hwvad = val & MICFIL_PARAM_HWVAD; + micfil->param.dc_out_bypass = val & MICFIL_PARAM_DC_OUT_BYPASS; + micfil->param.dc_in_bypass = val & MICFIL_PARAM_DC_IN_BYPASS; + micfil->param.low_power = val & MICFIL_PARAM_LOW_POWER; + micfil->param.fil_out_width = val & MICFIL_PARAM_FIL_OUT_WIDTH; + micfil->param.fifo_ptrwid = (val & MICFIL_PARAM_FIFO_PTRWID_MASK) >> + MICFIL_PARAM_FIFO_PTRWID_SHIFT; + micfil->param.npair = (val & MICFIL_PARAM_NPAIR_MASK) >> + MICFIL_PARAM_NPAIR_SHIFT; + + return 0; +} + /* The SRES is a self-negated bit which provides the CPU with the * capability to initialize the PDM Interface module through the * slave-bus interface. This bit always reads as zero, and this @@ -717,12 +764,6 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops fsl_micfil_dai_ops = { - .startup = fsl_micfil_startup, - .trigger = fsl_micfil_trigger, - .hw_params = fsl_micfil_hw_params, -}; - static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev); @@ -760,8 +801,14 @@ static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } +static const struct snd_soc_dai_ops fsl_micfil_dai_ops = { + .probe = fsl_micfil_dai_probe, + .startup = fsl_micfil_startup, + .trigger = fsl_micfil_trigger, + .hw_params = fsl_micfil_hw_params, +}; + static struct snd_soc_dai_driver fsl_micfil_dai = { - .probe = fsl_micfil_dai_probe, .capture = { .stream_name = "CPU-Capture", .channels_min = 1, @@ -825,6 +872,9 @@ static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg) case REG_MICFIL_DC_CTRL: case REG_MICFIL_OUT_CTRL: case REG_MICFIL_OUT_STAT: + case REG_MICFIL_FSYNC_CTRL: + case REG_MICFIL_VERID: + case REG_MICFIL_PARAM: case REG_MICFIL_VAD0_CTRL1: case REG_MICFIL_VAD0_CTRL2: case REG_MICFIL_VAD0_STAT: @@ -849,6 +899,7 @@ static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg) case REG_MICFIL_DC_CTRL: case REG_MICFIL_OUT_CTRL: case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */ + case REG_MICFIL_FSYNC_CTRL: case REG_MICFIL_VAD0_CTRL1: case REG_MICFIL_VAD0_CTRL2: case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */ @@ -873,6 +924,8 @@ static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg) case REG_MICFIL_DATACH5: case REG_MICFIL_DATACH6: case REG_MICFIL_DATACH7: + case REG_MICFIL_VERID: + case REG_MICFIL_PARAM: case REG_MICFIL_VAD0_STAT: case REG_MICFIL_VAD0_NDATA: return true; @@ -1031,6 +1084,9 @@ static irqreturn_t hwvad_err_isr(int irq, void *devid) return IRQ_HANDLED; } +static int fsl_micfil_runtime_suspend(struct device *dev); +static int fsl_micfil_runtime_resume(struct device *dev); + static int fsl_micfil_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1044,7 +1100,7 @@ static int fsl_micfil_probe(struct platform_device *pdev) return -ENOMEM; micfil->pdev = pdev; - strncpy(micfil->name, np->name, sizeof(micfil->name) - 1); + strscpy(micfil->name, np->name, sizeof(micfil->name)); micfil->soc = of_device_get_match_data(&pdev->dev); @@ -1150,6 +1206,25 @@ static int fsl_micfil_probe(struct platform_device *pdev) platform_set_drvdata(pdev, micfil); pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = fsl_micfil_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) + goto err_pm_get_sync; + + /* Get micfil version */ + ret = fsl_micfil_use_verid(&pdev->dev); + if (ret < 0) + dev_warn(&pdev->dev, "Error reading MICFIL version: %d\n", ret); + + ret = pm_runtime_put_sync(&pdev->dev); + if (ret < 0 && ret != -ENOSYS) + goto err_pm_get_sync; + regcache_cache_only(micfil->regmap, true); /* @@ -1174,6 +1249,9 @@ static int fsl_micfil_probe(struct platform_device *pdev) return ret; +err_pm_get_sync: + if (!pm_runtime_status_suspended(&pdev->dev)) + fsl_micfil_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); @@ -1185,7 +1263,7 @@ static void fsl_micfil_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused fsl_micfil_runtime_suspend(struct device *dev) +static int fsl_micfil_runtime_suspend(struct device *dev) { struct fsl_micfil *micfil = dev_get_drvdata(dev); @@ -1197,7 +1275,7 @@ static int __maybe_unused fsl_micfil_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused fsl_micfil_runtime_resume(struct device *dev) +static int fsl_micfil_runtime_resume(struct device *dev) { struct fsl_micfil *micfil = dev_get_drvdata(dev); int ret; @@ -1219,26 +1297,12 @@ static int __maybe_unused fsl_micfil_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused fsl_micfil_suspend(struct device *dev) -{ - pm_runtime_force_suspend(dev); - - return 0; -} - -static int __maybe_unused fsl_micfil_resume(struct device *dev) -{ - pm_runtime_force_resume(dev); - - return 0; -} - static const struct dev_pm_ops fsl_micfil_pm_ops = { SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, fsl_micfil_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(fsl_micfil_suspend, - fsl_micfil_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static struct platform_driver fsl_micfil_driver = { diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h index fee9fe3d9119..c6b902ba0a53 100644 --- a/sound/soc/fsl/fsl_micfil.h +++ b/sound/soc/fsl/fsl_micfil.h @@ -24,6 +24,9 @@ #define REG_MICFIL_DC_CTRL 0x64 #define REG_MICFIL_OUT_CTRL 0x74 #define REG_MICFIL_OUT_STAT 0x7C +#define REG_MICFIL_FSYNC_CTRL 0x80 +#define REG_MICFIL_VERID 0x84 +#define REG_MICFIL_PARAM 0x88 #define REG_MICFIL_VAD0_CTRL1 0x90 #define REG_MICFIL_VAD0_CTRL2 0x94 #define REG_MICFIL_VAD0_STAT 0x98 @@ -39,6 +42,8 @@ #define MICFIL_CTRL1_DBG BIT(28) #define MICFIL_CTRL1_SRES BIT(27) #define MICFIL_CTRL1_DBGE BIT(26) +#define MICFIL_CTRL1_DECFILS BIT(20) +#define MICFIL_CTRL1_FSYNCEN BIT(16) #define MICFIL_CTRL1_DISEL_DISABLE 0 #define MICFIL_CTRL1_DISEL_DMA 1 @@ -82,6 +87,29 @@ #define MICFIL_DC_CUTOFF_152Hz 2 #define MICFIL_DC_BYPASS 3 +/* MICFIL VERID Register -- REG_MICFIL_VERID */ +#define MICFIL_VERID_MAJOR_SHIFT 24 +#define MICFIL_VERID_MAJOR_MASK GENMASK(31, 24) +#define MICFIL_VERID_MINOR_SHIFT 16 +#define MICFIL_VERID_MINOR_MASK GENMASK(23, 16) +#define MICFIL_VERID_FEATURE_SHIFT 0 +#define MICFIL_VERID_FEATURE_MASK GENMASK(15, 0) + +/* MICFIL PARAM Register -- REG_MICFIL_PARAM */ +#define MICFIL_PARAM_NUM_HWVAD_SHIFT 24 +#define MICFIL_PARAM_NUM_HWVAD_MASK GENMASK(27, 24) +#define MICFIL_PARAM_HWVAD_ZCD BIT(19) +#define MICFIL_PARAM_HWVAD_ENERGY_MODE BIT(17) +#define MICFIL_PARAM_HWVAD BIT(16) +#define MICFIL_PARAM_DC_OUT_BYPASS BIT(11) +#define MICFIL_PARAM_DC_IN_BYPASS BIT(10) +#define MICFIL_PARAM_LOW_POWER BIT(9) +#define MICFIL_PARAM_FIL_OUT_WIDTH BIT(8) +#define MICFIL_PARAM_FIFO_PTRWID_SHIFT 4 +#define MICFIL_PARAM_FIFO_PTRWID_MASK GENMASK(7, 4) +#define MICFIL_PARAM_NPAIR_SHIFT 0 +#define MICFIL_PARAM_NPAIR_MASK GENMASK(3, 0) + /* MICFIL HWVAD0 Control 1 Register -- REG_MICFIL_VAD0_CTRL1*/ #define MICFIL_VAD0_CTRL1_CHSEL GENMASK(26, 24) #define MICFIL_VAD0_CTRL1_CICOSR GENMASK(19, 16) @@ -146,4 +174,40 @@ #define MICFIL_HWVAD_ENVELOPE_MODE 0 #define MICFIL_HWVAD_ENERGY_MODE 1 +/** + * struct fsl_micfil_verid - version id data + * @version: version number + * @feature: feature specification number + */ +struct fsl_micfil_verid { + u32 version; + u32 feature; +}; + +/** + * struct fsl_micfil_param - parameter data + * @hwvad_num: the number of HWVADs + * @hwvad_zcd: HWVAD zero-cross detector is active + * @hwvad_energy_mode: HWVAD energy mode is active + * @hwvad: HWVAD is active + * @dc_out_bypass: points out if the output DC remover is disabled + * @dc_in_bypass: points out if the input DC remover is disabled + * @low_power: low power decimation filter + * @fil_out_width: filter output width + * @fifo_ptrwid: FIFO pointer width + * @npair: number of microphone pairs + */ +struct fsl_micfil_param { + u32 hwvad_num; + bool hwvad_zcd; + bool hwvad_energy_mode; + bool hwvad; + bool dc_out_bypass; + bool dc_in_bypass; + bool low_power; + bool fil_out_width; + u32 fifo_ptrwid; + u32 npair; +}; + #endif /* _FSL_MICFIL_H */ diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index 7cbb8e4758cc..56d6b0b039a2 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -372,8 +372,8 @@ static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai, struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); unsigned int channels = params_channels(params); unsigned int slot_width; + snd_pcm_format_t format; struct snd_mask f_new; - unsigned int i; if (!channels || channels > nb_ts) { dev_err(qmc_dai->dev, "channels %u not supported\n", @@ -384,10 +384,10 @@ static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai, slot_width = (nb_ts / channels) * 8; snd_mask_none(&f_new); - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { - if (snd_mask_test(f_old, i)) { - if (snd_pcm_format_physical_width(i) <= slot_width) - snd_mask_set(&f_new, i); + pcm_for_each_format(format) { + if (snd_mask_test_format(f_old, format)) { + if (snd_pcm_format_physical_width(format) <= slot_width) + snd_mask_set_format(&f_new, format); } } @@ -551,26 +551,26 @@ static const struct snd_soc_dai_ops qmc_dai_ops = { static u64 qmc_audio_formats(u8 nb_ts) { - u64 formats; - unsigned int chan_width; unsigned int format_width; - int i; + unsigned int chan_width; + snd_pcm_format_t format; + u64 formats_mask; if (!nb_ts) return 0; - formats = 0; + formats_mask = 0; chan_width = nb_ts * 8; - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { + pcm_for_each_format(format) { /* * Support format other than little-endian (ie big-endian or * without endianness such as 8bit formats) */ - if (snd_pcm_format_little_endian(i) == 1) + if (snd_pcm_format_little_endian(format) == 1) continue; /* Support physical width multiple of 8bit */ - format_width = snd_pcm_format_physical_width(i); + format_width = snd_pcm_format_physical_width(format); if (format_width == 0 || format_width % 8) continue; @@ -581,9 +581,9 @@ static u64 qmc_audio_formats(u8 nb_ts) if (format_width > chan_width || chan_width % format_width) continue; - formats |= (1ULL << i); + formats_mask |= pcm_format_to_bits(format); } - return formats; + return formats_mask; } static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *np, diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c index 15b48b5ea856..abe19a8a7aa7 100644 --- a/sound/soc/fsl/fsl_rpmsg.c +++ b/sound/soc/fsl/fsl_rpmsg.c @@ -170,12 +170,20 @@ static const struct fsl_rpmsg_soc_data imx8mp_data = { SNDRV_PCM_FMTBIT_S32_LE, }; +static const struct fsl_rpmsg_soc_data imx93_data = { + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, +}; + static const struct of_device_id fsl_rpmsg_ids[] = { { .compatible = "fsl,imx7ulp-rpmsg-audio", .data = &imx7ulp_data}, { .compatible = "fsl,imx8mm-rpmsg-audio", .data = &imx8mm_data}, { .compatible = "fsl,imx8mn-rpmsg-audio", .data = &imx8mn_data}, { .compatible = "fsl,imx8mp-rpmsg-audio", .data = &imx8mp_data}, { .compatible = "fsl,imx8ulp-rpmsg-audio", .data = &imx7ulp_data}, + { .compatible = "fsl,imx93-rpmsg-audio", .data = &imx93_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids); diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index f7676d30c82f..1e4020fae05a 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -849,17 +849,6 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream, return ret; } -static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { - .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio, - .set_sysclk = fsl_sai_set_dai_sysclk, - .set_fmt = fsl_sai_set_dai_fmt, - .set_tdm_slot = fsl_sai_set_dai_tdm_slot, - .hw_params = fsl_sai_hw_params, - .hw_free = fsl_sai_hw_free, - .trigger = fsl_sai_trigger, - .startup = fsl_sai_startup, -}; - static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); @@ -885,6 +874,18 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } +static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { + .probe = fsl_sai_dai_probe, + .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio, + .set_sysclk = fsl_sai_set_dai_sysclk, + .set_fmt = fsl_sai_set_dai_fmt, + .set_tdm_slot = fsl_sai_set_dai_tdm_slot, + .hw_params = fsl_sai_hw_params, + .hw_free = fsl_sai_hw_free, + .trigger = fsl_sai_trigger, + .startup = fsl_sai_startup, +}; + static int fsl_sai_dai_resume(struct snd_soc_component *component) { struct fsl_sai *sai = snd_soc_component_get_drvdata(component); @@ -903,7 +904,6 @@ static int fsl_sai_dai_resume(struct snd_soc_component *component) } static struct snd_soc_dai_driver fsl_sai_dai_template = { - .probe = fsl_sai_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 1, diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 3fd26f2cdd60..78d9dfbe6548 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -514,6 +514,10 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, int ret; switch (sample_rate) { + case 22050: + rate = SPDIF_TXRATE_22050; + csfs = IEC958_AES3_CON_FS_22050; + break; case 32000: rate = SPDIF_TXRATE_32000; csfs = IEC958_AES3_CON_FS_32000; @@ -761,14 +765,6 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops fsl_spdif_dai_ops = { - .startup = fsl_spdif_startup, - .hw_params = fsl_spdif_hw_params, - .trigger = fsl_spdif_trigger, - .shutdown = fsl_spdif_shutdown, -}; - - /* * FSL SPDIF IEC958 controller(mixer) functions * @@ -1279,8 +1275,15 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops fsl_spdif_dai_ops = { + .probe = fsl_spdif_dai_probe, + .startup = fsl_spdif_startup, + .hw_params = fsl_spdif_hw_params, + .trigger = fsl_spdif_trigger, + .shutdown = fsl_spdif_shutdown, +}; + static struct snd_soc_dai_driver fsl_spdif_dai = { - .probe = &fsl_spdif_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 2, @@ -1424,7 +1427,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, struct clk *clk, u64 savesub, enum spdif_txrate index, bool round) { - static const u32 rate[] = { 32000, 44100, 48000, 88200, 96000, 176400, + static const u32 rate[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, }; bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk); u64 rate_ideal, rate_actual, sub; @@ -1485,7 +1488,7 @@ out: static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, enum spdif_txrate index) { - static const u32 rate[] = { 32000, 44100, 48000, 88200, 96000, 176400, + static const u32 rate[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, }; struct platform_device *pdev = spdif_priv->pdev; struct device *dev = &pdev->dev; diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h index 75b42a692c90..2bc1b10c17d4 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -175,7 +175,8 @@ enum spdif_gainsel { /* SPDIF tx rate */ enum spdif_txrate { - SPDIF_TXRATE_32000 = 0, + SPDIF_TXRATE_22050 = 0, + SPDIF_TXRATE_32000, SPDIF_TXRATE_44100, SPDIF_TXRATE_48000, SPDIF_TXRATE_88200, @@ -191,7 +192,8 @@ enum spdif_txrate { #define SPDIF_QSUB_SIZE (SPDIF_UBITS_SIZE / 8) -#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \ +#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | \ SNDRV_PCM_RATE_88200 | \ diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 53ed3701b0b0..079ac04272b8 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1152,6 +1152,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { + .probe = fsl_ssi_dai_probe, .startup = fsl_ssi_startup, .shutdown = fsl_ssi_shutdown, .hw_params = fsl_ssi_hw_params, @@ -1162,7 +1163,6 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { }; static struct snd_soc_dai_driver fsl_ssi_dai_template = { - .probe = fsl_ssi_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 1, @@ -1187,7 +1187,6 @@ static const struct snd_soc_component_driver fsl_ssi_component = { static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { .symmetric_channels = 1, - .probe = fsl_ssi_dai_probe, .playback = { .stream_name = "CPU AC97 Playback", .channels_min = 2, diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 318fe77683f5..fa0a15263c66 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -888,13 +888,6 @@ static struct snd_kcontrol_new fsl_xcvr_tx_ctls[] = { }, }; -static const struct snd_soc_dai_ops fsl_xcvr_dai_ops = { - .prepare = fsl_xcvr_prepare, - .startup = fsl_xcvr_startup, - .shutdown = fsl_xcvr_shutdown, - .trigger = fsl_xcvr_trigger, -}; - static int fsl_xcvr_dai_probe(struct snd_soc_dai *dai) { struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); @@ -915,8 +908,15 @@ static int fsl_xcvr_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops fsl_xcvr_dai_ops = { + .probe = fsl_xcvr_dai_probe, + .prepare = fsl_xcvr_prepare, + .startup = fsl_xcvr_startup, + .shutdown = fsl_xcvr_shutdown, + .trigger = fsl_xcvr_trigger, +}; + static struct snd_soc_dai_driver fsl_xcvr_dai = { - .probe = fsl_xcvr_dai_probe, .ops = &fsl_xcvr_dai_ops, .playback = { .stream_name = "CPU-Playback", diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c index d5234ac4b09b..289e47c03d40 100644 --- a/sound/soc/fsl/imx-audio-rpmsg.c +++ b/sound/soc/fsl/imx-audio-rpmsg.c @@ -116,7 +116,6 @@ static struct rpmsg_device_id imx_audio_rpmsg_id_table[] = { static struct rpmsg_driver imx_audio_rpmsg_driver = { .drv.name = "imx_audio_rpmsg", - .drv.owner = THIS_MODULE, .id_table = imx_audio_rpmsg_id_table, .probe = imx_audio_rpmsg_probe, .callback = imx_audio_rpmsg_cb, diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c index 85bd36fb68a2..6f0d031c1d5f 100644 --- a/sound/soc/fsl/imx-es8328.c +++ b/sound/soc/fsl/imx-es8328.c @@ -37,6 +37,16 @@ static struct snd_soc_jack_gpio headset_jack_gpios[] = { }; static struct snd_soc_jack headset_jack; +static struct snd_soc_jack_pin headset_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, +}; static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) { @@ -46,9 +56,11 @@ 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_card_jack_new(rtd->card, "Headphone", - SND_JACK_HEADPHONE | SND_JACK_BTN_0, - &headset_jack); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headphone", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); if (ret) return ret; @@ -68,6 +80,11 @@ static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = { SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0), }; +static const struct snd_kcontrol_new imx_es8328_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + static int imx_es8328_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -183,6 +200,8 @@ static int imx_es8328_probe(struct platform_device *pdev) data->card.dev = dev; data->card.dapm_widgets = imx_es8328_dapm_widgets; data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets); + data->card.controls = imx_es8328_controls; + data->card.num_controls = ARRAY_SIZE(imx_es8328_controls); ret = snd_soc_of_parse_card_name(&data->card, "model"); if (ret) { dev_err(dev, "Unable to parse card name\n"); diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index 765dad607bf6..d63782b8bdef 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -228,6 +228,10 @@ static int imx_rpmsg_pcm_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct rpmsg_info *info = dev_get_drvdata(component->dev); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev); + struct snd_pcm_hardware pcm_hardware; struct rpmsg_msg *msg; int ret = 0; int cmd; @@ -255,10 +259,11 @@ static int imx_rpmsg_pcm_open(struct snd_soc_component *component, info->send_message(msg, info); - imx_rpmsg_pcm_hardware.period_bytes_max = - imx_rpmsg_pcm_hardware.buffer_bytes_max / 2; + pcm_hardware = imx_rpmsg_pcm_hardware; + pcm_hardware.buffer_bytes_max = rpmsg->buffer_size; + pcm_hardware.period_bytes_max = pcm_hardware.buffer_bytes_max / 2; - snd_soc_set_runtime_hwparams(substream, &imx_rpmsg_pcm_hardware); + snd_soc_set_runtime_hwparams(substream, &pcm_hardware); ret = snd_pcm_hw_constraint_integer(substream->runtime, SNDRV_PCM_HW_PARAM_PERIODS); @@ -597,7 +602,6 @@ static int imx_rpmsg_pcm_new(struct snd_soc_component *component, if (ret) return ret; - imx_rpmsg_pcm_hardware.buffer_bytes_max = rpmsg->buffer_size; return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC, pcm->card->dev, rpmsg->buffer_size); } diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 40a4a2667394..1671bcd4ee3d 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -222,6 +222,7 @@ static int psc_ac97_probe(struct snd_soc_dai *cpu_dai) * psc_ac97_dai_template: template CPU Digital Audio Interface */ static const struct snd_soc_dai_ops psc_ac97_analog_ops = { + .probe = psc_ac97_probe, .hw_params = psc_ac97_hw_analog_params, .trigger = psc_ac97_trigger, }; @@ -233,7 +234,6 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = { static struct snd_soc_dai_driver psc_ac97_dai[] = { { .name = "mpc5200-psc-ac97.0", - .probe = psc_ac97_probe, .playback = { .stream_name = "AC97 Playback", .channels_min = 1, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index c6e0f9132193..844a2ef15948 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -60,7 +60,7 @@ static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc) struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc); if (dai && (dai->component->driver->pcm_construct || - dai->driver->pcm_new)) + (dai->driver->ops && dai->driver->ops->pcm_new))) return true; return false; @@ -126,7 +126,7 @@ static int graph_parse_node(struct asoc_simple_priv *priv, graph_parse_mclk_fs(top, ep, dai_props); - ret = asoc_graph_parse_dai(ep, dlc, cpu); + ret = asoc_graph_parse_dai(dev, ep, dlc, cpu); if (ret < 0) return ret; @@ -425,77 +425,6 @@ static int graph_for_each_link(struct asoc_simple_priv *priv, return ret; } -static int graph_get_dais_count(struct asoc_simple_priv *priv, - struct link_info *li); - -int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev) -{ - struct snd_soc_card *card = simple_priv_to_card(priv); - struct link_info *li; - int ret; - - li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); - if (!li) - return -ENOMEM; - - card->owner = THIS_MODULE; - card->dev = dev; - - ret = graph_get_dais_count(priv, li); - if (ret < 0) - return ret; - - if (!li->link) - return -EINVAL; - - ret = asoc_simple_init_priv(priv, li); - if (ret < 0) - return ret; - - priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); - if (IS_ERR(priv->pa_gpio)) { - ret = PTR_ERR(priv->pa_gpio); - dev_err(dev, "failed to get amplifier gpio: %d\n", ret); - return ret; - } - - ret = asoc_simple_parse_widgets(card, NULL); - if (ret < 0) - return ret; - - ret = asoc_simple_parse_routing(card, NULL); - if (ret < 0) - return ret; - - memset(li, 0, sizeof(*li)); - ret = graph_for_each_link(priv, li, - graph_dai_link_of, - graph_dai_link_of_dpcm); - if (ret < 0) - goto err; - - ret = asoc_simple_parse_card_name(card, NULL); - if (ret < 0) - goto err; - - snd_soc_card_set_drvdata(card, priv); - - asoc_simple_debug_info(priv); - - ret = devm_snd_soc_register_card(dev, card); - if (ret < 0) - goto err; - - devm_kfree(dev, li); - return 0; - -err: - asoc_simple_clean_reference(card); - - return dev_err_probe(dev, ret, "parse error\n"); -} -EXPORT_SYMBOL_GPL(audio_graph_parse_of); - static int graph_count_noml(struct asoc_simple_priv *priv, struct device_node *cpu_ep, struct device_node *codec_ep, @@ -612,6 +541,74 @@ static int graph_get_dais_count(struct asoc_simple_priv *priv, graph_count_dpcm); } +int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev) +{ + struct snd_soc_card *card = simple_priv_to_card(priv); + struct link_info *li; + int ret; + + li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); + if (!li) + return -ENOMEM; + + card->owner = THIS_MODULE; + card->dev = dev; + + ret = graph_get_dais_count(priv, li); + if (ret < 0) + return ret; + + if (!li->link) + return -EINVAL; + + ret = asoc_simple_init_priv(priv, li); + if (ret < 0) + return ret; + + priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); + if (IS_ERR(priv->pa_gpio)) { + ret = PTR_ERR(priv->pa_gpio); + dev_err(dev, "failed to get amplifier gpio: %d\n", ret); + return ret; + } + + ret = asoc_simple_parse_widgets(card, NULL); + if (ret < 0) + return ret; + + ret = asoc_simple_parse_routing(card, NULL); + if (ret < 0) + return ret; + + memset(li, 0, sizeof(*li)); + ret = graph_for_each_link(priv, li, + graph_dai_link_of, + graph_dai_link_of_dpcm); + if (ret < 0) + goto err; + + ret = asoc_simple_parse_card_name(card, NULL); + if (ret < 0) + goto err; + + snd_soc_card_set_drvdata(card, priv); + + asoc_simple_debug_info(priv); + + ret = devm_snd_soc_register_card(dev, card); + if (ret < 0) + goto err; + + devm_kfree(dev, li); + return 0; + +err: + asoc_simple_clean_reference(card); + + return dev_err_probe(dev, ret, "parse error\n"); +} +EXPORT_SYMBOL_GPL(audio_graph_parse_of); + static int graph_probe(struct platform_device *pdev) { struct asoc_simple_priv *priv; diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi index 2ac0de3c21da..8acaa2ddb335 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -34,6 +34,13 @@ * ... * }; * + * + * Below sample doesn't use "format" property, + * because test-component driver (test-cpu/test-codec) is supporting + * snd_soc_dai_ops :: .auto_selectable_formats. + * see + * snd_soc_runtime_get_dai_fmt() + * linux/sound/soc/generic/test-component.c :: test_dai_formats */ / { /* diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 542c4a114940..b1c675c6b6db 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -47,6 +47,18 @@ see graph_parse_daifmt(). + "format" property is no longer needed on DT if both CPU/Codec drivers are + supporting snd_soc_dai_ops :: .auto_selectable_formats. + see + snd_soc_runtime_get_dai_fmt() + + sample driver + linux/sound/soc/sh/rcar/core.c + linux/sound/soc/codecs/ak4613.c + linux/sound/soc/codecs/pcm3168a.c + linux/sound/soc/soc-utils.c + linux/sound/soc/generic/test-component.c + ************************************ Normal Audio-Graph ************************************ @@ -407,7 +419,7 @@ static int __graph_parse_node(struct asoc_simple_priv *priv, graph_parse_mclk_fs(ep, dai_props); - ret = asoc_graph_parse_dai(ep, dlc, &is_single_links); + ret = asoc_graph_parse_dai(dev, ep, dlc, &is_single_links); if (ret < 0) return ret; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 3019626b0592..5b18a4af022f 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -649,7 +649,7 @@ void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platfo * simple-card.c :: simple_count_noml() */ if (!platforms->of_node) - platforms->of_node = cpus->of_node; + snd_soc_dlc_use_cpu_as_platform(platforms, cpus); } EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform); @@ -1066,12 +1066,12 @@ static int graph_get_dai_id(struct device_node *ep) return id; } -int asoc_graph_parse_dai(struct device_node *ep, - struct snd_soc_dai_link_component *dlc, - int *is_single_link) +int asoc_graph_parse_dai(struct device *dev, struct device_node *ep, + struct snd_soc_dai_link_component *dlc, int *is_single_link) { struct device_node *node; struct of_phandle_args args = {}; + struct snd_soc_dai *dai; int ret; if (!ep) @@ -1079,6 +1079,20 @@ int asoc_graph_parse_dai(struct device_node *ep, node = of_graph_get_port_parent(ep); + /* + * Try to find from DAI node + */ + args.np = ep; + dai = snd_soc_get_dai_via_args(&args); + if (dai) { + dlc->dai_name = snd_soc_dai_name_get(dai); + dlc->dai_args = snd_soc_copy_dai_args(dev, &args); + if (!dlc->dai_args) + return -ENOMEM; + + goto parse_dai_end; + } + /* Get dai->name */ args.np = node; args.args[0] = graph_get_dai_id(ep); @@ -1109,6 +1123,7 @@ int asoc_graph_parse_dai(struct device_node *ep, return ret; } +parse_dai_end: if (is_single_link) *is_single_link = of_graph_get_endpoint_count(node) == 1; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 0745bf6a09aa..190f11366e84 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -52,11 +52,13 @@ static int asoc_simple_parse_platform(struct device_node *node, return 0; } -static int asoc_simple_parse_dai(struct device_node *node, +static int asoc_simple_parse_dai(struct device *dev, + struct device_node *node, struct snd_soc_dai_link_component *dlc, int *is_single_link) { struct of_phandle_args args; + struct snd_soc_dai *dai; int ret; if (!node) @@ -71,6 +73,19 @@ static int asoc_simple_parse_dai(struct device_node *node, return ret; /* + * Try to find from DAI args + */ + dai = snd_soc_get_dai_via_args(&args); + if (dai) { + dlc->dai_name = snd_soc_dai_name_get(dai); + dlc->dai_args = snd_soc_copy_dai_args(dev, &args); + if (!dlc->dai_args) + return -ENOMEM; + + goto parse_dai_end; + } + + /* * FIXME * * Here, dlc->dai_name is pointer to CPU/Codec DAI name. @@ -93,6 +108,7 @@ static int asoc_simple_parse_dai(struct device_node *node, if (ret < 0) return ret; +parse_dai_end: if (is_single_link) *is_single_link = !args.args_count; @@ -156,7 +172,7 @@ static int simple_parse_node(struct asoc_simple_priv *priv, simple_parse_mclk_fs(top, np, dai_props, prefix); - ret = asoc_simple_parse_dai(np, dlc, cpu); + ret = asoc_simple_parse_dai(dev, np, dlc, cpu); if (ret) return ret; @@ -346,6 +362,7 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv, struct device *dev = simple_priv_to_dev(priv); struct device_node *top = dev->of_node; struct device_node *node; + struct device_node *add_devs; uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev); bool is_top = 0; int ret = 0; @@ -357,6 +374,8 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv, is_top = 1; } + add_devs = of_get_child_by_name(top, PREFIX "additional-devs"); + /* loop for all dai-link */ do { struct asoc_simple_data adata; @@ -365,6 +384,12 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv, struct device_node *np; int num = of_get_child_count(node); + /* Skip additional-devs node */ + if (node == add_devs) { + node = of_get_next_child(top, node); + continue; + } + /* get codec */ codec = of_get_child_by_name(node, is_top ? PREFIX "codec" : "codec"); @@ -378,12 +403,15 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv, /* get convert-xxx property */ memset(&adata, 0, sizeof(adata)); - for_each_child_of_node(node, np) + for_each_child_of_node(node, np) { + if (np == add_devs) + continue; simple_parse_convert(dev, np, &adata); + } /* loop for all CPU/Codec node */ for_each_child_of_node(node, np) { - if (plat == np) + if (plat == np || add_devs == np) continue; /* * It is DPCM @@ -426,6 +454,7 @@ static int __simple_for_each_link(struct asoc_simple_priv *priv, } while (!is_top && node); error: + of_node_put(add_devs); of_node_put(node); return ret; } @@ -463,6 +492,31 @@ static int simple_for_each_link(struct asoc_simple_priv *priv, return ret; } +static void simple_depopulate_aux(void *data) +{ + struct asoc_simple_priv *priv = data; + + of_platform_depopulate(simple_priv_to_dev(priv)); +} + +static int simple_populate_aux(struct asoc_simple_priv *priv) +{ + struct device *dev = simple_priv_to_dev(priv); + struct device_node *node; + int ret; + + node = of_get_child_by_name(dev->of_node, PREFIX "additional-devs"); + if (!node) + return 0; + + ret = of_platform_populate(node, NULL, NULL, dev); + of_node_put(node); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, simple_depopulate_aux, priv); +} + static int simple_parse_of(struct asoc_simple_priv *priv, struct link_info *li) { struct snd_soc_card *card = simple_priv_to_card(priv); @@ -492,6 +546,10 @@ static int simple_parse_of(struct asoc_simple_priv *priv, struct link_info *li) if (ret < 0) return ret; + ret = simple_populate_aux(priv); + if (ret < 0) + return ret; + ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); return ret; diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c index 27219a9e7d0d..dd7d2a077248 100644 --- a/sound/soc/hisilicon/hi6210-i2s.c +++ b/sound/soc/hisilicon/hi6210-i2s.c @@ -511,6 +511,7 @@ static int hi6210_i2s_dai_probe(struct snd_soc_dai *dai) static const struct snd_soc_dai_ops hi6210_i2s_dai_ops = { + .probe = hi6210_i2s_dai_probe, .trigger = hi6210_i2s_trigger, .hw_params = hi6210_i2s_hw_params, .set_fmt = hi6210_i2s_set_fmt, @@ -519,7 +520,6 @@ static const struct snd_soc_dai_ops hi6210_i2s_dai_ops = { }; static const struct snd_soc_dai_driver hi6210_i2s_dai_init = { - .probe = hi6210_i2s_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index b7ab8467b5cf..b6b6339c164b 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -370,12 +370,6 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static const struct snd_soc_dai_ops img_i2s_in_dai_ops = { - .trigger = img_i2s_in_trigger, - .hw_params = img_i2s_in_hw_params, - .set_fmt = img_i2s_in_set_fmt -}; - static int img_i2s_in_dai_probe(struct snd_soc_dai *dai) { struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); @@ -385,6 +379,13 @@ static int img_i2s_in_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops img_i2s_in_dai_ops = { + .probe = img_i2s_in_dai_probe, + .trigger = img_i2s_in_trigger, + .hw_params = img_i2s_in_hw_params, + .set_fmt = img_i2s_in_set_fmt +}; + static const struct snd_soc_component_driver img_i2s_in_component = { .name = "img-i2s-in", .legacy_dai_naming = 1, @@ -468,7 +469,6 @@ static int img_i2s_in_probe(struct platform_device *pdev) i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO; i2s->dma_data.addr_width = 4; - i2s->dai_driver.probe = img_i2s_in_dai_probe; i2s->dai_driver.capture.channels_min = 2; i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2; i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000; diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index fe95ddfb8407..41ea5ba52181 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -376,12 +376,6 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static const struct snd_soc_dai_ops img_i2s_out_dai_ops = { - .trigger = img_i2s_out_trigger, - .hw_params = img_i2s_out_hw_params, - .set_fmt = img_i2s_out_set_fmt -}; - static int img_i2s_out_dai_probe(struct snd_soc_dai *dai) { struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); @@ -391,6 +385,13 @@ static int img_i2s_out_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops img_i2s_out_dai_ops = { + .probe = img_i2s_out_dai_probe, + .trigger = img_i2s_out_trigger, + .hw_params = img_i2s_out_hw_params, + .set_fmt = img_i2s_out_set_fmt +}; + static const struct snd_soc_component_driver img_i2s_out_component = { .name = "img-i2s-out", .legacy_dai_naming = 1, @@ -504,7 +505,6 @@ static int img_i2s_out_probe(struct platform_device *pdev) i2s->dma_data.addr_width = 4; i2s->dma_data.maxburst = 4; - i2s->dai_driver.probe = img_i2s_out_dai_probe; i2s->dai_driver.playback.channels_min = 2; i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2; i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000; diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index df1291ee2b3b..815e68a7048c 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -174,12 +174,6 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static const struct snd_soc_dai_ops img_prl_out_dai_ops = { - .trigger = img_prl_out_trigger, - .hw_params = img_prl_out_hw_params, - .set_fmt = img_prl_out_set_fmt -}; - static int img_prl_out_dai_probe(struct snd_soc_dai *dai) { struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); @@ -189,8 +183,14 @@ static int img_prl_out_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops img_prl_out_dai_ops = { + .probe = img_prl_out_dai_probe, + .trigger = img_prl_out_trigger, + .hw_params = img_prl_out_hw_params, + .set_fmt = img_prl_out_set_fmt +}; + static struct snd_soc_dai_driver img_prl_out_dai = { - .probe = img_prl_out_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c index 558062a1804a..9646e9d3f0bc 100644 --- a/sound/soc/img/img-spdif-in.c +++ b/sound/soc/img/img-spdif-in.c @@ -682,11 +682,6 @@ static int img_spdif_in_hw_params(struct snd_pcm_substream *substream, return img_spdif_in_do_clkgen_single(spdif, rate); } -static const struct snd_soc_dai_ops img_spdif_in_dai_ops = { - .trigger = img_spdif_in_trigger, - .hw_params = img_spdif_in_hw_params -}; - static int img_spdif_in_dai_probe(struct snd_soc_dai *dai) { struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); @@ -699,8 +694,13 @@ static int img_spdif_in_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops img_spdif_in_dai_ops = { + .probe = img_spdif_in_dai_probe, + .trigger = img_spdif_in_trigger, + .hw_params = img_spdif_in_hw_params +}; + static struct snd_soc_dai_driver img_spdif_in_dai = { - .probe = img_spdif_in_dai_probe, .capture = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c index b13e128e50d6..dfa72afa946e 100644 --- a/sound/soc/img/img-spdif-out.c +++ b/sound/soc/img/img-spdif-out.c @@ -287,11 +287,6 @@ static int img_spdif_out_hw_params(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops img_spdif_out_dai_ops = { - .trigger = img_spdif_out_trigger, - .hw_params = img_spdif_out_hw_params -}; - static int img_spdif_out_dai_probe(struct snd_soc_dai *dai) { struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); @@ -304,8 +299,13 @@ static int img_spdif_out_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops img_spdif_out_dai_ops = { + .probe = img_spdif_out_dai_probe, + .trigger = img_spdif_out_trigger, + .hw_params = img_spdif_out_hw_params +}; + static struct snd_soc_dai_driver img_spdif_out_dai = { - .probe = img_spdif_out_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index ba4597bdf32e..6f986c7bbc8b 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -467,6 +467,7 @@ static const struct snd_soc_dai_ops sst_media_dai_ops = { }; static const struct snd_soc_dai_ops sst_compr_dai_ops = { + .compress_new = snd_soc_new_compress, .mute_stream = sst_media_digital_mute, }; @@ -510,7 +511,6 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { }, { .name = "compress-cpu-dai", - .compress_new = snd_soc_new_compress, .ops = &sst_compr_dai_ops, .playback = { .stream_name = "Compress Playback", diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index a0d29510d2bc..e0357d257c6c 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/firmware.h> +#include <linux/pci.h> #include <linux/pm_runtime.h> #include <linux/pm_qos.h> #include <linux/async.h> @@ -174,9 +175,9 @@ int sst_driver_ops(struct intel_sst_drv *sst) { switch (sst->dev_id) { - case SST_MRFLD_PCI_ID: - case SST_BYT_ACPI_ID: - case SST_CHV_ACPI_ID: + case PCI_DEVICE_ID_INTEL_SST_TNG: + case PCI_DEVICE_ID_INTEL_SST_BYT: + case PCI_DEVICE_ID_INTEL_SST_BSW: sst->tstamp = SST_TIME_STAMP_MRFLD; sst->ops = &mrfld_ops; return 0; @@ -221,8 +222,13 @@ static void sst_init_locks(struct intel_sst_drv *ctx) spin_lock_init(&ctx->block_lock); } +/* + * Driver handles PCI IDs in ACPI - sst_acpi_probe() - and we are using only + * device ID part. If real ACPI ID appears, the kstrtouint() returns error, so + * we are fine with using unsigned short as dev_id type. + */ int sst_alloc_drv_context(struct intel_sst_drv **ctx, - struct device *dev, unsigned int dev_id) + struct device *dev, unsigned short dev_id) { *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL); if (!(*ctx)) diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h index 4d37d39fd8f4..126903e126e4 100644 --- a/sound/soc/intel/atom/sst/sst.h +++ b/sound/soc/intel/atom/sst/sst.h @@ -20,9 +20,6 @@ /* driver names */ #define SST_DRV_NAME "intel_sst_driver" -#define SST_MRFLD_PCI_ID 0x119A -#define SST_BYT_ACPI_ID 0x80860F28 -#define SST_CHV_ACPI_ID 0x808622A8 #define SST_SUSPEND_DELAY 2000 #define FW_CONTEXT_MEM (64*1024) @@ -358,7 +355,7 @@ struct sst_fw_save { struct intel_sst_drv { int sst_state; int irq_num; - unsigned int dev_id; + unsigned short dev_id; void __iomem *ddr; void __iomem *shim; void __iomem *mailbox; @@ -523,7 +520,7 @@ int sst_register(struct device *); int sst_unregister(struct device *); int sst_alloc_drv_context(struct intel_sst_drv **ctx, - struct device *dev, unsigned int dev_id); + struct device *dev, unsigned short dev_id); 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); diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c index 4058b4f80a0c..d1e64c3500be 100644 --- a/sound/soc/intel/atom/sst/sst_pci.c +++ b/sound/soc/intel/atom/sst/sst_pci.c @@ -32,7 +32,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) /* map registers */ /* DDR base */ - if (ctx->dev_id == SST_MRFLD_PCI_ID) { + if (ctx->dev_id == PCI_DEVICE_ID_INTEL_SST_TNG) { ctx->ddr_base = pci_resource_start(pci, 0); /* check that the relocated IMR base matches with FW Binary */ ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base); @@ -173,7 +173,7 @@ static void intel_sst_remove(struct pci_dev *pci) /* PCI Routines */ static const struct pci_device_id intel_sst_ids[] = { - { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0}, + { PCI_DEVICE_DATA(INTEL, SST_TNG, 0) }, { 0, } }; diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index 862a19ae5429..288221db7323 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -173,10 +173,11 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, u32 length; int pvt_id, ret = 0; struct sst_block *block = NULL; + u8 bytes_block = bytes->block; dev_dbg(sst_drv_ctx->dev, "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n", - bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id, + bytes->type, bytes->ipc_msg, bytes_block, bytes->task_id, bytes->pipe_id, bytes->len); if (sst_create_ipc_msg(&msg, true)) @@ -185,12 +186,12 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, pvt_id = sst_assign_pvt_id(sst_drv_ctx); sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg, bytes->task_id, 1, pvt_id); - msg->mrfld_header.p.header_high.part.res_rqd = bytes->block; + msg->mrfld_header.p.header_high.part.res_rqd = bytes_block; length = bytes->len; msg->mrfld_header.p.header_low_payload = length; dev_dbg(sst_drv_ctx->dev, "length is %d\n", length); memcpy(msg->mailbox_data, &bytes->bytes, bytes->len); - if (bytes->block) { + if (bytes_block) { block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id); if (block == NULL) { kfree(msg); @@ -203,7 +204,7 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d", msg->mrfld_header.p.header_low_payload); - if (bytes->block) { + if (bytes_block) { ret = sst_wait_timeout(sst_drv_ctx, block); if (ret) { dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret); @@ -216,7 +217,7 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, * copy the reply and send back * we need to update only sz and payload */ - if (bytes->block) { + if (bytes_block) { unsigned char *r = block->data; dev_dbg(sst_drv_ctx->dev, "read back %d bytes", @@ -224,7 +225,7 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, memcpy(bytes->bytes, r, bytes->len); } } - if (bytes->block) + if (bytes_block) sst_free_block(sst_drv_ctx, block); out: test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id); diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 60f8fb0bff95..59a13feec57b 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -136,6 +136,14 @@ static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = { .tplg_filename = "max98927-tplg.bin", }, { + .id = "10EC5663", + .drv_name = "avs_rt5663", + .mach_params = { + .i2s_link_mask = AVS_SSP(1), + }, + .tplg_filename = "rt5663-tplg.bin", + }, + { .id = "MX98373", .drv_name = "avs_max98373", .mach_params = { @@ -159,6 +167,14 @@ static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = { }, .tplg_filename = "da7219-tplg.bin", }, + { + .id = "ESSX8336", + .drv_name = "avs_es8336", + .mach_params = { + .i2s_link_mask = AVS_SSP(0), + }, + .tplg_filename = "es8336-tplg.bin", + }, {}, }; @@ -263,14 +279,14 @@ struct avs_acpi_boards { }; #define AVS_MACH_ENTRY(_id, _mach) \ - { .id = (_id), .machs = (_mach), } + { .id = PCI_DEVICE_ID_INTEL_##_id, .machs = (_mach), } /* supported I2S boards per platform */ static const struct avs_acpi_boards i2s_boards[] = { - AVS_MACH_ENTRY(0x9d70, avs_skl_i2s_machines), /* SKL */ - AVS_MACH_ENTRY(0x9d71, avs_kbl_i2s_machines), /* KBL */ - AVS_MACH_ENTRY(0x5a98, avs_apl_i2s_machines), /* APL */ - AVS_MACH_ENTRY(0x3198, avs_gml_i2s_machines), /* GML */ + AVS_MACH_ENTRY(HDA_SKL_LP, avs_skl_i2s_machines), + AVS_MACH_ENTRY(HDA_KBL_LP, avs_kbl_i2s_machines), + AVS_MACH_ENTRY(HDA_APL, avs_apl_i2s_machines), + AVS_MACH_ENTRY(HDA_GML, avs_gml_i2s_machines), {}, }; diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig index e4c230efe8d7..07353d37ecae 100644 --- a/sound/soc/intel/avs/boards/Kconfig +++ b/sound/soc/intel/avs/boards/Kconfig @@ -22,6 +22,16 @@ config SND_SOC_INTEL_AVS_MACH_DMIC Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_AVS_MACH_ES8336 + tristate "es8336 I2S board" + depends on X86 && I2C + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_ES8316 + help + This adds support for AVS with ES8336 I2S codec configuration. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + config SND_SOC_INTEL_AVS_MACH_HDAUDIO tristate "HD-Audio generic board" select SND_SOC_HDA @@ -115,6 +125,16 @@ config SND_SOC_INTEL_AVS_MACH_RT298 Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_AVS_MACH_RT5663 + tristate "rt5663 in I2S mode" + depends on I2C + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_RT5663 + help + This adds support for ASoC machine driver with RT5663 I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + config SND_SOC_INTEL_AVS_MACH_RT5682 tristate "rt5682 in I2S mode" depends on I2C diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile index b81343420370..34347bcd1e7d 100644 --- a/sound/soc/intel/avs/boards/Makefile +++ b/sound/soc/intel/avs/boards/Makefile @@ -2,6 +2,7 @@ snd-soc-avs-da7219-objs := da7219.o snd-soc-avs-dmic-objs := dmic.o +snd-soc-avs-es8336-objs := es8336.o snd-soc-avs-hdaudio-objs := hdaudio.o snd-soc-avs-i2s-test-objs := i2s_test.o snd-soc-avs-max98927-objs := max98927.o @@ -12,11 +13,13 @@ snd-soc-avs-probe-objs := probe.o snd-soc-avs-rt274-objs := rt274.o snd-soc-avs-rt286-objs := rt286.o snd-soc-avs-rt298-objs := rt298.o +snd-soc-avs-rt5663-objs := rt5663.o snd-soc-avs-rt5682-objs := rt5682.o snd-soc-avs-ssm4567-objs := ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DA7219) += snd-soc-avs-da7219.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_DMIC) += snd-soc-avs-dmic.o +obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_ES8336) += snd-soc-avs-es8336.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_HDAUDIO) += snd-soc-avs-hdaudio.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_I2S_TEST) += snd-soc-avs-i2s-test.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o @@ -27,5 +30,6 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o +obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5663) += snd-soc-avs-rt5663.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5682) += snd-soc-avs-rt5682.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_SSM4567) += snd-soc-avs-ssm4567.o diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index 964a763732ab..85014d98f7e8 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -22,6 +22,7 @@ static const struct snd_kcontrol_new card_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static int platform_clock_control(struct snd_soc_dapm_widget *w, @@ -55,6 +56,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget card_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), }; @@ -68,6 +70,22 @@ static const struct snd_soc_dapm_route card_base_routes[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, +}; + +static const struct snd_soc_jack_pin card_headset_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) @@ -75,7 +93,9 @@ static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); struct snd_soc_component *component = codec_dai->component; struct snd_soc_card *card = runtime->card; + struct snd_soc_jack_pin *pins; struct snd_soc_jack *jack; + int num_pins; int clk_freq; int ret; @@ -91,14 +111,20 @@ static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) return ret; } + num_pins = ARRAY_SIZE(card_headset_pins); + pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + if (!pins) + return -ENOMEM; + /* * Headset buttons map to the google Reference headset. * These can be configured by userspace. */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3 | SND_JACK_LINEOUT, jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3 | SND_JACK_LINEOUT, + jack, pins, num_pins); if (ret) { dev_err(card->dev, "Headset Jack creation failed: %d\n", ret); return ret; diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c new file mode 100644 index 000000000000..0a023f871d93 --- /dev/null +++ b/sound/soc/intel/avs/boards/es8336.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/processor.h> +#include <linux/slab.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include <asm/intel-family.h> + +#define ES8336_CODEC_DAI "ES8316 HiFi" + +struct avs_card_drvdata { + struct snd_soc_jack jack; + struct gpio_desc *gpiod; +}; + +static const struct acpi_gpio_params enable_gpio = { 0, 0, true }; + +static const struct acpi_gpio_mapping speaker_gpios[] = { + { "speaker-enable-gpios", &enable_gpio, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO }, + { } +}; + +static int avs_es8336_speaker_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_card *card = w->dapm->card; + struct avs_card_drvdata *data; + bool speaker_en; + + data = snd_soc_card_get_drvdata(card); + /* As enable_gpio has active_low=true, logic is inverted. */ + speaker_en = !SND_SOC_DAPM_EVENT_ON(event); + + gpiod_set_value_cansleep(data->gpiod, speaker_en); + return 0; +} + +static const struct snd_soc_dapm_widget card_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Internal Mic", NULL), + + SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0, + avs_es8336_speaker_power_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +}; + +static const struct snd_soc_dapm_route card_routes[] = { + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + + /* + * There is no separate speaker output instead the speakers are muxed to + * the HP outputs. The mux is controlled by the "Speaker Power" widget. + */ + {"Speaker", NULL, "HPOL"}, + {"Speaker", NULL, "HPOR"}, + {"Speaker", NULL, "Speaker Power"}, + + /* Mic route map */ + {"MIC1", NULL, "Internal Mic"}, + {"MIC2", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new card_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Internal Mic"), +}; + +static struct snd_soc_jack_pin card_headset_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); + struct snd_soc_component *component = codec_dai->component; + struct snd_soc_card *card = runtime->card; + struct snd_soc_jack_pin *pins; + struct avs_card_drvdata *data; + struct gpio_desc *gpiod; + int num_pins, ret; + + data = snd_soc_card_get_drvdata(card); + num_pins = ARRAY_SIZE(card_headset_pins); + + pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + if (!pins) + return -ENOMEM; + + ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0, + &data->jack, pins, num_pins); + if (ret) + return ret; + + ret = devm_acpi_dev_add_driver_gpios(codec_dai->dev, speaker_gpios); + if (ret) + dev_warn(codec_dai->dev, "Unable to add GPIO mapping table\n"); + + gpiod = gpiod_get_optional(codec_dai->dev, "speaker-enable", GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) + return dev_err_probe(codec_dai->dev, PTR_ERR(gpiod), "Get gpiod failed: %ld\n", + PTR_ERR(gpiod)); + + data->gpiod = gpiod; + snd_jack_set_key(data->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_soc_component_set_jack(component, &data->jack, NULL); + + card->dapm.idle_bias_off = true; + + return 0; +} + +static void avs_es8336_codec_exit(struct snd_soc_pcm_runtime *runtime) +{ + struct avs_card_drvdata *data = snd_soc_card_get_drvdata(runtime->card); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); + + snd_soc_component_set_jack(codec_dai->component, NULL, NULL); + gpiod_put(data->gpiod); +} + +static int avs_es8336_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); + int clk_freq; + int ret; + + switch (boot_cpu_data.x86_model) { + case INTEL_FAM6_KABYLAKE_L: + case INTEL_FAM6_KABYLAKE: + clk_freq = 24000000; + break; + default: + clk_freq = 19200000; + break; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, 1, clk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) + dev_err(runtime->dev, "Set codec sysclk failed: %d\n", ret); + + return ret; +} + +static const struct snd_soc_ops avs_es8336_ops = { + .hw_params = avs_es8336_hw_params, +}; + +static int avs_es8336_be_fixup(struct snd_soc_pcm_runtime *runtime, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate, *channels; + struct snd_mask *fmt; + + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will convert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSPN to 24 bit */ + snd_mask_none(fmt); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_3LE); + + return 0; +} +static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port, + struct snd_soc_dai_link **dai_link) +{ + struct snd_soc_dai_link_component *platform; + struct snd_soc_dai_link *dl; + + dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL); + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); + if (!dl || !platform) + return -ENOMEM; + + platform->name = platform_name; + + dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); + dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); + dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL); + if (!dl->name || !dl->cpus || !dl->codecs) + return -ENOMEM; + + dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port); + dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-ESSX8336:00"); + dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, ES8336_CODEC_DAI); + if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name) + return -ENOMEM; + + dl->num_cpus = 1; + dl->num_codecs = 1; + dl->platforms = platform; + dl->num_platforms = 1; + dl->id = 0; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + dl->init = avs_es8336_codec_init; + dl->exit = avs_es8336_codec_exit; + dl->be_hw_params_fixup = avs_es8336_be_fixup; + dl->ops = &avs_es8336_ops; + dl->nonatomic = 1; + dl->no_pcm = 1; + dl->dpcm_capture = 1; + dl->dpcm_playback = 1; + + *dai_link = dl; + + return 0; +} + +static int avs_card_suspend_pre(struct snd_soc_card *card) +{ + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, ES8336_CODEC_DAI); + + return snd_soc_component_set_jack(codec_dai->component, NULL, NULL); +} + +static int avs_card_resume_post(struct snd_soc_card *card) +{ + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, ES8336_CODEC_DAI); + struct avs_card_drvdata *data = snd_soc_card_get_drvdata(card); + + return snd_soc_component_set_jack(codec_dai->component, &data->jack, NULL); +} + +static int avs_es8336_probe(struct platform_device *pdev) +{ + struct snd_soc_dai_link *dai_link; + struct snd_soc_acpi_mach *mach; + struct avs_card_drvdata *data; + struct snd_soc_card *card; + struct device *dev = &pdev->dev; + const char *pname; + int ssp_port, ret; + + mach = dev_get_platdata(dev); + pname = mach->mach_params.platform; + ssp_port = __ffs(mach->mach_params.i2s_link_mask); + + ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link); + if (ret) { + dev_err(dev, "Failed to create dai link: %d", ret); + return ret; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!data || !card) + return -ENOMEM; + + card->name = "avs_es8336"; + card->dev = dev; + card->owner = THIS_MODULE; + card->suspend_pre = avs_card_suspend_pre; + card->resume_post = avs_card_resume_post; + card->dai_link = dai_link; + card->num_links = 1; + card->controls = card_controls; + card->num_controls = ARRAY_SIZE(card_controls); + card->dapm_widgets = card_widgets; + card->num_dapm_widgets = ARRAY_SIZE(card_widgets); + card->dapm_routes = card_routes; + card->num_dapm_routes = ARRAY_SIZE(card_routes); + card->fully_routed = true; + snd_soc_card_set_drvdata(card, data); + + ret = snd_soc_fixup_dai_links_platform_name(card, pname); + if (ret) + return ret; + + return devm_snd_soc_register_card(dev, card); +} + +static struct platform_driver avs_es8336_driver = { + .probe = avs_es8336_probe, + .driver = { + .name = "avs_es8336", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(avs_es8336_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:avs_es8336"); diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c new file mode 100644 index 000000000000..770b36d05bf4 --- /dev/null +++ b/sound/soc/intel/avs/boards/rt5663.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022-2023 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski <cezary.rojewski@intel.com> +// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// + +#include <linux/clk.h> +#include <linux/input.h> +#include <linux/module.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "../../../codecs/rt5663.h" + +#define RT5663_CODEC_DAI "rt5663-aif" + +struct rt5663_private { + struct snd_soc_jack jack; +}; + +static const struct snd_kcontrol_new card_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static const struct snd_soc_dapm_widget card_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route card_routes[] = { + /* HP jack connectors */ + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, + + /* Mic jacks */ + { "IN1P", NULL, "Headset Mic" }, + { "IN1N", NULL, "Headset Mic" }, +}; + +static struct snd_soc_jack_pin card_headset_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int avs_rt5663_codec_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct rt5663_private *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_jack_pin *pins; + struct snd_soc_jack *jack; + int num_pins, ret; + + jack = &priv->jack; + num_pins = ARRAY_SIZE(card_headset_pins); + + pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + if (!pins) + return -ENOMEM; + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack, + pins, num_pins); + if (ret) + return ret; + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + + snd_soc_component_set_jack(asoc_rtd_to_codec(runtime, 0)->component, jack, NULL); + + return 0; +} + +static void avs_rt5663_codec_exit(struct snd_soc_pcm_runtime *runtime) +{ + snd_soc_component_set_jack(asoc_rtd_to_codec(runtime, 0)->component, NULL, NULL); +} + +static int +avs_rt5663_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate, *channels; + struct snd_mask *fmt; + + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will convert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSPN to 24 bit */ + snd_mask_none(fmt); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int avs_rt5663_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + int ret; + + /* use ASRC for internal clocks, as PLL rate isn't multiple of BCLK */ + rt5663_sel_asrc_clk_src(codec_dai->component, + RT5663_DA_STEREO_FILTER | RT5663_AD_STEREO_FILTER, + RT5663_CLK_SEL_I2S1_ASRC); + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5663_SCLK_S_MCLK, 24576000, SND_SOC_CLOCK_IN); + + return ret; +} + +static const struct snd_soc_ops avs_rt5663_ops = { + .hw_params = avs_rt5663_hw_params, +}; + + +static int avs_create_dai_link(struct device *dev, const char *platform_name, int ssp_port, + struct snd_soc_dai_link **dai_link) +{ + struct snd_soc_dai_link_component *platform; + struct snd_soc_dai_link *dl; + + dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL); + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); + if (!dl || !platform) + return -ENOMEM; + + platform->name = platform_name; + + dl->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_port); + dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); + dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL); + if (!dl->name || !dl->cpus || !dl->codecs) + return -ENOMEM; + + dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_port); + dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5663:00"); + dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5663_CODEC_DAI); + if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name) + return -ENOMEM; + + dl->num_cpus = 1; + dl->num_codecs = 1; + dl->platforms = platform; + dl->num_platforms = 1; + dl->id = 0; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + dl->init = avs_rt5663_codec_init; + dl->exit = avs_rt5663_codec_exit; + dl->be_hw_params_fixup = avs_rt5663_be_fixup; + dl->nonatomic = 1; + dl->no_pcm = 1; + dl->dpcm_capture = 1; + dl->dpcm_playback = 1; + dl->ops = &avs_rt5663_ops; + + *dai_link = dl; + + return 0; +} + +static int avs_card_suspend_pre(struct snd_soc_card *card) +{ + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5663_CODEC_DAI); + + return snd_soc_component_set_jack(codec_dai->component, NULL, NULL); +} + +static int avs_card_resume_post(struct snd_soc_card *card) +{ + struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5663_CODEC_DAI); + struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card); + + return snd_soc_component_set_jack(codec_dai->component, jack, NULL); +} + +static int avs_rt5663_probe(struct platform_device *pdev) +{ + struct snd_soc_dai_link *dai_link; + struct snd_soc_acpi_mach *mach; + struct snd_soc_card *card; + struct rt5663_private *priv; + struct device *dev = &pdev->dev; + const char *pname; + int ssp_port, ret; + + mach = dev_get_platdata(dev); + pname = mach->mach_params.platform; + ssp_port = __ffs(mach->mach_params.i2s_link_mask); + + ret = avs_create_dai_link(dev, pname, ssp_port, &dai_link); + if (ret) { + dev_err(dev, "Failed to create dai link: %d", ret); + return ret; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!priv || !card) + return -ENOMEM; + + card->name = "avs_rt5663"; + card->dev = dev; + card->owner = THIS_MODULE; + card->suspend_pre = avs_card_suspend_pre; + card->resume_post = avs_card_resume_post; + card->dai_link = dai_link; + card->num_links = 1; + card->controls = card_controls; + card->num_controls = ARRAY_SIZE(card_controls); + card->dapm_widgets = card_widgets; + card->num_dapm_widgets = ARRAY_SIZE(card_widgets); + card->dapm_routes = card_routes; + card->num_dapm_routes = ARRAY_SIZE(card_routes); + card->fully_routed = true; + snd_soc_card_set_drvdata(card, priv); + + ret = snd_soc_fixup_dai_links_platform_name(card, pname); + if (ret) + return ret; + + return devm_snd_soc_register_card(dev, card); +} + +static struct platform_driver avs_rt5663_driver = { + .probe = avs_rt5663_probe, + .driver = { + .name = "avs_rt5663", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(avs_rt5663_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:avs_rt5663"); diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c index 7142a67900bf..b93468ae0977 100644 --- a/sound/soc/intel/avs/boards/rt5682.c +++ b/sound/soc/intel/avs/boards/rt5682.c @@ -79,14 +79,31 @@ static const struct snd_soc_dapm_route card_base_routes[] = { { "IN1P", NULL, "Headset Mic" }, }; +static struct snd_soc_jack_pin card_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; - struct snd_soc_jack *jack; struct snd_soc_card *card = runtime->card; - int ret; + struct snd_soc_jack_pin *pins; + struct snd_soc_jack *jack; + int num_pins, ret; jack = snd_soc_card_get_drvdata(card); + num_pins = ARRAY_SIZE(card_jack_pins); + + pins = devm_kmemdup(card->dev, card_jack_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + if (!pins) + return -ENOMEM; /* Need to enable ASRC function for 24MHz mclk rate */ if ((avs_rt5682_quirk & AVS_RT5682_MCLK_EN) && @@ -95,12 +112,10 @@ static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime) RT5682_AD_STEREO1_FILTER, RT5682_CLK_SEL_I2S1_ASRC); } - /* - * Headset buttons map to the google Reference headset. - * These can be configured by userspace. - */ - ret = snd_soc_card_jack_new(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack); + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, jack, + pins, num_pins); if (ret) { dev_err(card->dev, "Headset Jack creation failed: %d\n", ret); return ret; @@ -130,39 +145,36 @@ avs_rt5682_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_para { struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); - int clk_id, clk_freq; - int pll_out, ret; + int pll_source, freq_in, freq_out; + int ret; if (avs_rt5682_quirk & AVS_RT5682_MCLK_EN) { - clk_id = RT5682_PLL1_S_MCLK; + pll_source = RT5682_PLL1_S_MCLK; if (avs_rt5682_quirk & AVS_RT5682_MCLK_24MHZ) - clk_freq = 24000000; + freq_in = 24000000; else - clk_freq = 19200000; + freq_in = 19200000; } else { - clk_id = RT5682_PLL1_S_BCLK1; - clk_freq = params_rate(params) * 50; + pll_source = RT5682_PLL1_S_BCLK1; + freq_in = params_rate(params) * 50; } - pll_out = params_rate(params) * 512; + freq_out = params_rate(params) * 512; - ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); + ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, pll_source, freq_in, freq_out); if (ret < 0) - dev_err(runtime->dev, "snd_soc_dai_set_pll err = %d\n", ret); + dev_err(runtime->dev, "Set PLL failed: %d\n", ret); - /* Configure sysclk for codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, pll_out, SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, freq_out, SND_SOC_CLOCK_IN); if (ret < 0) - dev_err(runtime->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + dev_err(runtime->dev, "Set sysclk failed: %d\n", ret); - /* slot_width should equal or large than data length, set them be the same */ + /* slot_width should be equal or larger than data length. */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x0, 0x0, 2, params_width(params)); - if (ret < 0) { - dev_err(runtime->dev, "set TDM slot err:%d\n", ret); - return ret; - } + if (ret < 0) + dev_err(runtime->dev, "Set TDM slot failed: %d\n", ret); - return 0; + return ret; } static const struct snd_soc_ops avs_rt5682_ops = { @@ -220,6 +232,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_rt5682_codec_init; dl->exit = avs_rt5682_codec_exit; dl->be_hw_params_fixup = avs_rt5682_be_fixup; diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 637501850728..859b217fc761 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -745,14 +745,14 @@ static const struct avs_spec apl_desc = { }; static const struct pci_device_id avs_ids[] = { - { PCI_VDEVICE(INTEL, 0x9d70), (unsigned long)&skl_desc }, /* SKL */ - { PCI_VDEVICE(INTEL, 0xa170), (unsigned long)&skl_desc }, /* SKL-H */ - { PCI_VDEVICE(INTEL, 0x9d71), (unsigned long)&skl_desc }, /* KBL */ - { PCI_VDEVICE(INTEL, 0xa171), (unsigned long)&skl_desc }, /* KBL-H */ - { PCI_VDEVICE(INTEL, 0xa2f0), (unsigned long)&skl_desc }, /* KBL-S */ - { PCI_VDEVICE(INTEL, 0xa3f0), (unsigned long)&skl_desc }, /* CML-V */ - { PCI_VDEVICE(INTEL, 0x5a98), (unsigned long)&apl_desc }, /* APL */ - { PCI_VDEVICE(INTEL, 0x3198), (unsigned long)&apl_desc }, /* GML */ + { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_SKL, &skl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_KBL_LP, &skl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_KBL, &skl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_KBL_H, &skl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CML_S, &skl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_APL, &apl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_GML, &apl_desc) }, { 0 } }; MODULE_DEVICE_TABLE(pci, avs_ids); diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 275928281c6c..4cab8c6c4576 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -249,7 +249,7 @@ static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr return count; } -static const struct snd_soc_cdai_ops avs_probe_dai_ops = { +static const struct snd_soc_cdai_ops avs_probe_cdai_ops = { .startup = avs_probe_compr_open, .shutdown = avs_probe_compr_free, .set_params = avs_probe_compr_set_params, @@ -257,6 +257,10 @@ static const struct snd_soc_cdai_ops avs_probe_dai_ops = { .pointer = avs_probe_compr_pointer, }; +static const struct snd_soc_dai_ops avs_probe_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static const struct snd_compress_ops avs_probe_compress_ops = { .copy = avs_probe_compr_copy, }; @@ -264,8 +268,8 @@ static const struct snd_compress_ops avs_probe_compress_ops = { static struct snd_soc_dai_driver probe_cpu_dais[] = { { .name = "Probe Extraction CPU DAI", - .compress_new = snd_soc_new_compress, - .cops = &avs_probe_dai_ops, + .cops = &avs_probe_cdai_ops, + .ops = &avs_probe_dai_ops, .capture = { .stream_name = "Probe Extraction", .channels_min = 1, diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index cdb4ec500261..45d0eb2a8e71 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1388,12 +1388,12 @@ static int avs_route_load(struct snd_soc_component *comp, int index, port = __ffs(mach->mach_params.i2s_link_mask); snprintf(buf, len, route->source, port); - strncpy((char *)route->source, buf, len); + strscpy((char *)route->source, buf, len); snprintf(buf, len, route->sink, port); - strncpy((char *)route->sink, buf, len); + strscpy((char *)route->sink, buf, len); if (route->control) { snprintf(buf, len, route->control, port); - strncpy((char *)route->control, buf, len); + strscpy((char *)route->control, buf, len); } } diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index f472f603ab75..0ae6eecc8851 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -475,7 +475,7 @@ endif ## SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC if SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL config SND_SOC_INTEL_SOF_RT5682_MACH - tristate "SOF with rt5682 codec in I2S Mode" + tristate "SOF with rt5650/rt5682 codec in I2S Mode" depends on I2C && ACPI depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\ (MFD_INTEL_LPSS || COMPILE_TEST)) ||\ @@ -485,6 +485,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH select SND_SOC_RT1011 select SND_SOC_RT1015 select SND_SOC_RT1015P + select SND_SOC_RT5645 select SND_SOC_RT5682_I2C select SND_SOC_RT5682S select SND_SOC_DMIC @@ -494,7 +495,7 @@ config SND_SOC_INTEL_SOF_RT5682_MACH select SND_SOC_INTEL_SOF_REALTEK_COMMON help This adds support for ASoC machine driver for SOF platforms - with rt5682 codec. + with rt5650 or rt5682 codec. Say Y if you have such a device. If unsure select "N". @@ -670,14 +671,15 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH select SND_SOC_RT711_SDCA_SDW select SND_SOC_RT712_SDCA_SDW select SND_SOC_RT712_SDCA_DMIC_SDW + select SND_SOC_RT715_SDW + select SND_SOC_RT715_SDCA_SDW select SND_SOC_RT1308_SDW select SND_SOC_RT1308 select SND_SOC_RT1316_SDW select SND_SOC_RT1318_SDW - select SND_SOC_RT715_SDW - select SND_SOC_RT715_SDCA_SDW select SND_SOC_RT5682_SDW select SND_SOC_CS42L42_SDW + select SND_SOC_CS35L56_SDW select SND_SOC_DMIC select SND_SOC_INTEL_HDA_DSP_COMMON select SND_SOC_INTEL_SOF_MAXIM_COMMON diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 931415d9cf6f..a570b5b40f22 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -42,7 +42,7 @@ snd-soc-sof-sdw-objs += sof_sdw.o \ sof_sdw_rt711.o sof_sdw_rt_sdca_jack_common.o \ sof_sdw_rt712_sdca.o sof_sdw_rt715.o \ sof_sdw_rt715_sdca.o sof_sdw_dmic.o \ - sof_sdw_cs42l42.o \ + sof_sdw_cs42l42.o sof_sdw_cs_amp.o \ sof_sdw_hdmi.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o diff --git a/sound/soc/intel/boards/bdw_rt286.c b/sound/soc/intel/boards/bdw_rt286.c index b7687a93a923..036579331d8f 100644 --- a/sound/soc/intel/boards/bdw_rt286.c +++ b/sound/soc/intel/boards/bdw_rt286.c @@ -187,6 +187,9 @@ static int card_suspend_pre(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, "rt286-aif1"); + if (!codec_dai) + return 0; + return snd_soc_component_set_jack(codec_dai->component, NULL, NULL); } @@ -194,6 +197,9 @@ static int card_resume_post(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, "rt286-aif1"); + if (!codec_dai) + return 0; + return snd_soc_component_set_jack(codec_dai->component, &card_headset, NULL); } diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index c593995facaa..cbfff466c5c8 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -90,6 +90,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, static const struct snd_kcontrol_new broxton_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_kcontrol_new max98357a_controls[] = { @@ -104,6 +105,7 @@ static const struct snd_kcontrol_new max98390_controls[] = { static const struct snd_soc_dapm_widget broxton_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SPK("HDMI1", NULL), SND_SOC_DAPM_SPK("HDMI2", NULL), @@ -150,6 +152,7 @@ static const struct snd_soc_dapm_route audio_map[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, }; static const struct snd_soc_dapm_route max98357a_routes[] = { @@ -194,6 +197,10 @@ static struct snd_soc_jack_pin jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd, diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c index f2382d4cb76f..5c9e06ed1a53 100644 --- a/sound/soc/intel/boards/bytcr_wm5102.c +++ b/sound/soc/intel/boards/bytcr_wm5102.c @@ -131,6 +131,7 @@ static const struct snd_soc_dapm_widget byt_wm5102_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Internal Mic", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), @@ -144,6 +145,7 @@ static const struct snd_soc_dapm_route byt_wm5102_audio_map[] = { {"Headset Mic", NULL, "Platform Clock"}, {"Internal Mic", NULL, "Platform Clock"}, {"Speaker", NULL, "Platform Clock"}, + {"Line Out", NULL, "Platform Clock"}, {"Speaker", NULL, "SPKOUTLP"}, {"Speaker", NULL, "SPKOUTLN"}, @@ -177,6 +179,7 @@ static const struct snd_kcontrol_new byt_wm5102_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Internal Mic"), SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static struct snd_soc_jack_pin byt_wm5102_pins[] = { @@ -188,6 +191,10 @@ static struct snd_soc_jack_pin byt_wm5102_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static int byt_wm5102_init(struct snd_soc_pcm_runtime *runtime) diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index 18365ce6bcba..97149513076f 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -83,12 +83,14 @@ static const struct snd_kcontrol_new kabylake_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Spk"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_soc_dapm_widget kabylake_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Spk", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SPK("HDMI1", NULL), SND_SOC_DAPM_SPK("HDMI2", NULL), @@ -107,6 +109,10 @@ static struct snd_soc_jack_pin jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static const struct snd_soc_dapm_route kabylake_map[] = { @@ -147,6 +153,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, }; static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index ad4223fee0c5..a1f8234c77bd 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -102,6 +102,7 @@ static const struct snd_kcontrol_new kabylake_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Left Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_soc_dapm_widget kabylake_widgets[] = { @@ -109,6 +110,7 @@ static const struct snd_soc_dapm_widget kabylake_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Left Spk", NULL), SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SPK("HDMI1", NULL), SND_SOC_DAPM_SPK("HDMI2", NULL), @@ -127,6 +129,10 @@ static struct snd_soc_jack_pin jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_MICROPHONE, + }, }; static const struct snd_soc_dapm_route kabylake_map[] = { @@ -182,6 +188,7 @@ static const struct snd_soc_dapm_route kabylake_ssp1_map[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, }; static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, diff --git a/sound/soc/intel/boards/sof_da7219_max98373.c b/sound/soc/intel/boards/sof_da7219_max98373.c index 740aa11cb019..bbd47e7e4343 100644 --- a/sound/soc/intel/boards/sof_da7219_max98373.c +++ b/sound/soc/intel/boards/sof_da7219_max98373.c @@ -65,6 +65,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), SOC_DAPM_PIN_SWITCH("Left Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"), }; @@ -72,6 +73,7 @@ static const struct snd_kcontrol_new controls[] = { static const struct snd_kcontrol_new m98360a_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), SOC_DAPM_PIN_SWITCH("Spk"), }; @@ -79,6 +81,7 @@ static const struct snd_kcontrol_new m98360a_controls[] = { static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SPK("Left Spk", NULL), SND_SOC_DAPM_SPK("Right Spk", NULL), @@ -98,6 +101,7 @@ static const struct snd_soc_dapm_route audio_map[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, { "Left Spk", NULL, "Left BE_OUT" }, { "Right Spk", NULL, "Right BE_OUT" }, @@ -110,6 +114,7 @@ static const struct snd_soc_dapm_route audio_map[] = { static const struct snd_soc_dapm_widget max98360a_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SPK("Spk", NULL), @@ -128,6 +133,7 @@ static const struct snd_soc_dapm_route max98360a_map[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, {"Spk", NULL, "Speaker"}, @@ -144,6 +150,10 @@ static struct snd_soc_jack_pin jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static struct snd_soc_jack headset; diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index d6c38d8ea2ff..f8a3e8a91761 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -798,6 +798,16 @@ static const struct platform_device_id board_ids[] = { SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | SOF_ES8336_JD_INVERTED), }, + { + .name = "rpl_es83x6_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) | + SOF_NO_OF_HDMI_CAPTURE_SSP(2) | + SOF_HDMI_CAPTURE_1_SSP(0) | + SOF_HDMI_CAPTURE_2_SSP(2) | + SOF_SSP_HDMI_CAPTURE_PRESENT | + SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | + SOF_ES8336_JD_INVERTED), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index 112e89951da0..628b6d5d3ee4 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -4,6 +4,7 @@ #include <linux/module.h> #include <linux/string.h> #include <sound/pcm.h> +#include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-acpi.h> #include <sound/soc-dai.h> @@ -11,6 +12,18 @@ #include <uapi/sound/asound.h> #include "sof_maxim_common.h" +/* helper function to get the number of specific codec */ +static unsigned int get_num_codecs(const char *hid) +{ + struct acpi_device *adev; + unsigned int dev_num = 0; + + for_each_acpi_dev_match(adev, hid, NULL, -1) + dev_num++; + + return dev_num; +} + #define MAX_98373_PIN_NAME 16 const struct snd_soc_dapm_route max_98373_dapm_routes[] = { @@ -168,17 +181,6 @@ static struct snd_soc_codec_conf max_98390_codec_conf[] = { .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), .name_prefix = "Left", }, -}; - -static struct snd_soc_codec_conf max_98390_4spk_codec_conf[] = { - { - .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME), - .name_prefix = "Right", - }, - { - .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), - .name_prefix = "Left", - }, { .dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME), .name_prefix = "Tweeter Right", @@ -189,19 +191,7 @@ static struct snd_soc_codec_conf max_98390_4spk_codec_conf[] = { }, }; -struct snd_soc_dai_link_component max_98390_components[] = { - { - .name = MAX_98390_DEV0_NAME, - .dai_name = MAX_98390_CODEC_DAI, - }, - { - .name = MAX_98390_DEV1_NAME, - .dai_name = MAX_98390_CODEC_DAI, - }, -}; -EXPORT_SYMBOL_NS(max_98390_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); - -struct snd_soc_dai_link_component max_98390_4spk_components[] = { +static struct snd_soc_dai_link_component max_98390_components[] = { { .name = MAX_98390_DEV0_NAME, .dai_name = MAX_98390_CODEC_DAI, @@ -219,62 +209,56 @@ struct snd_soc_dai_link_component max_98390_4spk_components[] = { .dai_name = MAX_98390_CODEC_DAI, }, }; -EXPORT_SYMBOL_NS(max_98390_4spk_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +static const struct { + unsigned int tx; + unsigned int rx; +} max_98390_tdm_mask[] = { + {.tx = 0x01, .rx = 0x3}, + {.tx = 0x02, .rx = 0x3}, + {.tx = 0x04, .rx = 0x3}, + {.tx = 0x08, .rx = 0x3}, +}; static int max_98390_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai; - int i; + int i, ret; for_each_rtd_codec_dais(rtd, i, codec_dai) { - if (i >= ARRAY_SIZE(max_98390_4spk_components)) { + if (i >= ARRAY_SIZE(max_98390_tdm_mask)) { dev_err(codec_dai->dev, "invalid codec index %d\n", i); return -ENODEV; } - if (!strcmp(codec_dai->component->name, MAX_98390_DEV0_NAME)) { - /* DEV0 tdm slot configuration Right */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x01, 3, 4, 32); - } - if (!strcmp(codec_dai->component->name, MAX_98390_DEV1_NAME)) { - /* DEV1 tdm slot configuration Left */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x02, 3, 4, 32); - } - - if (!strcmp(codec_dai->component->name, MAX_98390_DEV2_NAME)) { - /* DEVi2 tdm slot configuration Tweeter Right */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x04, 3, 4, 32); - } - if (!strcmp(codec_dai->component->name, MAX_98390_DEV3_NAME)) { - /* DEV3 tdm slot configuration Tweeter Left */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x08, 3, 4, 32); + ret = snd_soc_dai_set_tdm_slot(codec_dai, max_98390_tdm_mask[i].tx, + max_98390_tdm_mask[i].rx, 4, + params_width(params)); + if (ret < 0) { + dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n", + ret); + return ret; } } return 0; } -int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd) +static int max_98390_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; + unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID); int ret; - /* add regular speakers dapm route */ - ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes, - ARRAY_SIZE(max_98390_dapm_routes)); - if (ret) { - dev_err(rtd->dev, "unable to add Left/Right Speaker dapm, ret %d\n", ret); - return ret; - } - - /* add widgets/controls/dapm for tweeter speakers */ - if (acpi_dev_present("MX98390", "3", -1)) { + switch (num_codecs) { + case 4: + /* add widgets/controls/dapm for tweeter speakers */ ret = snd_soc_dapm_new_controls(&card->dapm, max_98390_tt_dapm_widgets, ARRAY_SIZE(max_98390_tt_dapm_widgets)); - if (ret) { - dev_err(rtd->dev, "unable to add tweeter dapm controls, ret %d\n", ret); + dev_err(rtd->dev, "unable to add tweeter dapm widgets, ret %d\n", + ret); /* Don't need to add routes if widget addition failed */ return ret; } @@ -282,33 +266,79 @@ int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd) ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols, ARRAY_SIZE(max_98390_tt_kcontrols)); if (ret) { - dev_err(rtd->dev, "unable to add tweeter card controls, ret %d\n", ret); + dev_err(rtd->dev, "unable to add tweeter controls, ret %d\n", + ret); return ret; } ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_tt_dapm_routes, ARRAY_SIZE(max_98390_tt_dapm_routes)); - if (ret) - dev_err(rtd->dev, - "unable to add Tweeter Left/Right Speaker dapm, ret %d\n", ret); + if (ret) { + dev_err(rtd->dev, "unable to add tweeter dapm routes, ret %d\n", + ret); + return ret; + } + + fallthrough; + case 2: + /* add regular speakers dapm route */ + ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes, + ARRAY_SIZE(max_98390_dapm_routes)); + if (ret) { + dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", + ret); + return ret; + } + break; + default: + dev_err(rtd->dev, "invalid codec number %d\n", num_codecs); + return -EINVAL; } + return ret; } -EXPORT_SYMBOL_NS(max_98390_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON); -const struct snd_soc_ops max_98390_ops = { +static const struct snd_soc_ops max_98390_ops = { .hw_params = max_98390_hw_params, }; -EXPORT_SYMBOL_NS(max_98390_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON); -void max_98390_set_codec_conf(struct snd_soc_card *card, int ch) +void max_98390_dai_link(struct device *dev, struct snd_soc_dai_link *link) +{ + unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID); + + link->codecs = max_98390_components; + + switch (num_codecs) { + case 2: + case 4: + link->num_codecs = num_codecs; + break; + default: + dev_err(dev, "invalid codec number %d for %s\n", num_codecs, + MAX_98390_ACPI_HID); + break; + } + + link->init = max_98390_init; + link->ops = &max_98390_ops; +} +EXPORT_SYMBOL_NS(max_98390_dai_link, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +void max_98390_set_codec_conf(struct device *dev, struct snd_soc_card *card) { - if (ch == ARRAY_SIZE(max_98390_4spk_codec_conf)) { - card->codec_conf = max_98390_4spk_codec_conf; - card->num_configs = ARRAY_SIZE(max_98390_4spk_codec_conf); - } else { - card->codec_conf = max_98390_codec_conf; - card->num_configs = ARRAY_SIZE(max_98390_codec_conf); + unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID); + + card->codec_conf = max_98390_codec_conf; + + switch (num_codecs) { + case 2: + case 4: + card->num_configs = num_codecs; + break; + default: + dev_err(dev, "invalid codec number %d for %s\n", num_codecs, + MAX_98390_ACPI_HID); + break; } } EXPORT_SYMBOL_NS(max_98390_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); diff --git a/sound/soc/intel/boards/sof_maxim_common.h b/sound/soc/intel/boards/sof_maxim_common.h index 7a8c53049e4d..a095b47b856b 100644 --- a/sound/soc/intel/boards/sof_maxim_common.h +++ b/sound/soc/intel/boards/sof_maxim_common.h @@ -27,18 +27,15 @@ int max_98373_trigger(struct snd_pcm_substream *substream, int cmd); /* * Maxim MAX98390 */ -#define MAX_98390_CODEC_DAI "max98390-aif1" -#define MAX_98390_DEV0_NAME "i2c-MX98390:00" -#define MAX_98390_DEV1_NAME "i2c-MX98390:01" -#define MAX_98390_DEV2_NAME "i2c-MX98390:02" -#define MAX_98390_DEV3_NAME "i2c-MX98390:03" - -extern struct snd_soc_dai_link_component max_98390_components[2]; -extern struct snd_soc_dai_link_component max_98390_4spk_components[4]; -extern const struct snd_soc_ops max_98390_ops; - -void max_98390_set_codec_conf(struct snd_soc_card *card, int ch); -int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd); +#define MAX_98390_ACPI_HID "MX98390" +#define MAX_98390_CODEC_DAI "max98390-aif1" +#define MAX_98390_DEV0_NAME "i2c-" MAX_98390_ACPI_HID ":00" +#define MAX_98390_DEV1_NAME "i2c-" MAX_98390_ACPI_HID ":01" +#define MAX_98390_DEV2_NAME "i2c-" MAX_98390_ACPI_HID ":02" +#define MAX_98390_DEV3_NAME "i2c-" MAX_98390_ACPI_HID ":03" + +void max_98390_dai_link(struct device *dev, struct snd_soc_dai_link *link); +void max_98390_set_codec_conf(struct device *dev, struct snd_soc_card *card); /* * Maxim MAX98357A/MAX98360A diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c index 4fc6e1c6aef3..46b7ecf6f9f1 100644 --- a/sound/soc/intel/boards/sof_nau8825.c +++ b/sound/soc/intel/boards/sof_nau8825.c @@ -684,6 +684,16 @@ static const struct platform_device_id board_ids[] = { SOF_BT_OFFLOAD_SSP(2) | SOF_SSP_BT_OFFLOAD_PRESENT), }, + { + .name = "rpl_nau8318_8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_NAU8318_SPEAKER_AMP_PRESENT | + SOF_NAU8825_SSP_AMP(1) | + SOF_NAU8825_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 7c034d671cf3..fae091b9b55c 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -22,6 +22,7 @@ #include <sound/soc-acpi.h> #include "../../codecs/rt5682.h" #include "../../codecs/rt5682s.h" +#include "../../codecs/rt5645.h" #include "../../codecs/hdac_hdmi.h" #include "../common/soc-intel-quirks.h" #include "hda_dsp_common.h" @@ -58,9 +59,14 @@ #define SOF_SSP_BT_OFFLOAD_PRESENT BIT(22) #define SOF_RT5682S_HEADPHONE_CODEC_PRESENT BIT(23) #define SOF_MAX98390_SPEAKER_AMP_PRESENT BIT(24) -#define SOF_MAX98390_TWEETER_SPEAKER_PRESENT BIT(25) #define SOF_RT1019_SPEAKER_AMP_PRESENT BIT(26) +#define SOF_RT5650_HEADPHONE_CODEC_PRESENT BIT(27) +/* HDMI capture*/ +#define SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT 27 +#define SOF_SSP_HDMI_CAPTURE_PRESENT_MASK (GENMASK(30, 27)) +#define SOF_HDMI_CAPTURE_SSP_MASK(quirk) \ + (((quirk) << SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT) & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) /* Default: MCLK on, MCLK 19.2M, SSP0 */ static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN | @@ -197,23 +203,6 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { .callback = sof_rt5682_quirk_cb, .matches = { DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), - DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S_4SPK"), - }, - .driver_data = (void *)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_SPEAKER_AMP_PRESENT | - SOF_MAX98390_SPEAKER_AMP_PRESENT | - SOF_MAX98390_TWEETER_SPEAKER_PRESENT | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - - }, - { - .callback = sof_rt5682_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_I2S_AMP_SSP2"), }, .driver_data = (void *)(SOF_RT5682_MCLK_EN | @@ -305,6 +294,7 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_jack *jack; + int extra_jack_data; int ret; /* need to enable ASRC function for 24MHz mclk rate */ @@ -315,7 +305,16 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) RT5682S_DA_STEREO1_FILTER | RT5682S_AD_STEREO1_FILTER, RT5682S_CLK_SEL_I2S1_ASRC); - else + else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) { + rt5645_sel_asrc_clk_src(component, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + rt5645_sel_asrc_clk_src(component, + RT5645_DA_MONO_L_FILTER | + RT5645_DA_MONO_R_FILTER, + RT5645_CLK_SEL_I2S2_ASRC); + } else rt5682_sel_asrc_clk_src(component, RT5682_DA_STEREO1_FILTER | RT5682_AD_STEREO1_FILTER, @@ -365,7 +364,12 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - ret = snd_soc_component_set_jack(component, jack, NULL); + + if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) { + extra_jack_data = SND_JACK_MICROPHONE | SND_JACK_BTN_0; + ret = snd_soc_component_set_jack(component, jack, &extra_jack_data); + } else + ret = snd_soc_component_set_jack(component, jack, NULL); if (ret) { dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); @@ -402,6 +406,8 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream, if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) pll_source = RT5682S_PLL_S_MCLK; + else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) + pll_source = RT5645_PLL1_S_MCLK; else pll_source = RT5682_PLL1_S_MCLK; @@ -422,6 +428,8 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream, } else { if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) pll_source = RT5682S_PLL_S_BCLK1; + else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) + pll_source = RT5645_PLL1_S_BCLK1; else pll_source = RT5682_PLL1_S_BCLK1; @@ -431,6 +439,9 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream, if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) { pll_id = RT5682S_PLL2; clk_id = RT5682S_SCLK_S_PLL2; + } else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) { + pll_id = 0; /* not used in codec driver */ + clk_id = RT5645_SCLK_S_PLL1; } else { pll_id = RT5682_PLL1; clk_id = RT5682_SCLK_S_PLL1; @@ -559,11 +570,30 @@ static const struct snd_soc_dapm_route sof_map[] = { { "IN1P", NULL, "Headset Mic" }, }; +static const struct snd_soc_dapm_route rt5650_spk_dapm_routes[] = { + /* speaker */ + { "Left Spk", NULL, "SPOL" }, + { "Right Spk", NULL, "SPOR" }, +}; + static const struct snd_soc_dapm_route dmic_map[] = { /* digital mics */ {"DMic", NULL, "SoC DMIC"}, }; +static int rt5650_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_add_routes(&card->dapm, rt5650_spk_dapm_routes, + ARRAY_SIZE(rt5650_spk_dapm_routes)); + if (ret) + dev_err(rtd->dev, "fail to add dapm routes, ret=%d\n", ret); + + return ret; +} + static int dmic_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -614,6 +644,17 @@ static struct snd_soc_dai_link_component rt5682s_component[] = { } }; +static struct snd_soc_dai_link_component rt5650_components[] = { + { + .name = "i2c-10EC5650:00", + .dai_name = "rt5645-aif1", + }, + { + .name = "i2c-10EC5650:00", + .dai_name = "rt5645-aif2", + } +}; + static struct snd_soc_dai_link_component dmic_component[] = { { .name = "dmic-codec", @@ -634,6 +675,7 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, struct snd_soc_dai_link_component *cpus; struct snd_soc_dai_link *links; int i, id = 0; + int hdmi_id_offset = 0; links = devm_kcalloc(dev, sof_audio_card_rt5682.num_links, sizeof(struct snd_soc_dai_link), GFP_KERNEL); @@ -652,6 +694,9 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) { links[id].codecs = rt5682s_component; links[id].num_codecs = ARRAY_SIZE(rt5682s_component); + } else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) { + links[id].codecs = &rt5650_components[0]; + links[id].num_codecs = 1; } else { links[id].codecs = rt5682_component; links[id].num_codecs = ARRAY_SIZE(rt5682_component); @@ -793,17 +838,12 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, sof_rt1011_dai_link(&links[id]); } else if (sof_rt5682_quirk & SOF_MAX98390_SPEAKER_AMP_PRESENT) { - if (sof_rt5682_quirk & - SOF_MAX98390_TWEETER_SPEAKER_PRESENT) { - links[id].codecs = max_98390_4spk_components; - links[id].num_codecs = ARRAY_SIZE(max_98390_4spk_components); - } else { - links[id].codecs = max_98390_components; - links[id].num_codecs = ARRAY_SIZE(max_98390_components); - } - links[id].init = max_98390_spk_codec_init; - links[id].ops = &max_98390_ops; - + max_98390_dai_link(dev, &links[id]); + } else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) { + links[id].codecs = &rt5650_components[1]; + links[id].num_codecs = 1; + links[id].init = rt5650_spk_init; + links[id].ops = &sof_rt5682_ops; } else { max_98357a_dai_link(&links[id]); } @@ -857,6 +897,34 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, links[id].num_cpus = 1; } + /* HDMI-In SSP */ + if (sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) { + unsigned long hdmi_in_ssp = (sof_rt5682_quirk & + SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) >> + SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT; + int port = 0; + + for_each_set_bit(port, &hdmi_in_ssp, 32) { + links[id].cpus = &cpus[id]; + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d Pin", port); + if (!links[id].cpus->dai_name) + return NULL; + links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", port); + if (!links[id].name) + return NULL; + links[id].id = id + hdmi_id_offset; + links[id].codecs = &asoc_dummy_dlc; + links[id].num_codecs = 1; + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].dpcm_capture = 1; + links[id].no_pcm = 1; + links[id].num_cpus = 1; + id++; + } + } + return links; devm_err: return NULL; @@ -890,6 +958,12 @@ static int sof_audio_probe(struct platform_device *pdev) /* Detect the headset codec variant */ if (acpi_dev_present("RTL5682", NULL, -1)) sof_rt5682_quirk |= SOF_RT5682S_HEADPHONE_CODEC_PRESENT; + else if (acpi_dev_present("10EC5650", NULL, -1)) { + sof_rt5682_quirk |= SOF_RT5650_HEADPHONE_CODEC_PRESENT; + + sof_audio_card_rt5682.name = devm_kstrdup(&pdev->dev, "rt5650", + GFP_KERNEL); + } if (soc_intel_is_byt() || soc_intel_is_cht()) { is_legacy_cpu = 1; @@ -951,17 +1025,17 @@ static int sof_audio_probe(struct platform_device *pdev) else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) sof_rt1015p_codec_conf(&sof_audio_card_rt5682); else if (sof_rt5682_quirk & SOF_MAX98390_SPEAKER_AMP_PRESENT) { - if (sof_rt5682_quirk & SOF_MAX98390_TWEETER_SPEAKER_PRESENT) - max_98390_set_codec_conf(&sof_audio_card_rt5682, - ARRAY_SIZE(max_98390_4spk_components)); - else - max_98390_set_codec_conf(&sof_audio_card_rt5682, - ARRAY_SIZE(max_98390_components)); + max_98390_set_codec_conf(&pdev->dev, &sof_audio_card_rt5682); } if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) sof_audio_card_rt5682.num_links++; + if (sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) + sof_audio_card_rt5682.num_links += + hweight32((sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) >> + SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT); + dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp, dmic_be_num, hdmi_num, ctx->idisp_codec); if (!dai_links) @@ -1129,6 +1203,22 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_BT_OFFLOAD_PRESENT), }, { + .name = "adl_rt5682_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(1) | + SOF_RT5682_NUM_HDMIDEV(3) | + /* SSP 0 and SSP 2 are used for HDMI IN */ + SOF_HDMI_CAPTURE_SSP_MASK(0x5)), + }, + { + .name = "rpl_mx98357_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(2) | + SOF_RT5682_NUM_HDMIDEV(4)), + }, + { .name = "rpl_mx98360_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | @@ -1178,6 +1268,14 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_SSP_AMP(0) | SOF_RT5682_NUM_HDMIDEV(3)), }, + { + .name = "jsl_rt5650", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_MCLK_24MHZ | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1)), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 0201029899ca..5a1c750e6ae6 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -311,6 +311,16 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { .callback = sof_sdw_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE") + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2 | + SOF_SDW_FOUR_SPK), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF") }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | @@ -467,7 +477,9 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Google"), DMI_MATCH(DMI_PRODUCT_NAME, "Rex"), }, - .driver_data = (void *)(SOF_SDW_PCH_DMIC), + .driver_data = (void *)(SOF_SDW_PCH_DMIC | + SOF_BT_OFFLOAD_SSP(1) | + SOF_SSP_BT_OFFLOAD_PRESENT), }, /* LunarLake devices */ { @@ -511,9 +523,8 @@ int sdw_prepare(struct snd_pcm_substream *substream) dai = asoc_rtd_to_cpu(rtd, 0); sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); - if (IS_ERR(sdw_stream)) { - dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); return PTR_ERR(sdw_stream); } @@ -531,9 +542,8 @@ int sdw_trigger(struct snd_pcm_substream *substream, int cmd) dai = asoc_rtd_to_cpu(rtd, 0); sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); - if (IS_ERR(sdw_stream)) { - dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); return PTR_ERR(sdw_stream); } @@ -555,7 +565,7 @@ int sdw_trigger(struct snd_pcm_substream *substream, int cmd) } if (ret) - dev_err(rtd->dev, "%s trigger %d failed: %d", __func__, cmd, ret); + dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret); return ret; } @@ -619,9 +629,8 @@ int sdw_hw_free(struct snd_pcm_substream *substream) dai = asoc_rtd_to_cpu(rtd, 0); sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); - if (IS_ERR(sdw_stream)) { - dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); return PTR_ERR(sdw_stream); } @@ -895,6 +904,19 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .part_id = 0x3556, + .dais = { + { + .direction = {true, true}, + .dai_name = "cs35l56-sdw1", + .dai_type = SOF_SDW_DAI_TYPE_AMP, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, + .init = sof_sdw_cs_amp_init, + }, + }, + .dai_num = 1, + }, + { .part_id = 0x4242, .dais = { { @@ -940,10 +962,10 @@ static struct sof_sdw_codec_info codec_info_list[] = { .version_id = 0, .dais = { { - .direction = {true, false}, + .direction = {true, true}, .dai_name = "sdw-mockup-aif1", .dai_type = SOF_SDW_DAI_TYPE_AMP, - .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = NULL, }, }, @@ -965,7 +987,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { }, }; -static inline int find_codec_info_part(u64 adr) +static inline int find_codec_info_part(const u64 adr) { unsigned int part_id, sdw_version; int i; @@ -994,14 +1016,10 @@ static inline int find_codec_info_acpi(const u8 *acpi_id) return -EINVAL; for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) - if (!memcmp(codec_info_list[i].acpi_id, acpi_id, - ACPI_ID_LEN)) - break; - - if (i == ARRAY_SIZE(codec_info_list)) - return -EINVAL; + if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN)) + return i; - return i; + return -EINVAL; } /* @@ -1009,10 +1027,10 @@ static inline int find_codec_info_acpi(const u8 *acpi_id) * Since some sdw slaves may be aggregated, the CPU DAI number * may be larger than the number of BE dailinks. */ -static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_link_adr *links, - int *sdw_be_num, int *sdw_cpu_dai_num) +static int get_dailink_info(struct device *dev, + const struct snd_soc_acpi_link_adr *adr_link, + int *sdw_be_num, int *sdw_cpu_dai_num, int *codecs_num) { - const struct snd_soc_acpi_link_adr *link; bool group_visited[SDW_MAX_GROUPS]; bool no_aggregation; int i; @@ -1022,27 +1040,45 @@ static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_li *sdw_cpu_dai_num = 0; *sdw_be_num = 0; - if (!links) + if (!adr_link) return -EINVAL; for (i = 0; i < SDW_MAX_GROUPS; i++) group_visited[i] = false; - for (link = links; link->num_adr; link++) { + for (; adr_link->num_adr; adr_link++) { const struct snd_soc_acpi_endpoint *endpoint; struct sof_sdw_codec_info *codec_info; int codec_index; int stream; u64 adr; - for (i = 0; i < link->num_adr; i++) { - adr = link->adr_d[i].adr; + /* make sure the link mask has a single bit set */ + if (!is_power_of_2(adr_link->mask)) + return -EINVAL; + + for (i = 0; i < adr_link->num_adr; i++) { + adr = adr_link->adr_d[i].adr; codec_index = find_codec_info_part(adr); if (codec_index < 0) return codec_index; + codec_info = &codec_info_list[codec_index]; - endpoint = link->adr_d[i].endpoints; + *codecs_num += codec_info->dai_num; + + if (!adr_link->adr_d[i].name_prefix) { + dev_err(dev, "codec 0x%llx does not have a name prefix\n", + adr_link->adr_d[i].adr); + return -EINVAL; + } + + endpoint = adr_link->adr_d[i].endpoints; + if (endpoint->aggregated && !endpoint->group_id) { + dev_err(dev, "invalid group id on link %x\n", + adr_link->mask); + return -EINVAL; + } for (j = 0; j < codec_info->dai_num; j++) { /* count DAI number for playback and capture */ @@ -1090,17 +1126,16 @@ static void init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links dai_links->ops = ops; } -static bool is_unique_device(const struct snd_soc_acpi_link_adr *link, +static bool is_unique_device(const struct snd_soc_acpi_link_adr *adr_link, unsigned int sdw_version, unsigned int mfg_id, unsigned int part_id, unsigned int class_id, - int index_in_link - ) + int index_in_link) { int i; - for (i = 0; i < link->num_adr; i++) { + for (i = 0; i < adr_link->num_adr; i++) { unsigned int sdw1_version, mfg1_id, part1_id, class1_id; u64 adr; @@ -1108,7 +1143,7 @@ static bool is_unique_device(const struct snd_soc_acpi_link_adr *link, if (i == index_in_link) continue; - adr = link->adr_d[i].adr; + adr = adr_link->adr_d[i].adr; sdw1_version = SDW_VERSION(adr); mfg1_id = SDW_MFG_ID(adr); @@ -1125,83 +1160,50 @@ static bool is_unique_device(const struct snd_soc_acpi_link_adr *link, return true; } -static int create_codec_dai_name(struct device *dev, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link_component *codec, - int offset, - struct snd_soc_codec_conf *codec_conf, - int codec_count, - int *codec_conf_index, - int adr_index, - int dai_index) +static int fill_sdw_codec_dlc(struct device *dev, + const struct snd_soc_acpi_link_adr *adr_link, + struct snd_soc_dai_link_component *codec, + int adr_index, int dai_index) { - int _codec_index = -1; - int i; - - /* sanity check */ - if (*codec_conf_index + link->num_adr - adr_index > codec_count) { - dev_err(dev, "codec_conf: out-of-bounds access requested\n"); - return -EINVAL; - } - - for (i = adr_index; i < link->num_adr; i++) { - unsigned int sdw_version, unique_id, mfg_id; - unsigned int link_id, part_id, class_id; - int codec_index, comp_index; - char *codec_str; - u64 adr; - - adr = link->adr_d[i].adr; - - sdw_version = SDW_VERSION(adr); - link_id = SDW_DISCO_LINK_ID(adr); - unique_id = SDW_UNIQUE_ID(adr); - mfg_id = SDW_MFG_ID(adr); - part_id = SDW_PART_ID(adr); - class_id = SDW_CLASS_ID(adr); - - comp_index = i - adr_index + offset; - if (is_unique_device(link, sdw_version, mfg_id, part_id, - class_id, i)) { - codec_str = "sdw:%01x:%04x:%04x:%02x"; - codec[comp_index].name = - devm_kasprintf(dev, GFP_KERNEL, codec_str, - link_id, mfg_id, part_id, - class_id); - } else { - codec_str = "sdw:%01x:%04x:%04x:%02x:%01x"; - codec[comp_index].name = - devm_kasprintf(dev, GFP_KERNEL, codec_str, - link_id, mfg_id, part_id, - class_id, unique_id); - } - - if (!codec[comp_index].name) - return -ENOMEM; - - codec_index = find_codec_info_part(adr); - if (codec_index < 0) - return codec_index; - if (_codec_index != -1 && codec_index != _codec_index) { - dev_dbg(dev, "Different devices on the same sdw link\n"); - break; - } - _codec_index = codec_index; + unsigned int sdw_version, unique_id, mfg_id, link_id, part_id, class_id; + u64 adr = adr_link->adr_d[adr_index].adr; + int codec_index; - codec[comp_index].dai_name = - codec_info_list[codec_index].dais[dai_index].dai_name; + codec_index = find_codec_info_part(adr); + if (codec_index < 0) + return codec_index; - codec_conf[*codec_conf_index].dlc = codec[comp_index]; - codec_conf[*codec_conf_index].name_prefix = link->adr_d[i].name_prefix; + sdw_version = SDW_VERSION(adr); + link_id = SDW_DISCO_LINK_ID(adr); + unique_id = SDW_UNIQUE_ID(adr); + mfg_id = SDW_MFG_ID(adr); + part_id = SDW_PART_ID(adr); + class_id = SDW_CLASS_ID(adr); + + if (codec_info_list[codec_index].codec_name) + codec->name = devm_kstrdup(dev, + codec_info_list[codec_index].codec_name, + GFP_KERNEL); + else if (is_unique_device(adr_link, sdw_version, mfg_id, part_id, + class_id, adr_index)) + codec->name = devm_kasprintf(dev, GFP_KERNEL, + "sdw:%01x:%04x:%04x:%02x", link_id, + mfg_id, part_id, class_id); + else + codec->name = devm_kasprintf(dev, GFP_KERNEL, + "sdw:%01x:%04x:%04x:%02x:%01x", link_id, + mfg_id, part_id, class_id, unique_id); + + if (!codec->name) + return -ENOMEM; - ++*codec_conf_index; - } + codec->dai_name = codec_info_list[codec_index].dais[dai_index].dai_name; return 0; } static int set_codec_init_func(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, + const struct snd_soc_acpi_link_adr *adr_link, struct snd_soc_dai_link *dai_links, bool playback, int group_id, int adr_index, int dai_index) { @@ -1212,34 +1214,34 @@ static int set_codec_init_func(struct snd_soc_card *card, * Initialize the codec. If codec is part of an aggregated * group (group_id>0), initialize all codecs belonging to * same group. - * The first link should start with link->adr_d[adr_index] + * The first link should start with adr_link->adr_d[adr_index] * because that is the device that we want to initialize and * we should end immediately if it is not aggregated (group_id=0) */ - for ( ; i < link->num_adr; i++) { + for ( ; i < adr_link->num_adr; i++) { int codec_index; - codec_index = find_codec_info_part(link->adr_d[i].adr); - + codec_index = find_codec_info_part(adr_link->adr_d[i].adr); if (codec_index < 0) return codec_index; /* The group_id is > 0 iff the codec is aggregated */ - if (link->adr_d[i].endpoints->group_id != group_id) + if (adr_link->adr_d[i].endpoints->group_id != group_id) continue; if (codec_info_list[codec_index].dais[dai_index].init) codec_info_list[codec_index].dais[dai_index].init(card, - link, + adr_link, dai_links, &codec_info_list[codec_index], playback); if (!group_id) return 0; } + i = 0; - link++; - } while (link->mask); + adr_link++; + } while (adr_link->mask); return 0; } @@ -1261,73 +1263,45 @@ static int set_codec_init_func(struct snd_soc_card *card, static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, struct device *dev, int *cpu_dai_id, int *cpu_dai_num, int *codec_num, unsigned int *group_id, - bool *group_generated, int adr_index) + int adr_index) { - const struct snd_soc_acpi_adr_device *adr_d; - const struct snd_soc_acpi_link_adr *adr_next; - bool no_aggregation; - int index = 0; + bool no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; int i; - no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; - adr_d = &adr_link->adr_d[adr_index]; - - /* make sure the link mask has a single bit set */ - if (!is_power_of_2(adr_link->mask)) - return -EINVAL; - - cpu_dai_id[index++] = ffs(adr_link->mask) - 1; - if (!adr_d->endpoints->aggregated || no_aggregation) { + if (!adr_link->adr_d[adr_index].endpoints->aggregated || no_aggregation) { + cpu_dai_id[0] = ffs(adr_link->mask) - 1; *cpu_dai_num = 1; *codec_num = 1; *group_id = 0; return 0; } - *group_id = adr_d->endpoints->group_id; - - /* Count endpoints with the same group_id in the adr_link */ *codec_num = 0; - for (i = 0; i < adr_link->num_adr; i++) { - if (adr_link->adr_d[i].endpoints->aggregated && - adr_link->adr_d[i].endpoints->group_id == *group_id) - (*codec_num)++; - } + *cpu_dai_num = 0; + *group_id = adr_link->adr_d[adr_index].endpoints->group_id; - /* gather other link ID of slaves in the same group */ - for (adr_next = adr_link + 1; adr_next && adr_next->num_adr; - adr_next++) { - const struct snd_soc_acpi_endpoint *endpoint; + /* Count endpoints with the same group_id in the adr_link */ + for (; adr_link && adr_link->num_adr; adr_link++) { + unsigned int link_codecs = 0; - endpoint = adr_next->adr_d->endpoints; - if (!endpoint->aggregated || - endpoint->group_id != *group_id) - continue; + for (i = 0; i < adr_link->num_adr; i++) { + if (adr_link->adr_d[i].endpoints->aggregated && + adr_link->adr_d[i].endpoints->group_id == *group_id) + link_codecs++; + } - /* make sure the link mask has a single bit set */ - if (!is_power_of_2(adr_next->mask)) - return -EINVAL; + if (link_codecs) { + *codec_num += link_codecs; - if (index >= SDW_MAX_CPU_DAIS) { - dev_err(dev, " cpu_dai_id array overflows"); - return -EINVAL; - } + if (*cpu_dai_num >= SDW_MAX_CPU_DAIS) { + dev_err(dev, "cpu_dai_id array overflowed\n"); + return -EINVAL; + } - cpu_dai_id[index++] = ffs(adr_next->mask) - 1; - for (i = 0; i < adr_next->num_adr; i++) { - if (adr_next->adr_d[i].endpoints->aggregated && - adr_next->adr_d[i].endpoints->group_id == *group_id) - (*codec_num)++; + cpu_dai_id[(*cpu_dai_num)++] = ffs(adr_link->mask) - 1; } } - /* - * indicate CPU DAIs for this group have been generated - * to avoid generating CPU DAIs for this group again. - */ - group_generated[*group_id] = true; - *cpu_dai_num = index; - return 0; } @@ -1344,37 +1318,36 @@ static void set_dailink_map(struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_m static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; -static int create_sdw_dailink(struct snd_soc_card *card, - struct device *dev, int *link_index, +static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, struct snd_soc_dai_link *dai_links, int sdw_be_num, int sdw_cpu_dai_num, struct snd_soc_dai_link_component *cpus, - const struct snd_soc_acpi_link_adr *link, - int *cpu_id, bool *group_generated, - struct snd_soc_codec_conf *codec_conf, - int codec_count, int *link_id, + const struct snd_soc_acpi_link_adr *adr_link, + int *cpu_id, struct snd_soc_codec_conf *codec_conf, + int codec_count, int *be_id, int *codec_conf_index, bool *ignore_pch_dmic, bool append_dai_type, int adr_index, int dai_index) { - const struct snd_soc_acpi_link_adr *link_next; + struct device *dev = card->dev; + const struct snd_soc_acpi_link_adr *adr_link_next; struct snd_soc_dai_link_component *codecs; struct sof_sdw_codec_info *codec_info; int cpu_dai_id[SDW_MAX_CPU_DAIS]; int cpu_dai_num, cpu_dai_index; unsigned int group_id; - int codec_idx = 0; + int codec_dlc_index = 0; int codec_index; int codec_num; int stream; int i = 0; + int j, k; int ret; - int k; - ret = get_slave_info(link, dev, cpu_dai_id, &cpu_dai_num, &codec_num, - &group_id, group_generated, adr_index); + ret = get_slave_info(adr_link, dev, cpu_dai_id, &cpu_dai_num, &codec_num, + &group_id, adr_index); if (ret) return ret; @@ -1383,32 +1356,50 @@ static int create_sdw_dailink(struct snd_soc_card *card, return -ENOMEM; /* generate codec name on different links in the same group */ - for (link_next = link; link_next && link_next->num_adr && - i < cpu_dai_num; link_next++) { - const struct snd_soc_acpi_endpoint *endpoints; - - endpoints = link_next->adr_d->endpoints; - if (group_id && (!endpoints->aggregated || - endpoints->group_id != group_id)) - continue; - + j = adr_index; + for (adr_link_next = adr_link; adr_link_next && adr_link_next->num_adr && + i < cpu_dai_num; adr_link_next++) { /* skip the link excluded by this processed group */ - if (cpu_dai_id[i] != ffs(link_next->mask) - 1) + if (cpu_dai_id[i] != ffs(adr_link_next->mask) - 1) continue; - ret = create_codec_dai_name(dev, link_next, codecs, codec_idx, - codec_conf, codec_count, codec_conf_index, - adr_index, dai_index); - if (ret < 0) - return ret; + /* j reset after loop, adr_index only applies to first link */ + for (; j < adr_link_next->num_adr; j++) { + const struct snd_soc_acpi_endpoint *endpoints; + + endpoints = adr_link_next->adr_d[j].endpoints; + + if (group_id && (!endpoints->aggregated || + endpoints->group_id != group_id)) + continue; + + /* sanity check */ + if (*codec_conf_index >= codec_count) { + dev_err(dev, "codec_conf array overflowed\n"); + return -EINVAL; + } + + ret = fill_sdw_codec_dlc(dev, adr_link_next, + &codecs[codec_dlc_index], + j, dai_index); + if (ret) + return ret; + + codec_conf[*codec_conf_index].dlc = codecs[codec_dlc_index]; + codec_conf[*codec_conf_index].name_prefix = + adr_link_next->adr_d[j].name_prefix; + + codec_dlc_index++; + (*codec_conf_index)++; + } + j = 0; /* check next link to create codec dai in the processed group */ i++; - codec_idx += link_next->num_adr; } /* find codec info to create BE DAI */ - codec_index = find_codec_info_part(link->adr_d[adr_index].adr); + codec_index = find_codec_info_part(adr_link->adr_d[adr_index].adr); if (codec_index < 0) return codec_index; codec_info = &codec_info_list[codec_index]; @@ -1431,9 +1422,9 @@ static int create_sdw_dailink(struct snd_soc_card *card, if (!codec_info->dais[dai_index].direction[stream]) continue; - *link_id = codec_info->dais[dai_index].dailink[stream]; - if (*link_id < 0) { - dev_err(dev, "Invalid dailink id %d\n", *link_id); + *be_id = codec_info->dais[dai_index].dailink[stream]; + if (*be_id < 0) { + dev_err(dev, "Invalid dailink id %d\n", *be_id); return -EINVAL; } @@ -1466,7 +1457,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, return -ENOMEM; if (cpu_dai_index >= sdw_cpu_dai_num) { - dev_err(dev, "invalid cpu dai index %d", + dev_err(dev, "invalid cpu dai index %d\n", cpu_dai_index); return -EINVAL; } @@ -1479,18 +1470,18 @@ static int create_sdw_dailink(struct snd_soc_card *card, * not be larger than sdw_be_num */ if (*link_index >= sdw_be_num) { - dev_err(dev, "invalid dai link index %d", *link_index); + dev_err(dev, "invalid dai link index %d\n", *link_index); return -EINVAL; } if (*cpu_id >= sdw_cpu_dai_num) { - dev_err(dev, " invalid cpu dai index %d", *cpu_id); + dev_err(dev, "invalid cpu dai index %d\n", *cpu_id); return -EINVAL; } playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); capture = (stream == SNDRV_PCM_STREAM_CAPTURE); - init_dai_link(dev, dai_links + *link_index, (*link_id)++, name, + init_dai_link(dev, dai_links + *link_index, (*be_id)++, name, playback, capture, cpus + *cpu_id, cpu_dai_num, codecs, codec_num, @@ -1504,10 +1495,10 @@ static int create_sdw_dailink(struct snd_soc_card *card, set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num); dai_links[*link_index].codec_ch_maps = sdw_codec_ch_maps; - ret = set_codec_init_func(card, link, dai_links + (*link_index)++, + ret = set_codec_init_func(card, adr_link, dai_links + (*link_index)++, playback, group_id, adr_index, dai_index); if (ret < 0) { - dev_err(dev, "failed to init codec %d", codec_index); + dev_err(dev, "failed to init codec %d\n", codec_index); return ret; } @@ -1519,65 +1510,26 @@ static int create_sdw_dailink(struct snd_soc_card *card, #define IDISP_CODEC_MASK 0x4 -static int sof_card_codec_conf_alloc(struct device *dev, - struct snd_soc_acpi_mach_params *mach_params, - struct snd_soc_codec_conf **codec_conf, - int *codec_conf_count) +static int sof_card_dai_links_create(struct snd_soc_card *card) { - const struct snd_soc_acpi_link_adr *adr_link; - struct snd_soc_codec_conf *c_conf; - int num_codecs = 0; - int codec_index; - int i; - - adr_link = mach_params->links; - if (!adr_link) - return -EINVAL; - - /* generate DAI links by each sdw link */ - for (; adr_link->num_adr; adr_link++) { - for (i = 0; i < adr_link->num_adr; i++) { - if (!adr_link->adr_d[i].name_prefix) { - dev_err(dev, "codec 0x%llx does not have a name prefix\n", - adr_link->adr_d[i].adr); - return -EINVAL; - } - codec_index = find_codec_info_part(adr_link->adr_d[i].adr); - if (codec_index < 0) - return codec_index; - num_codecs += codec_info_list[codec_index].dai_num; - } - } - - c_conf = devm_kzalloc(dev, num_codecs * sizeof(*c_conf), GFP_KERNEL); - if (!c_conf) - return -ENOMEM; - - *codec_conf = c_conf; - *codec_conf_count = num_codecs; - - return 0; -} - -static int sof_card_dai_links_create(struct device *dev, - struct snd_soc_acpi_mach *mach, - struct snd_soc_card *card) -{ - int ssp_num, sdw_be_num = 0, hdmi_num = 0, dmic_num; + struct device *dev = card->dev; + struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); + int sdw_be_num = 0, ssp_num = 0, dmic_num = 0, hdmi_num = 0, bt_num = 0; struct mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_dai_link_component *idisp_components; struct snd_soc_dai_link_component *ssp_components; - struct snd_soc_acpi_mach_params *mach_params; - const struct snd_soc_acpi_link_adr *adr_link; + struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; + const struct snd_soc_acpi_link_adr *adr_link = mach_params->links; + bool aggregation = !(sof_sdw_quirk & SOF_SDW_NO_AGGREGATION); struct snd_soc_dai_link_component *cpus; struct snd_soc_codec_conf *codec_conf; bool append_dai_type = false; bool ignore_pch_dmic = false; - int codec_conf_count; + int codec_conf_num = 0; int codec_conf_index = 0; - bool group_generated[SDW_MAX_GROUPS]; + bool group_generated[SDW_MAX_GROUPS] = { }; int ssp_codec_index, ssp_mask; - struct snd_soc_dai_link *links; + struct snd_soc_dai_link *dai_links; int num_links, link_index = 0; char *name, *cpu_name; int total_cpu_dai_num; @@ -1585,30 +1537,15 @@ static int sof_card_dai_links_create(struct device *dev, int i, j, be_id = 0; int codec_index; int cpu_id = 0; - int comp_num; int ret; - mach_params = &mach->mach_params; - - /* allocate codec conf, will be populated when dailinks are created */ - ret = sof_card_codec_conf_alloc(dev, mach_params, &codec_conf, &codec_conf_count); - if (ret < 0) + ret = get_dailink_info(dev, adr_link, &sdw_be_num, &sdw_cpu_dai_num, + &codec_conf_num); + if (ret < 0) { + dev_err(dev, "failed to get sdw link info %d\n", ret); return ret; - - /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ - for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) - codec_info_list[i].amp_num = 0; - - if (mach_params->codec_mask & IDISP_CODEC_MASK) { - ctx->idisp_codec = true; - - if (sof_sdw_quirk & SOF_SDW_TGL_HDMI) - hdmi_num = SOF_TGL_HDMI_COUNT; - else - hdmi_num = SOF_PRE_TGL_HDMI_COUNT; } - ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk); /* * on generic tgl platform, I2S or sdw mode is supported * based on board rework. A ACPI device is registered in @@ -1616,54 +1553,52 @@ static int sof_card_dai_links_create(struct device *dev, * Here check ACPI ID to confirm I2S is supported. */ ssp_codec_index = find_codec_info_acpi(mach->id); - ssp_num = ssp_codec_index >= 0 ? hweight_long(ssp_mask) : 0; - comp_num = hdmi_num + ssp_num; + if (ssp_codec_index >= 0) { + ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk); + ssp_num = hweight_long(ssp_mask); + } - ret = get_sdw_dailink_info(dev, mach_params->links, - &sdw_be_num, &sdw_cpu_dai_num); - if (ret < 0) { - dev_err(dev, "failed to get sdw link info %d", ret); - return ret; + if (mach_params->codec_mask & IDISP_CODEC_MASK) { + ctx->idisp_codec = true; + + if (sof_sdw_quirk & SOF_SDW_TGL_HDMI) + hdmi_num = SOF_TGL_HDMI_COUNT; + else + hdmi_num = SOF_PRE_TGL_HDMI_COUNT; } /* enable dmic01 & dmic16k */ - dmic_num = (sof_sdw_quirk & SOF_SDW_PCH_DMIC || mach_params->dmic_num) ? 2 : 0; - comp_num += dmic_num; + if (sof_sdw_quirk & SOF_SDW_PCH_DMIC || mach_params->dmic_num) + dmic_num = 2; if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) - comp_num++; + bt_num = 1; - dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d", sdw_be_num, ssp_num, - dmic_num, ctx->idisp_codec ? hdmi_num : 0); + dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n", + sdw_be_num, ssp_num, dmic_num, hdmi_num, bt_num); /* allocate BE dailinks */ - num_links = comp_num + sdw_be_num; - links = devm_kcalloc(dev, num_links, sizeof(*links), GFP_KERNEL); + num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num; + dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL); + if (!dai_links) + return -ENOMEM; /* allocated CPU DAIs */ - total_cpu_dai_num = comp_num + sdw_cpu_dai_num; - cpus = devm_kcalloc(dev, total_cpu_dai_num, sizeof(*cpus), - GFP_KERNEL); + total_cpu_dai_num = sdw_cpu_dai_num + ssp_num + dmic_num + hdmi_num + bt_num; + cpus = devm_kcalloc(dev, total_cpu_dai_num, sizeof(*cpus), GFP_KERNEL); + if (!cpus) + return -ENOMEM; - if (!links || !cpus) + /* allocate codec conf, will be populated when dailinks are created */ + codec_conf = devm_kcalloc(dev, codec_conf_num, sizeof(*codec_conf), + GFP_KERNEL); + if (!codec_conf) return -ENOMEM; /* SDW */ if (!sdw_be_num) goto SSP; - adr_link = mach_params->links; - if (!adr_link) - return -EINVAL; - - /* - * SoundWire Slaves aggregated in the same group may be - * located on different hardware links. Clear array to indicate - * CPU DAIs for this group have not been generated. - */ - for (i = 0; i < SDW_MAX_GROUPS; i++) - group_generated[i] = false; - for (i = 0; i < SDW_MAX_LINKS; i++) sdw_pin_index[i] = SDW_INTEL_BIDIR_PDI_BASE; @@ -1702,11 +1637,6 @@ out: const struct snd_soc_acpi_endpoint *endpoint; endpoint = adr_link->adr_d[i].endpoints; - if (endpoint->aggregated && !endpoint->group_id) { - dev_err(dev, "invalid group id on link %x", - adr_link->mask); - continue; - } /* this group has been generated */ if (endpoint->aggregated && @@ -1719,17 +1649,20 @@ out: return codec_index; for (j = 0; j < codec_info_list[codec_index].dai_num ; j++) { - ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num, - sdw_cpu_dai_num, cpus, adr_link, - &cpu_id, group_generated, - codec_conf, codec_conf_count, + ret = create_sdw_dailink(card, &link_index, dai_links, + sdw_be_num, sdw_cpu_dai_num, cpus, + adr_link, &cpu_id, + codec_conf, codec_conf_num, &be_id, &codec_conf_index, &ignore_pch_dmic, append_dai_type, i, j); if (ret < 0) { - dev_err(dev, "failed to create dai link %d", link_index); + dev_err(dev, "failed to create dai link %d\n", link_index); return ret; } } + + if (aggregation && endpoint->aggregated) + group_generated[endpoint->group_id] = true; } } @@ -1773,13 +1706,13 @@ SSP: playback = info->dais[0].direction[SNDRV_PCM_STREAM_PLAYBACK]; capture = info->dais[0].direction[SNDRV_PCM_STREAM_CAPTURE]; - init_dai_link(dev, links + link_index, be_id, name, + init_dai_link(dev, dai_links + link_index, be_id, name, playback, capture, cpus + cpu_id, 1, ssp_components, 1, NULL, info->ops); - ret = info->dais[0].init(card, NULL, links + link_index, info, 0); + ret = info->dais[0].init(card, NULL, dai_links + link_index, info, 0); if (ret < 0) return ret; @@ -1794,7 +1727,7 @@ DMIC: goto HDMI; } cpus[cpu_id].dai_name = "DMIC01 Pin"; - init_dai_link(dev, links + link_index, be_id, "dmic01", + init_dai_link(dev, dai_links + link_index, be_id, "dmic01", 0, 1, // DMIC only supports capture cpus + cpu_id, 1, dmic_component, 1, @@ -1802,7 +1735,7 @@ DMIC: INC_ID(be_id, cpu_id, link_index); cpus[cpu_id].dai_name = "DMIC16k Pin"; - init_dai_link(dev, links + link_index, be_id, "dmic16k", + init_dai_link(dev, dai_links + link_index, be_id, "dmic16k", 0, 1, // DMIC only supports capture cpus + cpu_id, 1, dmic_component, 1, @@ -1845,7 +1778,7 @@ HDMI: return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; - init_dai_link(dev, links + link_index, be_id, name, + init_dai_link(dev, dai_links + link_index, be_id, name, 1, 0, // HDMI only supports playback cpus + cpu_id, 1, idisp_components + i, 1, @@ -1866,15 +1799,15 @@ HDMI: return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; - init_dai_link(dev, links + link_index, be_id, name, 1, 1, - cpus + cpu_id, 1, &asoc_dummy_dlc, 1, NULL, NULL); + init_dai_link(dev, dai_links + link_index, be_id, name, 1, 1, + cpus + cpu_id, 1, &asoc_dummy_dlc, 1, NULL, NULL); } - card->dai_link = links; + card->dai_link = dai_links; card->num_links = num_links; card->codec_conf = codec_conf; - card->num_configs = codec_conf_count; + card->num_configs = codec_conf_num; return 0; } @@ -1913,15 +1846,15 @@ static struct snd_soc_card card_sof_sdw = { static struct snd_soc_dai_link *mc_find_codec_dai_used(struct snd_soc_card *card, const char *dai_name) { - struct snd_soc_dai_link *link; + struct snd_soc_dai_link *dai_link; int i; int j; - for_each_card_prelinks(card, i, link) { - for (j = 0; j < link->num_codecs; j++) { + for_each_card_prelinks(card, i, dai_link) { + for (j = 0; j < dai_link->num_codecs; j++) { /* Check each codec in a link */ - if (!strcmp(link->codecs[j].dai_name, dai_name)) - return link; + if (!strcmp(dai_link->codecs[j].dai_name, dai_name)) + return dai_link; } } return NULL; @@ -1929,7 +1862,7 @@ static struct snd_soc_dai_link *mc_find_codec_dai_used(struct snd_soc_card *card static void mc_dailink_exit_loop(struct snd_soc_card *card) { - struct snd_soc_dai_link *link; + struct snd_soc_dai_link *dai_link; int ret; int i, j; @@ -1942,10 +1875,11 @@ static void mc_dailink_exit_loop(struct snd_soc_card *card) * We don't need to call .exit function if there is no matched * dai link found. */ - link = mc_find_codec_dai_used(card, codec_info_list[i].dais[j].dai_name); - if (link) { + dai_link = mc_find_codec_dai_used(card, + codec_info_list[i].dais[j].dai_name); + if (dai_link) { /* Do the .exit function if the codec dai is used in the link */ - ret = codec_info_list[i].dais[j].exit(card, link); + ret = codec_info_list[i].dais[j].exit(card, dai_link); if (ret) dev_warn(card->dev, "codec exit failed %d\n", @@ -1959,34 +1893,38 @@ static void mc_dailink_exit_loop(struct snd_soc_card *card) static int mc_probe(struct platform_device *pdev) { struct snd_soc_card *card = &card_sof_sdw; - struct snd_soc_acpi_mach *mach; + struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev); struct mc_private *ctx; int amp_num = 0, i; int ret; - dev_dbg(&pdev->dev, "Entry\n"); + card->dev = &pdev->dev; + + dev_dbg(card->dev, "Entry\n"); - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(card->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + snd_soc_card_set_drvdata(card, ctx); + dmi_check_system(sof_sdw_quirk_table); if (quirk_override != -1) { - dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", + dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", sof_sdw_quirk, quirk_override); sof_sdw_quirk = quirk_override; } - log_quirks(&pdev->dev); - INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + log_quirks(card->dev); - card->dev = &pdev->dev; - snd_soc_card_set_drvdata(card, ctx); + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ + for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) + codec_info_list[i].amp_num = 0; - mach = pdev->dev.platform_data; - ret = sof_card_dai_links_create(&pdev->dev, mach, - card); + ret = sof_card_dai_links_create(card); if (ret < 0) return ret; @@ -2017,7 +1955,7 @@ static int mc_probe(struct platform_device *pdev) card->long_name = sdw_card_long_name; /* Register the card */ - ret = devm_snd_soc_register_card(&pdev->dev, card); + ret = devm_snd_soc_register_card(card->dev, card); if (ret) { dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); mc_dailink_exit_loop(card); @@ -2036,6 +1974,12 @@ static void mc_remove(struct platform_device *pdev) mc_dailink_exit_loop(card); } +static const struct platform_device_id mc_id_table[] = { + { "sof_sdw", }, + {} +}; +MODULE_DEVICE_TABLE(platform, mc_id_table); + static struct platform_driver sof_sdw_driver = { .driver = { .name = "sof_sdw", @@ -2043,6 +1987,7 @@ static struct platform_driver sof_sdw_driver = { }, .probe = mc_probe, .remove_new = mc_remove, + .id_table = mc_id_table, }; module_platform_driver(sof_sdw_driver); @@ -2052,6 +1997,5 @@ MODULE_AUTHOR("Bard Liao <yung-chuan.liao@linux.intel.com>"); MODULE_AUTHOR("Rander Wang <rander.wang@linux.intel.com>"); MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:sof_sdw"); MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 37402170d5f9..2f4fe6bc3d5d 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -80,6 +80,7 @@ struct sof_sdw_dai_info { struct sof_sdw_codec_info { const int part_id; const int version_id; + const char *codec_name; int amp_num; const u8 acpi_id[ACPI_ID_LEN]; const bool ignore_pch_dmic; @@ -205,4 +206,10 @@ int sof_sdw_cs42l42_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback); +/* CS AMP support */ +int sof_sdw_cs_amp_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); #endif diff --git a/sound/soc/intel/boards/sof_sdw_cs_amp.c b/sound/soc/intel/boards/sof_sdw_cs_amp.c new file mode 100644 index 000000000000..98f6546f484b --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_cs_amp.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2023 Intel Corporation + +/* + * sof_sdw_cs_amp - Helpers to handle CS35L56 from generic machine driver + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "sof_sdw_common.h" + +#define CODEC_NAME_SIZE 8 + +static int cs_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + const char *dai_name = rtd->dai_link->codecs->dai_name; + struct snd_soc_card *card = rtd->card; + char codec_name[CODEC_NAME_SIZE]; + + snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name); + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s spk:%s", + card->components, codec_name); + if (!card->components) + return -ENOMEM; + + return 0; +} + + +int sof_sdw_cs_amp_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + /* Count amp number and do init on playback link only. */ + if (!playback) + return 0; + + info->amp_num++; + dai_links->init = cs_spk_init; + + return 0; +} diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c index 0aef718e82b2..5aa16fd3939b 100644 --- a/sound/soc/intel/boards/sof_ssp_amp.c +++ b/sound/soc/intel/boards/sof_ssp_amp.c @@ -472,6 +472,15 @@ static const struct platform_device_id board_ids[] = { SOF_NO_OF_HDMI_PLAYBACK(3) | SOF_HDMI_PLAYBACK_PRESENT), }, + { + .name = "rpl_lt6911_hdmi_ssp", + .driver_data = (kernel_ulong_t)(SOF_NO_OF_HDMI_CAPTURE_SSP(2) | + SOF_HDMI_CAPTURE_1_SSP(0) | + SOF_HDMI_CAPTURE_2_SSP(2) | + SOF_SSP_HDMI_CAPTURE_PRESENT | + SOF_NO_OF_HDMI_PLAYBACK(3) | + SOF_HDMI_PLAYBACK_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 30ca5416c9a3..f1a5cb825ff1 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -684,6 +684,10 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, return 0; } +static const struct snd_soc_dai_ops catpt_dai_ops = { + .pcm_new = catpt_dai_pcm_new, +}; + static struct snd_soc_dai_driver dai_drivers[] = { /* FE DAIs */ { @@ -764,7 +768,6 @@ static struct snd_soc_dai_driver dai_drivers[] = { { .name = "ssp0-port", .id = CATPT_SSP_IFACE_0, - .pcm_new = catpt_dai_pcm_new, .playback = { .channels_min = 1, .channels_max = 8, @@ -773,11 +776,11 @@ static struct snd_soc_dai_driver dai_drivers[] = { .channels_min = 1, .channels_max = 8, }, + .ops = &catpt_dai_ops, }, { .name = "ssp1-port", .id = CATPT_SSP_IFACE_1, - .pcm_new = catpt_dai_pcm_new, .playback = { .channels_min = 1, .channels_max = 8, @@ -786,6 +789,7 @@ static struct snd_soc_dai_driver dai_drivers[] = { .channels_min = 1, .channels_max = 8, }, + .ops = &catpt_dai_ops, }, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index bcd66e0094b4..8e995edf4c10 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -570,6 +570,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .comp_ids = &adl_rt5682_rt5682s_hp, + .drv_name = "adl_rt5682_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_lt6911_hdmi, + .sof_tplg_filename = "sof-adl-rt5682-ssp1-hdmi-ssp02.tplg", + }, + { + .comp_ids = &adl_rt5682_rt5682s_hp, .drv_name = "adl_rt5682", .sof_tplg_filename = "sof-adl-rt5682.tplg", }, diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index f5c7e1bbded0..f56bd7d656e9 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -34,6 +34,11 @@ static const struct snd_soc_acpi_codecs mx98360a_spk = { .codecs = {"MX98360A"} }; +static struct snd_soc_acpi_codecs rt5650_spk = { + .num_codecs = 1, + .codecs = {"10EC5650"} +}; + static const struct snd_soc_acpi_codecs rt5682_rt5682s_hp = { .num_codecs = 2, .codecs = {"10EC5682", "RTL5682"}, @@ -98,6 +103,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, + { + .id = "10EC5650", + .drv_name = "jsl_rt5650", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rt5650_spk, + .sof_tplg_filename = "sof-jsl-rt5650.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_jsl_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index ed9821adc1d9..0304246d2922 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -161,6 +161,33 @@ static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1318_1_group1_adr[] = { + { + .adr = 0x000130025D131801ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "rt1318-1" + } +}; + +static const struct snd_soc_acpi_adr_device rt1318_2_group1_adr[] = { + { + .adr = 0x000232025D131801ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1318-2" + } +}; + +static const struct snd_soc_acpi_adr_device rt714_0_adr[] = { + { + .adr = 0x000030025D071401ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt714" + } +}; + static const struct snd_soc_acpi_adr_device rt714_1_adr[] = { { .adr = 0x000130025D071401ull, @@ -232,6 +259,25 @@ static const struct snd_soc_acpi_link_adr mtl_3_in_1_sdca[] = { {} }; +static const struct snd_soc_acpi_link_adr mtl_sdw_rt1318_l12_rt714_l0[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1318_1_group1_adr), + .adr_d = rt1318_1_group1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1318_2_group1_adr), + .adr_d = rt1318_2_group1_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt714_0_adr), + .adr_d = rt714_0_adr, + }, + {} +}; + static const struct snd_soc_acpi_adr_device mx8363_2_adr[] = { { .adr = 0x000230019F836300ull, @@ -299,6 +345,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg", }, { + .link_mask = GENMASK(2, 0), + .links = mtl_sdw_rt1318_l12_rt714_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg" + }, + { .link_mask = GENMASK(3, 0), .links = mtl_3_in_1_sdca, .drv_name = "sof_sdw", diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c index 302a08018572..122673c1dae2 100644 --- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c @@ -308,6 +308,15 @@ static const struct snd_soc_acpi_link_adr rpl_sdw_rt1316_link12_rt714_link0[] = {} }; +static const struct snd_soc_acpi_link_adr rpl_sdca_rvp[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_sdca_0_adr), + .adr_d = rt711_sdca_0_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr rplp_crb[] = { { .mask = BIT(2), @@ -322,6 +331,16 @@ static const struct snd_soc_acpi_codecs rpl_rt5682_hp = { .codecs = {"10EC5682", "RTL5682"}, }; +static const struct snd_soc_acpi_codecs rpl_essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + +static const struct snd_soc_acpi_codecs rpl_max98357a_amp = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + static const struct snd_soc_acpi_codecs rpl_max98360a_amp = { .num_codecs = 1, .codecs = {"MX98360A"}, @@ -332,6 +351,16 @@ static const struct snd_soc_acpi_codecs rpl_max98373_amp = { .codecs = {"MX98373"} }; +static const struct snd_soc_acpi_codecs rpl_lt6911_hdmi = { + .num_codecs = 1, + .codecs = {"INTC10B0"} +}; + +static const struct snd_soc_acpi_codecs rpl_nau8318_amp = { + .num_codecs = 1, + .codecs = {"NVTN2012"} +}; + static const struct snd_soc_acpi_codecs rpl_rt1019p_amp = { .num_codecs = 1, .codecs = {"RTL1019"} @@ -340,6 +369,13 @@ static const struct snd_soc_acpi_codecs rpl_rt1019p_amp = { struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { { .comp_ids = &rpl_rt5682_hp, + .drv_name = "rpl_mx98357_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rpl_max98357a_amp, + .sof_tplg_filename = "sof-rpl-max98357a-rt5682.tplg", + }, + { + .comp_ids = &rpl_rt5682_hp, .drv_name = "rpl_mx98360_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_max98360a_amp, @@ -353,12 +389,39 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { .sof_tplg_filename = "sof-rpl-max98373-nau8825.tplg", }, { + .id = "10508825", + .drv_name = "rpl_nau8318_8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rpl_nau8318_amp, + .sof_tplg_filename = "sof-rpl-nau8318-nau8825.tplg", + }, + { .comp_ids = &rpl_rt5682_hp, .drv_name = "rpl_rt1019_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_rt1019p_amp, .sof_tplg_filename = "sof-rpl-rt1019-rt5682.tplg", }, + { + .comp_ids = &rpl_essx_83x6, + .drv_name = "rpl_es83x6_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rpl_lt6911_hdmi, + .sof_tplg_filename = "sof-rpl-es83x6-ssp1-hdmi-ssp02.tplg", + }, + { + .comp_ids = &rpl_essx_83x6, + .drv_name = "sof-essx8336", + .sof_tplg_filename = "sof-rpl-es83x6", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, + }, + { + .id = "INTC10B0", + .drv_name = "rpl_lt6911_hdmi_ssp", + .sof_tplg_filename = "sof-rpl-nocodec-hdmi-ssp02.tplg" + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_machines); @@ -414,6 +477,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[] = { .sof_tplg_filename = "sof-rpl-rt711-l0.tplg", }, { + .link_mask = 0x1, /* link0 required */ + .links = rpl_sdca_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-rpl-rt711-l0.tplg", + }, + { .link_mask = 0x4, /* link2 required */ .links = rplp_crb, .drv_name = "sof_sdw", diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c index b4893365d01d..6b06b7b5ede8 100644 --- a/sound/soc/intel/keembay/kmb_platform.c +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -733,6 +733,7 @@ static int kmb_dai_hw_free(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops kmb_dai_ops = { + .probe = kmb_probe, .startup = kmb_dai_startup, .trigger = kmb_dai_trigger, .hw_params = kmb_dai_hw_params, @@ -755,7 +756,6 @@ static struct snd_soc_dai_driver intel_kmb_hdmi_dai[] = { SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE), }, .ops = &kmb_dai_ops, - .probe = kmb_probe, }, }; @@ -787,7 +787,6 @@ static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = { SNDRV_PCM_FMTBIT_S16_LE), }, .ops = &kmb_dai_ops, - .probe = kmb_probe, }, }; @@ -807,7 +806,6 @@ static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = { SNDRV_PCM_FMTBIT_S16_LE), }, .ops = &kmb_dai_ops, - .probe = kmb_probe, }, }; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index d31509298a0a..fc2eb04da172 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -169,7 +169,7 @@ static struct skl_dsp_loader_ops bxt_get_loader_ops(void) static const struct skl_dsp_ops dsp_ops[] = { { - .id = 0x9d70, + .id = PCI_DEVICE_ID_INTEL_HDA_SKL_LP, .num_cores = 2, .loader_ops = skl_get_loader_ops, .init = skl_sst_dsp_init, @@ -177,7 +177,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .cleanup = skl_sst_dsp_cleanup }, { - .id = 0x9d71, + .id = PCI_DEVICE_ID_INTEL_HDA_KBL_LP, .num_cores = 2, .loader_ops = skl_get_loader_ops, .init = skl_sst_dsp_init, @@ -185,7 +185,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .cleanup = skl_sst_dsp_cleanup }, { - .id = 0x5a98, + .id = PCI_DEVICE_ID_INTEL_HDA_APL, .num_cores = 2, .loader_ops = bxt_get_loader_ops, .init = bxt_sst_dsp_init, @@ -193,7 +193,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .cleanup = bxt_sst_dsp_cleanup }, { - .id = 0x3198, + .id = PCI_DEVICE_ID_INTEL_HDA_GML, .num_cores = 2, .loader_ops = bxt_get_loader_ops, .init = bxt_sst_dsp_init, @@ -201,7 +201,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .cleanup = bxt_sst_dsp_cleanup }, { - .id = 0x9dc8, + .id = PCI_DEVICE_ID_INTEL_HDA_CNL_LP, .num_cores = 4, .loader_ops = bxt_get_loader_ops, .init = cnl_sst_dsp_init, @@ -209,7 +209,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .cleanup = cnl_sst_dsp_cleanup }, { - .id = 0xa348, + .id = PCI_DEVICE_ID_INTEL_HDA_CNL_H, .num_cores = 4, .loader_ops = bxt_get_loader_ops, .init = cnl_sst_dsp_init, @@ -217,7 +217,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .cleanup = cnl_sst_dsp_cleanup }, { - .id = 0x02c8, + .id = PCI_DEVICE_ID_INTEL_HDA_CML_LP, .num_cores = 4, .loader_ops = bxt_get_loader_ops, .init = cnl_sst_dsp_init, @@ -225,7 +225,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .cleanup = cnl_sst_dsp_cleanup }, { - .id = 0x06c8, + .id = PCI_DEVICE_ID_INTEL_HDA_CML_H, .num_cores = 4, .loader_ops = bxt_get_loader_ops, .init = cnl_sst_dsp_init, diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a4209d88b0c6..ac3dc8c63c26 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -13,6 +13,7 @@ #include <linux/pci.h> #include <linux/pm_runtime.h> #include <linux/delay.h> +#include <sound/hdaudio.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include "skl.h" @@ -152,7 +153,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) * The recommended SDxFMT programming sequence for BXT * platforms is to couple the stream before writing the format */ - if (IS_BXT(skl->pci)) { + if (HDA_CONTROLLER_IS_APL(skl->pci)) { snd_hdac_ext_stream_decouple(bus, stream, false); err = snd_hdac_stream_setup(hdac_stream(stream)); snd_hdac_ext_stream_decouple(bus, stream, true); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 998bd0232cf1..77408a981b97 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -608,8 +608,8 @@ struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id) static void init_skl_xtal_rate(int pci_id) { switch (pci_id) { - case 0x9d70: - case 0x9d71: + case PCI_DEVICE_ID_INTEL_HDA_SKL_LP: + case PCI_DEVICE_ID_INTEL_HDA_KBL_LP: skl_clk_src[0].rate = 24000000; return; @@ -1145,44 +1145,28 @@ static void skl_remove(struct pci_dev *pci) /* PCI IDs */ static const struct pci_device_id skl_ids[] = { #if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL) - /* Sunrise Point-LP */ - { PCI_DEVICE(0x8086, 0x9d70), - .driver_data = (unsigned long)&snd_soc_acpi_intel_skl_machines}, + { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &snd_soc_acpi_intel_skl_machines) }, #endif #if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL) - /* BXT-P */ - { PCI_DEVICE(0x8086, 0x5a98), - .driver_data = (unsigned long)&snd_soc_acpi_intel_bxt_machines}, + { PCI_DEVICE_DATA(INTEL, HDA_APL, &snd_soc_acpi_intel_bxt_machines) }, #endif #if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) - /* KBL */ - { PCI_DEVICE(0x8086, 0x9D71), - .driver_data = (unsigned long)&snd_soc_acpi_intel_kbl_machines}, + { PCI_DEVICE_DATA(INTEL, HDA_KBL_LP, &snd_soc_acpi_intel_kbl_machines) }, #endif #if IS_ENABLED(CONFIG_SND_SOC_INTEL_GLK) - /* GLK */ - { PCI_DEVICE(0x8086, 0x3198), - .driver_data = (unsigned long)&snd_soc_acpi_intel_glk_machines}, + { PCI_DEVICE_DATA(INTEL, HDA_GML, &snd_soc_acpi_intel_glk_machines) }, #endif #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL) - /* CNL */ - { PCI_DEVICE(0x8086, 0x9dc8), - .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, + { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, &snd_soc_acpi_intel_cnl_machines) }, #endif #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CFL) - /* CFL */ - { PCI_DEVICE(0x8086, 0xa348), - .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, + { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, &snd_soc_acpi_intel_cnl_machines) }, #endif #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CML_LP) - /* CML-LP */ - { PCI_DEVICE(0x8086, 0x02c8), - .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, + { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &snd_soc_acpi_intel_cnl_machines) }, #endif #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CML_H) - /* CML-H */ - { PCI_DEVICE(0x8086, 0x06c8), - .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, + { PCI_DEVICE_DATA(INTEL, HDA_CML_H, &snd_soc_acpi_intel_cnl_machines) }, #endif { 0, } }; diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index 578af21769c9..517619531615 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -328,6 +328,7 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = { + .probe = jz4740_i2s_dai_probe, .startup = jz4740_i2s_startup, .shutdown = jz4740_i2s_shutdown, .trigger = jz4740_i2s_trigger, @@ -341,7 +342,6 @@ static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = { SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_driver jz4740_i2s_dai = { - .probe = jz4740_i2s_dai_probe, .playback = { .channels_min = 1, .channels_max = 2, @@ -384,7 +384,6 @@ static const struct i2s_soc_info x1000_i2s_soc_info = { }; static struct snd_soc_dai_driver jz4770_i2s_dai = { - .probe = jz4740_i2s_dai_probe, .playback = { .channels_min = 1, .channels_max = 2, diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c index 9ded16329747..406ee8db1a3c 100644 --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -208,7 +208,7 @@ static struct platform_driver loongson_audio_driver = { .driver = { .name = "loongson-asoc-card", .pm = &snd_soc_pm_ops, - .of_match_table = of_match_ptr(loongson_asoc_dt_ids), + .of_match_table = loongson_asoc_dt_ids, }, }; module_platform_driver(loongson_audio_driver); diff --git a/sound/soc/loongson/loongson_i2s.c b/sound/soc/loongson/loongson_i2s.c index b919f0fe8361..d45228a3a558 100644 --- a/sound/soc/loongson/loongson_i2s.c +++ b/sound/soc/loongson/loongson_i2s.c @@ -204,13 +204,6 @@ static int loongson_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static const struct snd_soc_dai_ops loongson_i2s_dai_ops = { - .trigger = loongson_i2s_trigger, - .hw_params = loongson_i2s_hw_params, - .set_sysclk = loongson_i2s_set_dai_sysclk, - .set_fmt = loongson_i2s_set_fmt, -}; - static int loongson_i2s_dai_probe(struct snd_soc_dai *cpu_dai) { struct loongson_i2s *i2s = dev_get_drvdata(cpu_dai->dev); @@ -222,9 +215,16 @@ static int loongson_i2s_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } +static const struct snd_soc_dai_ops loongson_i2s_dai_ops = { + .probe = loongson_i2s_dai_probe, + .trigger = loongson_i2s_trigger, + .hw_params = loongson_i2s_hw_params, + .set_sysclk = loongson_i2s_set_dai_sysclk, + .set_fmt = loongson_i2s_set_fmt, +}; + struct snd_soc_dai_driver loongson_i2s_dai = { .name = "loongson-i2s", - .probe = loongson_i2s_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 1, diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 90db67e0ce4f..8d1bc8814486 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -54,6 +54,26 @@ config SND_SOC_MT6797_MT6351 Select Y if you have such device. If unsure select "N". +config SND_SOC_MT7986 + tristate "ASoC support for Mediatek MT7986 chip" + depends on ARCH_MEDIATEK + select SND_SOC_MEDIATEK + help + This adds ASoC platform driver support for MediaTek MT7986 chip + that can be used with other codecs. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MT7986_WM8960 + tristate "ASoc Audio driver for MT7986 with WM8960 codec" + depends on SND_SOC_MT7986 && I2C + select SND_SOC_WM8960 + help + This adds support for ASoC machine driver for MediaTek MT7986 + boards with the WM8960 codecs. + Select Y if you have such device. + If unsure select "N". + config SND_SOC_MT8173 tristate "ASoC support for Mediatek MT8173 chip" depends on ARCH_MEDIATEK diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 3de38cfc69e5..3938e7f75c2e 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_SND_SOC_MEDIATEK) += common/ obj-$(CONFIG_SND_SOC_MT2701) += mt2701/ obj-$(CONFIG_SND_SOC_MT6797) += mt6797/ +obj-$(CONFIG_SND_SOC_MT7986) += mt7986/ obj-$(CONFIG_SND_SOC_MT8173) += mt8173/ obj-$(CONFIG_SND_SOC_MT8183) += mt8183/ obj-$(CONFIG_SND_SOC_MT8186) += mt8186/ diff --git a/sound/soc/mediatek/common/mtk-btcvsd.c b/sound/soc/mediatek/common/mtk-btcvsd.c index 1ba0633e542f..c12d170fa1de 100644 --- a/sound/soc/mediatek/common/mtk-btcvsd.c +++ b/sound/soc/mediatek/common/mtk-btcvsd.c @@ -696,11 +696,10 @@ static int wait_for_bt_irq(struct mtk_btcvsd_snd *bt, } static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt, - char __user *buf, + struct iov_iter *buf, size_t count) { ssize_t read_size = 0, read_count = 0, cur_read_idx, cont; - unsigned int cur_buf_ofs = 0; unsigned long avail; unsigned long flags; unsigned int packet_size = bt->rx->packet_size; @@ -743,10 +742,9 @@ static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt, if (read_size > cont) read_size = cont; - if (copy_to_user(buf + cur_buf_ofs, - bt->rx_packet_buf + cur_read_idx, - read_size)) { - dev_warn(bt->dev, "%s(), copy_to_user fail\n", + if (copy_to_iter(bt->rx_packet_buf + cur_read_idx, + read_size, buf) != read_size) { + dev_warn(bt->dev, "%s(), copy_to_iter fail\n", __func__); return -EFAULT; } @@ -756,7 +754,6 @@ static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt, spin_unlock_irqrestore(&bt->rx_lock, flags); read_count += read_size; - cur_buf_ofs += read_size; count -= read_size; } @@ -777,11 +774,10 @@ static ssize_t mtk_btcvsd_snd_read(struct mtk_btcvsd_snd *bt, } static ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt, - char __user *buf, + struct iov_iter *buf, size_t count) { int written_size = count, avail, cur_write_idx, write_size, cont; - unsigned int cur_buf_ofs = 0; unsigned long flags; unsigned int packet_size = bt->tx->packet_size; @@ -835,11 +831,9 @@ static ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt, if (write_size > cont) write_size = cont; - if (copy_from_user(bt->tx_packet_buf + - cur_write_idx, - buf + cur_buf_ofs, - write_size)) { - dev_warn(bt->dev, "%s(), copy_from_user fail\n", + if (copy_from_iter(bt->tx_packet_buf + cur_write_idx, + write_size, buf) != write_size) { + dev_warn(bt->dev, "%s(), copy_from_iter fail\n", __func__); return -EFAULT; } @@ -847,7 +841,6 @@ static ssize_t mtk_btcvsd_snd_write(struct mtk_btcvsd_snd *bt, spin_lock_irqsave(&bt->tx_lock, flags); bt->tx->packet_w += write_size / packet_size; spin_unlock_irqrestore(&bt->tx_lock, flags); - cur_buf_ofs += write_size; count -= write_size; } @@ -1033,7 +1026,7 @@ static snd_pcm_uframes_t mtk_pcm_btcvsd_pointer( static int mtk_pcm_btcvsd_copy(struct snd_soc_component *component, struct snd_pcm_substream *substream, int channel, unsigned long pos, - void __user *buf, unsigned long count) + struct iov_iter *buf, unsigned long count) { struct mtk_btcvsd_snd *bt = snd_soc_component_get_drvdata(component); @@ -1274,7 +1267,7 @@ static const struct snd_soc_component_driver mtk_btcvsd_snd_platform = { .prepare = mtk_pcm_btcvsd_prepare, .trigger = mtk_pcm_btcvsd_trigger, .pointer = mtk_pcm_btcvsd_pointer, - .copy_user = mtk_pcm_btcvsd_copy, + .copy = mtk_pcm_btcvsd_copy, }; static int mtk_btcvsd_snd_probe(struct platform_device *pdev) diff --git a/sound/soc/mediatek/mt7986/Makefile b/sound/soc/mediatek/mt7986/Makefile new file mode 100644 index 000000000000..fc4c82559b29 --- /dev/null +++ b/sound/soc/mediatek/mt7986/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +# platform driver +snd-soc-mt7986-afe-objs := \ + mt7986-afe-pcm.o \ + mt7986-dai-etdm.o + +obj-$(CONFIG_SND_SOC_MT7986) += snd-soc-mt7986-afe.o +obj-$(CONFIG_SND_SOC_MT7986_WM8960) += mt7986-wm8960.o diff --git a/sound/soc/mediatek/mt7986/mt7986-afe-common.h b/sound/soc/mediatek/mt7986/mt7986-afe-common.h new file mode 100644 index 000000000000..fc3bb31e5167 --- /dev/null +++ b/sound/soc/mediatek/mt7986/mt7986-afe-common.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt7986-afe-common.h -- MediaTek 7986 audio driver definitions + * + * Copyright (c) 2023 MediaTek Inc. + * Authors: Vic Wu <vic.wu@mediatek.com> + * Maso Huang <maso.huang@mediatek.com> + */ + +#ifndef _MT_7986_AFE_COMMON_H_ +#define _MT_7986_AFE_COMMON_H_ + +#include <sound/soc.h> +#include <linux/clk.h> +#include <linux/list.h> +#include <linux/regmap.h> +#include "../common/mtk-base-afe.h" + +enum { + MT7986_MEMIF_DL1, + MT7986_MEMIF_VUL12, + MT7986_MEMIF_NUM, + MT7986_DAI_ETDM = MT7986_MEMIF_NUM, + MT7986_DAI_NUM, +}; + +enum { + MT7986_IRQ_0, + MT7986_IRQ_1, + MT7986_IRQ_2, + MT7986_IRQ_NUM, +}; + +struct mt7986_afe_private { + struct clk_bulk_data *clks; + int num_clks; + + int pm_runtime_bypass_reg_ctl; + + /* dai */ + void *dai_priv[MT7986_DAI_NUM]; +}; + +unsigned int mt7986_afe_rate_transform(struct device *dev, + unsigned int rate); + +/* dai register */ +int mt7986_dai_etdm_register(struct mtk_base_afe *afe); +#endif diff --git a/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c new file mode 100644 index 000000000000..d497e1129889 --- /dev/null +++ b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC AFE platform driver for MT7986 + * + * Copyright (c) 2023 MediaTek Inc. + * Authors: Vic Wu <vic.wu@mediatek.com> + * Maso Huang <maso.huang@mediatek.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/pm_runtime.h> + +#include "mt7986-afe-common.h" +#include "mt7986-reg.h" +#include "../common/mtk-afe-platform-driver.h" +#include "../common/mtk-afe-fe-dai.h" + +enum { + MTK_AFE_RATE_8K = 0, + MTK_AFE_RATE_11K = 1, + MTK_AFE_RATE_12K = 2, + MTK_AFE_RATE_16K = 4, + MTK_AFE_RATE_22K = 5, + MTK_AFE_RATE_24K = 6, + MTK_AFE_RATE_32K = 8, + MTK_AFE_RATE_44K = 9, + MTK_AFE_RATE_48K = 10, + MTK_AFE_RATE_88K = 13, + MTK_AFE_RATE_96K = 14, + MTK_AFE_RATE_176K = 17, + MTK_AFE_RATE_192K = 18, +}; + +enum { + CLK_INFRA_AUD_BUS_CK = 0, + CLK_INFRA_AUD_26M_CK, + CLK_INFRA_AUD_L_CK, + CLK_INFRA_AUD_AUD_CK, + CLK_INFRA_AUD_EG2_CK, + CLK_NUM +}; + +static const char *aud_clks[CLK_NUM] = { + [CLK_INFRA_AUD_BUS_CK] = "aud_bus_ck", + [CLK_INFRA_AUD_26M_CK] = "aud_26m_ck", + [CLK_INFRA_AUD_L_CK] = "aud_l_ck", + [CLK_INFRA_AUD_AUD_CK] = "aud_aud_ck", + [CLK_INFRA_AUD_EG2_CK] = "aud_eg2_ck", +}; + +unsigned int mt7986_afe_rate_transform(struct device *dev, unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_RATE_8K; + case 11025: + return MTK_AFE_RATE_11K; + case 12000: + return MTK_AFE_RATE_12K; + case 16000: + return MTK_AFE_RATE_16K; + case 22050: + return MTK_AFE_RATE_22K; + case 24000: + return MTK_AFE_RATE_24K; + case 32000: + return MTK_AFE_RATE_32K; + case 44100: + return MTK_AFE_RATE_44K; + case 48000: + return MTK_AFE_RATE_48K; + case 88200: + return MTK_AFE_RATE_88K; + case 96000: + return MTK_AFE_RATE_96K; + case 176400: + return MTK_AFE_RATE_176K; + case 192000: + return MTK_AFE_RATE_192K; + default: + dev_warn(dev, "%s(), rate %u invalid, using %d!!!\n", + __func__, rate, MTK_AFE_RATE_48K); + return MTK_AFE_RATE_48K; + } +} + +static const struct snd_pcm_hardware mt7986_afe_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = 256, + .period_bytes_max = 4 * 48 * 1024, + .periods_min = 2, + .periods_max = 256, + .buffer_bytes_max = 8 * 48 * 1024, + .fifo_size = 0, +}; + +static int mt7986_memif_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + + return mt7986_afe_rate_transform(afe->dev, rate); +} + +static int mt7986_irq_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + + return mt7986_afe_rate_transform(afe->dev, rate); +} + +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mt7986_memif_dai_driver[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL1", + .id = MT7986_MEMIF_DL1, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL1", + .id = MT7986_MEMIF_VUL12, + .capture = { + .stream_name = "UL1", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, +}; + +static const struct snd_kcontrol_new o018_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I150_Switch", AFE_CONN018_4, 22, 1, 0), +}; + +static const struct snd_kcontrol_new o019_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I151_Switch", AFE_CONN019_4, 23, 1, 0), +}; + +static const struct snd_soc_dapm_widget mt7986_memif_widgets[] = { + /* DL */ + SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* UL */ + SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0, + o018_mix, ARRAY_SIZE(o018_mix)), + SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0, + o019_mix, ARRAY_SIZE(o019_mix)), +}; + +static const struct snd_soc_dapm_route mt7986_memif_routes[] = { + {"I032", NULL, "DL1"}, + {"I033", NULL, "DL1"}, + {"UL1", NULL, "O018"}, + {"UL1", NULL, "O019"}, + {"O018", "I150_Switch", "I150"}, + {"O019", "I151_Switch", "I151"}, +}; + +static const struct snd_soc_component_driver mt7986_afe_pcm_dai_component = { + .name = "mt7986-afe-pcm-dai", +}; + +static const struct mtk_base_memif_data memif_data[MT7986_MEMIF_NUM] = { + [MT7986_MEMIF_DL1] = { + .name = "DL1", + .id = MT7986_MEMIF_DL1, + .reg_ofs_base = AFE_DL0_BASE, + .reg_ofs_cur = AFE_DL0_CUR, + .reg_ofs_end = AFE_DL0_END, + .reg_ofs_base_msb = AFE_DL0_BASE_MSB, + .reg_ofs_cur_msb = AFE_DL0_CUR_MSB, + .reg_ofs_end_msb = AFE_DL0_END_MSB, + .fs_reg = AFE_DL0_CON0, + .fs_shift = DL0_MODE_SFT, + .fs_maskbit = DL0_MODE_MASK, + .mono_reg = AFE_DL0_CON0, + .mono_shift = DL0_MONO_SFT, + .enable_reg = AFE_DL0_CON0, + .enable_shift = DL0_ON_SFT, + .hd_reg = AFE_DL0_CON0, + .hd_shift = DL0_HD_MODE_SFT, + .hd_align_reg = AFE_DL0_CON0, + .hd_align_mshift = DL0_HALIGN_SFT, + .pbuf_reg = AFE_DL0_CON0, + .pbuf_shift = DL0_PBUF_SIZE_SFT, + .minlen_reg = AFE_DL0_CON0, + .minlen_shift = DL0_MINLEN_SFT, + }, + [MT7986_MEMIF_VUL12] = { + .name = "VUL12", + .id = MT7986_MEMIF_VUL12, + .reg_ofs_base = AFE_VUL0_BASE, + .reg_ofs_cur = AFE_VUL0_CUR, + .reg_ofs_end = AFE_VUL0_END, + .reg_ofs_base_msb = AFE_VUL0_BASE_MSB, + .reg_ofs_cur_msb = AFE_VUL0_CUR_MSB, + .reg_ofs_end_msb = AFE_VUL0_END_MSB, + .fs_reg = AFE_VUL0_CON0, + .fs_shift = VUL0_MODE_SFT, + .fs_maskbit = VUL0_MODE_MASK, + .mono_reg = AFE_VUL0_CON0, + .mono_shift = VUL0_MONO_SFT, + .enable_reg = AFE_VUL0_CON0, + .enable_shift = VUL0_ON_SFT, + .hd_reg = AFE_VUL0_CON0, + .hd_shift = VUL0_HD_MODE_SFT, + .hd_align_reg = AFE_VUL0_CON0, + .hd_align_mshift = VUL0_HALIGN_SFT, + }, +}; + +static const struct mtk_base_irq_data irq_data[MT7986_IRQ_NUM] = { + [MT7986_IRQ_0] = { + .id = MT7986_IRQ_0, + .irq_cnt_reg = AFE_IRQ0_MCU_CFG1, + .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, + .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, + .irq_fs_reg = AFE_IRQ0_MCU_CFG0, + .irq_fs_shift = IRQ_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ0_MCU_CFG0, + .irq_en_shift = IRQ_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ0_MCU_CLR_SFT, + }, + [MT7986_IRQ_1] = { + .id = MT7986_IRQ_1, + .irq_cnt_reg = AFE_IRQ1_MCU_CFG1, + .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, + .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, + .irq_fs_reg = AFE_IRQ1_MCU_CFG0, + .irq_fs_shift = IRQ_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ1_MCU_CFG0, + .irq_en_shift = IRQ_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ1_MCU_CLR_SFT, + }, + [MT7986_IRQ_2] = { + .id = MT7986_IRQ_2, + .irq_cnt_reg = AFE_IRQ2_MCU_CFG1, + .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, + .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, + .irq_fs_reg = AFE_IRQ2_MCU_CFG0, + .irq_fs_shift = IRQ_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ2_MCU_CFG0, + .irq_en_shift = IRQ_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ2_MCU_CLR_SFT, + }, +}; + +static bool mt7986_is_volatile_reg(struct device *dev, unsigned int reg) +{ + /* + * Those auto-gen regs are read-only, so put it as volatile because + * volatile registers cannot be cached, which means that they cannot + * be set when power is off + */ + + switch (reg) { + case AFE_DL0_CUR_MSB: + case AFE_DL0_CUR: + case AFE_DL0_RCH_MON: + case AFE_DL0_LCH_MON: + case AFE_VUL0_CUR_MSB: + case AFE_VUL0_CUR: + case AFE_IRQ_MCU_STATUS: + case AFE_MEMIF_RD_MON: + case AFE_MEMIF_WR_MON: + return true; + default: + return false; + }; +} + +static const struct regmap_config mt7986_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .volatile_reg = mt7986_is_volatile_reg, + .max_register = AFE_MAX_REGISTER, + .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1), +}; + +static int mt7986_init_clock(struct mtk_base_afe *afe) +{ + struct mt7986_afe_private *afe_priv = afe->platform_priv; + int ret, i; + + afe_priv->clks = devm_kcalloc(afe->dev, CLK_NUM, + sizeof(*afe_priv->clks), GFP_KERNEL); + if (!afe_priv->clks) + return -ENOMEM; + afe_priv->num_clks = CLK_NUM; + + for (i = 0; i < afe_priv->num_clks; i++) + afe_priv->clks[i].id = aud_clks[i]; + + ret = devm_clk_bulk_get(afe->dev, afe_priv->num_clks, afe_priv->clks); + if (ret) + return dev_err_probe(afe->dev, ret, "Failed to get clocks\n"); + + return 0; +} + +static irqreturn_t mt7986_afe_irq_handler(int irq_id, void *dev) +{ + struct mtk_base_afe *afe = dev; + struct mtk_base_afe_irq *irq; + u32 mcu_en, status, status_mcu; + int i, ret; + irqreturn_t irq_ret = IRQ_HANDLED; + + /* get irq that is sent to MCU */ + regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en); + + ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status); + /* only care IRQ which is sent to MCU */ + status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS; + + if (ret || status_mcu == 0) { + dev_err(afe->dev, "%s(), irq status err, ret %d, status 0x%x, mcu_en 0x%x\n", + __func__, ret, status, mcu_en); + + irq_ret = IRQ_NONE; + goto err_irq; + } + + for (i = 0; i < MT7986_MEMIF_NUM; i++) { + struct mtk_base_afe_memif *memif = &afe->memif[i]; + + if (!memif->substream) + continue; + + if (memif->irq_usage < 0) + continue; + + irq = &afe->irqs[memif->irq_usage]; + + if (status_mcu & (1 << irq->irq_data->irq_en_shift)) + snd_pcm_period_elapsed(memif->substream); + } + +err_irq: + /* clear irq */ + regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, status_mcu); + + return irq_ret; +} + +static int mt7986_afe_runtime_suspend(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt7986_afe_private *afe_priv = afe->platform_priv; + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + goto skip_regmap; + + /* disable clk*/ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, 0x3fff, 0x3fff); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_APLL2_EN_MASK, 0); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_26M_EN_MASK, 0); + + /* make sure all irq status are cleared, twice intended */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CLR, 0xffff, 0xffff); + +skip_regmap: + clk_bulk_disable_unprepare(afe_priv->num_clks, afe_priv->clks); + + return 0; +} + +static int mt7986_afe_runtime_resume(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt7986_afe_private *afe_priv = afe->platform_priv; + int ret; + + ret = clk_bulk_prepare_enable(afe_priv->num_clks, afe_priv->clks); + if (ret) + return dev_err_probe(afe->dev, ret, "Failed to enable clocks\n"); + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + return 0; + + /* enable clk*/ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, 0x3fff, 0); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_APLL2_EN_MASK, + AUD_APLL2_EN); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_26M_EN_MASK, + AUD_26M_EN); + + return 0; +} + +static int mt7986_afe_component_probe(struct snd_soc_component *component) +{ + return mtk_afe_add_sub_dai_control(component); +} + +static const struct snd_soc_component_driver mt7986_afe_component = { + .name = AFE_PCM_NAME, + .probe = mt7986_afe_component_probe, + .pointer = mtk_afe_pcm_pointer, + .pcm_construct = mtk_afe_pcm_new, +}; + +static int mt7986_dai_memif_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mt7986_memif_dai_driver; + dai->num_dai_drivers = ARRAY_SIZE(mt7986_memif_dai_driver); + + dai->dapm_widgets = mt7986_memif_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mt7986_memif_widgets); + dai->dapm_routes = mt7986_memif_routes; + dai->num_dapm_routes = ARRAY_SIZE(mt7986_memif_routes); + + return 0; +} + +typedef int (*dai_register_cb)(struct mtk_base_afe *); +static const dai_register_cb dai_register_cbs[] = { + mt7986_dai_etdm_register, + mt7986_dai_memif_register, +}; + +static int mt7986_afe_pcm_dev_probe(struct platform_device *pdev) +{ + struct mtk_base_afe *afe; + struct mt7986_afe_private *afe_priv; + struct device *dev; + int i, irq_id, ret; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + platform_set_drvdata(pdev, afe); + + afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), + GFP_KERNEL); + if (!afe->platform_priv) + return -ENOMEM; + + afe_priv = afe->platform_priv; + afe->dev = &pdev->dev; + dev = afe->dev; + + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + + /* initial audio related clock */ + ret = mt7986_init_clock(afe); + if (ret) + return dev_err_probe(dev, ret, "Cannot initialize clocks\n"); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + /* enable clock for regcache get default value from hw */ + afe_priv->pm_runtime_bypass_reg_ctl = true; + pm_runtime_get_sync(&pdev->dev); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mt7986_afe_regmap_config); + + pm_runtime_put_sync(&pdev->dev); + if (IS_ERR(afe->regmap)) + return PTR_ERR(afe->regmap); + + afe_priv->pm_runtime_bypass_reg_ctl = false; + + /* init memif */ + afe->memif_size = MT7986_MEMIF_NUM; + afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), + GFP_KERNEL); + if (!afe->memif) + return -ENOMEM; + + for (i = 0; i < afe->memif_size; i++) { + afe->memif[i].data = &memif_data[i]; + afe->memif[i].irq_usage = -1; + } + + mutex_init(&afe->irq_alloc_lock); + + /* irq initialize */ + afe->irqs_size = MT7986_IRQ_NUM; + afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs), + GFP_KERNEL); + if (!afe->irqs) + return -ENOMEM; + + for (i = 0; i < afe->irqs_size; i++) + afe->irqs[i].irq_data = &irq_data[i]; + + /* request irq */ + irq_id = platform_get_irq(pdev, 0); + if (irq_id < 0) { + ret = irq_id; + return dev_err_probe(dev, ret, "No irq found\n"); + } + ret = devm_request_irq(dev, irq_id, mt7986_afe_irq_handler, + IRQF_TRIGGER_NONE, "asys-isr", (void *)afe); + if (ret) + return dev_err_probe(dev, ret, "Failed to request irq for asys-isr\n"); + + /* init sub_dais */ + INIT_LIST_HEAD(&afe->sub_dais); + + for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { + ret = dai_register_cbs[i](afe); + if (ret) + return dev_err_probe(dev, ret, "DAI register failed, i: %d\n", i); + } + + /* init dai_driver and component_driver */ + ret = mtk_afe_combine_sub_dai(afe); + if (ret) + return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n"); + + afe->mtk_afe_hardware = &mt7986_afe_hardware; + afe->memif_fs = mt7986_memif_fs; + afe->irq_fs = mt7986_irq_fs; + + afe->runtime_resume = mt7986_afe_runtime_resume; + afe->runtime_suspend = mt7986_afe_runtime_suspend; + + /* register component */ + ret = devm_snd_soc_register_component(&pdev->dev, + &mt7986_afe_component, + NULL, 0); + if (ret) + return dev_err_probe(dev, ret, "Cannot register AFE component\n"); + + ret = devm_snd_soc_register_component(afe->dev, + &mt7986_afe_pcm_dai_component, + afe->dai_drivers, + afe->num_dai_drivers); + if (ret) + return dev_err_probe(dev, ret, "Cannot register PCM DAI component\n"); + + return 0; +} + +static void mt7986_afe_pcm_dev_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + mt7986_afe_runtime_suspend(&pdev->dev); +} + +static const struct of_device_id mt7986_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt7986-afe" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mt7986_afe_pcm_dt_match); + +static const struct dev_pm_ops mt7986_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mt7986_afe_runtime_suspend, + mt7986_afe_runtime_resume, NULL) +}; + +static struct platform_driver mt7986_afe_pcm_driver = { + .driver = { + .name = "mt7986-audio", + .of_match_table = mt7986_afe_pcm_dt_match, + .pm = &mt7986_afe_pm_ops, + }, + .probe = mt7986_afe_pcm_dev_probe, + .remove_new = mt7986_afe_pcm_dev_remove, +}; +module_platform_driver(mt7986_afe_pcm_driver); + +MODULE_DESCRIPTION("MediaTek SoC AFE platform driver for ALSA MT7986"); +MODULE_AUTHOR("Vic Wu <vic.wu@mediatek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c b/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c new file mode 100644 index 000000000000..e523d33846fe --- /dev/null +++ b/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC Audio DAI eTDM Control + * + * Copyright (c) 2023 MediaTek Inc. + * Authors: Vic Wu <vic.wu@mediatek.com> + * Maso Huang <maso.huang@mediatek.com> + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include "mt7986-afe-common.h" +#include "mt7986-reg.h" + +#define HOPPING_CLK 0 +#define APLL_CLK 1 +#define MTK_DAI_ETDM_FORMAT_I2S 0 +#define MTK_DAI_ETDM_FORMAT_DSPA 4 +#define MTK_DAI_ETDM_FORMAT_DSPB 5 + +enum { + MTK_ETDM_RATE_8K = 0, + MTK_ETDM_RATE_12K = 1, + MTK_ETDM_RATE_16K = 2, + MTK_ETDM_RATE_24K = 3, + MTK_ETDM_RATE_32K = 4, + MTK_ETDM_RATE_48K = 5, + MTK_ETDM_RATE_96K = 7, + MTK_ETDM_RATE_192K = 9, + MTK_ETDM_RATE_11K = 16, + MTK_ETDM_RATE_22K = 17, + MTK_ETDM_RATE_44K = 18, + MTK_ETDM_RATE_88K = 19, + MTK_ETDM_RATE_176K = 20, +}; + +struct mtk_dai_etdm_priv { + bool bck_inv; + bool lrck_inv; + bool slave_mode; + unsigned int format; +}; + +static unsigned int mt7986_etdm_rate_transform(struct device *dev, unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_ETDM_RATE_8K; + case 11025: + return MTK_ETDM_RATE_11K; + case 12000: + return MTK_ETDM_RATE_12K; + case 16000: + return MTK_ETDM_RATE_16K; + case 22050: + return MTK_ETDM_RATE_22K; + case 24000: + return MTK_ETDM_RATE_24K; + case 32000: + return MTK_ETDM_RATE_32K; + case 44100: + return MTK_ETDM_RATE_44K; + case 48000: + return MTK_ETDM_RATE_48K; + case 88200: + return MTK_ETDM_RATE_88K; + case 96000: + return MTK_ETDM_RATE_96K; + case 176400: + return MTK_ETDM_RATE_176K; + case 192000: + return MTK_ETDM_RATE_192K; + default: + dev_warn(dev, "%s(), rate %u invalid, using %d!!!\n", + __func__, rate, MTK_ETDM_RATE_48K); + return MTK_ETDM_RATE_48K; + } +} + +static int get_etdm_wlen(unsigned int bitwidth) +{ + return bitwidth <= 16 ? 16 : 32; +} + +/* dai component */ +/* interconnection */ + +static const struct snd_kcontrol_new o124_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I032_Switch", AFE_CONN124_1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new o125_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I033_Switch", AFE_CONN125_1, 1, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = { + + /* DL */ + SND_SOC_DAPM_MIXER("I150", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I151", SND_SOC_NOPM, 0, 0, NULL, 0), + /* UL */ + SND_SOC_DAPM_MIXER("O124", SND_SOC_NOPM, 0, 0, o124_mix, ARRAY_SIZE(o124_mix)), + SND_SOC_DAPM_MIXER("O125", SND_SOC_NOPM, 0, 0, o125_mix, ARRAY_SIZE(o125_mix)), +}; + +static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = { + {"I150", NULL, "ETDM Capture"}, + {"I151", NULL, "ETDM Capture"}, + {"ETDM Playback", NULL, "O124"}, + {"ETDM Playback", NULL, "O125"}, + {"O124", "I032_Switch", "I032"}, + {"O125", "I033_Switch", "I033"}, +}; + +/* dai ops */ +static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt7986_afe_private *afe_priv = afe->platform_priv; + int ret; + + ret = clk_bulk_prepare_enable(afe_priv->num_clks, afe_priv->clks); + if (ret) + return dev_err_probe(afe->dev, ret, "Failed to enable clocks\n"); + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_OUT5_PDN_MASK, 0); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_IN5_PDN_MASK, 0); + + return 0; +} + +static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt7986_afe_private *afe_priv = afe->platform_priv; + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_OUT5_PDN_MASK, + CLK_OUT5_PDN); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_IN5_PDN_MASK, + CLK_IN5_PDN); + + clk_bulk_disable_unprepare(afe_priv->num_clks, afe_priv->clks); +} + +static unsigned int get_etdm_ch_fixup(unsigned int channels) +{ + if (channels > 16) + return 24; + else if (channels > 8) + return 16; + else if (channels > 4) + return 8; + else if (channels > 2) + return 4; + else + return 2; +} + +static int mtk_dai_etdm_config(struct mtk_base_afe *afe, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, + int stream) +{ + struct mt7986_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id]; + unsigned int rate = params_rate(params); + unsigned int etdm_rate = mt7986_etdm_rate_transform(afe->dev, rate); + unsigned int afe_rate = mt7986_afe_rate_transform(afe->dev, rate); + unsigned int channels = params_channels(params); + unsigned int bit_width = params_width(params); + unsigned int wlen = get_etdm_wlen(bit_width); + unsigned int val = 0; + unsigned int mask = 0; + + dev_dbg(afe->dev, "%s(), stream %d, rate %u, bitwidth %u\n", + __func__, stream, rate, bit_width); + + /* CON0 */ + mask |= ETDM_BIT_LEN_MASK; + val |= FIELD_PREP(ETDM_BIT_LEN_MASK, bit_width - 1); + mask |= ETDM_WRD_LEN_MASK; + val |= FIELD_PREP(ETDM_WRD_LEN_MASK, wlen - 1); + mask |= ETDM_FMT_MASK; + val |= FIELD_PREP(ETDM_FMT_MASK, etdm_data->format); + mask |= ETDM_CH_NUM_MASK; + val |= FIELD_PREP(ETDM_CH_NUM_MASK, get_etdm_ch_fixup(channels) - 1); + mask |= RELATCH_SRC_MASK; + val |= FIELD_PREP(RELATCH_SRC_MASK, APLL_CLK); + + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + /* set ETDM_OUT5_CON0 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, mask, val); + + /* set ETDM_OUT5_CON4 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, + OUT_RELATCH_MASK, OUT_RELATCH(afe_rate)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, + OUT_CLK_SRC_MASK, OUT_CLK_SRC(APLL_CLK)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, + OUT_SEL_FS_MASK, OUT_SEL_FS(etdm_rate)); + + /* set ETDM_OUT5_CON5 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON5, + ETDM_CLK_DIV_MASK, ETDM_CLK_DIV); + break; + case SNDRV_PCM_STREAM_CAPTURE: + /* set ETDM_IN5_CON0 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, mask, val); + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, + ETDM_SYNC_MASK, ETDM_SYNC); + + /* set ETDM_IN5_CON2 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON2, + IN_CLK_SRC_MASK, IN_CLK_SRC(APLL_CLK)); + + /* set ETDM_IN5_CON3 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON3, + IN_SEL_FS_MASK, IN_SEL_FS(etdm_rate)); + + /* set ETDM_IN5_CON4 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON4, + IN_RELATCH_MASK, IN_RELATCH(afe_rate)); + break; + default: + break; + } + + return 0; +} + +static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + mtk_dai_etdm_config(afe, params, dai, SNDRV_PCM_STREAM_PLAYBACK); + mtk_dai_etdm_config(afe, params, dai, SNDRV_PCM_STREAM_CAPTURE); + + return 0; +} + +static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + dev_dbg(afe->dev, "%s(), cmd %d, dai id %d\n", __func__, cmd, dai->id); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_EN_MASK, + ETDM_EN); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_EN_MASK, + ETDM_EN); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_EN_MASK, + 0); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_EN_MASK, + 0); + break; + default: + break; + } + + return 0; +} + +static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt7986_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + void *priv_data; + + switch (dai->id) { + case MT7986_DAI_ETDM: + break; + default: + dev_warn(afe->dev, "%s(), id %d not support\n", + __func__, dai->id); + return -EINVAL; + } + + priv_data = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_etdm_priv), + GFP_KERNEL); + if (!priv_data) + return -ENOMEM; + + afe_priv->dai_priv[dai->id] = priv_data; + etdm_data = afe_priv->dai_priv[dai->id]; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA; + break; + case SND_SOC_DAIFMT_DSP_B: + etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + etdm_data->bck_inv = false; + etdm_data->lrck_inv = false; + break; + case SND_SOC_DAIFMT_NB_IF: + etdm_data->bck_inv = false; + etdm_data->lrck_inv = true; + break; + case SND_SOC_DAIFMT_IB_NF: + etdm_data->bck_inv = true; + etdm_data->lrck_inv = false; + break; + case SND_SOC_DAIFMT_IB_IF: + etdm_data->bck_inv = true; + etdm_data->lrck_inv = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + etdm_data->slave_mode = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + etdm_data->slave_mode = false; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_etdm_ops = { + .startup = mtk_dai_etdm_startup, + .shutdown = mtk_dai_etdm_shutdown, + .hw_params = mtk_dai_etdm_hw_params, + .trigger = mtk_dai_etdm_trigger, + .set_fmt = mtk_dai_etdm_set_fmt, +}; + +/* dai driver */ +#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { + { + .name = "ETDM", + .id = MT7986_DAI_ETDM, + .capture = { + .stream_name = "ETDM Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .playback = { + .stream_name = "ETDM Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .ops = &mtk_dai_etdm_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, +}; + +int mt7986_dai_etdm_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_etdm_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver); + + dai->dapm_widgets = mtk_dai_etdm_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets); + dai->dapm_routes = mtk_dai_etdm_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes); + + return 0; +} diff --git a/sound/soc/mediatek/mt7986/mt7986-reg.h b/sound/soc/mediatek/mt7986/mt7986-reg.h new file mode 100644 index 000000000000..c2b200743c3f --- /dev/null +++ b/sound/soc/mediatek/mt7986/mt7986-reg.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt7986-reg.h -- MediaTek 7986 audio driver reg definition + * + * Copyright (c) 2023 MediaTek Inc. + * Authors: Vic Wu <vic.wu@mediatek.com> + * Maso Huang <maso.huang@mediatek.com> + */ + +#ifndef _MT7986_REG_H_ +#define _MT7986_REG_H_ + +#define AUDIO_TOP_CON2 0x0008 +#define AUDIO_TOP_CON4 0x0010 +#define AUDIO_ENGEN_CON0 0x0014 +#define AFE_IRQ_MCU_EN 0x0100 +#define AFE_IRQ_MCU_STATUS 0x0120 +#define AFE_IRQ_MCU_CLR 0x0128 +#define AFE_IRQ0_MCU_CFG0 0x0140 +#define AFE_IRQ0_MCU_CFG1 0x0144 +#define AFE_IRQ1_MCU_CFG0 0x0148 +#define AFE_IRQ1_MCU_CFG1 0x014c +#define AFE_IRQ2_MCU_CFG0 0x0150 +#define AFE_IRQ2_MCU_CFG1 0x0154 +#define ETDM_IN5_CON0 0x13f0 +#define ETDM_IN5_CON1 0x13f4 +#define ETDM_IN5_CON2 0x13f8 +#define ETDM_IN5_CON3 0x13fc +#define ETDM_IN5_CON4 0x1400 +#define ETDM_OUT5_CON0 0x1570 +#define ETDM_OUT5_CON4 0x1580 +#define ETDM_OUT5_CON5 0x1584 +#define ETDM_4_7_COWORK_CON0 0x15e0 +#define ETDM_4_7_COWORK_CON1 0x15e4 +#define AFE_CONN018_1 0x1b44 +#define AFE_CONN018_4 0x1b50 +#define AFE_CONN019_1 0x1b64 +#define AFE_CONN019_4 0x1b70 +#define AFE_CONN124_1 0x2884 +#define AFE_CONN124_4 0x2890 +#define AFE_CONN125_1 0x28a4 +#define AFE_CONN125_4 0x28b0 +#define AFE_CONN_RS_0 0x3920 +#define AFE_CONN_RS_3 0x392c +#define AFE_CONN_16BIT_0 0x3960 +#define AFE_CONN_16BIT_3 0x396c +#define AFE_CONN_24BIT_0 0x3980 +#define AFE_CONN_24BIT_3 0x398c +#define AFE_MEMIF_CON0 0x3d98 +#define AFE_MEMIF_RD_MON 0x3da0 +#define AFE_MEMIF_WR_MON 0x3da4 +#define AFE_DL0_BASE_MSB 0x3e40 +#define AFE_DL0_BASE 0x3e44 +#define AFE_DL0_CUR_MSB 0x3e48 +#define AFE_DL0_CUR 0x3e4c +#define AFE_DL0_END_MSB 0x3e50 +#define AFE_DL0_END 0x3e54 +#define AFE_DL0_RCH_MON 0x3e58 +#define AFE_DL0_LCH_MON 0x3e5c +#define AFE_DL0_CON0 0x3e60 +#define AFE_VUL0_BASE_MSB 0x4220 +#define AFE_VUL0_BASE 0x4224 +#define AFE_VUL0_CUR_MSB 0x4228 +#define AFE_VUL0_CUR 0x422c +#define AFE_VUL0_END_MSB 0x4230 +#define AFE_VUL0_END 0x4234 +#define AFE_VUL0_CON0 0x4238 + +#define AFE_MAX_REGISTER AFE_VUL0_CON0 +#define AFE_IRQ_STATUS_BITS 0x7 +#define AFE_IRQ_CNT_SHIFT 0 +#define AFE_IRQ_CNT_MASK 0xffffff + +/* AUDIO_TOP_CON2 */ +#define CLK_OUT5_PDN BIT(14) +#define CLK_OUT5_PDN_MASK BIT(14) +#define CLK_IN5_PDN BIT(7) +#define CLK_IN5_PDN_MASK BIT(7) + +/* AUDIO_TOP_CON4 */ +#define PDN_APLL_TUNER2 BIT(12) +#define PDN_APLL_TUNER2_MASK BIT(12) + +/* AUDIO_ENGEN_CON0 */ +#define AUD_APLL2_EN BIT(3) +#define AUD_APLL2_EN_MASK BIT(3) +#define AUD_26M_EN BIT(0) +#define AUD_26M_EN_MASK BIT(0) + +/* AFE_DL0_CON0 */ +#define DL0_ON_SFT 28 +#define DL0_ON_MASK 0x1 +#define DL0_ON_MASK_SFT BIT(28) +#define DL0_MINLEN_SFT 20 +#define DL0_MINLEN_MASK 0xf +#define DL0_MINLEN_MASK_SFT (0xf << 20) +#define DL0_MODE_SFT 8 +#define DL0_MODE_MASK 0x1f +#define DL0_MODE_MASK_SFT (0x1f << 8) +#define DL0_PBUF_SIZE_SFT 5 +#define DL0_PBUF_SIZE_MASK 0x3 +#define DL0_PBUF_SIZE_MASK_SFT (0x3 << 5) +#define DL0_MONO_SFT 4 +#define DL0_MONO_MASK 0x1 +#define DL0_MONO_MASK_SFT BIT(4) +#define DL0_HALIGN_SFT 2 +#define DL0_HALIGN_MASK 0x1 +#define DL0_HALIGN_MASK_SFT BIT(2) +#define DL0_HD_MODE_SFT 0 +#define DL0_HD_MODE_MASK 0x3 +#define DL0_HD_MODE_MASK_SFT (0x3 << 0) + +/* AFE_VUL0_CON0 */ +#define VUL0_ON_SFT 28 +#define VUL0_ON_MASK 0x1 +#define VUL0_ON_MASK_SFT BIT(28) +#define VUL0_MODE_SFT 8 +#define VUL0_MODE_MASK 0x1f +#define VUL0_MODE_MASK_SFT (0x1f << 8) +#define VUL0_MONO_SFT 4 +#define VUL0_MONO_MASK 0x1 +#define VUL0_MONO_MASK_SFT BIT(4) +#define VUL0_HALIGN_SFT 2 +#define VUL0_HALIGN_MASK 0x1 +#define VUL0_HALIGN_MASK_SFT BIT(2) +#define VUL0_HD_MODE_SFT 0 +#define VUL0_HD_MODE_MASK 0x3 +#define VUL0_HD_MODE_MASK_SFT (0x3 << 0) + +/* AFE_IRQ_MCU_CON */ +#define IRQ_MCU_MODE_SFT 4 +#define IRQ_MCU_MODE_MASK 0x1f +#define IRQ_MCU_MODE_MASK_SFT (0x1f << 4) +#define IRQ_MCU_ON_SFT 0 +#define IRQ_MCU_ON_MASK 0x1 +#define IRQ_MCU_ON_MASK_SFT BIT(0) +#define IRQ0_MCU_CLR_SFT 0 +#define IRQ0_MCU_CLR_MASK 0x1 +#define IRQ0_MCU_CLR_MASK_SFT BIT(0) +#define IRQ1_MCU_CLR_SFT 1 +#define IRQ1_MCU_CLR_MASK 0x1 +#define IRQ1_MCU_CLR_MASK_SFT BIT(1) +#define IRQ2_MCU_CLR_SFT 2 +#define IRQ2_MCU_CLR_MASK 0x1 +#define IRQ2_MCU_CLR_MASK_SFT BIT(2) + +/* ETDM_IN5_CON2 */ +#define IN_CLK_SRC(x) ((x) << 10) +#define IN_CLK_SRC_SFT 10 +#define IN_CLK_SRC_MASK GENMASK(12, 10) + +/* ETDM_IN5_CON3 */ +#define IN_SEL_FS(x) ((x) << 26) +#define IN_SEL_FS_SFT 26 +#define IN_SEL_FS_MASK GENMASK(30, 26) + +/* ETDM_IN5_CON4 */ +#define IN_RELATCH(x) ((x) << 20) +#define IN_RELATCH_SFT 20 +#define IN_RELATCH_MASK GENMASK(24, 20) +#define IN_CLK_INV BIT(18) +#define IN_CLK_INV_MASK BIT(18) + +/* ETDM_IN5_CON0 & ETDM_OUT5_CON0 */ +#define RELATCH_SRC_MASK GENMASK(30, 28) +#define ETDM_CH_NUM_MASK GENMASK(27, 23) +#define ETDM_WRD_LEN_MASK GENMASK(20, 16) +#define ETDM_BIT_LEN_MASK GENMASK(15, 11) +#define ETDM_FMT_MASK GENMASK(8, 6) +#define ETDM_SYNC BIT(1) +#define ETDM_SYNC_MASK BIT(1) +#define ETDM_EN BIT(0) +#define ETDM_EN_MASK BIT(0) + +/* ETDM_OUT5_CON4 */ +#define OUT_RELATCH(x) ((x) << 24) +#define OUT_RELATCH_SFT 24 +#define OUT_RELATCH_MASK GENMASK(28, 24) +#define OUT_CLK_SRC(x) ((x) << 6) +#define OUT_CLK_SRC_SFT 6 +#define OUT_CLK_SRC_MASK GENMASK(8, 6) +#define OUT_SEL_FS(x) (x) +#define OUT_SEL_FS_SFT 0 +#define OUT_SEL_FS_MASK GENMASK(4, 0) + +/* ETDM_OUT5_CON5 */ +#define ETDM_CLK_DIV BIT(12) +#define ETDM_CLK_DIV_MASK BIT(12) +#define OUT_CLK_INV BIT(9) +#define OUT_CLK_INV_MASK BIT(9) + +/* ETDM_4_7_COWORK_CON0 */ +#define OUT_SEL(x) ((x) << 12) +#define OUT_SEL_SFT 12 +#define OUT_SEL_MASK GENMASK(15, 12) +#endif diff --git a/sound/soc/mediatek/mt7986/mt7986-wm8960.c b/sound/soc/mediatek/mt7986/mt7986-wm8960.c new file mode 100644 index 000000000000..364d13b1c426 --- /dev/null +++ b/sound/soc/mediatek/mt7986/mt7986-wm8960.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mt7986-wm8960.c -- MT7986-WM8960 ALSA SoC machine driver + * + * Copyright (c) 2023 MediaTek Inc. + * Authors: Vic Wu <vic.wu@mediatek.com> + * Maso Huang <maso.huang@mediatek.com> + */ + +#include <linux/module.h> +#include <sound/soc.h> + +#include "mt7986-afe-common.h" + +struct mt7986_wm8960_priv { + struct device_node *platform_node; + struct device_node *codec_node; +}; + +static const struct snd_soc_dapm_widget mt7986_wm8960_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), +}; + +static const struct snd_kcontrol_new mt7986_wm8960_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("AMIC"), +}; + +SND_SOC_DAILINK_DEFS(playback, + DAILINK_COMP_ARRAY(COMP_CPU("DL1")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture, + DAILINK_COMP_ARRAY(COMP_CPU("UL1")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(codec, + DAILINK_COMP_ARRAY(COMP_CPU("ETDM")), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link mt7986_wm8960_dai_links[] = { + /* FE */ + { + .name = "wm8960-playback", + .stream_name = "wm8960-playback", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback), + }, + { + .name = "wm8960-capture", + .stream_name = "wm8960-capture", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture), + }, + /* BE */ + { + .name = "wm8960-codec", + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_GATED, + .dpcm_playback = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(codec), + }, +}; + +static struct snd_soc_card mt7986_wm8960_card = { + .name = "mt7986-wm8960", + .owner = THIS_MODULE, + .dai_link = mt7986_wm8960_dai_links, + .num_links = ARRAY_SIZE(mt7986_wm8960_dai_links), + .controls = mt7986_wm8960_controls, + .num_controls = ARRAY_SIZE(mt7986_wm8960_controls), + .dapm_widgets = mt7986_wm8960_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt7986_wm8960_widgets), +}; + +static int mt7986_wm8960_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt7986_wm8960_card; + struct snd_soc_dai_link *dai_link; + struct device_node *platform, *codec; + struct mt7986_wm8960_priv *priv; + int ret, i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform = of_get_child_by_name(pdev->dev.of_node, "platform"); + + if (platform) { + priv->platform_node = of_parse_phandle(platform, "sound-dai", 0); + of_node_put(platform); + + if (!priv->platform_node) { + dev_err(&pdev->dev, "Failed to parse platform/sound-dai property\n"); + return -EINVAL; + } + } else { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->platforms->name) + continue; + dai_link->platforms->of_node = priv->platform_node; + } + + card->dev = &pdev->dev; + + codec = of_get_child_by_name(pdev->dev.of_node, "codec"); + + if (codec) { + priv->codec_node = of_parse_phandle(codec, "sound-dai", 0); + of_node_put(codec); + + if (!priv->codec_node) { + of_node_put(priv->platform_node); + dev_err(&pdev->dev, "Failed to parse codec/sound-dai property\n"); + return -EINVAL; + } + } else { + of_node_put(priv->platform_node); + dev_err(&pdev->dev, "Property 'codec' missing or invalid\n"); + return -EINVAL; + } + + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->codecs->name) + continue; + dai_link->codecs->of_node = priv->codec_node; + } + + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) { + dev_err(&pdev->dev, "Failed to parse audio-routing: %d\n", ret); + goto err_of_node_put; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(&pdev->dev, "%s snd_soc_register_card fail: %d\n", __func__, ret); + goto err_of_node_put; + } + +err_of_node_put: + of_node_put(priv->codec_node); + of_node_put(priv->platform_node); + return ret; +} + +static void mt7986_wm8960_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct mt7986_wm8960_priv *priv = snd_soc_card_get_drvdata(card); + + of_node_put(priv->codec_node); + of_node_put(priv->platform_node); +} + +static const struct of_device_id mt7986_wm8960_machine_dt_match[] = { + {.compatible = "mediatek,mt7986-wm8960-sound"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mt7986_wm8960_machine_dt_match); + +static struct platform_driver mt7986_wm8960_machine = { + .driver = { + .name = "mt7986-wm8960", + .of_match_table = mt7986_wm8960_machine_dt_match, + }, + .probe = mt7986_wm8960_machine_probe, + .remove_new = mt7986_wm8960_machine_remove, +}; + +module_platform_driver(mt7986_wm8960_machine); + +/* Module information */ +MODULE_DESCRIPTION("MT7986 WM8960 ALSA SoC machine driver"); +MODULE_AUTHOR("Vic Wu <vic.wu@mediatek.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("mt7986 wm8960 soc card"); diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index c2b0619b6158..bfb2094758ff 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -70,7 +70,7 @@ static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; /* enable jack detection */ - ret = snd_soc_card_jack_new_pins(card, "Headphone", SND_JACK_HEADPHONE, + ret = snd_soc_card_jack_new_pins(card, "Headphone", SND_JACK_HEADSET, &mt8173_max98090_jack, mt8173_max98090_jack_pins, ARRAY_SIZE(mt8173_max98090_jack_pins)); diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index f803f121659d..e502cd1670ba 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -40,6 +40,17 @@ static const struct snd_kcontrol_new mt8173_rt5650_rt5514_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), }; +static struct snd_soc_jack_pin mt8173_rt5650_rt5514_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -82,11 +93,13 @@ static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime) RT5645_CLK_SEL_I2S1_ASRC); /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_rt5514_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5514_jack, + mt8173_rt5650_rt5514_jack_pins, + ARRAY_SIZE(mt8173_rt5650_rt5514_jack_pins)); if (ret) { dev_err(card->dev, "Can't new Headset Jack %d\n", ret); return ret; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 8794720cea3a..ffb094284bfb 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -44,6 +44,17 @@ static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), }; +static struct snd_soc_jack_pin mt8173_rt5650_rt5676_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -95,11 +106,13 @@ static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime) RT5677_CLK_SEL_I2S2_ASRC); /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_rt5676_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5676_jack, + mt8173_rt5650_rt5676_jack_pins, + ARRAY_SIZE(mt8173_rt5650_rt5676_jack_pins)); if (ret) { dev_err(card->dev, "Can't new Headset Jack %d\n", ret); return ret; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 3ece4b5eaca2..18cf84bb25c7 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -54,6 +54,17 @@ static const struct snd_kcontrol_new mt8173_rt5650_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), }; +static struct snd_soc_jack_pin mt8173_rt5650_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -128,11 +139,13 @@ static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime) } /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_jack, + mt8173_rt5650_jack_pins, + ARRAY_SIZE(mt8173_rt5650_jack_pins)); if (ret) { dev_err(card->dev, "Can't new Headset Jack %d\n", ret); return ret; diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 87bb04846991..701fbcc0f2c9 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -29,6 +29,21 @@ struct mt8183_da7219_max98357_priv { struct snd_soc_jack headset_jack, hdmi_jack; }; +static struct snd_soc_jack_pin mt8183_da7219_max98357_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, +}; + static int mt8183_mt6358_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -572,13 +587,15 @@ mt8183_da7219_max98357_headset_init(struct snd_soc_component *component) snd_soc_card_get_drvdata(component->card); /* Enable Headset and 4 Buttons Jack detection */ - ret = snd_soc_card_jack_new(component->card, - "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3 | - SND_JACK_LINEOUT, - &priv->headset_jack); + ret = snd_soc_card_jack_new_pins(component->card, + "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_LINEOUT, + &priv->headset_jack, + mt8183_da7219_max98357_jack_pins, + ARRAY_SIZE(mt8183_da7219_max98357_jack_pins)); if (ret) return ret; @@ -609,12 +626,18 @@ static struct snd_soc_codec_conf mt6358_codec_conf[] = { }; static const struct snd_kcontrol_new mt8183_da7219_max98357_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Speakers"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_soc_dapm_widget mt8183_da7219_max98357_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_SPK("Line Out", NULL), SND_SOC_DAPM_PINCTRL("TDM_OUT_PINCTRL", "aud_tdm_out_on", "aud_tdm_out_off"), }; @@ -657,14 +680,20 @@ static struct snd_soc_codec_conf mt8183_da7219_rt1015_codec_conf[] = { }; static const struct snd_kcontrol_new mt8183_da7219_rt1015_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Left Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_soc_dapm_widget mt8183_da7219_rt1015_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Left Spk", NULL), SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_PINCTRL("TDM_OUT_PINCTRL", "aud_tdm_out_on", "aud_tdm_out_off"), }; diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index ce9aedde7e1e..850f4d949d97 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -592,11 +592,38 @@ static struct snd_soc_dai_link mt8183_mt6358_ts3a227_dai_links[] = { }, }; +static const +struct snd_kcontrol_new mt8183_mt6358_ts3a227_max98357_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static const +struct snd_soc_dapm_widget mt8183_mt6358_ts3a227_max98357_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static struct snd_soc_jack_pin mt8183_mt6358_ts3a227_max98357_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static struct snd_soc_card mt8183_mt6358_ts3a227_max98357_card = { .name = "mt8183_mt6358_ts3a227_max98357", .owner = THIS_MODULE, .dai_link = mt8183_mt6358_ts3a227_dai_links, .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links), + .controls = mt8183_mt6358_ts3a227_max98357_snd_controls, + .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls), + .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets), }; static struct snd_soc_card mt8183_mt6358_ts3a227_max98357b_card = { @@ -604,6 +631,10 @@ static struct snd_soc_card mt8183_mt6358_ts3a227_max98357b_card = { .owner = THIS_MODULE, .dai_link = mt8183_mt6358_ts3a227_dai_links, .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links), + .controls = mt8183_mt6358_ts3a227_max98357_snd_controls, + .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls), + .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets), }; static struct snd_soc_codec_conf mt8183_mt6358_ts3a227_rt1015_amp_conf[] = { @@ -624,6 +655,10 @@ static struct snd_soc_card mt8183_mt6358_ts3a227_rt1015_card = { .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links), .codec_conf = mt8183_mt6358_ts3a227_rt1015_amp_conf, .num_configs = ARRAY_SIZE(mt8183_mt6358_ts3a227_rt1015_amp_conf), + .controls = mt8183_mt6358_ts3a227_max98357_snd_controls, + .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls), + .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets), }; static struct snd_soc_card mt8183_mt6358_ts3a227_rt1015p_card = { @@ -631,6 +666,10 @@ static struct snd_soc_card mt8183_mt6358_ts3a227_rt1015p_card = { .owner = THIS_MODULE, .dai_link = mt8183_mt6358_ts3a227_dai_links, .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links), + .controls = mt8183_mt6358_ts3a227_max98357_snd_controls, + .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls), + .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets), }; static int @@ -641,12 +680,14 @@ mt8183_mt6358_ts3a227_max98357_headset_init(struct snd_soc_component *component) snd_soc_card_get_drvdata(component->card); /* Enable Headset and 4 Buttons Jack detection */ - ret = snd_soc_card_jack_new(component->card, - "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &priv->headset_jack); + ret = snd_soc_card_jack_new_pins(component->card, + "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &priv->headset_jack, + mt8183_mt6358_ts3a227_max98357_jack_pins, + ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_jack_pins)); if (ret) return ret; diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c index 539e3a023bc4..70ec101890d3 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c @@ -13,8 +13,6 @@ #include "mt8186-afe-clk.h" #include "mt8186-audsys-clk.h" -static DEFINE_MUTEX(mutex_request_dram); - static const char *aud_clks[CLK_NUM] = { [CLK_AFE] = "aud_afe_clk", [CLK_DAC] = "aud_dac_clk", diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c index a868a04ed4e7..b86159f70a33 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c @@ -2815,7 +2815,6 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; struct mt8186_afe_private *afe_priv; - struct resource *res; struct reset_control *rstc; struct device *dev = &pdev->dev; int i, ret, irq_id; @@ -2836,8 +2835,7 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev) afe_priv = afe->platform_priv; afe->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - afe->base_addr = devm_ioremap_resource(dev, res); + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(afe->base_addr)) return PTR_ERR(afe->base_addr); diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c index 0432f9d89020..aa8e00bba19b 100644 --- a/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c +++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c @@ -46,6 +46,10 @@ static struct snd_soc_jack_pin mt8186_jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static struct snd_soc_codec_conf mt8186_mt6366_da7219_max98357_codec_conf[] = { @@ -964,6 +968,7 @@ mt8186_mt6366_da7219_max98357_widgets[] = { SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_OUTPUT("HDMI1"), SND_SOC_DAPM_MIXER(SOF_DMA_DL1, SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0), @@ -996,6 +1001,7 @@ mt8186_mt6366_da7219_max98357_controls[] = { SOC_DAPM_PIN_SWITCH("Speakers"), SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), SOC_DAPM_PIN_SWITCH("HDMI1"), }; diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c index 6a24b339444b..5e14655c5617 100644 --- a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c +++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c @@ -16,6 +16,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_platform.h> +#include <linux/of_reserved_mem.h> #include <linux/pm_runtime.h> #include <linux/soc/mediatek/infracfg.h> #include <linux/reset.h> @@ -3193,11 +3194,15 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; struct mt8188_afe_private *afe_priv; - struct device *dev; + struct device *dev = &pdev->dev; struct reset_control *rstc; struct regmap *infra_ao; int i, irq_id, ret; + ret = of_reserved_mem_device_init(dev); + if (ret) + dev_dbg(dev, "failed to assign memory region: %d\n", ret); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); if (ret) return ret; @@ -3213,7 +3218,6 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev) afe_priv = afe->platform_priv; afe->dev = &pdev->dev; - dev = afe->dev; afe->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(afe->base_addr)) diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index ac69c23e0da1..9017f48b6272 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -723,6 +723,9 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { }, .dynamic = 1, .dpcm_playback = 1, + .dpcm_merged_chan = 1, + .dpcm_merged_rate = 1, + .dpcm_merged_format = 1, SND_SOC_DAILINK_REG(playback2), }, [DAI_LINK_DL3_FE] = { @@ -734,6 +737,9 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { }, .dynamic = 1, .dpcm_playback = 1, + .dpcm_merged_chan = 1, + .dpcm_merged_rate = 1, + .dpcm_merged_format = 1, SND_SOC_DAILINK_REG(playback3), }, [DAI_LINK_DL6_FE] = { @@ -745,6 +751,9 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { }, .dynamic = 1, .dpcm_playback = 1, + .dpcm_merged_chan = 1, + .dpcm_merged_rate = 1, + .dpcm_merged_format = 1, SND_SOC_DAILINK_REG(playback6), }, [DAI_LINK_DL7_FE] = { @@ -833,6 +842,9 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { }, .dynamic = 1, .dpcm_capture = 1, + .dpcm_merged_chan = 1, + .dpcm_merged_rate = 1, + .dpcm_merged_format = 1, SND_SOC_DAILINK_REG(capture4), }, [DAI_LINK_UL5_FE] = { @@ -844,6 +856,9 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { }, .dynamic = 1, .dpcm_capture = 1, + .dpcm_merged_chan = 1, + .dpcm_merged_rate = 1, + .dpcm_merged_format = 1, SND_SOC_DAILINK_REG(capture5), }, [DAI_LINK_UL6_FE] = { @@ -969,16 +984,6 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { }, }; -static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name) -{ - struct snd_ctl_elem_id sid; - - memset(&sid, 0, sizeof(sid)); - strcpy(sid.name, name); - sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - return snd_ctl_find_id(card, &sid); -} - static void mt8188_fixup_controls(struct snd_soc_card *card) { struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(card); @@ -995,7 +1000,7 @@ static void mt8188_fixup_controls(struct snd_soc_card *card) snd_soc_dapm_free_widget(w); } - kctl = ctl_find(card->snd_card, "Headphone Switch"); + kctl = snd_ctl_find_id_mixer(card->snd_card, "Headphone Switch"); if (kctl) snd_ctl_remove(card->snd_card, kctl); else diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c index eedb9165f911..fd4f9f8f032d 100644 --- a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c +++ b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c @@ -2456,25 +2456,6 @@ static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai, return mtk_dai_etdm_cal_mclk(afe, freq, dai->id); } -static const struct snd_soc_dai_ops mtk_dai_etdm_ops = { - .startup = mtk_dai_etdm_startup, - .shutdown = mtk_dai_etdm_shutdown, - .hw_params = mtk_dai_etdm_hw_params, - .trigger = mtk_dai_etdm_trigger, - .set_sysclk = mtk_dai_etdm_set_sysclk, - .set_fmt = mtk_dai_etdm_set_fmt, - .set_tdm_slot = mtk_dai_etdm_set_tdm_slot, -}; - -static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops = { - .startup = mtk_dai_hdmitx_dptx_startup, - .shutdown = mtk_dai_hdmitx_dptx_shutdown, - .hw_params = mtk_dai_hdmitx_dptx_hw_params, - .trigger = mtk_dai_hdmitx_dptx_trigger, - .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk, - .set_fmt = mtk_dai_etdm_set_fmt, -}; - /* dai driver */ #define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_384000) @@ -2505,6 +2486,36 @@ static int mtk_dai_etdm_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops = { + .startup = mtk_dai_hdmitx_dptx_startup, + .shutdown = mtk_dai_hdmitx_dptx_shutdown, + .hw_params = mtk_dai_hdmitx_dptx_hw_params, + .trigger = mtk_dai_hdmitx_dptx_trigger, + .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk, + .set_fmt = mtk_dai_etdm_set_fmt, +}; + +static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops2 = { + .probe = mtk_dai_etdm_probe, + .startup = mtk_dai_hdmitx_dptx_startup, + .shutdown = mtk_dai_hdmitx_dptx_shutdown, + .hw_params = mtk_dai_hdmitx_dptx_hw_params, + .trigger = mtk_dai_hdmitx_dptx_trigger, + .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk, + .set_fmt = mtk_dai_etdm_set_fmt, +}; + +static const struct snd_soc_dai_ops mtk_dai_etdm_ops = { + .probe = mtk_dai_etdm_probe, + .startup = mtk_dai_etdm_startup, + .shutdown = mtk_dai_etdm_shutdown, + .hw_params = mtk_dai_etdm_hw_params, + .trigger = mtk_dai_etdm_trigger, + .set_sysclk = mtk_dai_etdm_set_sysclk, + .set_fmt = mtk_dai_etdm_set_fmt, + .set_tdm_slot = mtk_dai_etdm_set_tdm_slot, +}; + static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { { .name = "DPTX", @@ -2529,7 +2540,6 @@ static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { .formats = MTK_ETDM_FORMATS, }, .ops = &mtk_dai_etdm_ops, - .probe = mtk_dai_etdm_probe, }, { .name = "ETDM2_IN", @@ -2542,7 +2552,6 @@ static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { .formats = MTK_ETDM_FORMATS, }, .ops = &mtk_dai_etdm_ops, - .probe = mtk_dai_etdm_probe, }, { .name = "ETDM1_OUT", @@ -2555,7 +2564,6 @@ static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { .formats = MTK_ETDM_FORMATS, }, .ops = &mtk_dai_etdm_ops, - .probe = mtk_dai_etdm_probe, }, { .name = "ETDM2_OUT", @@ -2568,7 +2576,6 @@ static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { .formats = MTK_ETDM_FORMATS, }, .ops = &mtk_dai_etdm_ops, - .probe = mtk_dai_etdm_probe, }, { .name = "ETDM3_OUT", @@ -2580,8 +2587,7 @@ static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { .rates = MTK_ETDM_RATES, .formats = MTK_ETDM_FORMATS, }, - .ops = &mtk_dai_hdmitx_dptx_ops, - .probe = mtk_dai_etdm_probe, + .ops = &mtk_dai_hdmitx_dptx_ops2, }, }; diff --git a/sound/soc/meson/aiu-acodec-ctrl.c b/sound/soc/meson/aiu-acodec-ctrl.c index d0f0ada5f4bc..7b04b97f7b41 100644 --- a/sound/soc/meson/aiu-acodec-ctrl.c +++ b/sound/soc/meson/aiu-acodec-ctrl.c @@ -103,6 +103,8 @@ static int aiu_acodec_ctrl_input_hw_params(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops aiu_acodec_ctrl_input_ops = { + .probe = meson_codec_glue_input_dai_probe, + .remove = meson_codec_glue_input_dai_remove, .hw_params = aiu_acodec_ctrl_input_hw_params, .set_fmt = meson_codec_glue_input_set_fmt, }; @@ -130,8 +132,6 @@ static const struct snd_soc_dai_ops aiu_acodec_ctrl_output_ops = { .name = "ACODEC CTRL " xname, \ .playback = AIU_ACODEC_STREAM(xname, "Playback", 8), \ .ops = &aiu_acodec_ctrl_input_ops, \ - .probe = meson_codec_glue_input_dai_probe, \ - .remove = meson_codec_glue_input_dai_remove, \ } #define AIU_ACODEC_OUTPUT(xname) { \ diff --git a/sound/soc/meson/aiu-codec-ctrl.c b/sound/soc/meson/aiu-codec-ctrl.c index 84c10956c241..ee0ef6301010 100644 --- a/sound/soc/meson/aiu-codec-ctrl.c +++ b/sound/soc/meson/aiu-codec-ctrl.c @@ -75,6 +75,8 @@ static const struct snd_soc_dapm_widget aiu_hdmi_ctrl_widgets[] = { }; static const struct snd_soc_dai_ops aiu_codec_ctrl_input_ops = { + .probe = meson_codec_glue_input_dai_probe, + .remove = meson_codec_glue_input_dai_remove, .hw_params = meson_codec_glue_input_hw_params, .set_fmt = meson_codec_glue_input_set_fmt, }; @@ -102,8 +104,6 @@ static const struct snd_soc_dai_ops aiu_codec_ctrl_output_ops = { .name = "CODEC CTRL " xname, \ .playback = AIU_CODEC_CTRL_STREAM(xname, "Playback"), \ .ops = &aiu_codec_ctrl_input_ops, \ - .probe = meson_codec_glue_input_dai_probe, \ - .remove = meson_codec_glue_input_dai_remove, \ } #define AIU_CODEC_CTRL_OUTPUT(xname) { \ diff --git a/sound/soc/meson/aiu-fifo-i2s.c b/sound/soc/meson/aiu-fifo-i2s.c index 59e00a74b5f8..7d833500c799 100644 --- a/sound/soc/meson/aiu-fifo-i2s.c +++ b/sound/soc/meson/aiu-fifo-i2s.c @@ -140,6 +140,9 @@ static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream, } const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops = { + .pcm_new = aiu_fifo_pcm_new, + .probe = aiu_fifo_i2s_dai_probe, + .remove = aiu_fifo_dai_remove, .trigger = aiu_fifo_i2s_trigger, .prepare = aiu_fifo_i2s_prepare, .hw_params = aiu_fifo_i2s_hw_params, diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c index ddbd2fc40185..fa91f3c53fa4 100644 --- a/sound/soc/meson/aiu-fifo-spdif.c +++ b/sound/soc/meson/aiu-fifo-spdif.c @@ -155,6 +155,9 @@ static int fifo_spdif_hw_params(struct snd_pcm_substream *substream, } const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = { + .pcm_new = aiu_fifo_pcm_new, + .probe = aiu_fifo_spdif_dai_probe, + .remove = aiu_fifo_dai_remove, .trigger = fifo_spdif_trigger, .prepare = fifo_spdif_prepare, .hw_params = fifo_spdif_hw_params, diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index da351a60df0c..7109b81cc3d0 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -121,9 +121,6 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { .formats = AIU_FORMATS, }, .ops = &aiu_fifo_i2s_dai_ops, - .pcm_new = aiu_fifo_pcm_new, - .probe = aiu_fifo_i2s_dai_probe, - .remove = aiu_fifo_dai_remove, }, [CPU_SPDIF_FIFO] = { .name = "SPDIF FIFO", @@ -137,9 +134,6 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { .formats = AIU_FORMATS, }, .ops = &aiu_fifo_spdif_dai_ops, - .pcm_new = aiu_fifo_pcm_new, - .probe = aiu_fifo_spdif_dai_probe, - .remove = aiu_fifo_dai_remove, }, [CPU_I2S_ENCODER] = { .name = "I2S Encoder", diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c index 61f9d417fd60..8c166a5f338c 100644 --- a/sound/soc/meson/axg-frddr.c +++ b/sound/soc/meson/axg-frddr.c @@ -100,6 +100,7 @@ static const struct snd_soc_dai_ops axg_frddr_ops = { .hw_params = axg_frddr_dai_hw_params, .startup = axg_frddr_dai_startup, .shutdown = axg_frddr_dai_shutdown, + .pcm_new = axg_frddr_pcm_new, }; static struct snd_soc_dai_driver axg_frddr_dai_drv = { @@ -112,7 +113,6 @@ static struct snd_soc_dai_driver axg_frddr_dai_drv = { .formats = AXG_FIFO_FORMATS, }, .ops = &axg_frddr_ops, - .pcm_new = axg_frddr_pcm_new, }; static const char * const axg_frddr_sel_texts[] = { @@ -175,6 +175,7 @@ static const struct snd_soc_dai_ops g12a_frddr_ops = { .hw_params = axg_frddr_dai_hw_params, .startup = axg_frddr_dai_startup, .shutdown = axg_frddr_dai_shutdown, + .pcm_new = axg_frddr_pcm_new, }; static struct snd_soc_dai_driver g12a_frddr_dai_drv = { @@ -187,7 +188,6 @@ static struct snd_soc_dai_driver g12a_frddr_dai_drv = { .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_frddr_ops, - .pcm_new = axg_frddr_pcm_new, }; static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel1_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT, diff --git a/sound/soc/meson/axg-pdm.c b/sound/soc/meson/axg-pdm.c index ad43cb2a1e3f..d59050914d3c 100644 --- a/sound/soc/meson/axg-pdm.c +++ b/sound/soc/meson/axg-pdm.c @@ -294,13 +294,6 @@ static void axg_pdm_shutdown(struct snd_pcm_substream *substream, clk_disable_unprepare(priv->dclk); } -static const struct snd_soc_dai_ops axg_pdm_dai_ops = { - .trigger = axg_pdm_trigger, - .hw_params = axg_pdm_hw_params, - .startup = axg_pdm_startup, - .shutdown = axg_pdm_shutdown, -}; - static void axg_pdm_set_hcic_ctrl(struct axg_pdm *priv) { const struct axg_pdm_hcic *hcic = &priv->cfg->filters->hcic; @@ -440,6 +433,15 @@ static int axg_pdm_dai_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops axg_pdm_dai_ops = { + .probe = axg_pdm_dai_probe, + .remove = axg_pdm_dai_remove, + .trigger = axg_pdm_trigger, + .hw_params = axg_pdm_hw_params, + .startup = axg_pdm_startup, + .shutdown = axg_pdm_shutdown, +}; + static struct snd_soc_dai_driver axg_pdm_dai_drv = { .name = "PDM", .capture = { @@ -453,8 +455,6 @@ static struct snd_soc_dai_driver axg_pdm_dai_drv = { SNDRV_PCM_FMTBIT_S32_LE), }, .ops = &axg_pdm_dai_ops, - .probe = axg_pdm_dai_probe, - .remove = axg_pdm_dai_remove, }; static const struct snd_soc_component_driver axg_pdm_component_drv = { diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c index e2cc4c4be758..d86880169075 100644 --- a/sound/soc/meson/axg-spdifin.c +++ b/sound/soc/meson/axg-spdifin.c @@ -267,6 +267,8 @@ static int axg_spdifin_dai_remove(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops axg_spdifin_ops = { + .probe = axg_spdifin_dai_probe, + .remove = axg_spdifin_dai_remove, .prepare = axg_spdifin_prepare, .startup = axg_spdifin_startup, .shutdown = axg_spdifin_shutdown, @@ -429,8 +431,6 @@ axg_spdifin_get_dai_drv(struct device *dev, struct axg_spdifin *priv) drv->name = "SPDIF Input"; drv->ops = &axg_spdifin_ops; - drv->probe = axg_spdifin_dai_probe; - drv->remove = axg_spdifin_dai_remove; drv->capture.stream_name = "Capture"; drv->capture.channels_min = 1; drv->capture.channels_max = 2; diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index 5e5e4c56d505..1c3d433cefd2 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -395,6 +395,8 @@ static int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops axg_tdm_iface_ops = { + .probe = axg_tdm_iface_probe_dai, + .remove = axg_tdm_iface_remove_dai, .set_sysclk = axg_tdm_iface_set_sysclk, .set_fmt = axg_tdm_iface_set_fmt, .startup = axg_tdm_iface_startup, @@ -423,8 +425,6 @@ static const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = { }, .id = TDM_IFACE_PAD, .ops = &axg_tdm_iface_ops, - .probe = axg_tdm_iface_probe_dai, - .remove = axg_tdm_iface_remove_dai, }, [TDM_IFACE_LOOPBACK] = { .name = "TDM Loopback", @@ -437,8 +437,6 @@ static const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = { }, .id = TDM_IFACE_LOOPBACK, .ops = &axg_tdm_iface_ops, - .probe = axg_tdm_iface_probe_dai, - .remove = axg_tdm_iface_remove_dai, }, }; diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index e9208e74e965..1a0be177b8fe 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -122,6 +122,7 @@ static const struct snd_soc_dai_ops axg_toddr_ops = { .hw_params = axg_toddr_dai_hw_params, .startup = axg_toddr_dai_startup, .shutdown = axg_toddr_dai_shutdown, + .pcm_new = axg_toddr_pcm_new, }; static struct snd_soc_dai_driver axg_toddr_dai_drv = { @@ -134,7 +135,6 @@ static struct snd_soc_dai_driver axg_toddr_dai_drv = { .formats = AXG_FIFO_FORMATS, }, .ops = &axg_toddr_ops, - .pcm_new = axg_toddr_pcm_new, }; static const char * const axg_toddr_sel_texts[] = { @@ -217,6 +217,7 @@ static const struct snd_soc_dai_ops g12a_toddr_ops = { .hw_params = axg_toddr_dai_hw_params, .startup = g12a_toddr_dai_startup, .shutdown = axg_toddr_dai_shutdown, + .pcm_new = axg_toddr_pcm_new, }; static struct snd_soc_dai_driver g12a_toddr_dai_drv = { @@ -229,7 +230,6 @@ static struct snd_soc_dai_driver g12a_toddr_dai_drv = { .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_toddr_ops, - .pcm_new = axg_toddr_pcm_new, }; static const struct snd_soc_component_driver g12a_toddr_component_drv = { diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c index ddc667956cf5..6c4503766fdc 100644 --- a/sound/soc/meson/g12a-toacodec.c +++ b/sound/soc/meson/g12a-toacodec.c @@ -162,6 +162,8 @@ static int g12a_toacodec_input_hw_params(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops g12a_toacodec_input_ops = { + .probe = meson_codec_glue_input_dai_probe, + .remove = meson_codec_glue_input_dai_remove, .hw_params = g12a_toacodec_input_hw_params, .set_fmt = meson_codec_glue_input_set_fmt, }; @@ -185,8 +187,6 @@ static const struct snd_soc_dai_ops g12a_toacodec_output_ops = { .id = (xid), \ .playback = TOACODEC_STREAM(xname, "Playback", 8), \ .ops = &g12a_toacodec_input_ops, \ - .probe = meson_codec_glue_input_dai_probe, \ - .remove = meson_codec_glue_input_dai_remove, \ } #define TOACODEC_OUTPUT(xname, xid) { \ diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c index 579a04ad4d19..f7ef9aa1eed8 100644 --- a/sound/soc/meson/g12a-tohdmitx.c +++ b/sound/soc/meson/g12a-tohdmitx.c @@ -140,6 +140,8 @@ static const struct snd_soc_dapm_widget g12a_tohdmitx_widgets[] = { }; static const struct snd_soc_dai_ops g12a_tohdmitx_input_ops = { + .probe = meson_codec_glue_input_dai_probe, + .remove = meson_codec_glue_input_dai_remove, .hw_params = meson_codec_glue_input_hw_params, .set_fmt = meson_codec_glue_input_set_fmt, }; @@ -172,8 +174,6 @@ static const struct snd_soc_dai_ops g12a_tohdmitx_output_ops = { .id = (xid), \ .playback = TOHDMITX_STREAM(xname, "Playback", xfmt, xchmax), \ .ops = &g12a_tohdmitx_input_ops, \ - .probe = meson_codec_glue_input_dai_probe, \ - .remove = meson_codec_glue_input_dai_remove, \ } #define TOHDMITX_OUT(xname, xid, xfmt, xchmax) { \ diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index a1ed141b8795..abfaf3cdf5bb 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -340,6 +340,7 @@ static int mmp_sspa_probe(struct snd_soc_dai *dai) SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops mmp_sspa_dai_ops = { + .probe = mmp_sspa_probe, .startup = mmp_sspa_startup, .shutdown = mmp_sspa_shutdown, .trigger = mmp_sspa_trigger, @@ -350,7 +351,6 @@ static const struct snd_soc_dai_ops mmp_sspa_dai_ops = { }; static struct snd_soc_dai_driver mmp_sspa_dai = { - .probe = mmp_sspa_probe, .playback = { .channels_min = 1, .channels_max = 128, diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 430dd446321e..b70034c07eee 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -591,7 +591,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, if (ret < 0) { const struct pxa_ssp_clock_mode *m; - int ssacd, acds; + int ssacd; for (m = pxa_ssp_clock_modes; m->rate; m++) { if (m->rate == rate) @@ -601,12 +601,6 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, if (!m->rate) return -EINVAL; - acds = m->acds; - - /* The values in the table are for 16 bits */ - if (width == 32) - acds--; - ret = pxa_ssp_set_pll(priv, bclk); if (ret < 0) return ret; @@ -819,6 +813,8 @@ static int pxa_ssp_remove(struct snd_soc_dai *dai) #define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops pxa_ssp_dai_ops = { + .probe = pxa_ssp_probe, + .remove = pxa_ssp_remove, .startup = pxa_ssp_startup, .shutdown = pxa_ssp_shutdown, .trigger = pxa_ssp_trigger, @@ -830,8 +826,6 @@ static const struct snd_soc_dai_ops pxa_ssp_dai_ops = { }; static struct snd_soc_dai_driver pxa_ssp_dai = { - .probe = pxa_ssp_probe, - .remove = pxa_ssp_remove, .playback = { .channels_min = 1, .channels_max = 8, diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 3e4c70403672..437bfccd04f8 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -329,6 +329,8 @@ static int pxa2xx_i2s_remove(struct snd_soc_dai *dai) SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) static const struct snd_soc_dai_ops pxa_i2s_dai_ops = { + .probe = pxa2xx_i2s_probe, + .remove = pxa2xx_i2s_remove, .startup = pxa2xx_i2s_startup, .shutdown = pxa2xx_i2s_shutdown, .trigger = pxa2xx_i2s_trigger, @@ -338,8 +340,6 @@ static const struct snd_soc_dai_ops pxa_i2s_dai_ops = { }; static struct snd_soc_dai_driver pxa_i2s_dai = { - .probe = pxa2xx_i2s_probe, - .remove = pxa2xx_i2s_remove, .playback = { .channels_min = 2, .channels_max = 2, @@ -370,18 +370,11 @@ static const struct snd_soc_component_driver pxa_i2s_component = { static int pxa2xx_i2s_drv_probe(struct platform_device *pdev) { - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *res; - if (!res) { - dev_err(&pdev->dev, "missing MMIO resource\n"); - return -ENXIO; - } - - i2s_reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(i2s_reg_base)) { - dev_err(&pdev->dev, "ioremap failed\n"); + i2s_reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(i2s_reg_base)) return PTR_ERR(i2s_reg_base); - } pxa2xx_i2s_pcm_stereo_out.addr = res->start + SADR; pxa2xx_i2s_pcm_stereo_in.addr = res->start + SADR; diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index e54b8961112f..6de533d45e7d 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -44,6 +44,17 @@ struct apq8016_sbc_data { #define DEFAULT_MCLK_RATE 9600000 #define MI2S_BCLK_RATE 1536000 +static struct snd_soc_jack_pin apq8016_sbc_jack_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s) { struct snd_soc_dai *codec_dai; @@ -90,13 +101,15 @@ static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s) if (!pdata->jack_setup) { struct snd_jack *jack; - rval = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_HEADPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3 | - SND_JACK_BTN_4, - &pdata->jack); + rval = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4, + &pdata->jack, + apq8016_sbc_jack_pins, + ARRAY_SIZE(apq8016_sbc_jack_pins)); if (rval < 0) { dev_err(card->dev, "Unable to add Headphone Jack\n"); @@ -255,8 +268,14 @@ static void msm8916_qdsp6_add_ops(struct snd_soc_card *card) } } -static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { +static const struct snd_kcontrol_new apq8016_sbc_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; +static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("Handset Mic", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Secondary Mic", NULL), @@ -285,6 +304,8 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev) card->owner = THIS_MODULE; card->dapm_widgets = apq8016_sbc_dapm_widgets; card->num_dapm_widgets = ARRAY_SIZE(apq8016_sbc_dapm_widgets); + card->controls = apq8016_sbc_snd_controls; + card->num_controls = ARRAY_SIZE(apq8016_sbc_snd_controls); ret = qcom_snd_parse_of(card); if (ret) diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index abaf694ee9a3..f919d46e18ca 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -41,7 +41,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { .channels_min = 1, .channels_max = 8, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, [MI2S_SECONDARY] = { @@ -62,7 +61,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { .channels_min = 1, .channels_max = 8, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, [MI2S_TERTIARY] = { @@ -83,7 +81,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { .channels_min = 1, .channels_max = 8, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, [MI2S_QUATERNARY] = { @@ -119,7 +116,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { .channels_min = 1, .channels_max = 8, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, }; diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index dbdaaa85ce48..39571fed4001 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -404,18 +404,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, return 0; } -const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { - .set_sysclk = lpass_cpu_daiops_set_sysclk, - .startup = lpass_cpu_daiops_startup, - .shutdown = lpass_cpu_daiops_shutdown, - .hw_params = lpass_cpu_daiops_hw_params, - .trigger = lpass_cpu_daiops_trigger, - .prepare = lpass_cpu_daiops_prepare, -}; -EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); - -int lpass_cpu_pcm_new(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai) +static int lpass_cpu_daiops_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { int ret; struct snd_soc_dai_driver *drv = dai->driver; @@ -431,9 +420,8 @@ int lpass_cpu_pcm_new(struct snd_soc_pcm_runtime *rtd, return 0; } -EXPORT_SYMBOL_GPL(lpass_cpu_pcm_new); -int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai) +static int lpass_cpu_daiops_probe(struct snd_soc_dai *dai) { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; @@ -446,7 +434,29 @@ int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai) return ret; } -EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe); + +const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { + .probe = lpass_cpu_daiops_probe, + .set_sysclk = lpass_cpu_daiops_set_sysclk, + .startup = lpass_cpu_daiops_startup, + .shutdown = lpass_cpu_daiops_shutdown, + .hw_params = lpass_cpu_daiops_hw_params, + .trigger = lpass_cpu_daiops_trigger, + .prepare = lpass_cpu_daiops_prepare, +}; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); + +const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops2 = { + .pcm_new = lpass_cpu_daiops_pcm_new, + .probe = lpass_cpu_daiops_probe, + .set_sysclk = lpass_cpu_daiops_set_sysclk, + .startup = lpass_cpu_daiops_startup, + .shutdown = lpass_cpu_daiops_shutdown, + .hw_params = lpass_cpu_daiops_hw_params, + .trigger = lpass_cpu_daiops_trigger, + .prepare = lpass_cpu_daiops_prepare, +}; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops2); static int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component, const struct of_phandle_args *args, diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index ef8a7984f232..2c97f295e394 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -51,7 +51,6 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { .channels_min = 1, .channels_max = 8, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }; diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index ef5cb40b2d9b..990d7c33f90f 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -1219,7 +1219,8 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component) static int lpass_platform_copy(struct snd_soc_component *component, struct snd_pcm_substream *substream, int channel, - unsigned long pos, void __user *buf, unsigned long bytes) + unsigned long pos, struct iov_iter *buf, + unsigned long bytes) { struct snd_pcm_runtime *rt = substream->runtime; unsigned int dai_id = component->id; @@ -1230,16 +1231,16 @@ static int lpass_platform_copy(struct snd_soc_component *component, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (is_cdc_dma_port(dai_id)) { - ret = copy_from_user_toio(dma_buf, buf, bytes); + ret = copy_from_iter_toio(dma_buf, buf, bytes); } else { - if (copy_from_user((void __force *)dma_buf, buf, bytes)) + if (copy_from_iter((void __force *)dma_buf, bytes, buf) != bytes) ret = -EFAULT; } } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { if (is_cdc_dma_port(dai_id)) { - ret = copy_to_user_fromio(buf, dma_buf, bytes); + ret = copy_to_iter_fromio(buf, dma_buf, bytes); } else { - if (copy_to_user(buf, (void __force *)dma_buf, bytes)) + if (copy_to_iter((void __force *)dma_buf, bytes, buf) != bytes) ret = -EFAULT; } } @@ -1260,7 +1261,7 @@ static const struct snd_soc_component_driver lpass_component_driver = { .pcm_construct = lpass_platform_pcm_new, .suspend = lpass_platform_pcmops_suspend, .resume = lpass_platform_pcmops_resume, - .copy_user = lpass_platform_copy, + .copy = lpass_platform_copy, }; diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c index 56db852f4eab..d16c0d83aaad 100644 --- a/sound/soc/qcom/lpass-sc7180.c +++ b/sound/soc/qcom/lpass-sc7180.c @@ -43,7 +43,6 @@ static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = { .channels_min = 2, .channels_max = 2, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, { .id = MI2S_SECONDARY, @@ -57,9 +56,7 @@ static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = { .channels_min = 2, .channels_max = 2, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, - .ops = &asoc_qcom_lpass_cpu_dai_ops, - .pcm_new = lpass_cpu_pcm_new, + .ops = &asoc_qcom_lpass_cpu_dai_ops2, }, { .id = LPASS_DP_RX, .name = "Hdmi", diff --git a/sound/soc/qcom/lpass-sc7280.c b/sound/soc/qcom/lpass-sc7280.c index bcf18fe8e14d..6b2eb25ed939 100644 --- a/sound/soc/qcom/lpass-sc7280.c +++ b/sound/soc/qcom/lpass-sc7280.c @@ -38,7 +38,6 @@ static struct snd_soc_dai_driver sc7280_lpass_cpu_dai_driver[] = { .channels_min = 2, .channels_max = 2, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, { .id = MI2S_SECONDARY, @@ -52,7 +51,6 @@ static struct snd_soc_dai_driver sc7280_lpass_cpu_dai_driver[] = { .channels_min = 2, .channels_max = 2, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, { .id = LPASS_DP_RX, diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index dd78600fc7b0..bdfe66ec3314 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -402,10 +402,8 @@ int asoc_qcom_lpass_platform_register(struct platform_device *); int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev); void asoc_qcom_lpass_cpu_platform_shutdown(struct platform_device *pdev); int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev); -int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai); extern const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops; -int lpass_cpu_pcm_new(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai); +extern const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops2; extern const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops; #endif /* __LPASS_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index dbff55a97162..3faa7e0eb0dd 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -619,44 +619,6 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"RX_CODEC_DMA_RX_7 Playback", NULL, "RX_CODEC_DMA_RX_7"}, }; -static const struct snd_soc_dai_ops q6hdmi_ops = { - .prepare = q6afe_dai_prepare, - .hw_params = q6hdmi_hw_params, - .shutdown = q6afe_dai_shutdown, -}; - -static const struct snd_soc_dai_ops q6i2s_ops = { - .prepare = q6afe_dai_prepare, - .hw_params = q6i2s_hw_params, - .set_fmt = q6i2s_set_fmt, - .shutdown = q6afe_dai_shutdown, - .set_sysclk = q6afe_mi2s_set_sysclk, -}; - -static const struct snd_soc_dai_ops q6slim_ops = { - .prepare = q6afe_dai_prepare, - .hw_params = q6slim_hw_params, - .shutdown = q6afe_dai_shutdown, - .set_channel_map = q6slim_set_channel_map, -}; - -static const struct snd_soc_dai_ops q6tdm_ops = { - .prepare = q6afe_dai_prepare, - .shutdown = q6afe_dai_shutdown, - .set_sysclk = q6afe_mi2s_set_sysclk, - .set_tdm_slot = q6tdm_set_tdm_slot, - .set_channel_map = q6tdm_set_channel_map, - .hw_params = q6tdm_hw_params, -}; - -static const struct snd_soc_dai_ops q6dma_ops = { - .prepare = q6afe_dai_prepare, - .shutdown = q6afe_dai_shutdown, - .set_sysclk = q6afe_mi2s_set_sysclk, - .set_channel_map = q6dma_set_channel_map, - .hw_params = q6dma_hw_params, -}; - static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -682,6 +644,54 @@ static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops q6hdmi_ops = { + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .prepare = q6afe_dai_prepare, + .hw_params = q6hdmi_hw_params, + .shutdown = q6afe_dai_shutdown, +}; + +static const struct snd_soc_dai_ops q6i2s_ops = { + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .prepare = q6afe_dai_prepare, + .hw_params = q6i2s_hw_params, + .set_fmt = q6i2s_set_fmt, + .shutdown = q6afe_dai_shutdown, + .set_sysclk = q6afe_mi2s_set_sysclk, +}; + +static const struct snd_soc_dai_ops q6slim_ops = { + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .prepare = q6afe_dai_prepare, + .hw_params = q6slim_hw_params, + .shutdown = q6afe_dai_shutdown, + .set_channel_map = q6slim_set_channel_map, +}; + +static const struct snd_soc_dai_ops q6tdm_ops = { + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .prepare = q6afe_dai_prepare, + .shutdown = q6afe_dai_shutdown, + .set_sysclk = q6afe_mi2s_set_sysclk, + .set_tdm_slot = q6tdm_set_tdm_slot, + .set_channel_map = q6tdm_set_channel_map, + .hw_params = q6tdm_hw_params, +}; + +static const struct snd_soc_dai_ops q6dma_ops = { + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .prepare = q6afe_dai_prepare, + .shutdown = q6afe_dai_shutdown, + .set_sysclk = q6afe_mi2s_set_sysclk, + .set_channel_map = q6dma_set_channel_map, + .hw_params = q6dma_hw_params, +}; + static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { SND_SOC_DAPM_AIF_IN("HDMI_RX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("SLIMBUS_0_RX", NULL, 0, SND_SOC_NOPM, 0, 0), @@ -1041,8 +1051,6 @@ static int q6afe_dai_dev_probe(struct platform_device *pdev) dev_set_drvdata(dev, dai_data); of_q6afe_parse_dai_data(dev, dai_data); - cfg.probe = msm_dai_q6_dai_probe; - cfg.remove = msm_dai_q6_dai_remove; cfg.q6hdmi_ops = &q6hdmi_ops; cfg.q6slim_ops = &q6slim_ops; cfg.q6i2s_ops = &q6i2s_ops; diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 5d44d07acd69..2a2a5bd98110 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -27,7 +27,7 @@ struct apm_graph_mgmt_cmd { #define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8) -struct q6apm *g_apm; +static struct q6apm *g_apm; int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode) { diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 8ee40732ce9e..f486bd639b9f 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -126,7 +126,6 @@ int q6apm_graph_media_format_shmem(struct q6apm_graph *graph, struct audioreach_module_config *cfg); /* read/write related */ -int q6apm_send_eos_nowait(struct q6apm_graph *graph); int q6apm_read(struct q6apm_graph *graph); int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags); @@ -144,7 +143,6 @@ int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, /* Callback for graph specific */ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid); -void q6apm_set_fe_dai_ops(struct snd_soc_dai_driver *dai_drv); int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph); bool q6apm_is_adsp_ready(void); diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 5fc8088e63c8..fe0666e9fd23 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -1230,6 +1230,10 @@ static struct snd_soc_dai_driver q6asm_fe_dais_template[] = { Q6ASM_FEDAI_DRIVER(8), }; +static const struct snd_soc_dai_ops q6asm_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static int of_q6asm_parse_dai_data(struct device *dev, struct q6asm_dai_data *pdata) { @@ -1272,7 +1276,7 @@ static int of_q6asm_parse_dai_data(struct device *dev, dai_drv->playback = empty_stream; if (of_property_read_bool(node, "is-compress-dai")) - dai_drv->compress_new = snd_soc_new_compress; + dai_drv->ops = &q6asm_dai_ops; } return 0; diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index ac937a6bf909..4919001de08b 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -603,9 +603,6 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, int i; for (i = 0; i < ARRAY_SIZE(q6dsp_audio_fe_dais); i++) { - q6dsp_audio_fe_dais[i].probe = cfg->probe; - q6dsp_audio_fe_dais[i].remove = cfg->remove; - switch (q6dsp_audio_fe_dais[i].id) { case HDMI_RX: case DISPLAY_PORT_RX: diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c index f5f7c64b23a2..57c5f35dfcc5 100644 --- a/sound/soc/qcom/sc7180.c +++ b/sound/soc/qcom/sc7180.c @@ -42,6 +42,17 @@ static void sc7180_jack_free(struct snd_jack *jack) snd_soc_component_set_jack(component, NULL, NULL); } +static struct snd_soc_jack_pin sc7180_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -51,13 +62,14 @@ static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd) struct snd_jack *jack; int rval; - rval = snd_soc_card_jack_new( - card, "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_HEADPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pdata->hs_jack); + rval = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &pdata->hs_jack, + sc7180_jack_pins, + ARRAY_SIZE(sc7180_jack_pins)); if (rval < 0) { dev_err(card->dev, "Unable to add Headset Jack\n"); @@ -297,6 +309,11 @@ static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), }; +static const struct snd_kcontrol_new sc7180_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = { SND_SOC_DAPM_MIC("DMIC", NULL), }; @@ -320,6 +337,11 @@ static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = { SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control), }; +static const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = { {"Dmic Mux", "Front Mic", "DMIC"}, {"Dmic Mux", "Rear Mic", "DMIC"}, @@ -348,10 +370,14 @@ static int sc7180_snd_platform_probe(struct platform_device *pdev) card->dev = dev; card->dapm_widgets = sc7180_snd_widgets; card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets); + card->controls = sc7180_snd_controls; + card->num_controls = ARRAY_SIZE(sc7180_snd_controls); if (of_property_read_bool(dev->of_node, "dmic-gpios")) { card->dapm_widgets = sc7180_snd_dual_mic_widgets, card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets), + card->controls = sc7180_snd_dual_mic_controls, + card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls), card->dapm_routes = sc7180_snd_dual_mic_audio_route, card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route), data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW); diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c index 787dd49e03f6..43010e4e2242 100644 --- a/sound/soc/qcom/sc7280.c +++ b/sound/soc/qcom/sc7280.c @@ -43,6 +43,17 @@ static void sc7280_jack_free(struct snd_jack *jack) snd_soc_component_set_jack(component, NULL, NULL); } +static struct snd_soc_jack_pin sc7280_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -54,13 +65,15 @@ static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd) int rval, i; if (!pdata->jack_setup) { - rval = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - 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, - &pdata->hs_jack); + rval = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + 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, + &pdata->hs_jack, + sc7280_jack_pins, + ARRAY_SIZE(sc7280_jack_pins)); if (rval < 0) { dev_err(card->dev, "Unable to add Headset Jack\n"); @@ -361,6 +374,11 @@ static const struct snd_soc_dapm_widget sc7280_snd_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), }; +static const struct snd_kcontrol_new sc7280_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + static int sc7280_snd_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -396,6 +414,8 @@ static int sc7280_snd_platform_probe(struct platform_device *pdev) card->dapm_widgets = sc7280_snd_widgets; card->num_dapm_widgets = ARRAY_SIZE(sc7280_snd_widgets); + card->controls = sc7280_snd_controls; + card->num_controls = ARRAY_SIZE(sc7280_snd_controls); ret = qcom_snd_parse_of(card); if (ret) diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 02612af714a8..29d23fe5dfa2 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -42,6 +42,17 @@ struct sdm845_snd_data { struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; }; +static struct snd_soc_jack_pin sdm845_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream, @@ -242,12 +253,14 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) if (!pdata->jack_setup) { - rval = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_HEADPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pdata->jack); + rval = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &pdata->jack, + sdm845_jack_pins, + ARRAY_SIZE(sdm845_jack_pins)); if (rval < 0) { dev_err(card->dev, "Unable to add Headphone Jack\n"); @@ -539,6 +552,11 @@ static const struct snd_soc_dapm_widget sdm845_snd_widgets[] = { SND_SOC_DAPM_MIC("Int Mic", NULL), }; +static const struct snd_kcontrol_new sdm845_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + static void sdm845_add_ops(struct snd_soc_card *card) { struct snd_soc_dai_link *link; @@ -572,6 +590,8 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) card->driver_name = DRIVER_NAME; card->dapm_widgets = sdm845_snd_widgets; card->num_dapm_widgets = ARRAY_SIZE(sdm845_snd_widgets); + card->controls = sdm845_snd_controls; + card->num_controls = ARRAY_SIZE(sdm845_snd_controls); card->dev = dev; card->owner = THIS_MODULE; dev_set_drvdata(dev, card); diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index 5e52e9d60d44..0f704d22d21b 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -41,13 +41,17 @@ static struct snd_soc_jack_pin rockchip_sound_jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, - + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_LINE("HDMI", NULL), }; @@ -56,6 +60,7 @@ static const struct snd_kcontrol_new rockchip_controls[] = { SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("Speakers"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), SOC_DAPM_PIN_SWITCH("Int Mic"), SOC_DAPM_PIN_SWITCH("HDMI"), }; diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 575a0b9b01e9..834fbb5cf810 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -539,6 +539,7 @@ static int rockchip_i2s_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = { + .probe = rockchip_i2s_dai_probe, .hw_params = rockchip_i2s_hw_params, .set_bclk_ratio = rockchip_i2s_set_bclk_ratio, .set_sysclk = rockchip_i2s_set_sysclk, @@ -547,7 +548,6 @@ static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = { }; static struct snd_soc_dai_driver rockchip_i2s_dai = { - .probe = rockchip_i2s_dai_probe, .ops = &rockchip_i2s_dai_ops, .symmetric_rate = 1, }; diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 166257c6ae14..d3700f3c98e6 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -1114,6 +1114,7 @@ static int rockchip_i2s_tdm_set_bclk_ratio(struct snd_soc_dai *dai, } static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = { + .probe = rockchip_i2s_tdm_dai_probe, .hw_params = rockchip_i2s_tdm_hw_params, .set_bclk_ratio = rockchip_i2s_tdm_set_bclk_ratio, .set_sysclk = rockchip_i2s_tdm_set_sysclk, @@ -1324,7 +1325,6 @@ static const struct of_device_id rockchip_i2s_tdm_match[] = { }; static const struct snd_soc_dai_driver i2s_tdm_dai = { - .probe = rockchip_i2s_tdm_dai_probe, .ops = &rockchip_i2s_tdm_dai_ops, }; diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index 52f9aae60be8..4756cfc23218 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -379,6 +379,7 @@ static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = { + .probe = rockchip_pdm_dai_probe, .set_fmt = rockchip_pdm_set_fmt, .trigger = rockchip_pdm_trigger, .hw_params = rockchip_pdm_hw_params, @@ -391,7 +392,6 @@ static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = { SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_driver rockchip_pdm_dai = { - .probe = rockchip_pdm_dai_probe, .capture = { .stream_name = "Capture", .channels_min = 2, @@ -584,7 +584,7 @@ static int rockchip_pdm_probe(struct platform_device *pdev) match = of_match_device(rockchip_pdm_match, &pdev->dev); if (match) - pdm->version = (enum rk_pdm_version)match->data; + pdm->version = (uintptr_t)match->data; if (pdm->version == RK_PDM_RK3308) { pdm->reset = devm_reset_control_get(&pdev->dev, "pdm-m"); diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index e73a342b7953..ef9fdf0386cb 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -22,6 +22,16 @@ #define DRV_NAME "rockchip-snd-rt5645" static struct snd_soc_jack headset_jack; +static struct snd_soc_jack_pin headset_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; static const struct snd_soc_dapm_widget rk_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), @@ -103,11 +113,13 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime) int ret; /* Enable Headset and 4 Buttons Jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &headset_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); if (ret) { dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret); return ret; diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 0b73fe94e4bb..1a24b78e9e02 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -202,12 +202,12 @@ static int rk_spdif_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops rk_spdif_dai_ops = { + .probe = rk_spdif_dai_probe, .hw_params = rk_spdif_hw_params, .trigger = rk_spdif_trigger, }; static struct snd_soc_dai_driver rk_spdif_dai = { - .probe = rk_spdif_dai_probe, .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 7492bb41456c..dd3cd2c9644a 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -620,10 +620,14 @@ static int aries_audio_probe(struct platform_device *pdev) /* Update card-name if provided through DT, else use default name */ snd_soc_of_parse_card_name(card, "model"); - ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); if (ret < 0) { - dev_err(dev, "Audio routing invalid/unspecified\n"); - return ret; + /* Backwards compatible way */ + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) { + dev_err(dev, "Audio routing invalid/unspecified\n"); + return ret; + } } aries_dai[1].dai_fmt = priv->variant->modem_dai_fmt; diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f3d98abd5f0d..3af48c9b5ab7 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1120,6 +1120,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops samsung_i2s_dai_ops = { + .probe = samsung_i2s_dai_probe, + .remove = samsung_i2s_dai_remove, .trigger = i2s_trigger, .hw_params = i2s_hw_params, .set_fmt = i2s_set_fmt, @@ -1188,9 +1190,6 @@ static int i2s_alloc_dais(struct samsung_i2s_priv *priv, for (i = 0; i < num_dais; i++) { dai_drv = &priv->dai_drv[i]; - dai_drv->probe = samsung_i2s_dai_probe; - dai_drv->remove = samsung_i2s_dai_remove; - dai_drv->symmetric_rate = 1; dai_drv->ops = &samsung_i2s_dai_ops; diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 5d8118e69359..fafadcef234e 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -225,12 +225,15 @@ static int bbclk_ev(struct snd_soc_dapm_widget *w, } static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("WM1250 Input"), SOC_DAPM_PIN_SWITCH("WM1250 Output"), }; static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_HP("Headset Mic", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), SND_SOC_DAPM_MIC("DMIC", NULL), @@ -255,6 +258,16 @@ static const struct snd_soc_dapm_route audio_paths[] = { }; static struct snd_soc_jack littlemill_headset; +static struct snd_soc_jack_pin littlemill_headset_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; static int littlemill_late_probe(struct snd_soc_card *card) { @@ -281,12 +294,14 @@ static int littlemill_late_probe(struct snd_soc_card *card) if (ret < 0) return ret; - 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); + ret = snd_soc_card_jack_new_pins(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, + littlemill_headset_pins, + ARRAY_SIZE(littlemill_headset_pins)); if (ret) return ret; diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 106770be6fc5..a79df871ea13 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -22,12 +22,16 @@ static struct snd_soc_jack lowland_headset; static struct snd_soc_jack_pin lowland_headset_pins[] = { { .pin = "Headphone", - .mask = SND_JACK_HEADPHONE | SND_JACK_LINEOUT, + .mask = SND_JACK_HEADPHONE, }, { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) @@ -140,11 +144,13 @@ static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("WM1250 Input"), SOC_DAPM_PIN_SWITCH("WM1250 Output"), SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SPK("Main Speaker", NULL), diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index 6931b9a45b3e..2ec7e16ddfa2 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -38,6 +38,17 @@ struct midas_priv { struct snd_soc_jack headset_jack; }; +static struct snd_soc_jack_pin headset_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate) { struct snd_soc_card *card = rtd->card; @@ -261,6 +272,7 @@ static const struct snd_soc_dapm_widget midas_dapm_widgets[] = { SND_SOC_DAPM_LINE("HDMI", NULL), SND_SOC_DAPM_LINE("FM In", midas_fm_set), + SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Main Mic", midas_mic_bias), SND_SOC_DAPM_MIC("Sub Mic", midas_submic_bias), @@ -305,11 +317,13 @@ static int midas_late_probe(struct snd_soc_card *card) return ret; } - 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, - &priv->headset_jack); + ret = snd_soc_card_jack_new_pins(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, + &priv->headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); if (ret) return ret; @@ -462,10 +476,14 @@ static int midas_probe(struct platform_device *pdev) return ret; } - ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); if (ret < 0) { - dev_err(dev, "Audio routing invalid/unspecified\n"); - return ret; + /* Backwards compatible way */ + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) { + dev_err(dev, "Audio routing invalid/unspecified\n"); + return ret; + } } cpu = of_get_child_by_name(dev->of_node, "cpu"); diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index a5442592bde4..c93cb5a86426 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -223,19 +223,20 @@ static int odroid_audio_probe(struct platform_device *pdev) if (ret < 0) return ret; - if (of_property_read_bool(dev->of_node, "samsung,audio-widgets")) { + if (of_property_present(dev->of_node, "samsung,audio-widgets")) { ret = snd_soc_of_parse_audio_simple_widgets(card, "samsung,audio-widgets"); if (ret < 0) return ret; } - if (of_property_read_bool(dev->of_node, "samsung,audio-routing")) { - ret = snd_soc_of_parse_audio_routing(card, - "samsung,audio-routing"); - if (ret < 0) - return ret; - } + ret = 0; + if (of_property_present(dev->of_node, "audio-routing")) + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + else if (of_property_present(dev->of_node, "samsung,audio-routing")) + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) + return ret; card->dai_link = odroid_card_dais; card->num_links = ARRAY_SIZE(odroid_card_dais); diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index 335fe5cb9cfc..d2cdc5c8e05b 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -432,14 +432,6 @@ static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai, return 0; } -static const struct snd_soc_dai_ops s3c_pcm_dai_ops = { - .set_sysclk = s3c_pcm_set_sysclk, - .set_clkdiv = s3c_pcm_set_clkdiv, - .trigger = s3c_pcm_trigger, - .hw_params = s3c_pcm_hw_params, - .set_fmt = s3c_pcm_set_fmt, -}; - static int s3c_pcm_dai_probe(struct snd_soc_dai *dai) { struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(dai); @@ -449,11 +441,19 @@ static int s3c_pcm_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops s3c_pcm_dai_ops = { + .probe = s3c_pcm_dai_probe, + .set_sysclk = s3c_pcm_set_sysclk, + .set_clkdiv = s3c_pcm_set_clkdiv, + .trigger = s3c_pcm_trigger, + .hw_params = s3c_pcm_hw_params, + .set_fmt = s3c_pcm_set_fmt, +}; + #define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000 #define S3C_PCM_DAI_DECLARE \ .symmetric_rate = 1, \ - .probe = s3c_pcm_dai_probe, \ .ops = &s3c_pcm_dai_ops, \ .playback = { \ .channels_min = 2, \ diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index d611ec9e5325..5ebf17f3de1e 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -523,10 +523,14 @@ static int tm2_probe(struct platform_device *pdev) return ret; } - ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); if (ret < 0) { - dev_err(dev, "Audio routing is not specified or invalid\n"); - return ret; + /* Backwards compatible way */ + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) { + dev_err(dev, "Audio routing is not specified or invalid\n"); + return ret; + } } card->aux_dev[0].dlc.of_node = of_parse_phandle(dev->of_node, diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 0b8926600d90..230c48648af3 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -358,8 +358,6 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) ckr = 0x80000000; /* BRGB output = 48kHz */ rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr); - rsnd_mod_write(adg_mod, BRRA, adg->brga); - rsnd_mod_write(adg_mod, BRRB, adg->brgb); dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n", (ckr) ? 'B' : 'A', @@ -372,9 +370,16 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) { struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct clk *clk; int i; + if (enable) { + rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr); + rsnd_mod_write(adg_mod, BRRA, adg->brga); + rsnd_mod_write(adg_mod, BRRB, adg->brgb); + } + for_each_rsnd_clkin(clk, adg, i) { if (enable) { clk_prepare_enable(clk); @@ -485,12 +490,12 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) struct device_node *np = dev->of_node; struct property *prop; u32 ckr, brgx, brga, brgb; - u32 rate, div; u32 req_rate[ADG_HZ_SIZE] = {}; uint32_t count = 0; unsigned long req_Hz[ADG_HZ_SIZE]; int clkout_size; int i, req_size; + int approximate = 0; const char *parent_clk_name = NULL; const char * const *clkout_name; int brg_table[] = { @@ -501,8 +506,8 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) }; ckr = 0; - brga = 2; /* default 1/6 */ - brgb = 2; /* default 1/6 */ + brga = 0xff; /* default */ + brgb = 0xff; /* default */ /* * ADG supports BRRA/BRRB output only @@ -537,17 +542,41 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) * rsnd_adg_ssi_clk_try_start() * rsnd_ssi_master_clk_start() */ + + /* + * [APPROXIMATE] + * + * clk_i (internal clock) can't create accurate rate, it will be approximate rate. + * + * <Note> + * + * clk_i needs x2 of required maximum rate. + * see + * - Minimum division of BRRA/BRRB + * - rsnd_ssi_clk_query() + * + * Sample Settings for TDM 8ch, 32bit width + * + * 8(ch) x 32(bit) x 44100(Hz) x 2<Note> = 22579200 + * 8(ch) x 32(bit) x 48000(Hz) x 2<Note> = 24576000 + * + * clock-frequency = <22579200 24576000>; + */ for_each_rsnd_clkin(clk, adg, i) { + u32 rate, div; + rate = clk_get_rate(clk); if (0 == rate) /* not used */ continue; /* BRGA */ - if (!adg->brg_rate[ADG_HZ_441] && (0 == rate % 44100)) { - div = 6; - if (req_Hz[ADG_HZ_441]) - div = rate / req_Hz[ADG_HZ_441]; + + if (i == CLKI) + /* see [APPROXIMATE] */ + rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_441]) * req_Hz[ADG_HZ_441]; + if (!adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441] && (0 == rate % 44100)) { + div = rate / req_Hz[ADG_HZ_441]; brgx = rsnd_adg_calculate_brgx(div); if (BRRx_MASK(brgx) == brgx) { brga = brgx; @@ -555,14 +584,18 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) ckr |= brg_table[i] << 20; if (req_Hz[ADG_HZ_441]) parent_clk_name = __clk_get_name(clk); + if (i == CLKI) + approximate = 1; } } /* BRGB */ - if (!adg->brg_rate[ADG_HZ_48] && (0 == rate % 48000)) { - div = 6; - if (req_Hz[ADG_HZ_48]) - div = rate / req_Hz[ADG_HZ_48]; + + if (i == CLKI) + /* see [APPROXIMATE] */ + rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_48]) * req_Hz[ADG_HZ_48]; + if (!adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48] && (0 == rate % 48000)) { + div = rate / req_Hz[ADG_HZ_48]; brgx = rsnd_adg_calculate_brgx(div); if (BRRx_MASK(brgx) == brgx) { brgb = brgx; @@ -570,10 +603,19 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) ckr |= brg_table[i] << 16; if (req_Hz[ADG_HZ_48]) parent_clk_name = __clk_get_name(clk); + if (i == CLKI) + approximate = 1; } } } + if (!(adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48]) && + !(adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441])) + goto rsnd_adg_get_clkout_end; + + if (approximate) + dev_info(dev, "It uses CLK_I as approximate rate"); + clkout_name = clkout_name_gen2; clkout_size = ARRAY_SIZE(clkout_name_gen2); if (rsnd_is_gen4(priv)) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 6a522e6dd85a..e29c2fee9521 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1085,17 +1085,6 @@ static u64 rsnd_soc_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; -static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { - .startup = rsnd_soc_dai_startup, - .shutdown = rsnd_soc_dai_shutdown, - .trigger = rsnd_soc_dai_trigger, - .set_fmt = rsnd_soc_dai_set_fmt, - .set_tdm_slot = rsnd_soc_set_dai_tdm_slot, - .prepare = rsnd_soc_dai_prepare, - .auto_selectable_formats = rsnd_soc_dai_formats, - .num_auto_selectable_formats = ARRAY_SIZE(rsnd_soc_dai_formats), -}; - static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct device_node *dai_np) @@ -1260,13 +1249,13 @@ int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name return i; } -static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, - int *is_graph) +static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) { struct device *dev = rsnd_priv_to_dev(priv); struct device_node *np = dev->of_node; - struct device_node *dai_node; - struct device_node *ret; + struct device_node *ports, *node; + int nr = 0; + int i = 0; *is_graph = 0; @@ -1274,26 +1263,53 @@ static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, * parse both previous dai (= rcar_sound,dai), and * graph dai (= ports/port) */ - dai_node = of_get_child_by_name(np, RSND_NODE_DAI); - if (dai_node) { - ret = dai_node; - goto of_node_compatible; - } - ret = np; + /* + * Simple-Card + */ + node = of_get_child_by_name(np, RSND_NODE_DAI); + if (!node) + goto audio_graph; - dai_node = of_graph_get_next_endpoint(np, NULL); - if (dai_node) - goto of_node_graph; + of_node_put(node); - return NULL; + for_each_child_of_node(np, node) { + if (!of_node_name_eq(node, RSND_NODE_DAI)) + continue; + + priv->component_dais[i] = of_get_child_count(node); + nr += priv->component_dais[i]; + i++; + if (i >= RSND_MAX_COMPONENT) { + dev_info(dev, "reach to max component\n"); + of_node_put(node); + break; + } + } + + return nr; + +audio_graph: + /* + * Audio-Graph-Card + */ + for_each_child_of_node(np, ports) { + if (!of_node_name_eq(ports, "ports") && + !of_node_name_eq(ports, "port")) + continue; + priv->component_dais[i] = of_graph_get_endpoint_count(ports); + nr += priv->component_dais[i]; + i++; + if (i >= RSND_MAX_COMPONENT) { + dev_info(dev, "reach to max component\n"); + of_node_put(node); + break; + } + } -of_node_graph: *is_graph = 1; -of_node_compatible: - of_node_put(dai_node); - return ret; + return nr; } @@ -1328,8 +1344,7 @@ static int rsnd_preallocate_pages(struct snd_soc_pcm_runtime *rtd, return 0; } -static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai) +static int rsnd_soc_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); int ret; @@ -1355,8 +1370,22 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd, return 0; } +static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { + .pcm_new = rsnd_soc_dai_pcm_new, + .startup = rsnd_soc_dai_startup, + .shutdown = rsnd_soc_dai_shutdown, + .trigger = rsnd_soc_dai_trigger, + .set_fmt = rsnd_soc_dai_set_fmt, + .set_tdm_slot = rsnd_soc_set_dai_tdm_slot, + .prepare = rsnd_soc_dai_prepare, + .auto_selectable_formats = rsnd_soc_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(rsnd_soc_dai_formats), +}; + static void __rsnd_dai_probe(struct rsnd_priv *priv, struct device_node *dai_np, + struct device_node *node_np, + uint32_t node_arg, int dai_i) { struct rsnd_dai_stream *io_playback; @@ -1374,10 +1403,16 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); + /* for multi Component */ + rdai->dai_args.np = node_np; + rdai->dai_args.args_count = 1; + rdai->dai_args.args[0] = node_arg; + rdai->priv = priv; drv->name = rdai->name; drv->ops = &rsnd_soc_dai_ops; - drv->pcm_new = rsnd_pcm_new; + drv->id = dai_i; + drv->dai_args = &rdai->dai_args; io_playback->rdai = rdai; io_capture->rdai = rdai; @@ -1441,21 +1476,15 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, static int rsnd_dai_probe(struct rsnd_priv *priv) { - struct device_node *dai_node; - struct device_node *dai_np; struct snd_soc_dai_driver *rdrv; struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np = dev->of_node; struct rsnd_dai *rdai; - int nr; + int nr = 0; int is_graph; int dai_i; - dai_node = rsnd_dai_of_node(priv, &is_graph); - if (is_graph) - nr = of_graph_get_endpoint_count(dai_node); - else - nr = of_get_child_count(dai_node); - + nr = rsnd_dai_of_node(priv, &is_graph); if (!nr) return -EINVAL; @@ -1473,26 +1502,42 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) */ dai_i = 0; if (is_graph) { - for_each_endpoint_of_node(dai_node, dai_np) { - __rsnd_dai_probe(priv, dai_np, dai_i); - if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { - rdai = rsnd_rdai_get(priv, dai_i); - - rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); - rsnd_parse_connect_graph(priv, &rdai->capture, dai_np); + struct device_node *ports; + struct device_node *dai_np; + + for_each_child_of_node(np, ports) { + if (!of_node_name_eq(ports, "ports") && + !of_node_name_eq(ports, "port")) + continue; + for_each_endpoint_of_node(ports, dai_np) { + __rsnd_dai_probe(priv, dai_np, dai_np, 0, dai_i); + if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { + rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); + rsnd_parse_connect_graph(priv, &rdai->capture, dai_np); + } + dai_i++; } - dai_i++; } } else { - for_each_child_of_node(dai_node, dai_np) { - __rsnd_dai_probe(priv, dai_np, dai_i); - if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { - rdai = rsnd_rdai_get(priv, dai_i); + struct device_node *node; + struct device_node *dai_np; + + for_each_child_of_node(np, node) { + if (!of_node_name_eq(node, RSND_NODE_DAI)) + continue; - rsnd_parse_connect_simple(priv, &rdai->playback, dai_np); - rsnd_parse_connect_simple(priv, &rdai->capture, dai_np); + for_each_child_of_node(node, dai_np) { + __rsnd_dai_probe(priv, dai_np, np, dai_i, dai_i); + if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { + rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_simple(priv, &rdai->playback, dai_np); + rsnd_parse_connect_simple(priv, &rdai->capture, dai_np); + } + dai_i++; } - dai_i++; } } @@ -1922,6 +1967,7 @@ static int rsnd_probe(struct platform_device *pdev) rsnd_dai_probe, }; int ret, i; + int ci; /* * init priv data @@ -1958,11 +2004,18 @@ static int rsnd_probe(struct platform_device *pdev) /* * asoc register */ - ret = devm_snd_soc_register_component(dev, &rsnd_soc_component, - priv->daidrv, rsnd_rdai_nr(priv)); - if (ret < 0) { - dev_err(dev, "cannot snd dai register\n"); - goto exit_snd_probe; + ci = 0; + for (i = 0; priv->component_dais[i] > 0; i++) { + int nr = priv->component_dais[i]; + + ret = devm_snd_soc_register_component(dev, &rsnd_soc_component, + priv->daidrv + ci, nr); + if (ret < 0) { + dev_err(dev, "cannot snd component register\n"); + goto exit_snd_probe; + } + + ci += nr; } pm_runtime_enable(dev); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 239705d52517..43c0d675cc34 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -545,6 +545,7 @@ struct rsnd_dai { struct rsnd_dai_stream capture; struct rsnd_priv *priv; struct snd_pcm_hw_constraint_list constraint; + struct of_phandle_args dai_args; int max_channels; /* 2ch - 16ch */ int ssi_lane; /* 1lane - 4lane */ @@ -702,6 +703,9 @@ struct rsnd_priv { struct snd_soc_dai_driver *daidrv; struct rsnd_dai *rdai; int rdai_nr; + +#define RSND_MAX_COMPONENT 3 + int component_dais[RSND_MAX_COMPONENT]; }; #define rsnd_priv_to_pdev(priv) ((priv)->pdev) diff --git a/sound/soc/soc-acpi.c b/sound/soc/soc-acpi.c index 142476f1396f..6d693b2ad5a3 100644 --- a/sound/soc/soc-acpi.c +++ b/sound/soc/soc-acpi.c @@ -125,5 +125,78 @@ struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) } EXPORT_SYMBOL_GPL(snd_soc_acpi_codec_list); +#define SDW_CODEC_ADR_MASK(_adr) ((_adr) & (SDW_DISCO_LINK_ID_MASK | SDW_VERSION_MASK | \ + SDW_MFG_ID_MASK | SDW_PART_ID_MASK)) + +/* Check if all Slaves defined on the link can be found */ +bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev, + const struct snd_soc_acpi_link_adr *link, + struct sdw_extended_slave_id *ids, + int num_slaves) +{ + unsigned int part_id, link_id, unique_id, mfg_id, version; + int i, j, k; + + for (i = 0; i < link->num_adr; i++) { + u64 adr = link->adr_d[i].adr; + int reported_part_count = 0; + + mfg_id = SDW_MFG_ID(adr); + part_id = SDW_PART_ID(adr); + link_id = SDW_DISCO_LINK_ID(adr); + version = SDW_VERSION(adr); + + for (j = 0; j < num_slaves; j++) { + /* find out how many identical parts were reported on that link */ + if (ids[j].link_id == link_id && + ids[j].id.part_id == part_id && + ids[j].id.mfg_id == mfg_id && + ids[j].id.sdw_version == version) + reported_part_count++; + } + + for (j = 0; j < num_slaves; j++) { + int expected_part_count = 0; + + if (ids[j].link_id != link_id || + ids[j].id.part_id != part_id || + ids[j].id.mfg_id != mfg_id || + ids[j].id.sdw_version != version) + continue; + + /* find out how many identical parts are expected */ + for (k = 0; k < link->num_adr; k++) { + u64 adr2 = link->adr_d[k].adr; + + if (SDW_CODEC_ADR_MASK(adr2) == SDW_CODEC_ADR_MASK(adr)) + expected_part_count++; + } + + if (reported_part_count == expected_part_count) { + /* + * we have to check unique id + * if there is more than one + * Slave on the link + */ + unique_id = SDW_UNIQUE_ID(adr); + if (reported_part_count == 1 || + ids[j].id.unique_id == unique_id) { + dev_dbg(dev, "found part_id %#x at link %d\n", part_id, link_id); + break; + } + } else { + dev_dbg(dev, "part_id %#x reported %d expected %d on link %d, skipping\n", + part_id, reported_part_count, expected_part_count, link_id); + } + } + if (j == num_slaves) { + dev_dbg(dev, "Slave part_id %#x not found\n", part_id); + return false; + } + } + return true; +} +EXPORT_SYMBOL_GPL(snd_soc_acpi_sdw_link_slaves_found); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("ALSA SoC ACPI module"); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 4356cc320fea..f18406dfa1e4 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -1052,9 +1052,9 @@ int snd_soc_pcm_component_sync_stop(struct snd_pcm_substream *substream) return 0; } -int snd_soc_pcm_component_copy_user(struct snd_pcm_substream *substream, - int channel, unsigned long pos, - void __user *buf, unsigned long bytes) +int snd_soc_pcm_component_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + struct iov_iter *buf, unsigned long bytes) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; @@ -1062,12 +1062,10 @@ int snd_soc_pcm_component_copy_user(struct snd_pcm_substream *substream, /* FIXME. it returns 1st copy now */ for_each_rtd_components(rtd, i, component) - if (component->driver->copy_user) - return soc_component_ret( - component, - component->driver->copy_user( - component, substream, channel, - pos, buf, bytes)); + if (component->driver->copy) + return soc_component_ret(component, + component->driver->copy(component, substream, + channel, pos, buf, bytes)); return -EINVAL; } diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 02fdb683f75f..b58921e7921f 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -193,6 +193,7 @@ open_err: snd_soc_dai_compr_shutdown(cpu_dai, cstream, 1); out: dpcm_path_put(&list); + snd_soc_dpcm_mutex_unlock(fe); be_err: fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; snd_soc_card_mutex_unlock(fe->card); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1a0bde23f5e6..cc442c52cdea 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -238,6 +238,81 @@ static inline void snd_soc_debugfs_exit(void) { } #endif +static int snd_soc_is_match_dai_args(struct of_phandle_args *args1, + struct of_phandle_args *args2) +{ + if (!args1 || !args2) + return 0; + + if (args1->np != args2->np) + return 0; + + for (int i = 0; i < args1->args_count; i++) + if (args1->args[i] != args2->args[i]) + return 0; + + return 1; +} + +static inline int snd_soc_dlc_component_is_empty(struct snd_soc_dai_link_component *dlc) +{ + return !(dlc->dai_args || dlc->name || dlc->of_node); +} + +static inline int snd_soc_dlc_component_is_invalid(struct snd_soc_dai_link_component *dlc) +{ + return (dlc->name && dlc->of_node); +} + +static inline int snd_soc_dlc_dai_is_empty(struct snd_soc_dai_link_component *dlc) +{ + return !(dlc->dai_args || dlc->dai_name); +} + +static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, + struct snd_soc_dai *dai) +{ + if (!dlc) + return 0; + + if (dlc->dai_args) + return snd_soc_is_match_dai_args(dai->driver->dai_args, dlc->dai_args); + + if (!dlc->dai_name) + return 1; + + /* see snd_soc_dai_name_get() */ + + if (strcmp(dlc->dai_name, dai->name) == 0) + return 1; + + if (dai->driver->name && + strcmp(dai->driver->name, dlc->dai_name) == 0) + return 1; + + if (dai->component->name && + strcmp(dlc->dai_name, dai->component->name) == 0) + return 1; + + return 0; +} + +const char *snd_soc_dai_name_get(struct snd_soc_dai *dai) +{ + /* see snd_soc_is_matching_dai() */ + if (dai->name) + return dai->name; + + if (dai->driver->name) + return dai->driver->name; + + if (dai->component->name) + return dai->component->name; + + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_name_get); + static int snd_soc_rtd_add_component(struct snd_soc_pcm_runtime *rtd, struct snd_soc_component *component) { @@ -734,6 +809,19 @@ static struct device_node return of_node; } +struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, struct of_phandle_args *args) +{ + struct of_phandle_args *ret = devm_kzalloc(dev, sizeof(*ret), GFP_KERNEL); + + if (!ret) + return NULL; + + *ret = *args; + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_copy_dai_args); + static int snd_soc_is_matching_component( const struct snd_soc_dai_link_component *dlc, struct snd_soc_component *component) @@ -743,6 +831,15 @@ static int snd_soc_is_matching_component( if (!dlc) return 0; + if (dlc->dai_args) { + struct snd_soc_dai *dai; + + for_each_component_dais(component, dai) + if (snd_soc_is_matching_dai(dlc, dai)) + return 1; + return 0; + } + component_of_node = soc_component_to_node(component); if (dlc->of_node && component_of_node != dlc->of_node) @@ -795,18 +892,11 @@ struct snd_soc_dai *snd_soc_find_dai( lockdep_assert_held(&client_mutex); /* Find CPU DAI from registered DAIs */ - for_each_component(component) { - if (!snd_soc_is_matching_component(dlc, component)) - continue; - for_each_component_dais(component, dai) { - if (dlc->dai_name && strcmp(dai->name, dlc->dai_name) - && (!dai->driver->name - || strcmp(dai->driver->name, dlc->dai_name))) - continue; - - return dai; - } - } + for_each_component(component) + if (snd_soc_is_matching_component(dlc, component)) + for_each_component_dais(component, dai) + if (snd_soc_is_matching_dai(dlc, dai)) + return dai; return NULL; } @@ -829,102 +919,100 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, struct snd_soc_dai_link *link) { int i; - struct snd_soc_dai_link_component *cpu, *codec, *platform; + struct snd_soc_dai_link_component *dlc; - for_each_link_codecs(link, i, codec) { + /* Codec check */ + for_each_link_codecs(link, i, dlc) { /* * Codec must be specified by 1 of name or OF node, * not both or neither. */ - if (!!codec->name == !!codec->of_node) { - dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", - link->name); - return -EINVAL; - } + if (snd_soc_dlc_component_is_invalid(dlc)) + goto component_invalid; + + if (snd_soc_dlc_component_is_empty(dlc)) + goto component_empty; /* Codec DAI name must be specified */ - if (!codec->dai_name) { - dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", - link->name); - return -EINVAL; - } + if (snd_soc_dlc_dai_is_empty(dlc)) + goto dai_empty; /* * Defer card registration if codec component is not added to * component list. */ - if (!soc_find_component(codec)) { - dev_dbg(card->dev, - "ASoC: codec component %s not found for link %s\n", - codec->name, link->name); - return -EPROBE_DEFER; - } + if (!soc_find_component(dlc)) + goto component_not_found; } - for_each_link_platforms(link, i, platform) { + /* Platform check */ + for_each_link_platforms(link, i, dlc) { /* * Platform may be specified by either name or OF node, but it * can be left unspecified, then no components will be inserted * in the rtdcom list */ - if (!!platform->name == !!platform->of_node) { - dev_err(card->dev, - "ASoC: Neither/both platform name/of_node are set for %s\n", - link->name); - return -EINVAL; - } + if (snd_soc_dlc_component_is_invalid(dlc)) + goto component_invalid; + + if (snd_soc_dlc_component_is_empty(dlc)) + goto component_empty; /* * Defer card registration if platform component is not added to * component list. */ - if (!soc_find_component(platform)) { - dev_dbg(card->dev, - "ASoC: platform component %s not found for link %s\n", - platform->name, link->name); - return -EPROBE_DEFER; - } + if (!soc_find_component(dlc)) + goto component_not_found; } - for_each_link_cpus(link, i, cpu) { + /* CPU check */ + for_each_link_cpus(link, i, dlc) { /* * CPU device may be specified by either name or OF node, but * can be left unspecified, and will be matched based on DAI * name alone.. */ - if (cpu->name && cpu->of_node) { - dev_err(card->dev, - "ASoC: Neither/both cpu name/of_node are set for %s\n", - link->name); - return -EINVAL; - } + if (snd_soc_dlc_component_is_invalid(dlc)) + goto component_invalid; - /* - * Defer card registration if cpu dai component is not added to - * component list. - */ - if ((cpu->of_node || cpu->name) && - !soc_find_component(cpu)) { - dev_dbg(card->dev, - "ASoC: cpu component %s not found for link %s\n", - cpu->name, link->name); - return -EPROBE_DEFER; - } - /* - * At least one of CPU DAI name or CPU device name/node must be - * specified - */ - if (!cpu->dai_name && - !(cpu->name || cpu->of_node)) { - dev_err(card->dev, - "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", - link->name); - return -EINVAL; + if (snd_soc_dlc_component_is_empty(dlc)) { + /* + * At least one of CPU DAI name or CPU device name/node must be specified + */ + if (snd_soc_dlc_dai_is_empty(dlc)) + goto component_dai_empty; + } else { + /* + * Defer card registration if Component is not added + */ + if (!soc_find_component(dlc)) + goto component_not_found; } } return 0; + +component_invalid: + dev_err(card->dev, "ASoC: Both Component name/of_node are set for %s\n", link->name); + return -EINVAL; + +component_empty: + dev_err(card->dev, "ASoC: Neither Component name/of_node are set for %s\n", link->name); + return -EINVAL; + +component_not_found: + dev_dbg(card->dev, "ASoC: Component %s not found for link %s\n", dlc->name, link->name); + return -EPROBE_DEFER; + +dai_empty: + dev_err(card->dev, "ASoC: DAI name is not set for %s\n", link->name); + return -EINVAL; + +component_dai_empty: + dev_err(card->dev, "ASoC: Neither DAI/Component name/of_node are set for %s\n", link->name); + return -EINVAL; } /** @@ -2930,6 +3018,14 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); +void snd_soc_dlc_use_cpu_as_platform(struct snd_soc_dai_link_component *platforms, + struct snd_soc_dai_link_component *cpus) +{ + platforms->of_node = cpus->of_node; + platforms->dai_args = cpus->dai_args; +} +EXPORT_SYMBOL_GPL(snd_soc_dlc_use_cpu_as_platform); + void snd_soc_of_parse_node_prefix(struct device_node *np, struct snd_soc_codec_conf *codec_conf, struct device_node *of_node, @@ -3235,11 +3331,12 @@ EXPORT_SYMBOL_GPL(snd_soc_get_stream_cpu); int snd_soc_get_dai_id(struct device_node *ep) { struct snd_soc_component *component; - struct snd_soc_dai_link_component dlc; + struct snd_soc_dai_link_component dlc = { + .of_node = of_graph_get_port_parent(ep), + }; int ret; - dlc.of_node = of_graph_get_port_parent(ep); - dlc.name = NULL; + /* * For example HDMI case, HDMI has video/sound port, * but ALSA SoC needs sound port number only. @@ -3264,8 +3361,6 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_ struct snd_soc_component *pos; int ret = -EPROBE_DEFER; - dlc->of_node = args->np; - mutex_lock(&client_mutex); for_each_component(pos) { struct device_node *component_of_node = soc_component_to_node(pos); @@ -3304,9 +3399,7 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_ id--; } - dlc->dai_name = dai->driver->name; - if (!dlc->dai_name) - dlc->dai_name = pos->name; + dlc->dai_name = snd_soc_dai_name_get(dai); } else if (ret) { /* * if another error than ENOTSUPP is returned go on and @@ -3319,6 +3412,10 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_ break; } + + if (ret == 0) + dlc->of_node = args->np; + mutex_unlock(&client_mutex); return ret; } @@ -3370,6 +3467,24 @@ int snd_soc_of_get_dai_name(struct device_node *of_node, } EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); +struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args) +{ + struct snd_soc_dai *dai; + struct snd_soc_component *component; + + mutex_lock(&client_mutex); + for_each_component(component) { + for_each_component_dais(component, dai) + if (snd_soc_is_match_dai_args(dai->driver->dai_args, dai_args)) + goto found; + } + dai = NULL; +found: + mutex_unlock(&client_mutex); + return dai; +} +EXPORT_SYMBOL_GPL(snd_soc_get_dai_via_args); + static void __snd_soc_of_put_component(struct snd_soc_dai_link_component *component) { if (component->of_node) { diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 02dd64dea179..3f33f0630ad8 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -424,6 +424,9 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai, { int ret = 0; + if (!snd_soc_dai_stream_valid(dai, substream->stream)) + return 0; + if (dai->driver->ops && dai->driver->ops->startup) ret = dai->driver->ops->startup(substream, dai); @@ -439,6 +442,9 @@ void snd_soc_dai_shutdown(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, int rollback) { + if (!snd_soc_dai_stream_valid(dai, substream->stream)) + return; + if (rollback && !soc_dai_mark_match(dai, substream, startup)) return; @@ -454,8 +460,9 @@ int snd_soc_dai_compress_new(struct snd_soc_dai *dai, struct snd_soc_pcm_runtime *rtd, int num) { int ret = -ENOTSUPP; - if (dai->driver->compress_new) - ret = dai->driver->compress_new(rtd, num); + if (dai->driver->ops && + dai->driver->ops->compress_new) + ret = dai->driver->ops->compress_new(rtd, num); return soc_dai_ret(dai, ret); } @@ -539,19 +546,20 @@ int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order) int i; for_each_rtd_dais(rtd, i, dai) { - if (dai->driver->probe_order != order) - continue; - if (dai->probed) continue; - if (dai->driver->probe) { - int ret = dai->driver->probe(dai); + if (dai->driver->ops) { + if (dai->driver->ops->probe_order != order) + continue; - if (ret < 0) - return soc_dai_ret(dai, ret); - } + if (dai->driver->ops->probe) { + int ret = dai->driver->ops->probe(dai); + if (ret < 0) + return soc_dai_ret(dai, ret); + } + } dai->probed = 1; } @@ -564,16 +572,19 @@ int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime *rtd, int order) int i, r, ret = 0; for_each_rtd_dais(rtd, i, dai) { - if (dai->driver->remove_order != order) + if (!dai->probed) continue; - if (dai->probed && - dai->driver->remove) { - r = dai->driver->remove(dai); - if (r < 0) - ret = r; /* use last error */ - } + if (dai->driver->ops) { + if (dai->driver->ops->remove_order != order) + continue; + if (dai->driver->ops->remove) { + r = dai->driver->ops->remove(dai); + if (r < 0) + ret = r; /* use last error */ + } + } dai->probed = 0; } @@ -586,8 +597,9 @@ int snd_soc_pcm_dai_new(struct snd_soc_pcm_runtime *rtd) int i; for_each_rtd_dais(rtd, i, dai) { - if (dai->driver->pcm_new) { - int ret = dai->driver->pcm_new(rtd, dai); + if (dai->driver->ops && + dai->driver->ops->pcm_new) { + int ret = dai->driver->ops->pcm_new(rtd, dai); if (ret < 0) return soc_dai_ret(dai, ret); } @@ -603,6 +615,8 @@ int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream) int i, ret; for_each_rtd_dais(rtd, i, dai) { + if (!snd_soc_dai_stream_valid(dai, substream->stream)) + continue; if (dai->driver->ops && dai->driver->ops->prepare) { ret = dai->driver->ops->prepare(substream, dai); @@ -619,6 +633,9 @@ static int soc_dai_trigger(struct snd_soc_dai *dai, { int ret = 0; + if (!snd_soc_dai_stream_valid(dai, substream->stream)) + return 0; + if (dai->driver->ops && dai->driver->ops->trigger) ret = dai->driver->ops->trigger(substream, cmd, dai); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 3091e8160bad..f07e83678373 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -916,6 +916,8 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, return -EINVAL; } } + if (w->no_wname_in_kcontrol_name) + wname_in_long_name = false; if (wname_in_long_name && kcname_in_long_name) { /* diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 3b99f619e37e..ff2166525dbc 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -287,32 +287,32 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( return snd_dmaengine_pcm_pointer(substream); } -static int dmaengine_copy_user(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - int channel, unsigned long hwoff, - void __user *buf, unsigned long bytes) +static int dmaengine_copy(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + struct iov_iter *buf, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; struct dmaengine_pcm *pcm = soc_component_to_pcm(component); int (*process)(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - void *buf, unsigned long bytes) = pcm->config->process; + struct iov_iter *buf, unsigned long bytes) = pcm->config->process; bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; void *dma_ptr = runtime->dma_area + hwoff + channel * (runtime->dma_bytes / runtime->channels); if (is_playback) - if (copy_from_user(dma_ptr, buf, bytes)) + if (copy_from_iter(dma_ptr, bytes, buf) != bytes) return -EFAULT; if (process) { - int ret = process(substream, channel, hwoff, (__force void *)buf, bytes); + int ret = process(substream, channel, hwoff, buf, bytes); if (ret < 0) return ret; } if (!is_playback) - if (copy_to_user(buf, dma_ptr, bytes)) + if (copy_to_iter(dma_ptr, bytes, buf) != bytes) return -EFAULT; return 0; @@ -337,7 +337,7 @@ static const struct snd_soc_component_driver dmaengine_pcm_component_process = { .hw_params = dmaengine_pcm_hw_params, .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, - .copy_user = dmaengine_copy_user, + .copy = dmaengine_copy, .pcm_construct = dmaengine_pcm_new, }; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index f951acb2ce36..b2cc13b9c77b 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -37,7 +37,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) struct snd_soc_jack_pin *pin; unsigned int sync = 0; - if (!jack) + if (!jack || !jack->jack) return; trace_snd_soc_jack_report(jack, mask, status); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 3aa6b988cb4b..eb0723876851 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2977,8 +2977,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.ioctl = snd_soc_pcm_component_ioctl; if (drv->sync_stop) rtd->ops.sync_stop = snd_soc_pcm_component_sync_stop; - if (drv->copy_user) - rtd->ops.copy_user = snd_soc_pcm_component_copy_user; + if (drv->copy) + rtd->ops.copy = snd_soc_pcm_component_copy; if (drv->page) rtd->ops.page = snd_soc_pcm_component_page; if (drv->mmap) diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index ad08d4f75a7b..2362c282ec8b 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1560,6 +1560,10 @@ static void set_dai_flags(struct snd_soc_dai_driver *dai_drv, 1 : 0; } +static const struct snd_soc_dai_ops tplg_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static int soc_tplg_dai_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { @@ -1601,7 +1605,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, } if (pcm->compress) - dai_drv->compress_new = snd_soc_new_compress; + dai_drv->ops = &tplg_dai_ops; /* pass control to component driver for optional further init */ ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL); @@ -2568,7 +2572,6 @@ EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load); /* remove dynamic controls from the component driver */ int snd_soc_tplg_component_remove(struct snd_soc_component *comp) { - struct snd_card *card = comp->card->snd_card; struct snd_soc_dobj *dobj, *next_dobj; int pass; @@ -2576,7 +2579,6 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp) for (pass = SOC_TPLG_PASS_END; pass >= SOC_TPLG_PASS_START; pass--) { /* remove mixer controls */ - down_write(&card->controls_rwsem); list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list, list) { @@ -2611,7 +2613,6 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp) break; } } - up_write(&card->controls_rwsem); } /* let caller know if FW can be freed when no objects are left */ diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index 1cb92d6030e3..f2faa08f0c0e 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -2,7 +2,7 @@ # This file is provided under a dual BSD/GPLv2 license. When using or # redistributing this file, you may do so under either license. # -# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. config SND_SOC_SOF_AMD_TOPLEVEL tristate "SOF support for AMD audio DSPs" @@ -21,6 +21,7 @@ config SND_SOC_SOF_AMD_COMMON select SND_SOC_SOF_PCI_DEV select SND_AMD_ACP_CONFIG select SND_SOC_SOF_XTENSA + select SND_SOC_SOF_ACP_PROBES select SND_SOC_ACPI if ACPI help This option is not user-selectable but automatically handled by @@ -33,6 +34,16 @@ config SND_SOC_SOF_AMD_RENOIR help Select this option for SOF support on AMD Renoir platform +config SND_SOC_SOF_AMD_VANGOGH + tristate "SOF support for VANGOGH" + depends on SND_SOC_SOF_PCI + select SND_SOC_SOF_AMD_COMMON + help + Select this option for SOF support + on AMD Vangogh platform. + Say Y if you want to enable SOF on Vangogh. + If unsure select "N". + config SND_SOC_SOF_AMD_REMBRANDT tristate "SOF support for REMBRANDT" depends on SND_SOC_SOF_PCI @@ -42,4 +53,11 @@ config SND_SOC_SOF_AMD_REMBRANDT Say Y if you want to enable SOF on Rembrandt. If unsure select "N". +config SND_SOC_SOF_ACP_PROBES + tristate + select SND_SOC_SOF_DEBUG_PROBES + help + This option is not user-selectable but automatically handled by + 'select' statements at a higher level + endif diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile index 5626d13b3e69..f3b375e67a6f 100644 --- a/sound/soc/sof/amd/Makefile +++ b/sound/soc/sof/amd/Makefile @@ -2,12 +2,15 @@ # This file is provided under a dual BSD/GPLv2 license. When using or # redistributing this file, you may do so under either license. # -# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o acp-trace.o acp-common.o +snd-sof-amd-acp-$(CONFIG_SND_SOC_SOF_ACP_PROBES) = acp-probes.o snd-sof-amd-renoir-objs := pci-rn.o renoir.o snd-sof-amd-rembrandt-objs := pci-rmb.o rembrandt.o +snd-sof-amd-vangogh-objs := pci-vangogh.o vangogh.o obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) +=snd-sof-amd-renoir.o obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) +=snd-sof-amd-rembrandt.o +obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) +=snd-sof-amd-vangogh.o diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c index df36b411a12e..3a0c7688dcfe 100644 --- a/sound/soc/sof/amd/acp-common.c +++ b/sound/soc/sof/amd/acp-common.c @@ -196,6 +196,10 @@ struct snd_sof_dsp_ops sof_acp_common_ops = { .dbg_dump = amd_sof_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, .dsp_arch_ops = &sof_xtensa_arch_ops, + + /* probe client device registation */ + .register_ipc_clients = acp_probes_register, + .unregister_ipc_clients = acp_probes_unregister, }; EXPORT_SYMBOL_NS(sof_acp_common_ops, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index 920155dee819..a913f1cc4c80 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -3,7 +3,7 @@ * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * - * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + * Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. * * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> */ @@ -49,27 +49,33 @@ #define ACP_CONTROL 0x1004 #define ACP3X_I2S_PIN_CONFIG 0x1400 +#define ACP5X_I2S_PIN_CONFIG 0x1400 #define ACP6X_I2S_PIN_CONFIG 0x1440 /* Registers offsets from ACP_PGFSM block */ #define ACP3X_PGFSM_BASE 0x141C +#define ACP5X_PGFSM_BASE 0x1424 #define ACP6X_PGFSM_BASE 0x1024 #define PGFSM_CONTROL_OFFSET 0x0 #define PGFSM_STATUS_OFFSET 0x4 #define ACP3X_CLKMUX_SEL 0x1424 +#define ACP5X_CLKMUX_SEL 0x142C #define ACP6X_CLKMUX_SEL 0x102C /* Registers from ACP_INTR block */ #define ACP3X_EXT_INTR_STAT 0x1808 +#define ACP5X_EXT_INTR_STAT 0x1808 #define ACP6X_EXT_INTR_STAT 0x1A0C #define ACP3X_DSP_SW_INTR_BASE 0x1814 +#define ACP5X_DSP_SW_INTR_BASE 0x1814 #define ACP6X_DSP_SW_INTR_BASE 0x1808 #define DSP_SW_INTR_CNTL_OFFSET 0x0 #define DSP_SW_INTR_STAT_OFFSET 0x4 #define DSP_SW_INTR_TRIG_OFFSET 0x8 #define ACP_ERROR_STATUS 0x18C4 #define ACP3X_AXI2DAGB_SEM_0 0x1880 +#define ACP5X_AXI2DAGB_SEM_0 0x1884 #define ACP6X_AXI2DAGB_SEM_0 0x1874 /* Registers from ACP_SHA block */ @@ -81,6 +87,7 @@ #define ACP_SHA_DMA_CMD_STS 0x1CC0 #define ACP_SHA_DMA_ERR_STATUS 0x1CC4 #define ACP_SHA_TRANSFER_BYTE_CNT 0x1CC8 +#define ACP_SHA_DMA_INCLUDE_HDR 0x1CCC #define ACP_SHA_PSP_ACK 0x1C74 #define ACP_SCRATCH_REG_0 0x10000 diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 8a0fc635a997..fcb54f545fea 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -155,6 +155,8 @@ out: irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; + const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); + struct acp_dev_data *adata = sdev->pdata->hw_pdata; unsigned int dsp_msg_write = sdev->debug_box.offset + offsetof(struct scratch_ipc_conf, sof_dsp_msg_write); unsigned int dsp_ack_write = sdev->debug_box.offset + @@ -168,6 +170,8 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) if ((status & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { snd_sof_dsp_panic(sdev, sdev->dsp_box.offset + sizeof(status), true); + status = 0; + acp_mailbox_write(sdev, sdev->dsp_box.offset, &status, sizeof(status)); return IRQ_HANDLED; } snd_sof_ipc_msgs_rx(sdev); @@ -197,9 +201,35 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) acp_mailbox_read(sdev, sdev->debug_box.offset, &status, sizeof(u32)); if ((status & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { snd_sof_dsp_panic(sdev, sdev->dsp_oops_offset, true); + status = 0; + acp_mailbox_write(sdev, sdev->debug_box.offset, &status, sizeof(status)); return IRQ_HANDLED; } + if (desc->probe_reg_offset) { + u32 val; + u32 posn; + + /* Probe register consists of two parts + * (0-30) bit has cumulative position value + * 31 bit is a synchronization flag between DSP and CPU + * for the position update + */ + val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->probe_reg_offset); + if (val & PROBE_STATUS_BIT) { + posn = val & ~PROBE_STATUS_BIT; + if (adata->probe_stream) { + /* Probe related posn value is of 31 bits limited to 2GB + * once wrapped DSP won't send posn interrupt. + */ + adata->probe_stream->cstream_posn = posn; + snd_compr_fragment_elapsed(adata->probe_stream->cstream); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->probe_reg_offset, posn); + ipc_irq = true; + } + } + } + if (!ipc_irq) dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n"); diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index a4bce5a3ae48..a427673cfb03 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -3,7 +3,7 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2021 Advanced Micro Devices, Inc. +// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. // // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> @@ -158,7 +158,11 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) int ret; adata = sdev->pdata->hw_pdata; - size_fw = adata->fw_bin_size; + + if (adata->signed_fw_image) + size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; + else + size_fw = adata->fw_bin_size; page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; adata->fw_bin_page_count = page_count; @@ -203,6 +207,7 @@ EXPORT_SYMBOL_NS(acp_dsp_pre_fw_run, SND_SOC_SOF_AMD_COMMON); int acp_sof_dsp_run(struct snd_sof_dev *sdev) { + struct acp_dev_data *adata = sdev->pdata->hw_pdata; const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); int val; @@ -211,7 +216,7 @@ int acp_sof_dsp_run(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "ACP_DSP0_RUNSTALL : 0x%0x\n", val); /* Some platforms won't support fusion DSP,keep offset zero for no support */ - if (desc->fusion_dsp_offset) { + if (desc->fusion_dsp_offset && adata->enable_fw_debug) { snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->fusion_dsp_offset, ACP_DSP_RUN); val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->fusion_dsp_offset); dev_dbg(sdev->dev, "ACP_DSP0_FUSION_RUNSTALL : 0x%0x\n", val); @@ -219,3 +224,34 @@ int acp_sof_dsp_run(struct snd_sof_dev *sdev) return 0; } EXPORT_SYMBOL_NS(acp_sof_dsp_run, SND_SOC_SOF_AMD_COMMON); + +int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct acp_dev_data *adata = plat_data->hw_pdata; + int ret; + + ret = request_firmware(&sdev->basefw.fw, adata->fw_code_bin, sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "sof signed firmware code bin is missing\n"); + return ret; + } else { + dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_code_bin); + } + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, 0, + (void *)sdev->basefw.fw->data, sdev->basefw.fw->size); + + ret = request_firmware(&adata->fw_dbin, adata->fw_data_bin, sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "sof signed firmware data bin is missing\n"); + return ret; + + } else { + dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_data_bin); + } + + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, + (void *)adata->fw_dbin->data, adata->fw_dbin->size); + return ret; +} +EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp-probes.c b/sound/soc/sof/amd/acp-probes.c new file mode 100644 index 000000000000..778cf1a8b610 --- /dev/null +++ b/sound/soc/sof/amd/acp-probes.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Advanced Micro Devices, Inc. +// +// Authors: V Sujith Kumar Reddy <Vsujithkumar.Reddy@amd.com> + +/* + * Probe interface for generic AMD audio ACP DSP block + */ + +#include <linux/module.h> +#include <sound/soc.h> +#include "../sof-priv.h" +#include "../sof-client-probes.h" +#include "../sof-client.h" +#include "../ops.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +static int acp_probes_compr_startup(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai, u32 *stream_id) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct acp_dsp_stream *stream; + struct acp_dev_data *adata; + + adata = sdev->pdata->hw_pdata; + stream = acp_dsp_stream_get(sdev, 0); + if (!stream) + return -ENODEV; + + stream->cstream = cstream; + cstream->runtime->private_data = stream; + + adata->probe_stream = stream; + *stream_id = stream->stream_tag; + + return 0; +} + +static int acp_probes_compr_shutdown(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct acp_dsp_stream *stream = cstream->runtime->private_data; + struct acp_dev_data *adata; + int ret; + + ret = acp_dsp_stream_put(sdev, stream); + if (ret < 0) { + dev_err(sdev->dev, "Failed to release probe compress stream\n"); + return ret; + } + + adata = sdev->pdata->hw_pdata; + stream->cstream = NULL; + cstream->runtime->private_data = NULL; + adata->probe_stream = NULL; + + return 0; +} + +static int acp_probes_compr_set_params(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_soc_dai *dai) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct acp_dsp_stream *stream = cstream->runtime->private_data; + unsigned int buf_offset, index; + u32 size; + int ret; + + stream->dmab = cstream->runtime->dma_buffer_p; + stream->num_pages = PFN_UP(cstream->runtime->dma_bytes); + size = cstream->runtime->buffer_size; + + ret = acp_dsp_stream_config(sdev, stream); + if (ret < 0) { + acp_dsp_stream_put(sdev, stream); + return ret; + } + + /* write buffer size of stream in scratch memory */ + + buf_offset = sdev->debug_box.offset + + offsetof(struct scratch_reg_conf, buf_size); + index = stream->stream_tag - 1; + buf_offset = buf_offset + index * 4; + + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + buf_offset, size); + + return 0; +} + +static int acp_probes_compr_trigger(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + int cmd, struct snd_soc_dai *dai) +{ + /* Nothing to do here, as it is a mandatory callback just defined */ + return 0; +} + +static int acp_probes_compr_pointer(struct sof_client_dev *cdev, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *dai) +{ + struct acp_dsp_stream *stream = cstream->runtime->private_data; + struct snd_soc_pcm_stream *pstream; + + pstream = &dai->driver->capture; + tstamp->copied_total = stream->cstream_posn; + tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates); + + return 0; +} + +/* SOF client implementation */ +static const struct sof_probes_host_ops acp_probes_ops = { + .startup = acp_probes_compr_startup, + .shutdown = acp_probes_compr_shutdown, + .set_params = acp_probes_compr_set_params, + .trigger = acp_probes_compr_trigger, + .pointer = acp_probes_compr_pointer, +}; + +int acp_probes_register(struct snd_sof_dev *sdev) +{ + return sof_client_dev_register(sdev, "acp-probes", 0, &acp_probes_ops, + sizeof(acp_probes_ops)); +} +EXPORT_SYMBOL_NS(acp_probes_register, SND_SOC_SOF_AMD_COMMON); + +void acp_probes_unregister(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "acp-probes", 0); +} +EXPORT_SYMBOL_NS(acp_probes_unregister, SND_SOC_SOF_AMD_COMMON); + +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); + diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index afb505461ea1..19a801908b56 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -3,7 +3,7 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. // // Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com> // Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> @@ -20,6 +20,26 @@ #include "acp.h" #include "acp-dsp-offset.h" +#define SECURED_FIRMWARE 1 + +static bool enable_fw_debug; +module_param(enable_fw_debug, bool, 0444); +MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug"); + +const struct dmi_system_id acp_sof_quirk_table[] = { + { + /* Valve Jupiter device */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Valve"), + DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"), + }, + .driver_data = (void *)SECURED_FIRMWARE, + }, + {} +}; +EXPORT_SYMBOL_GPL(acp_sof_quirk_table); + static int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data) { pci_write_config_dword(dev, 0x60, smn_addr); @@ -28,12 +48,14 @@ static int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data) return 0; } -static int smn_read(struct pci_dev *dev, u32 smn_addr, u32 *data) +static int smn_read(struct pci_dev *dev, u32 smn_addr) { + u32 data = 0; + pci_write_config_dword(dev, 0x60, smn_addr); - pci_read_config_dword(dev, 0x64, data); + pci_read_config_dword(dev, 0x64, &data); - return 0; + return data; } static void init_dma_descriptor(struct acp_dev_data *adata) @@ -150,15 +172,13 @@ int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr, static int psp_mbox_ready(struct acp_dev_data *adata, bool ack) { struct snd_sof_dev *sdev = adata->dev; - int timeout; + int ret; u32 data; - for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) { - msleep(20); - smn_read(adata->smn_dev, MP0_C2PMSG_114_REG, &data); - if (data & MBOX_READY_MASK) - return 0; - } + ret = read_poll_timeout(smn_read, data, data & MBOX_READY_MASK, MBOX_DELAY_US, + ACP_PSP_TIMEOUT_US, false, adata->smn_dev, MP0_C2PMSG_114_REG); + if (!ret) + return 0; dev_err(sdev->dev, "PSP error status %x\n", data & MBOX_STATUS_MASK); @@ -177,23 +197,19 @@ static int psp_mbox_ready(struct acp_dev_data *adata, bool ack) static int psp_send_cmd(struct acp_dev_data *adata, int cmd) { struct snd_sof_dev *sdev = adata->dev; - int ret, timeout; + int ret; u32 data; if (!cmd) return -EINVAL; /* Get a non-zero Doorbell value from PSP */ - for (timeout = ACP_PSP_TIMEOUT_COUNTER; timeout > 0; timeout--) { - msleep(MBOX_DELAY); - smn_read(adata->smn_dev, MP0_C2PMSG_73_REG, &data); - if (data) - break; - } + ret = read_poll_timeout(smn_read, data, data, MBOX_DELAY_US, ACP_PSP_TIMEOUT_US, false, + adata->smn_dev, MP0_C2PMSG_73_REG); - if (!timeout) { + if (ret) { dev_err(sdev->dev, "Failed to get Doorbell from MBOX %x\n", MP0_C2PMSG_73_REG); - return -EINVAL; + return ret; } /* Check if PSP is ready for new command */ @@ -239,6 +255,9 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, } } + if (adata->signed_fw_image) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_DESTINATION_ADDR, dest_addr); snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_MSG_LENGTH, image_length); @@ -322,14 +341,7 @@ static irqreturn_t acp_irq_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); - unsigned int val, count = ACP_HW_SEM_RETRY_COUNT; - - val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat); - if (val & ACP_SHA_STAT) { - /* Clear SHA interrupt raised by PSP */ - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, val); - return IRQ_HANDLED; - } + unsigned int count = ACP_HW_SEM_RETRY_COUNT; while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) { /* Wait until acquired HW Semaphore lock or timeout */ @@ -355,9 +367,9 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) unsigned int val; val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET); - if (val) { - val |= ACP_DSP_TO_HOST_IRQ; - snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, val); + if (val & ACP_DSP_TO_HOST_IRQ) { + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, + ACP_DSP_TO_HOST_IRQ); return IRQ_WAKE_THREAD; } @@ -411,7 +423,12 @@ static int acp_reset(struct snd_sof_dev *sdev) if (ret < 0) dev_err(sdev->dev, "timeout in releasing reset\n"); - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_clkmux_sel, ACP_CLOCK_ACLK); + if (desc->acp_clkmux_sel) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_clkmux_sel, ACP_CLOCK_ACLK); + + if (desc->ext_intr_enb) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_enb, 0x01); + return ret; } @@ -449,7 +466,6 @@ EXPORT_SYMBOL_NS(amd_sof_acp_suspend, SND_SOC_SOF_AMD_COMMON); int amd_sof_acp_resume(struct snd_sof_dev *sdev) { - const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); int ret; ret = acp_init(sdev); @@ -457,20 +473,17 @@ int amd_sof_acp_resume(struct snd_sof_dev *sdev) dev_err(sdev->dev, "ACP Init failed\n"); return ret; } - - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_clkmux_sel, ACP_CLOCK_ACLK); - - ret = acp_memory_init(sdev); - - return ret; + return acp_memory_init(sdev); } EXPORT_SYMBOL_NS(amd_sof_acp_resume, SND_SOC_SOF_AMD_COMMON); int amd_sof_acp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); + struct snd_sof_pdata *plat_data = sdev->pdata; struct acp_dev_data *adata; const struct sof_amd_acp_desc *chip; + const struct dmi_system_id *dmi_id; unsigned int addr; int ret; @@ -531,6 +544,21 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size; sdev->debug_box.size = BOX_SIZE_1024; + adata->signed_fw_image = false; + dmi_id = dmi_first_match(acp_sof_quirk_table); + if (dmi_id && dmi_id->driver_data) { + adata->fw_code_bin = kasprintf(GFP_KERNEL, "%s/sof-%s-code.bin", + plat_data->fw_filename_prefix, + chip->name); + adata->fw_data_bin = kasprintf(GFP_KERNEL, "%s/sof-%s-data.bin", + plat_data->fw_filename_prefix, + chip->name); + adata->signed_fw_image = dmi_id->driver_data; + + dev_dbg(sdev->dev, "fw_code_bin:%s, fw_data_bin:%s\n", adata->fw_code_bin, + adata->fw_data_bin); + } + adata->enable_fw_debug = enable_fw_debug; acp_memory_init(sdev); acp_dsp_stream_init(sdev); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index dc624f727aa3..4dcceb764769 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -3,7 +3,7 @@ * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * - * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + * Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. * * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> */ @@ -11,6 +11,8 @@ #ifndef __SOF_AMD_ACP_H #define __SOF_AMD_ACP_H +#include <linux/dmi.h> + #include "../sof-priv.h" #include "../sof-audio.h" @@ -32,6 +34,7 @@ #define ACP_DSP_INTR_EN_MASK 0x00000001 #define ACP3X_SRAM_PTE_OFFSET 0x02050000 +#define ACP5X_SRAM_PTE_OFFSET 0x02050000 #define ACP6X_SRAM_PTE_OFFSET 0x03800000 #define PAGE_SIZE_4K_ENABLE 0x2 #define ACP_PAGE_SIZE 0x1000 @@ -40,6 +43,7 @@ #define DSP_FW_RUN_ENABLE 0x01 #define ACP_SHA_RUN 0x01 #define ACP_SHA_RESET 0x02 +#define ACP_SHA_HEADER 0x01 #define ACP_DMA_CH_RST 0x01 #define ACP_DMA_CH_GRACEFUL_RST_EN 0x10 #define ACP_ATU_CACHE_INVALID 0x01 @@ -56,17 +60,19 @@ #define ACP_DSP_TO_HOST_IRQ 0x04 #define ACP_RN_PCI_ID 0x01 +#define ACP_VANGOGH_PCI_ID 0x50 #define ACP_RMB_PCI_ID 0x6F #define HOST_BRIDGE_CZN 0x1630 +#define HOST_BRIDGE_VGH 0x1645 #define HOST_BRIDGE_RMB 0x14B5 #define ACP_SHA_STAT 0x8000 -#define ACP_PSP_TIMEOUT_COUNTER 5 +#define ACP_PSP_TIMEOUT_US 1000000 #define ACP_EXT_INTR_ERROR_STAT 0x20000000 #define MP0_C2PMSG_114_REG 0x3810AC8 #define MP0_C2PMSG_73_REG 0x3810A24 #define MBOX_ACP_SHA_DMA_COMMAND 0x70000 -#define MBOX_DELAY 1000 +#define MBOX_DELAY_US 1000 #define MBOX_READY_MASK 0x80000000 #define MBOX_STATUS_MASK 0xFFFF @@ -77,6 +83,9 @@ #define AMD_STACK_DUMP_SIZE 32 #define SRAM1_SIZE 0x13A000 +#define PROBE_STATUS_BIT BIT(31) + +#define ACP_FIRMWARE_SIGNATURE 0x100 enum clock_source { ACP_CLOCK_96M = 0, @@ -156,36 +165,47 @@ struct acp_dsp_stream { int active; unsigned int reg_offset; size_t posn_offset; + struct snd_compr_stream *cstream; + u64 cstream_posn; }; struct sof_amd_acp_desc { unsigned int rev; + const char *name; unsigned int host_bridge_id; u32 pgfsm_base; + u32 ext_intr_enb; u32 ext_intr_stat; u32 dsp_intr_base; u32 sram_pte_offset; u32 hw_semaphore_offset; u32 acp_clkmux_sel; u32 fusion_dsp_offset; + u32 probe_reg_offset; }; /* Common device data struct for ACP devices */ struct acp_dev_data { struct snd_sof_dev *dev; + const struct firmware *fw_dbin; /* DMIC device */ struct platform_device *dmic_dev; unsigned int fw_bin_size; unsigned int fw_data_bin_size; + const char *fw_code_bin; + const char *fw_data_bin; u32 fw_bin_page_count; dma_addr_t sha_dma_addr; u8 *bin_buf; dma_addr_t dma_addr; u8 *data_buf; + bool signed_fw_image; struct dma_descriptor dscr_info[ACP_MAX_DESC]; struct acp_dsp_stream stream_buf[ACP_MAX_STREAM]; struct acp_dsp_stream *dtrace_stream; struct pci_dev *smn_dev; + struct acp_dsp_stream *probe_stream; + bool enable_fw_debug; }; void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); @@ -205,6 +225,7 @@ int amd_sof_acp_remove(struct snd_sof_dev *sdev); /* DSP Loader callbacks */ int acp_sof_dsp_run(struct snd_sof_dev *sdev); int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev); +int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev); int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type); /* Block IO callbacks */ @@ -248,6 +269,8 @@ extern struct snd_sof_dsp_ops sof_acp_common_ops; extern struct snd_sof_dsp_ops sof_renoir_ops; int sof_renoir_ops_init(struct snd_sof_dev *sdev); +extern struct snd_sof_dsp_ops sof_vangogh_ops; +int sof_vangogh_ops_init(struct snd_sof_dev *sdev); extern struct snd_sof_dsp_ops sof_rembrandt_ops; int sof_rembrandt_ops_init(struct snd_sof_dev *sdev); @@ -273,4 +296,10 @@ static inline const struct sof_amd_acp_desc *get_chip_info(struct snd_sof_pdata return desc->chip_info; } + +int acp_probes_register(struct snd_sof_dev *sdev); +void acp_probes_unregister(struct snd_sof_dev *sdev); + +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[]; +extern const struct dmi_system_id acp_sof_quirk_table[]; #endif diff --git a/sound/soc/sof/amd/pci-rmb.c b/sound/soc/sof/amd/pci-rmb.c index 58b3092425f1..9935e457b467 100644 --- a/sound/soc/sof/amd/pci-rmb.c +++ b/sound/soc/sof/amd/pci-rmb.c @@ -25,6 +25,7 @@ #define ACP6x_REG_START 0x1240000 #define ACP6x_REG_END 0x125C000 +#define ACP6X_FUTURE_REG_ACLK_0 0x1854 static const struct sof_amd_acp_desc rembrandt_chip_info = { .rev = 6, @@ -36,6 +37,7 @@ static const struct sof_amd_acp_desc rembrandt_chip_info = { .hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0, .acp_clkmux_sel = ACP6X_CLKMUX_SEL, .fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL, + .probe_reg_offset = ACP6X_FUTURE_REG_ACLK_0, }; static const struct sof_dev_desc rembrandt_desc = { diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c index 7409e21ce5aa..c72d5d8aff8e 100644 --- a/sound/soc/sof/amd/pci-rn.c +++ b/sound/soc/sof/amd/pci-rn.c @@ -25,6 +25,7 @@ #define ACP3x_REG_START 0x1240000 #define ACP3x_REG_END 0x125C000 +#define ACP3X_FUTURE_REG_ACLK_0 0x1860 static const struct sof_amd_acp_desc renoir_chip_info = { .rev = 3, @@ -35,6 +36,7 @@ static const struct sof_amd_acp_desc renoir_chip_info = { .sram_pte_offset = ACP3X_SRAM_PTE_OFFSET, .hw_semaphore_offset = ACP3X_AXI2DAGB_SEM_0, .acp_clkmux_sel = ACP3X_CLKMUX_SEL, + .probe_reg_offset = ACP3X_FUTURE_REG_ACLK_0, }; static const struct sof_dev_desc renoir_desc = { diff --git a/sound/soc/sof/amd/pci-vangogh.c b/sound/soc/sof/amd/pci-vangogh.c new file mode 100644 index 000000000000..d8be42fbcb6d --- /dev/null +++ b/sound/soc/sof/amd/pci-vangogh.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Venkata Prasad Potturu <venkataprasad.potturu@amd.com> + +/*. + * PCI interface for Vangogh ACP device + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <sound/sof.h> +#include <sound/soc-acpi.h> + +#include "../ops.h" +#include "../sof-pci-dev.h" +#include "../../amd/mach-config.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +#define ACP5X_FUTURE_REG_ACLK_0 0x1864 + +static const struct sof_amd_acp_desc vangogh_chip_info = { + .rev = 5, + .name = "vangogh", + .host_bridge_id = HOST_BRIDGE_VGH, + .pgfsm_base = ACP5X_PGFSM_BASE, + .ext_intr_stat = ACP5X_EXT_INTR_STAT, + .dsp_intr_base = ACP5X_DSP_SW_INTR_BASE, + .sram_pte_offset = ACP5X_SRAM_PTE_OFFSET, + .hw_semaphore_offset = ACP5X_AXI2DAGB_SEM_0, + .acp_clkmux_sel = ACP5X_CLKMUX_SEL, + .probe_reg_offset = ACP5X_FUTURE_REG_ACLK_0, +}; + +static const struct sof_dev_desc vangogh_desc = { + .machines = snd_soc_acpi_amd_vangogh_sof_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .chip_info = &vangogh_chip_info, + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "amd/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "amd/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-vangogh.ri", + }, + .nocodec_tplg_filename = "sof-acp.tplg", + .ops = &sof_vangogh_ops, + .ops_init = sof_vangogh_ops_init, +}; + +static int acp_pci_vgh_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + unsigned int flag; + + if (pci->revision != ACP_VANGOGH_PCI_ID) + return -ENODEV; + + flag = snd_amd_acp_find_config(pci); + if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) + return -ENODEV; + + return sof_pci_probe(pci, pci_id); +}; + +static void acp_pci_vgh_remove(struct pci_dev *pci) +{ + sof_pci_remove(pci); +} + +/* PCI IDs */ +static const struct pci_device_id vgh_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID), + .driver_data = (unsigned long)&vangogh_desc}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, vgh_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_amd_vgh_driver = { + .name = KBUILD_MODNAME, + .id_table = vgh_pci_ids, + .probe = acp_pci_vgh_probe, + .remove = acp_pci_vgh_remove, + .driver = { + .pm = &sof_pci_pm, + }, +}; +module_pci_driver(snd_sof_pci_amd_vgh_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON); +MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV); diff --git a/sound/soc/sof/amd/vangogh.c b/sound/soc/sof/amd/vangogh.c new file mode 100644 index 000000000000..de15d21aa6d9 --- /dev/null +++ b/sound/soc/sof/amd/vangogh.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Advanced Micro Devices, Inc. +// +// Authors: Venkata Prasad Potturu <venkataprasad.potturu@amd.com> + +/* + * Hardware interface for Audio DSP on Vangogh platform + */ + +#include <linux/platform_device.h> +#include <linux/module.h> + +#include "../ops.h" +#include "../sof-audio.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +#define I2S_HS_INSTANCE 0 +#define I2S_BT_INSTANCE 1 +#define I2S_SP_INSTANCE 2 +#define PDM_DMIC_INSTANCE 3 +#define I2S_HS_VIRTUAL_INSTANCE 4 + +static struct snd_soc_dai_driver vangogh_sof_dai[] = { + [I2S_HS_INSTANCE] = { + .id = I2S_HS_INSTANCE, + .name = "acp-sof-hs", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S HS controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [I2S_BT_INSTANCE] = { + .id = I2S_BT_INSTANCE, + .name = "acp-sof-bt", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S BT controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [I2S_SP_INSTANCE] = { + .id = I2S_SP_INSTANCE, + .name = "acp-sof-sp", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S SP controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [PDM_DMIC_INSTANCE] = { + .id = PDM_DMIC_INSTANCE, + .name = "acp-sof-dmic", + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [I2S_HS_VIRTUAL_INSTANCE] = { + .id = I2S_HS_VIRTUAL_INSTANCE, + .name = "acp-sof-hs-virtual", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S HS-Virtual controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, +}; + +/* Vangogh ops */ +struct snd_sof_dsp_ops sof_vangogh_ops; +EXPORT_SYMBOL_NS(sof_vangogh_ops, SND_SOC_SOF_AMD_COMMON); + +int sof_vangogh_ops_init(struct snd_sof_dev *sdev) +{ + const struct dmi_system_id *dmi_id; + + /* common defaults */ + memcpy(&sof_vangogh_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); + + sof_vangogh_ops.drv = vangogh_sof_dai; + sof_vangogh_ops.num_drv = ARRAY_SIZE(vangogh_sof_dai); + + dmi_id = dmi_first_match(acp_sof_quirk_table); + if (dmi_id && dmi_id->driver_data) + sof_vangogh_ops.load_firmware = acp_sof_load_signed_firmware; + + return 0; +} + +MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON); +MODULE_DESCRIPTION("VANGOGH SOF Driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 69c1a370d3b6..9d0107932117 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -262,6 +262,22 @@ config SND_SOC_SOF_METEORLAKE Say Y if you have such a device. If unsure select "N". +config SND_SOC_SOF_INTEL_LNL + tristate + select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE + select SND_SOC_SOF_INTEL_IPC4 + +config SND_SOC_SOF_LUNARLAKE + tristate "SOF support for Lunarlake" + default SND_SOC_SOF_PCI + select SND_SOC_SOF_INTEL_LNL + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Lunarlake processors. + Say Y if you have such a device. + If unsure select "N". + config SND_SOC_SOF_HDA_COMMON tristate select SND_SOC_SOF_INTEL_COMMON diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index fdb463c12e91..030574dbc998 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -7,7 +7,7 @@ snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ hda-dai.o hda-dai-ops.o hda-bus.o \ skl.o hda-loader-skl.o \ - apl.o cnl.o tgl.o icl.o mtl.o hda-common-ops.o + apl.o cnl.o tgl.o icl.o mtl.o lnl.o hda-common-ops.o snd-sof-intel-hda-mlink-objs := hda-mlink.o @@ -31,6 +31,7 @@ snd-sof-pci-intel-cnl-objs := pci-cnl.o snd-sof-pci-intel-icl-objs := pci-icl.o snd-sof-pci-intel-tgl-objs := pci-tgl.o snd-sof-pci-intel-mtl-objs := pci-mtl.o +snd-sof-pci-intel-lnl-objs := pci-lnl.o obj-$(CONFIG_SND_SOC_SOF_MERRIFIELD) += snd-sof-pci-intel-tng.o obj-$(CONFIG_SND_SOC_SOF_INTEL_SKL) += snd-sof-pci-intel-skl.o @@ -39,3 +40,4 @@ obj-$(CONFIG_SND_SOC_SOF_INTEL_CNL) += snd-sof-pci-intel-cnl.o obj-$(CONFIG_SND_SOC_SOF_INTEL_ICL) += snd-sof-pci-intel-icl.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TGL) += snd-sof-pci-intel-tgl.o obj-$(CONFIG_SND_SOC_SOF_INTEL_MTL) += snd-sof-pci-intel-mtl.o +obj-$(CONFIG_SND_SOC_SOF_INTEL_LNL) += snd-sof-pci-intel-lnl.o diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index a95222e53ecf..c6fbf4285262 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -466,6 +466,7 @@ const struct sof_intel_dsp_desc cnl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, @@ -501,6 +502,7 @@ const struct sof_intel_dsp_desc jsl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index f33051eac1c0..494ced2b746e 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -7,6 +7,7 @@ #include <sound/pcm_params.h> #include <sound/hdaudio_ext.h> +#include <sound/hda-mlink.h> #include <sound/sof/ipc4/header.h> #include <uapi/sound/sof/header.h> #include "../ipc4-priv.h" @@ -144,9 +145,17 @@ static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *dai; struct hdac_ext_stream *hext_stream; - hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); + /* only allocate a stream_tag for the first DAI in the dailink */ + dai = asoc_rtd_to_cpu(rtd, 0); + if (dai == cpu_dai) + hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); + else + hext_stream = snd_soc_dai_get_dma_data(dai, substream); + if (!hext_stream) return NULL; @@ -159,9 +168,14 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai struct snd_pcm_substream *substream) { struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *dai; + /* only release a stream_tag for the first DAI in the dailink */ + dai = asoc_rtd_to_cpu(rtd, 0); + if (dai == cpu_dai) + snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); - snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); } static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, @@ -219,6 +233,77 @@ static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev, return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); } +static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int format_val; + + format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), + params_format(params), + params_physical_width(params), + 0); + + dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, + params_rate(params), params_channels(params), params_format(params)); + + return format_val; +} + +static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int format_val; + snd_pcm_format_t format; + unsigned int channels; + unsigned int width; + + channels = params_channels(params); + format = params_format(params); + width = params_physical_width(params); + + if (format == SNDRV_PCM_FORMAT_S16_LE) { + format = SNDRV_PCM_FORMAT_S32_LE; + channels /= 2; + width = 32; + } + + format_val = snd_hdac_calc_stream_format(params_rate(params), channels, + format, + width, + 0); + + dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, + params_rate(params), channels, format); + + return format_val; +} + +static struct hdac_ext_link *ssp_get_hlink(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + + return hdac_bus_eml_ssp_get_hlink(bus); +} + +static struct hdac_ext_link *dmic_get_hlink(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + + return hdac_bus_eml_dmic_get_hlink(bus); +} + +static struct hdac_ext_link *sdw_get_hlink(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + + return hdac_bus_eml_sdw_get_hlink(bus); +} + static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { @@ -234,6 +319,9 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; + if (pipe_widget->instance_id < 0) + return 0; + mutex_lock(&ipc4_data->pipeline_state_mutex); switch (cmd) { @@ -297,6 +385,9 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; + if (pipe_widget->instance_id < 0) + return 0; + mutex_lock(&ipc4_data->pipeline_state_mutex); switch (cmd) { @@ -343,6 +434,28 @@ out: return ret; } +static struct hdac_ext_stream *sdw_hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev, + struct snd_soc_dai *cpu_dai, + struct snd_pcm_substream *substream) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dai *dai = swidget->private; + struct sof_ipc4_copier *ipc4_copier = dai->private; + struct sof_ipc4_alh_configuration_blob *blob; + + blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config; + + /* + * Starting with ACE_2_0, re-setting the device_count is mandatory to avoid using + * the multi-gateway firmware configuration. The DMA hardware can take care of + * multiple links without needing any firmware assistance + */ + blob->alh_cfg.device_count = 1; + + return hda_ipc4_get_hext_stream(sdev, cpu_dai, substream); +} + static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .get_hext_stream = hda_ipc4_get_hext_stream, .assign_hext_stream = hda_assign_hext_stream, @@ -357,6 +470,45 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .get_hlink = hda_get_hlink, }; +static const struct hda_dai_widget_dma_ops ssp_ipc4_dma_ops = { + .get_hext_stream = hda_ipc4_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .pre_trigger = hda_ipc4_pre_trigger, + .trigger = hda_trigger, + .post_trigger = hda_ipc4_post_trigger, + .calc_stream_format = generic_calc_stream_format, + .get_hlink = ssp_get_hlink, +}; + +static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = { + .get_hext_stream = hda_ipc4_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .pre_trigger = hda_ipc4_pre_trigger, + .trigger = hda_trigger, + .post_trigger = hda_ipc4_post_trigger, + .calc_stream_format = dmic_calc_stream_format, + .get_hlink = dmic_get_hlink, +}; + +static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = { + .get_hext_stream = sdw_hda_ipc4_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .pre_trigger = hda_ipc4_pre_trigger, + .trigger = hda_trigger, + .post_trigger = hda_ipc4_post_trigger, + .calc_stream_format = generic_calc_stream_format, + .get_hlink = sdw_get_hlink, +}; + static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { .get_hext_stream = hda_get_hext_stream, .assign_hext_stream = hda_assign_hext_stream, @@ -468,8 +620,13 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg case SOF_INTEL_IPC4: { struct sof_ipc4_copier *ipc4_copier = sdai->private; + const struct sof_intel_dsp_desc *chip; - if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) { + chip = get_chip_info(sdev->pdata); + + switch (ipc4_copier->dai_type) { + case SOF_DAI_INTEL_HDA: + { struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; @@ -478,6 +635,22 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg return &hda_ipc4_dma_ops; } + case SOF_DAI_INTEL_SSP: + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return NULL; + return &ssp_ipc4_dma_ops; + case SOF_DAI_INTEL_DMIC: + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return NULL; + return &dmic_ipc4_dma_ops; + case SOF_DAI_INTEL_ALH: + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return NULL; + return &sdw_ipc4_dma_ops; + + default: + break; + } break; } default: diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 863865f3d77e..f3cefd866081 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -10,6 +10,8 @@ #include <sound/pcm_params.h> #include <sound/hdaudio_ext.h> +#include <sound/hda-mlink.h> +#include <sound/hda_register.h> #include <sound/intel-nhlt.h> #include <sound/sof/ipc4/header.h> #include <uapi/sound/sof/header.h> @@ -329,6 +331,175 @@ static const struct snd_soc_dai_ops hda_dai_ops = { #endif +static struct sof_ipc4_copier *widget_to_copier(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dai *sdai = swidget->private; + struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)sdai->private; + + return ipc4_copier; +} + +static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + struct sof_ipc4_dma_config_tlv *dma_config_tlv; + const struct hda_dai_widget_dma_ops *ops; + struct sof_ipc4_dma_config *dma_config; + struct sof_ipc4_copier *ipc4_copier; + struct hdac_ext_stream *hext_stream; + struct hdac_stream *hstream; + struct snd_sof_dev *sdev; + int stream_id; + int ret; + + ops = hda_dai_get_ops(substream, cpu_dai); + if (!ops) { + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + + /* use HDaudio stream handling */ + ret = hda_dai_hw_params(substream, params, cpu_dai); + if (ret < 0) { + dev_err(cpu_dai->dev, "%s: hda_dai_hw_params failed: %d\n", __func__, ret); + return ret; + } + + /* get stream_id */ + sdev = widget_to_sdev(w); + hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); + + if (!hext_stream) { + dev_err(cpu_dai->dev, "%s: no hext_stream found\n", __func__); + return -ENODEV; + } + + hstream = &hext_stream->hstream; + stream_id = hstream->stream_tag; + + if (!stream_id) { + dev_err(cpu_dai->dev, "%s: no stream_id allocated\n", __func__); + return -ENODEV; + } + + /* configure TLV */ + ipc4_copier = widget_to_copier(w); + + dma_config_tlv = &ipc4_copier->dma_config_tlv; + dma_config_tlv->type = SOF_IPC4_GTW_DMA_CONFIG_ID; + /* dma_config_priv_size is zero */ + dma_config_tlv->length = sizeof(dma_config_tlv->dma_config); + + dma_config = &dma_config_tlv->dma_config; + + dma_config->dma_method = SOF_IPC4_DMA_METHOD_HDA; + dma_config->pre_allocated_by_host = 1; + dma_config->dma_channel_id = stream_id - 1; + dma_config->stream_id = stream_id; + dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */ + dma_config->dma_priv_config_size = 0; + + return 0; +} + +static int non_hda_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int stream = substream->stream; + + return non_hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai); +} + +static const struct snd_soc_dai_ops ssp_dai_ops = { + .hw_params = non_hda_dai_hw_params, + .hw_free = hda_dai_hw_free, + .trigger = hda_dai_trigger, + .prepare = non_hda_dai_prepare, +}; + +static const struct snd_soc_dai_ops dmic_dai_ops = { + .hw_params = non_hda_dai_hw_params, + .hw_free = hda_dai_hw_free, + .trigger = hda_dai_trigger, + .prepare = non_hda_dai_prepare, +}; + +int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai, + int link_id) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + const struct hda_dai_widget_dma_ops *ops; + struct hdac_ext_stream *hext_stream; + struct snd_sof_dev *sdev; + int ret; + + ret = non_hda_dai_hw_params(substream, params, cpu_dai); + if (ret < 0) { + dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_params failed %d\n", __func__, ret); + return ret; + } + + ops = hda_dai_get_ops(substream, cpu_dai); + sdev = widget_to_sdev(w); + hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); + + if (!hext_stream) + return -ENODEV; + + /* in the case of SoundWire we need to program the PCMSyCM registers */ + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, + GENMASK(params_channels(params) - 1, 0), + hdac_stream(hext_stream)->stream_tag, + substream->stream); + if (ret < 0) { + dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai, + int link_id) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + struct snd_sof_dev *sdev; + int ret; + + ret = hda_dai_hw_free(substream, cpu_dai); + if (ret < 0) { + dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_free failed %d\n", __func__, ret); + return ret; + } + + sdev = widget_to_sdev(w); + + /* in the case of SoundWire we need to reset the PCMSyCM registers */ + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, + 0, 0, substream->stream); + if (ret < 0) { + dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + return hda_dai_trigger(substream, cmd, cpu_dai); +} + static int hda_dai_suspend(struct hdac_bus *bus) { struct snd_soc_pcm_runtime *rtd; @@ -383,7 +554,42 @@ static int hda_dai_suspend(struct hdac_bus *bus) return 0; } -#endif +static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +{ + const struct sof_intel_dsp_desc *chip; + int i; + + chip = get_chip_info(sdev->pdata); + + if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { + for (i = 0; i < ops->num_drv; i++) { + if (strstr(ops->drv[i].name, "SSP")) + ops->drv[i].ops = &ssp_dai_ops; + } + } +} + +static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +{ + const struct sof_intel_dsp_desc *chip; + int i; + + chip = get_chip_info(sdev->pdata); + + if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { + for (i = 0; i < ops->num_drv; i++) { + if (strstr(ops->drv[i].name, "DMIC")) + ops->drv[i].ops = &dmic_dai_ops; + } + } +} + +#else + +static inline void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {} +static inline void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {} + +#endif /* CONFIG_SND_SOC_SOF_HDA_LINK */ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) { @@ -398,6 +604,9 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) #endif } + ssp_set_dai_drv_ops(sdev, ops); + dmic_set_dai_drv_ops(sdev, ops); + if (sdev->pdata->ipc_type == SOF_INTEL_IPC4 && !hda_use_tplg_nhlt) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index b7cbf66badf5..b592e687a87a 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -331,14 +331,19 @@ static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask) return !!(val & cmdsync_mask); } -static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num) +static u16 hdaml_link_get_lsdiid(u16 __iomem *lsdiid) { - u32 val; + return readw(lsdiid); +} + +static void hdaml_link_set_lsdiid(u16 __iomem *lsdiid, int dev_num) +{ + u16 val; - val = readl(lsdiid); + val = readw(lsdiid); val |= BIT(dev_num); - writel(val, lsdiid); + writew(val, lsdiid); } static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan, @@ -752,6 +757,22 @@ int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); +int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); + if (!h2link) + return -ENODEV; + + hlink = &h2link->hext_link; + + *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink)); + + return 0; +} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, SND_SOC_SOF_HDA_MLINK); + int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) { struct hdac_ext2_link *h2link; @@ -781,6 +802,8 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, { struct hdac_ext2_link *h2link; u16 __iomem *pcmsycm; + int hchan; + int lchan; u16 val; h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); @@ -791,17 +814,25 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, h2link->instance_offset * sublink + AZX_REG_SDW_SHIM_PCMSyCM(y); + if (channel_mask) { + hchan = __fls(channel_mask); + lchan = __ffs(channel_mask); + } else { + hchan = 0; + lchan = 0; + } + mutex_lock(&h2link->eml_lock); - hdaml_shim_map_stream_ch(pcmsycm, 0, hweight32(channel_mask), + hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan, stream_id, dir); mutex_unlock(&h2link->eml_lock); val = readw(pcmsycm); - dev_dbg(bus->dev, "channel_mask %#x stream_id %d dir %d pcmscm %#x\n", - channel_mask, stream_id, dir, val); + dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n", + sublink, channel_mask, stream_id, dir, val); return 0; } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, SND_SOC_SOF_HDA_MLINK); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index b13acb959653..0b0087abcc50 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -869,8 +869,8 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) return -ENOMEM; } - /* create capture streams */ - for (i = 0; i < num_capture; i++) { + /* create capture and playback streams */ + for (i = 0; i < num_total; i++) { struct sof_intel_hda_stream *hda_stream; hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream), @@ -909,69 +909,17 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) hstream->index = i; sd_offset = SOF_STREAM_SD_OFFSET(hstream); hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; - hstream->stream_tag = i + 1; hstream->opened = false; hstream->running = false; - hstream->direction = SNDRV_PCM_STREAM_CAPTURE; - /* memory alloc for stream BDL */ - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, - HDA_DSP_BDL_SIZE, &hstream->bdl); - if (ret < 0) { - dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); - return -ENOMEM; - } - hstream->posbuf = (__le32 *)(bus->posbuf.area + - (hstream->index) * 8); - - list_add_tail(&hstream->list, &bus->stream_list); - } - - /* create playback streams */ - for (i = num_capture; i < num_total; i++) { - struct sof_intel_hda_stream *hda_stream; - - hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream), - GFP_KERNEL); - if (!hda_stream) - return -ENOMEM; - - hda_stream->sdev = sdev; - - hext_stream = &hda_stream->hext_stream; - - if (sdev->bar[HDA_DSP_PP_BAR]) { - hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + - SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; - - hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + - SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + - SOF_HDA_PPLC_INTERVAL * i; - } - - hstream = &hext_stream->hstream; - - /* do we support SPIB */ - if (sdev->bar[HDA_DSP_SPIB_BAR]) { - hstream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + - SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + - SOF_HDA_SPIB_SPIB; - - hstream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + - SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + - SOF_HDA_SPIB_MAXFIFO; + if (i < num_capture) { + hstream->stream_tag = i + 1; + hstream->direction = SNDRV_PCM_STREAM_CAPTURE; + } else { + hstream->stream_tag = i - num_capture + 1; + hstream->direction = SNDRV_PCM_STREAM_PLAYBACK; } - hstream->bus = bus; - hstream->sd_int_sta_mask = 1 << i; - hstream->index = i; - sd_offset = SOF_STREAM_SD_OFFSET(hstream); - hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; - hstream->stream_tag = i - num_capture + 1; - hstream->opened = false; - hstream->running = false; - hstream->direction = SNDRV_PCM_STREAM_PLAYBACK; - /* mem alloc for stream BDL */ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, HDA_DSP_BDL_SIZE, &hstream->bdl); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 64bebe1a72bb..15e6779efaa3 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -71,6 +71,11 @@ static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); break; + case SOF_INTEL_ACE_2_0: + interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | + BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); + interface_mask[1] = interface_mask[0]; /* all interfaces accessible without DSP */ + break; default: break; } @@ -107,6 +112,34 @@ struct sdw_intel_ops sdw_callback = { .params_stream = sdw_params_stream, }; +static int sdw_ace2x_params_stream(struct device *dev, + struct sdw_intel_stream_params_data *params_data) +{ + return sdw_hda_dai_hw_params(params_data->substream, + params_data->hw_params, + params_data->dai, + params_data->link_id); +} + +static int sdw_ace2x_free_stream(struct device *dev, + struct sdw_intel_stream_free_data *free_data) +{ + return sdw_hda_dai_hw_free(free_data->substream, + free_data->dai, + free_data->link_id); +} + +static int sdw_ace2x_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) +{ + return sdw_hda_dai_trigger(substream, cmd, dai); +} + +static struct sdw_intel_ops sdw_ace2x_callback = { + .params_stream = sdw_ace2x_params_stream, + .free_stream = sdw_ace2x_free_stream, + .trigger = sdw_ace2x_trigger, +}; + void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable) { struct sof_intel_hda_dev *hdev; @@ -174,6 +207,7 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev) res.shim_base = hdev->desc->sdw_shim_base; res.alh_base = hdev->desc->sdw_alh_base; res.ext = false; + res.ops = &sdw_callback; } else { /* * retrieve eml_lock needed to protect shared registers @@ -191,11 +225,13 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev) */ res.hw_ops = &sdw_intel_lnl_hw_ops; res.ext = true; + res.ops = &sdw_ace2x_callback; + } res.irq = sdev->ipc_irq; res.handle = hdev->info.handle; res.parent = sdev->dev; - res.ops = &sdw_callback; + res.dev = sdev->dev; res.clock_stop_quirks = sdw_clock_stop_quirks; res.hbus = sof_to_bus(sdev); @@ -363,14 +399,10 @@ static irqreturn_t hda_dsp_sdw_thread(int irq, void *context) return sdw_intel_thread(irq, context); } -static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) +bool hda_sdw_check_wakeen_irq_common(struct snd_sof_dev *sdev) { - u32 interface_mask = hda_get_interface_mask(sdev); struct sof_intel_hda_dev *hdev; - if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) - return false; - hdev = sdev->pdata->hw_pdata; if (hdev->sdw && snd_sof_dsp_read(sdev, HDA_DSP_BAR, @@ -380,6 +412,21 @@ static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) return false; } +static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) +{ + u32 interface_mask = hda_get_interface_mask(sdev); + const struct sof_intel_dsp_desc *chip; + + if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) + return false; + + chip = get_chip_info(sdev->pdata); + if (chip && chip->check_sdw_wakeen_irq) + return chip->check_sdw_wakeen_irq(sdev); + + return false; +} + void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) { u32 interface_mask = hda_get_interface_mask(sdev); @@ -1429,83 +1476,6 @@ static void hda_generic_machine_select(struct snd_sof_dev *sdev, #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) -#define SDW_CODEC_ADR_MASK(_adr) ((_adr) & (SDW_DISCO_LINK_ID_MASK | SDW_VERSION_MASK | \ - SDW_MFG_ID_MASK | SDW_PART_ID_MASK)) - -/* Check if all Slaves defined on the link can be found */ -static bool link_slaves_found(struct snd_sof_dev *sdev, - const struct snd_soc_acpi_link_adr *link, - struct sdw_intel_ctx *sdw) -{ - struct hdac_bus *bus = sof_to_bus(sdev); - struct sdw_intel_slave_id *ids = sdw->ids; - int num_slaves = sdw->num_slaves; - unsigned int part_id, link_id, unique_id, mfg_id, version; - int i, j, k; - - for (i = 0; i < link->num_adr; i++) { - u64 adr = link->adr_d[i].adr; - int reported_part_count = 0; - - mfg_id = SDW_MFG_ID(adr); - part_id = SDW_PART_ID(adr); - link_id = SDW_DISCO_LINK_ID(adr); - version = SDW_VERSION(adr); - - for (j = 0; j < num_slaves; j++) { - /* find out how many identical parts were reported on that link */ - if (ids[j].link_id == link_id && - ids[j].id.part_id == part_id && - ids[j].id.mfg_id == mfg_id && - ids[j].id.sdw_version == version) - reported_part_count++; - } - - for (j = 0; j < num_slaves; j++) { - int expected_part_count = 0; - - if (ids[j].link_id != link_id || - ids[j].id.part_id != part_id || - ids[j].id.mfg_id != mfg_id || - ids[j].id.sdw_version != version) - continue; - - /* find out how many identical parts are expected */ - for (k = 0; k < link->num_adr; k++) { - u64 adr2 = link->adr_d[k].adr; - - if (SDW_CODEC_ADR_MASK(adr2) == SDW_CODEC_ADR_MASK(adr)) - expected_part_count++; - } - - if (reported_part_count == expected_part_count) { - /* - * we have to check unique id - * if there is more than one - * Slave on the link - */ - unique_id = SDW_UNIQUE_ID(adr); - if (reported_part_count == 1 || - ids[j].id.unique_id == unique_id) { - dev_dbg(bus->dev, "found %x at link %d\n", - part_id, link_id); - break; - } - } else { - dev_dbg(bus->dev, "part %x reported %d expected %d on link %d, skipping\n", - part_id, reported_part_count, expected_part_count, link_id); - } - } - if (j == num_slaves) { - dev_dbg(bus->dev, - "Slave %x not found\n", - part_id); - return false; - } - } - return true; -} - static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; @@ -1549,7 +1519,9 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev * Try next machine if any expected Slaves * are not found on this link. */ - if (!link_slaves_found(sdev, link, hdev->sdw)) + if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link, + hdev->sdw->ids, + hdev->sdw->num_slaves)) break; } /* Found if all Slaves are checked */ diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 5b9e4ebcc18b..5c517ec57d4a 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -785,6 +785,7 @@ int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev); int hda_sdw_startup(struct snd_sof_dev *sdev); void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable); void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable); +bool hda_sdw_check_wakeen_irq_common(struct snd_sof_dev *sdev); void hda_sdw_process_wakeen(struct snd_sof_dev *sdev); bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev); @@ -813,6 +814,11 @@ static inline void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) { } +static inline bool hda_sdw_check_wakeen_irq_common(struct snd_sof_dev *sdev) +{ + return false; +} + static inline void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) { } @@ -824,6 +830,18 @@ static inline bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev) #endif +int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai, + int link_id); + +int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai, + int link_id); + +int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai); + /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; int hda_dsp_dais_suspend(struct snd_sof_dev *sdev); @@ -845,6 +863,8 @@ extern struct snd_sof_dsp_ops sof_icl_ops; int sof_icl_ops_init(struct snd_sof_dev *sdev); extern struct snd_sof_dsp_ops sof_mtl_ops; int sof_mtl_ops_init(struct snd_sof_dev *sdev); +extern struct snd_sof_dsp_ops sof_lnl_ops; +int sof_lnl_ops_init(struct snd_sof_dev *sdev); extern const struct sof_intel_dsp_desc skl_chip_info; extern const struct sof_intel_dsp_desc apl_chip_info; @@ -856,6 +876,7 @@ extern const struct sof_intel_dsp_desc ehl_chip_info; extern const struct sof_intel_dsp_desc jsl_chip_info; extern const struct sof_intel_dsp_desc adls_chip_info; extern const struct sof_intel_dsp_desc mtl_chip_info; +extern const struct sof_intel_dsp_desc lnl_chip_info; /* Probes support */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 0f249efc6a5a..7ac10167a90d 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -188,6 +188,7 @@ const struct sof_intel_dsp_desc icl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c new file mode 100644 index 000000000000..db94b45e53af --- /dev/null +++ b/sound/soc/sof/intel/lnl.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. + +/* + * Hardware interface for audio DSP on LunarLake. + */ + +#include <linux/firmware.h> +#include <sound/hda_register.h> +#include <sound/sof/ipc4/header.h> +#include <trace/events/sof_intel.h> +#include "../ipc4-priv.h" +#include "../ops.h" +#include "hda.h" +#include "hda-ipc.h" +#include "../sof-audio.h" +#include "mtl.h" +#include <sound/hda-mlink.h> + +/* LunarLake ops */ +struct snd_sof_dsp_ops sof_lnl_ops; +EXPORT_SYMBOL_NS(sof_lnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); + +static const struct snd_sof_debugfs_map lnl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS}, + {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, +}; + +/* this helps allows the DSP to setup DMIC/SSP */ +static int hdac_bus_offload_dmic_ssp(struct hdac_bus *bus) +{ + int ret; + + ret = hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP, true); + if (ret < 0) + return ret; + + ret = hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC, true); + if (ret < 0) + return ret; + + return 0; +} + +static int lnl_hda_dsp_probe(struct snd_sof_dev *sdev) +{ + int ret; + + ret = hda_dsp_probe(sdev); + if (ret < 0) + return ret; + + return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); +} + +static int lnl_hda_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + + ret = hda_dsp_resume(sdev); + if (ret < 0) + return ret; + + return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); +} + +static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + int ret; + + ret = hda_dsp_runtime_resume(sdev); + if (ret < 0) + return ret; + + return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); +} + +int sof_lnl_ops_init(struct snd_sof_dev *sdev) +{ + struct sof_ipc4_fw_data *ipc4_data; + + /* common defaults */ + memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); + + /* probe */ + sof_lnl_ops.probe = lnl_hda_dsp_probe; + + /* shutdown */ + sof_lnl_ops.shutdown = hda_dsp_shutdown; + + /* doorbell */ + sof_lnl_ops.irq_thread = mtl_ipc_irq_thread; + + /* ipc */ + sof_lnl_ops.send_msg = mtl_ipc_send_msg; + sof_lnl_ops.get_mailbox_offset = mtl_dsp_ipc_get_mailbox_offset; + sof_lnl_ops.get_window_offset = mtl_dsp_ipc_get_window_offset; + + /* debug */ + sof_lnl_ops.debug_map = lnl_dsp_debugfs; + sof_lnl_ops.debug_map_count = ARRAY_SIZE(lnl_dsp_debugfs); + sof_lnl_ops.dbg_dump = mtl_dsp_dump; + sof_lnl_ops.ipc_dump = mtl_ipc_dump; + + /* pre/post fw run */ + sof_lnl_ops.pre_fw_run = mtl_dsp_pre_fw_run; + sof_lnl_ops.post_fw_run = mtl_dsp_post_fw_run; + + /* parse platform specific extended manifest */ + sof_lnl_ops.parse_platform_ext_manifest = NULL; + + /* dsp core get/put */ + /* TODO: add core_get and core_put */ + + /* PM */ + sof_lnl_ops.resume = lnl_hda_dsp_resume; + sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume; + + sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; + + sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); + if (!sdev->private) + return -ENOMEM; + + ipc4_data = sdev->private; + ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; + + ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; + + /* External library loading support */ + ipc4_data->load_library = hda_dsp_ipc4_load_library; + + /* set DAI ops */ + hda_set_dai_drv_ops(sdev, &sof_lnl_ops); + + sof_lnl_ops.set_power_state = hda_dsp_set_power_state_ipc4; + + return 0; +}; +EXPORT_SYMBOL_NS(sof_lnl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON); + +/* Check if an SDW IRQ occurred */ +static bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + + return hdac_bus_eml_check_interrupt(bus, true, AZX_REG_ML_LEPTR_ID_SDW); +} + +static void lnl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + + hdac_bus_eml_enable_interrupt(bus, true, AZX_REG_ML_LEPTR_ID_SDW, enable); +} + +static int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev) +{ + lnl_enable_sdw_irq(sdev, false); + mtl_disable_ipc_interrupts(sdev); + return mtl_enable_interrupts(sdev, false); +} + +const struct sof_intel_dsp_desc lnl_chip_info = { + .cores_num = 5, + .init_core_mask = BIT(0), + .host_managed_cores_mask = BIT(0), + .ipc_req = MTL_DSP_REG_HFIPCXIDR, + .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY, + .ipc_ack = MTL_DSP_REG_HFIPCXIDA, + .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE, + .ipc_ctl = MTL_DSP_REG_HFIPCXCTL, + .rom_status_reg = MTL_DSP_ROM_STS, + .rom_init_timeout = 300, + .ssp_count = MTL_SSP_COUNT, + .d0i3_offset = MTL_HDA_VS_D0I3C, + .read_sdw_lcount = hda_sdw_check_lcount_ext, + .enable_sdw_irq = lnl_enable_sdw_irq, + .check_sdw_irq = lnl_dsp_check_sdw_irq, + .check_ipc_irq = mtl_dsp_check_ipc_irq, + .cl_init = mtl_dsp_cl_init, + .power_down_dsp = mtl_power_down_dsp, + .disable_interrupts = lnl_dsp_disable_interrupts, + .hw_ip_version = SOF_INTEL_ACE_2_0, +}; +EXPORT_SYMBOL_NS(lnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 30fe77fd87bf..b84ca58da9d5 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -91,7 +91,7 @@ static bool mtl_dsp_check_sdw_irq(struct snd_sof_dev *sdev) return false; } -static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc4_msg *msg_data = msg->msg_data; @@ -230,7 +230,7 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) } /* pre fw run operations */ -static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) +int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; u32 dsphfpwrsts; @@ -279,7 +279,7 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) return ret; } -static int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) +int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) { int ret; @@ -301,7 +301,7 @@ static int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) return 0; } -static void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags) +void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags) { char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR; u32 romdbgsts; @@ -495,7 +495,7 @@ err: return ret; } -static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) +irqreturn_t mtl_ipc_irq_thread(int irq, void *context) { struct sof_ipc4_msg notification_data = {{ 0 }}; struct snd_sof_dev *sdev = context; @@ -578,17 +578,17 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) return IRQ_HANDLED; } -static int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) +int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) { return MTL_DSP_MBOX_UPLINK_OFFSET; } -static int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) +int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) { return MTL_SRAM_WINDOW_OFFSET(id); } -static void mtl_ipc_dump(struct snd_sof_dev *sdev) +void mtl_ipc_dump(struct snd_sof_dev *sdev) { u32 hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl; @@ -612,9 +612,9 @@ static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev) return mtl_enable_interrupts(sdev, false); } -static u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream) +u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct hdac_stream *hstream = substream->runtime->private_data; u32 llp_l, llp_u; @@ -735,6 +735,7 @@ const struct sof_intel_dsp_desc mtl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = mtl_enable_sdw_irq, .check_sdw_irq = mtl_dsp_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = mtl_dsp_check_ipc_irq, .cl_init = mtl_dsp_cl_init, .power_down_dsp = mtl_power_down_dsp, diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index 2794fe6e8139..02181490f12a 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -82,10 +82,28 @@ #define MTL_DSP_REG_HfIMRIS1 0x162088 #define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0) +bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev); +int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); + void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev); void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev); -bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev); int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable); -int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); + +int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev); +int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev); +void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags); + int mtl_power_down_dsp(struct snd_sof_dev *sdev); +int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); + +irqreturn_t mtl_ipc_irq_thread(int irq, void *context); + +int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev); +int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id); + +void mtl_ipc_dump(struct snd_sof_dev *sdev); + +u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c index 69cad5a6bc72..460f87f25dac 100644 --- a/sound/soc/sof/intel/pci-apl.c +++ b/sound/soc/sof/intel/pci-apl.c @@ -85,12 +85,8 @@ static const struct sof_dev_desc glk_desc = { /* PCI IDs */ static const struct pci_device_id sof_pci_ids[] = { - { PCI_DEVICE(0x8086, 0x5a98), /* BXT-P (ApolloLake) */ - .driver_data = (unsigned long)&bxt_desc}, - { PCI_DEVICE(0x8086, 0x1a98),/* BXT-T */ - .driver_data = (unsigned long)&bxt_desc}, - { PCI_DEVICE(0x8086, 0x3198), /* GeminiLake */ - .driver_data = (unsigned long)&glk_desc}, + { PCI_DEVICE_DATA(INTEL, HDA_APL, &bxt_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_GML, &glk_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c index 8895508a0be6..e2c50e7b0aa7 100644 --- a/sound/soc/sof/intel/pci-cnl.c +++ b/sound/soc/sof/intel/pci-cnl.c @@ -120,16 +120,11 @@ static const struct sof_dev_desc cml_desc = { /* PCI IDs */ static const struct pci_device_id sof_pci_ids[] = { - { PCI_DEVICE(0x8086, 0x9dc8), /* CNL-LP */ - .driver_data = (unsigned long)&cnl_desc}, - { PCI_DEVICE(0x8086, 0xa348), /* CNL-H */ - .driver_data = (unsigned long)&cfl_desc}, - { PCI_DEVICE(0x8086, 0x02c8), /* CML-LP */ - .driver_data = (unsigned long)&cml_desc}, - { PCI_DEVICE(0x8086, 0x06c8), /* CML-H */ - .driver_data = (unsigned long)&cml_desc}, - { PCI_DEVICE(0x8086, 0xa3f0), /* CML-S */ - .driver_data = (unsigned long)&cml_desc}, + { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, &cnl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, &cfl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, &cml_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CML_H, &cml_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_CML_S, &cml_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c index 5fb5a820693e..0a65df3ed9e2 100644 --- a/sound/soc/sof/intel/pci-icl.c +++ b/sound/soc/sof/intel/pci-icl.c @@ -86,14 +86,10 @@ static const struct sof_dev_desc jsl_desc = { /* PCI IDs */ static const struct pci_device_id sof_pci_ids[] = { - { PCI_DEVICE(0x8086, 0x34C8), /* ICL-LP */ - .driver_data = (unsigned long)&icl_desc}, - { PCI_DEVICE(0x8086, 0x3dc8), /* ICL-H */ - .driver_data = (unsigned long)&icl_desc}, - { PCI_DEVICE(0x8086, 0x38c8), /* ICL-N */ - .driver_data = (unsigned long)&jsl_desc}, - { PCI_DEVICE(0x8086, 0x4dc8), /* JSL-N */ - .driver_data = (unsigned long)&jsl_desc}, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, &icl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, &icl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, &jsl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, &jsl_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c new file mode 100644 index 000000000000..1b12c280edb4 --- /dev/null +++ b/sound/soc/sof/intel/pci-lnl.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. +// +// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> +// + +#include <linux/module.h> +#include <linux/pci.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include <sound/sof.h> +#include "../ops.h" +#include "../sof-pci-dev.h" + +/* platform specific devices */ +#include "hda.h" +#include "mtl.h" + +static const struct sof_dev_desc lnl_desc = { + .use_acpi_target_states = true, + .machines = snd_soc_acpi_intel_lnl_machines, + .alt_machines = snd_soc_acpi_intel_lnl_sdw_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .chip_info = &lnl_chip_info, + .ipc_supported_mask = BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_INTEL_IPC4, + .dspless_mode_supported = true, + .default_fw_path = { + [SOF_INTEL_IPC4] = "intel/sof-ipc4/lnl", + }, + .default_tplg_path = { + [SOF_INTEL_IPC4] = "intel/sof-ace-tplg", + }, + .default_fw_filename = { + [SOF_INTEL_IPC4] = "sof-lnl.ri", + }, + .nocodec_tplg_filename = "sof-lnl-nocodec.tplg", + .ops = &sof_lnl_ops, + .ops_init = sof_lnl_ops_init, +}; + +/* PCI IDs */ +static const struct pci_device_id sof_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, &lnl_desc) }, /* LNL-P */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, sof_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_intel_lnl_driver = { + .name = "sof-audio-pci-intel-lnl", + .id_table = sof_pci_ids, + .probe = hda_pci_intel_probe, + .remove = sof_pci_remove, + .shutdown = sof_pci_shutdown, + .driver = { + .pm = &sof_pci_pm, + }, +}; +module_pci_driver(snd_sof_pci_intel_lnl_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HDA_COMMON); +MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV); diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c index e276e1e37fed..7868b0827e84 100644 --- a/sound/soc/sof/intel/pci-mtl.c +++ b/sound/soc/sof/intel/pci-mtl.c @@ -52,8 +52,7 @@ static const struct sof_dev_desc mtl_desc = { /* PCI IDs */ static const struct pci_device_id sof_pci_ids[] = { - { PCI_DEVICE(0x8086, 0x7E28), /* MTL */ - .driver_data = (unsigned long)&mtl_desc}, + { PCI_DEVICE_DATA(INTEL, HDA_MTL, &mtl_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); diff --git a/sound/soc/sof/intel/pci-skl.c b/sound/soc/sof/intel/pci-skl.c index 5e69af6eed34..a6588b138a8c 100644 --- a/sound/soc/sof/intel/pci-skl.c +++ b/sound/soc/sof/intel/pci-skl.c @@ -69,10 +69,8 @@ static struct sof_dev_desc kbl_desc = { /* PCI IDs */ static const struct pci_device_id sof_pci_ids[] = { - /* Sunrise Point-LP */ - { PCI_DEVICE(0x8086, 0x9d70), .driver_data = (unsigned long)&skl_desc}, - /* KBL */ - { PCI_DEVICE(0x8086, 0x9d71), .driver_data = (unsigned long)&kbl_desc}, + { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_KBL_LP, &kbl_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index ca37ff1bbd2a..d688f9373fb2 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -284,36 +284,21 @@ static const struct sof_dev_desc rpl_desc = { /* PCI IDs */ static const struct pci_device_id sof_pci_ids[] = { - { PCI_DEVICE(0x8086, 0xa0c8), /* TGL-LP */ - .driver_data = (unsigned long)&tgl_desc}, - { PCI_DEVICE(0x8086, 0x43c8), /* TGL-H */ - .driver_data = (unsigned long)&tglh_desc}, - { PCI_DEVICE(0x8086, 0x4b55), /* EHL */ - .driver_data = (unsigned long)&ehl_desc}, - { PCI_DEVICE(0x8086, 0x4b58), /* EHL */ - .driver_data = (unsigned long)&ehl_desc}, - { PCI_DEVICE(0x8086, 0x7ad0), /* ADL-S */ - .driver_data = (unsigned long)&adls_desc}, - { PCI_DEVICE(0x8086, 0x7a50), /* RPL-S */ - .driver_data = (unsigned long)&rpls_desc}, - { PCI_DEVICE(0x8086, 0x51c8), /* ADL-P */ - .driver_data = (unsigned long)&adl_desc}, - { PCI_DEVICE(0x8086, 0x51c9), /* ADL-PS */ - .driver_data = (unsigned long)&adl_desc}, - { PCI_DEVICE(0x8086, 0x51ca), /* RPL-P */ - .driver_data = (unsigned long)&rpl_desc}, - { PCI_DEVICE(0x8086, 0x51cb), /* RPL-P */ - .driver_data = (unsigned long)&rpl_desc}, - { PCI_DEVICE(0x8086, 0x51cc), /* ADL-M */ - .driver_data = (unsigned long)&adl_desc}, - { PCI_DEVICE(0x8086, 0x51cd), /* ADL-P */ - .driver_data = (unsigned long)&adl_desc}, - { PCI_DEVICE(0x8086, 0x51ce), /* RPL-M */ - .driver_data = (unsigned long)&rpl_desc}, - { PCI_DEVICE(0x8086, 0x51cf), /* RPL-PX */ - .driver_data = (unsigned long)&rpl_desc}, - { PCI_DEVICE(0x8086, 0x54c8), /* ADL-N */ - .driver_data = (unsigned long)&adl_n_desc}, + { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, &tgl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, &tglh_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, &ehl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, &ehl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, &adls_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, &rpls_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, &rpl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, &rpl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, &adl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, &rpl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, &rpl_desc) }, + { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, &adl_n_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index 8c22a00266c0..4ae4fe17cc0b 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -225,8 +225,7 @@ static const struct sof_dev_desc tng_desc = { /* PCI IDs */ static const struct pci_device_id sof_pci_ids[] = { - { PCI_DEVICE(0x8086, 0x119a), - .driver_data = (unsigned long)&tng_desc}, + { PCI_DEVICE_DATA(INTEL, SST_TNG, &tng_desc) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index 207df48e27cf..9515d753c816 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -189,6 +189,7 @@ struct sof_intel_dsp_desc { int (*read_sdw_lcount)(struct snd_sof_dev *sdev); void (*enable_sdw_irq)(struct snd_sof_dev *sdev, bool enable); bool (*check_sdw_irq)(struct snd_sof_dev *sdev); + bool (*check_sdw_wakeen_irq)(struct snd_sof_dev *sdev); bool (*check_ipc_irq)(struct snd_sof_dev *sdev); int (*power_down_dsp)(struct snd_sof_dev *sdev); int (*disable_interrupts)(struct snd_sof_dev *sdev); diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 8e2b07e1612b..bb9f20253c99 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -147,6 +147,7 @@ const struct sof_intel_dsp_desc tgl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, @@ -175,6 +176,7 @@ const struct sof_intel_dsp_desc tglh_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, @@ -203,6 +205,7 @@ const struct sof_intel_dsp_desc ehl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, @@ -231,6 +234,7 @@ const struct sof_intel_dsp_desc adls_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c index 35da85a45a9a..bd07f0472efd 100644 --- a/sound/soc/sof/ipc3-dtrace.c +++ b/sound/soc/sof/ipc3-dtrace.c @@ -196,15 +196,9 @@ static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user return -EINVAL; } - string = kmalloc(count + 1, GFP_KERNEL); - if (!string) - return -ENOMEM; - - if (copy_from_user(string, from, count)) { - ret = -EFAULT; - goto error; - } - string[count] = '\0'; + string = memdup_user_nul(from, count); + if (IS_ERR(string)) + return PTR_ERR(string); ret = trace_filter_parse(sdev, string, &num_elems, &elems); if (ret < 0) diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 304faf6425ab..cb58ee8c158a 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -309,6 +309,23 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = private->dai_config->afe.channels; channels->max = private->dai_config->afe.channels; + snd_mask_none(fmt); + + switch (private->dai_config->afe.format) { + case SOF_IPC_FRAME_S16_LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + break; + case SOF_IPC_FRAME_S24_4LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + break; + case SOF_IPC_FRAME_S32_LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); + break; + default: + dev_err(component->dev, "Not available format!\n"); + return -EINVAL; + } + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", channels->min, channels->max); diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 580960ff273d..fb40378ad084 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -312,7 +312,7 @@ static int ipc3_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data) } else { if (sof_debug_check_flag(SOF_DBG_PRINT_IPC_SUCCESS_LOGS)) ipc3_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd); - if (msg->reply_size) + if (reply_data && msg->reply_size) /* copy the data returned from DSP */ memcpy(reply_data, msg->reply_data, msg->reply_size); @@ -567,13 +567,10 @@ int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev, /* create read-only cc_version debugfs to store compiler version info */ /* use local copy of the cc_version to prevent data corruption */ if (sdev->first_boot) { - sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size, - GFP_KERNEL); - + sdev->cc_version = devm_kmemdup(sdev->dev, cc, cc->ext_hdr.hdr.size, GFP_KERNEL); if (!sdev->cc_version) return -ENOMEM; - memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size); ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version, cc->ext_hdr.hdr.size, "cc_version", 0444); diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 027416eb2f50..db19cd03ecad 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -23,7 +23,8 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state /* trigger a single pipeline */ if (trigger_list->count == 1) - return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_ids[0], state); + return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_instance_ids[0], + state); primary = state; primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE); @@ -42,15 +43,15 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, ipc_size); } -int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state) +int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 state) { struct sof_ipc4_msg msg = {{ 0 }}; u32 primary; - dev_dbg(sdev->dev, "ipc4 set pipeline %d state %d", id, state); + dev_dbg(sdev->dev, "ipc4 set pipeline instance %d state %d", instance_id, state); primary = state; - primary |= SOF_IPC4_GLB_PIPE_STATE_ID(id); + primary |= SOF_IPC4_GLB_PIPE_STATE_ID(instance_id); primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE); primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); @@ -79,19 +80,19 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, * for the first time */ if (spipe->started_count == spipe->paused_count) - trigger_list->pipeline_ids[trigger_list->count++] = + trigger_list->pipeline_instance_ids[trigger_list->count++] = pipe_widget->instance_id; break; case SOF_IPC4_PIPE_RESET: /* RESET if the pipeline is neither running nor paused */ if (!spipe->started_count && !spipe->paused_count) - trigger_list->pipeline_ids[trigger_list->count++] = + trigger_list->pipeline_instance_ids[trigger_list->count++] = pipe_widget->instance_id; break; case SOF_IPC4_PIPE_PAUSED: /* Pause the pipeline only when its started_count is 1 more than paused_count */ if (spipe->paused_count == (spipe->started_count - 1)) - trigger_list->pipeline_ids[trigger_list->count++] = + trigger_list->pipeline_instance_ids[trigger_list->count++] = pipe_widget->instance_id; break; default: @@ -113,7 +114,7 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, /* set state for pipeline if it was just triggered */ for (i = 0; i < trigger_list->count; i++) { - if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) { + if (trigger_list->pipeline_instance_ids[i] == pipe_widget->instance_id) { pipeline->state = state; break; } @@ -314,8 +315,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd); /* allocate memory for the pipeline data */ - trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count), - GFP_KERNEL); + trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids, + pipeline_list->count), GFP_KERNEL); if (!trigger_list) return -ENOMEM; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 11361e1cd688..f2a30cd31378 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -17,6 +17,19 @@ #include "ipc4-topology.h" #include "ops.h" +/* + * The ignore_cpc flag can be used to ignore the CPC value for all modules by + * using 0 instead. + * The CPC is sent to the firmware along with the SOF_IPC4_MOD_INIT_INSTANCE + * message and it is used for clock scaling. + * 0 as CPC value will instruct the firmware to use maximum frequency, thus + * deactivating the clock scaling. + */ +static bool ignore_cpc; +module_param_named(ipc4_ignore_cpc, ignore_cpc, bool, 0444); +MODULE_PARM_DESC(ipc4_ignore_cpc, + "Ignore CPC values. This option will disable clock scaling in firmware."); + #define SOF_IPC4_GAIN_PARAM_ID 0 #define SOF_IPC4_TPLG_ABI_SIZE 6 #define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2 @@ -970,9 +983,16 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * /* Update base_config->cpc from the module manifest */ sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); - dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n", - swidget->widget->name, base_config->ibs, base_config->obs, - base_config->cpc); + if (ignore_cpc) { + dev_dbg(sdev->dev, "%s: ibs / obs: %u / %u, forcing cpc to 0 from %u\n", + swidget->widget->name, base_config->ibs, base_config->obs, + base_config->cpc); + base_config->cpc = 0; + } else { + dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n", + swidget->widget->name, base_config->ibs, base_config->obs, + base_config->cpc); + } } static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev, @@ -1349,23 +1369,29 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s } #endif -static int ipc4_set_fmt_mask(struct snd_mask *fmt, unsigned int bit_depth) +static bool sof_ipc4_copier_is_single_format(struct snd_sof_dev *sdev, + struct sof_ipc4_pin_format *pin_fmts, + u32 pin_fmts_size) { - switch (bit_depth) { - case 16: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); - break; - case 24: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); - break; - case 32: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); - break; - default: - return -EINVAL; + struct sof_ipc4_audio_format *fmt; + u32 valid_bits; + int i; + + fmt = &pin_fmts[0].audio_fmt; + valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + /* check if all output formats in topology are the same */ + for (i = 1; i < pin_fmts_size; i++) { + u32 _valid_bits; + + fmt = &pin_fmts[i].audio_fmt; + _valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + if (_valid_bits != valid_bits) + return false; } - return 0; + return true; } static int @@ -1381,8 +1407,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *ref_params; struct sof_ipc4_copier *ipc4_copier; struct snd_sof_dai *dai; - struct snd_mask *fmt; - int out_sample_valid_bits; u32 gtw_cfg_config_length; u32 dma_config_tlv_size = 0; void **ipc_config_data; @@ -1392,6 +1416,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, u32 out_ref_rate, out_ref_channels; u32 deep_buffer_dma_ms = 0; int output_fmt_index; + bool single_output_format; dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id); @@ -1525,6 +1550,9 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return ret; /* set the reference params for output format selection */ + single_output_format = sof_ipc4_copier_is_single_format(sdev, + available_fmt->output_pin_fmts, + available_fmt->num_output_formats); switch (swidget->id) { case snd_soc_dapm_aif_in: case snd_soc_dapm_dai_out: @@ -1535,17 +1563,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; out_ref_rate = in_fmt->sampling_frequency; out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); - out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + + if (!single_output_format) + out_ref_valid_bits = + SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); break; } case snd_soc_dapm_aif_out: case snd_soc_dapm_dai_in: - out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); - if (out_ref_valid_bits < 0) - return out_ref_valid_bits; - out_ref_rate = params_rate(fe_params); out_ref_channels = params_channels(fe_params); + if (!single_output_format) { + out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + if (out_ref_valid_bits < 0) + return out_ref_valid_bits; + } break; default: /* @@ -1555,6 +1587,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return -EINVAL; } + /* + * if the output format is the same across all available output formats, choose + * that as the reference. + */ + if (single_output_format) { + struct sof_ipc4_audio_format *out_fmt; + + out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt; + out_ref_valid_bits = + SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg); + } + + dev_dbg(sdev->dev, "copier %s: reference output rate %d, channels %d valid_bits %d\n", + swidget->widget->name, out_ref_rate, out_ref_channels, out_ref_valid_bits); + output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &copier_data->base_config, available_fmt, out_ref_rate, out_ref_channels, out_ref_valid_bits); @@ -1664,11 +1711,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, } /* modify the input params for the next widget */ - fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT); - out_sample_valid_bits = - SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(copier_data->out_format.fmt_cfg); - snd_mask_none(fmt); - ret = ipc4_set_fmt_mask(fmt, out_sample_valid_bits); + ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &copier_data->out_format); if (ret) return ret; @@ -1746,6 +1789,12 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, gtw_cfg_config_length, &ipc4_copier->dma_config_tlv, dma_config_tlv_size); + /* + * Restore gateway config length now that IPC payload is prepared. This avoids + * counting the DMA CONFIG TLV multiple times + */ + copier_data->gtw_cfg.config_length = gtw_cfg_config_length / 4; + return 0; } @@ -1832,37 +1881,58 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_src *src = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt; - struct sof_ipc4_audio_format *in_fmt; + struct sof_ipc4_audio_format *out_audio_fmt; + struct sof_ipc4_audio_format *in_audio_fmt; u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; - struct snd_interval *rate; - int ret; + int output_format_index, input_format_index; - ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->base_config, - pipeline_params, available_fmt); - if (ret < 0) - return ret; + input_format_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->base_config, + pipeline_params, available_fmt); + if (input_format_index < 0) + return input_format_index; - in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; - out_ref_rate = in_fmt->sampling_frequency; - out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); - out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + /* + * For playback, the SRC sink rate will be configured based on the requested output + * format, which is restricted to only deal with DAI's with a single format for now. + */ + if (dir == SNDRV_PCM_STREAM_PLAYBACK && available_fmt->num_output_formats > 1) { + dev_err(sdev->dev, "Invalid number of output formats: %d for SRC %s\n", + available_fmt->num_output_formats, swidget->widget->name); + return -EINVAL; + } - ret = sof_ipc4_init_output_audio_fmt(sdev, &src->base_config, available_fmt, - out_ref_rate, out_ref_channels, out_ref_valid_bits); - if (ret < 0) { + /* + * SRC does not perform format conversion, so the output channels and valid bit depth must + * be the same as that of the input. + */ + in_audio_fmt = &available_fmt->input_pin_fmts[input_format_index].audio_fmt; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_audio_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_audio_fmt->fmt_cfg); + + /* + * For capture, the SRC module should convert the rate to match the rate requested by the + * PCM hw_params. Set the reference params based on the fe_params unconditionally as it + * will be ignored for playback anyway. + */ + out_ref_rate = params_rate(fe_params); + + output_format_index = sof_ipc4_init_output_audio_fmt(sdev, &src->base_config, + available_fmt, out_ref_rate, + out_ref_channels, out_ref_valid_bits); + if (output_format_index < 0) { dev_err(sdev->dev, "Failed to initialize output format for %s", swidget->widget->name); + return output_format_index; } /* update pipeline memory usage */ sof_ipc4_update_resource_usage(sdev, swidget, &src->base_config); - /* update pipeline_params for sink widgets */ - rate = hw_param_interval(pipeline_params, SNDRV_PCM_HW_PARAM_RATE); - rate->min = src->sink_rate; - rate->max = rate->min; + out_audio_fmt = &available_fmt->output_pin_fmts[output_format_index].audio_fmt; + src->sink_rate = out_audio_fmt->sampling_frequency; - return 0; + /* update pipeline_params for sink widgets */ + return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt); } static int @@ -2319,6 +2389,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget pipeline->mem_usage = 0; pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; ida_free(&pipeline_ida, swidget->instance_id); + swidget->instance_id = -EINVAL; } else { struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 6dcf14886e85..d75f17f4749c 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -144,11 +144,11 @@ struct sof_ipc4_pipeline { /** * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data * @count: Number of pipelines to be triggered - * @pipeline_ids: Flexible array of IDs of the pipelines to be triggered + * @pipeline_instance_ids: Flexible array of IDs of the pipelines to be triggered */ struct ipc4_pipeline_set_state_data { u32 count; - DECLARE_FLEX_ARRAY(u32, pipeline_ids); + DECLARE_FLEX_ARRAY(u32, pipeline_instance_ids); } __packed; /** diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index 3e0ea0e109e2..f587edf9e0a7 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -111,6 +111,14 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) dev_dbg(dev, "DMA %pR\n", &res); + adsp->pa_shared_dram = (phys_addr_t)res.start; + adsp->shared_size = resource_size(&res); + if (adsp->pa_shared_dram & DRAM_REMAP_MASK) { + dev_err(dev, "adsp shared dma memory(%#x) is not 4K-aligned\n", + (u32)adsp->pa_shared_dram); + return -EINVAL; + } + ret = of_reserved_mem_device_init(dev); if (ret) { dev_err(dev, "of_reserved_mem_device_init failed\n"); @@ -244,23 +252,18 @@ static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data) { struct device *dev = &pdev->dev; struct mtk_adsp_chip_info *adsp = data; - u32 shared_size; /* remap shared-dram base to be non-cachable */ - shared_size = TOTAL_SIZE_SHARED_DRAM_FROM_TAIL; - adsp->pa_shared_dram = adsp->pa_dram + adsp->dramsize - shared_size; - if (adsp->va_dram) { - adsp->shared_dram = adsp->va_dram + DSP_DRAM_SIZE - shared_size; - } else { - adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram, - shared_size); - if (!adsp->shared_dram) { - dev_err(dev, "ioremap failed for shared DRAM\n"); - return -ENOMEM; - } + adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram, + adsp->shared_size); + if (!adsp->shared_dram) { + dev_err(dev, "failed to ioremap base %pa size %#x\n", + adsp->shared_dram, adsp->shared_size); + return -ENOMEM; } - dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n", - adsp->shared_dram, &adsp->pa_shared_dram, shared_size); + + dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n", + adsp->shared_dram, &adsp->pa_shared_dram, adsp->shared_size); return 0; } @@ -307,9 +310,12 @@ static int mt8186_dsp_probe(struct snd_sof_dev *sdev) return -ENOMEM; } - sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, - priv->adsp->pa_dram, - priv->adsp->dramsize); + priv->adsp->va_sram = sdev->bar[SOF_FW_BLK_TYPE_IRAM]; + + sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap(sdev->dev, + priv->adsp->pa_dram, + priv->adsp->dramsize); + if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n", &priv->adsp->pa_dram, priv->adsp->dramsize); diff --git a/sound/soc/sof/sof-client-probes-ipc4.c b/sound/soc/sof/sof-client-probes-ipc4.c index ea21ef176c42..c56a85854d92 100644 --- a/sound/soc/sof/sof-client-probes-ipc4.c +++ b/sound/soc/sof/sof-client-probes-ipc4.c @@ -146,6 +146,9 @@ static int ipc4_probes_deinit(struct sof_client_dev *cdev) struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev); struct sof_ipc4_msg msg; + if (!mentry) + return -ENODEV; + msg.primary = mentry->id; msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_DELETE_INSTANCE); msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); @@ -197,6 +200,9 @@ static int ipc4_probes_points_add(struct sof_client_dev *cdev, struct sof_ipc4_msg msg; int i, ret; + if (!mentry) + return -ENODEV; + /* The sof_probe_point_desc and sof_ipc4_probe_point structs * are of same size and even the integers are the same in the * same order, and similar meaning, but since there is no @@ -247,6 +253,9 @@ static int ipc4_probes_points_remove(struct sof_client_dev *cdev, u32 *probe_point_ids; int i, ret; + if (!mentry) + return -ENODEV; + probe_point_ids = kcalloc(num_buffer_id, sizeof(*probe_point_ids), GFP_KERNEL); if (!probe_point_ids) diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index 8d9e9d5f40e4..740b637822db 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -354,10 +354,14 @@ static const struct file_operations sof_probes_points_remove_fops = { .owner = THIS_MODULE, }; +static const struct snd_soc_dai_ops sof_probes_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver sof_probes_dai_drv[] = { { .name = "Probe Extraction CPU DAI", - .compress_new = snd_soc_new_compress, + .ops = &sof_probes_dai_ops, .cops = &sof_probes_compr_ops, .capture = { .stream_name = "Probe Extraction", @@ -523,6 +527,7 @@ static void sof_probes_client_remove(struct auxiliary_device *auxdev) static const struct auxiliary_device_id sof_probes_client_id_table[] = { { .name = "snd_sof.hda-probes", }, + { .name = "snd_sof.acp-probes", }, {}, }; MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 698129dccc7d..a3a3af252259 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1117,10 +1117,11 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, { struct snd_soc_card *card = scomp->card; struct snd_soc_pcm_runtime *rtd; + const char *sname = w->sname; struct snd_soc_dai *cpu_dai; int i, stream; - if (!w->sname) + if (!sname) return; if (w->id == snd_soc_dapm_dai_out) @@ -1133,7 +1134,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, list_for_each_entry(rtd, &card->rtd_list, list) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || - strcmp(w->sname, rtd->dai_link->stream_name)) + strcmp(sname, rtd->dai_link->stream_name)) continue; for_each_rtd_cpu_dais(rtd, i, cpu_dai) @@ -1366,6 +1367,20 @@ err: return ret; } +static int get_w_no_wname_in_long_name(void *elem, void *object, u32 offset) +{ + struct snd_soc_tplg_vendor_value_elem *velem = elem; + struct snd_soc_dapm_widget *w = object; + + w->no_wname_in_kcontrol_name = !!le32_to_cpu(velem->value); + return 0; +} + +static const struct sof_topology_token dapm_widget_tokens[] = { + {SOF_TKN_COMP_NO_WNAME_IN_KCONTROL_NAME, SND_SOC_TPLG_TUPLE_TYPE_BOOL, + get_w_no_wname_in_long_name, 0} +}; + /* external widget init - used for any driver specific init */ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_widget *w, @@ -1396,6 +1411,14 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, ida_init(&swidget->output_queue_ida); ida_init(&swidget->input_queue_ida); + ret = sof_parse_tokens(scomp, w, dapm_widget_tokens, ARRAY_SIZE(dapm_widget_tokens), + priv->array, le32_to_cpu(priv->size)); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse dapm widget tokens for %s\n", + w->name); + goto widget_free; + } + ret = sof_parse_tokens(scomp, swidget, comp_pin_tokens, ARRAY_SIZE(comp_pin_tokens), priv->array, le32_to_cpu(priv->size)); @@ -2157,6 +2180,8 @@ static int sof_complete(struct snd_soc_component *scomp) struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct snd_sof_widget *swidget; + pipe_widget->instance_id = -EINVAL; + /* Update the scheduler widget's IPC structure */ if (widget_ops && widget_ops[pipe_widget->id].ipc_setup) { ret = widget_ops[pipe_widget->id].ipc_setup(pipe_widget); diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c index 5f5a6ca7dbda..8c117794b028 100644 --- a/sound/soc/starfive/jh7110_tdm.c +++ b/sound/soc/starfive/jh7110_tdm.c @@ -463,13 +463,6 @@ static int jh7110_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, return 0; } -static const struct snd_soc_dai_ops jh7110_tdm_dai_ops = { - .startup = jh7110_tdm_startup, - .hw_params = jh7110_tdm_hw_params, - .trigger = jh7110_tdm_trigger, - .set_fmt = jh7110_tdm_set_dai_fmt, -}; - static int jh7110_tdm_dai_probe(struct snd_soc_dai *dai) { struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai); @@ -479,6 +472,14 @@ static int jh7110_tdm_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops jh7110_tdm_dai_ops = { + .probe = jh7110_tdm_dai_probe, + .startup = jh7110_tdm_startup, + .hw_params = jh7110_tdm_hw_params, + .trigger = jh7110_tdm_trigger, + .set_fmt = jh7110_tdm_set_dai_fmt, +}; + #define JH7110_TDM_RATES SNDRV_PCM_RATE_8000_48000 #define JH7110_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -502,7 +503,6 @@ static struct snd_soc_dai_driver jh7110_tdm_dai = { .formats = JH7110_TDM_FORMATS, }, .ops = &jh7110_tdm_dai_ops, - .probe = jh7110_tdm_dai_probe, .symmetric_rate = 1, }; @@ -634,10 +634,9 @@ err_pm_disable: return ret; } -static int jh7110_tdm_dev_remove(struct platform_device *pdev) +static void jh7110_tdm_dev_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - return 0; } static const struct of_device_id jh7110_tdm_of_match[] = { @@ -661,7 +660,7 @@ static struct platform_driver jh7110_tdm_driver = { .pm = pm_ptr(&jh7110_tdm_pm_ops), }, .probe = jh7110_tdm_probe, - .remove = jh7110_tdm_dev_remove, + .remove_new = jh7110_tdm_dev_remove, }; module_platform_driver(jh7110_tdm_driver); diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c index a4d74d1e3c24..2c21a86421e6 100644 --- a/sound/soc/sti/sti_uniperif.c +++ b/sound/soc/sti/sti_uniperif.c @@ -369,10 +369,14 @@ static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai) return sti_uniperiph_dai_create_ctrl(dai); } -static const struct snd_soc_dai_driver sti_uniperiph_dai_template = { +static const struct snd_soc_dai_ops sti_uniperiph_dai_ops = { .probe = sti_uniperiph_dai_probe, }; +static const struct snd_soc_dai_driver sti_uniperiph_dai_template = { + .ops = &sti_uniperiph_dai_ops, +}; + static const struct snd_soc_component_driver sti_uniperiph_dai_component = { .name = "sti_cpu_dai", .suspend = sti_uniperiph_suspend, diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 387130701960..06a42130f5e4 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -953,6 +953,7 @@ static const struct regmap_config stm32_h7_i2s_regmap_conf = { }; static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { + .probe = stm32_i2s_dai_probe, .set_sysclk = stm32_i2s_set_sysclk, .set_fmt = stm32_i2s_set_dai_fmt, .startup = stm32_i2s_startup, @@ -1002,7 +1003,6 @@ static int stm32_i2s_dais_init(struct platform_device *pdev, if (!dai_ptr) return -ENOMEM; - dai_ptr->probe = stm32_i2s_dai_probe; dai_ptr->ops = &stm32_i2s_pcm_dai_ops; dai_ptr->id = 1; stm32_i2s_dai_init(&dai_ptr->playback, "playback"); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 271ec5b3378d..f9b5d5969155 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1222,6 +1222,19 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) } static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { + .probe = stm32_sai_dai_probe, + .set_sysclk = stm32_sai_set_sysclk, + .set_fmt = stm32_sai_set_dai_fmt, + .set_tdm_slot = stm32_sai_set_dai_tdm_slot, + .startup = stm32_sai_startup, + .hw_params = stm32_sai_hw_params, + .trigger = stm32_sai_trigger, + .shutdown = stm32_sai_shutdown, + .pcm_new = stm32_sai_pcm_new, +}; + +static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops2 = { + .probe = stm32_sai_dai_probe, .set_sysclk = stm32_sai_set_sysclk, .set_fmt = stm32_sai_set_dai_fmt, .set_tdm_slot = stm32_sai_set_dai_tdm_slot, @@ -1233,7 +1246,7 @@ static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - void *buf, unsigned long bytes) + struct iov_iter *buf, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); @@ -1287,8 +1300,6 @@ static const struct snd_pcm_hardware stm32_sai_pcm_hw = { }; static struct snd_soc_dai_driver stm32_sai_playback_dai = { - .probe = stm32_sai_dai_probe, - .pcm_new = stm32_sai_pcm_new, .id = 1, /* avoid call to fmt_single_name() */ .playback = { .channels_min = 1, @@ -1306,7 +1317,6 @@ static struct snd_soc_dai_driver stm32_sai_playback_dai = { }; static struct snd_soc_dai_driver stm32_sai_capture_dai = { - .probe = stm32_sai_dai_probe, .id = 1, /* avoid call to fmt_single_name() */ .capture = { .channels_min = 1, @@ -1320,7 +1330,7 @@ static struct snd_soc_dai_driver stm32_sai_capture_dai = { SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, }, - .ops = &stm32_sai_pcm_dai_ops, + .ops = &stm32_sai_pcm_dai_ops2, }; static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = { diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index a4066f271f2d..a359b528b26b 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -856,6 +856,7 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops stm32_spdifrx_pcm_dai_ops = { + .probe = stm32_spdifrx_dai_probe, .startup = stm32_spdifrx_startup, .hw_params = stm32_spdifrx_hw_params, .trigger = stm32_spdifrx_trigger, @@ -864,7 +865,6 @@ static const struct snd_soc_dai_ops stm32_spdifrx_pcm_dai_ops = { static struct snd_soc_dai_driver stm32_spdifrx_dai[] = { { - .probe = stm32_spdifrx_dai_probe, .capture = { .stream_name = "CPU-Capture", .channels_min = 1, diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 55328850aef5..f0a5fd901101 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -1252,9 +1252,12 @@ static int sun4i_codec_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops dummy_dai_ops = { + .probe = sun4i_codec_dai_probe, +}; + static struct snd_soc_dai_driver dummy_cpu_dai = { .name = "sun4i-codec-cpu-dai", - .probe = sun4i_codec_dai_probe, .playback = { .stream_name = "Playback", .channels_min = 1, @@ -1271,6 +1274,7 @@ static struct snd_soc_dai_driver dummy_cpu_dai = { .formats = SUN4I_CODEC_FORMATS, .sig_bits = 24, }, + .ops = &dummy_dai_ops, }; static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 669d712bbe9f..5124b6c9ceb4 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -1081,14 +1081,6 @@ static int sun4i_i2s_set_tdm_slot(struct snd_soc_dai *dai, return 0; } -static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { - .hw_params = sun4i_i2s_hw_params, - .set_fmt = sun4i_i2s_set_fmt, - .set_sysclk = sun4i_i2s_set_sysclk, - .set_tdm_slot = sun4i_i2s_set_tdm_slot, - .trigger = sun4i_i2s_trigger, -}; - static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) { struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); @@ -1100,12 +1092,20 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { + .probe = sun4i_i2s_dai_probe, + .hw_params = sun4i_i2s_hw_params, + .set_fmt = sun4i_i2s_set_fmt, + .set_sysclk = sun4i_i2s_set_sysclk, + .set_tdm_slot = sun4i_i2s_set_tdm_slot, + .trigger = sun4i_i2s_trigger, +}; + #define SUN4I_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_LE | \ SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_driver sun4i_i2s_dai = { - .probe = sun4i_i2s_dai_probe, .capture = { .stream_name = "Capture", .channels_min = 1, diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index ff18d4113aac..b849bb7cf58e 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -508,6 +508,7 @@ static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops sun4i_spdif_dai_ops = { + .probe = sun4i_spdif_soc_dai_probe, .startup = sun4i_spdif_startup, .trigger = sun4i_spdif_trigger, .hw_params = sun4i_spdif_hw_params, @@ -533,7 +534,6 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = { .rates = SUN4I_RATES, .formats = SUN4I_FORMATS, }, - .probe = sun4i_spdif_soc_dai_probe, .ops = &sun4i_spdif_dai_ops, .name = "spdif", }; @@ -718,7 +718,7 @@ static const struct dev_pm_ops sun4i_spdif_pm = { static struct platform_driver sun4i_spdif_driver = { .driver = { .name = "sun4i-spdif", - .of_match_table = of_match_ptr(sun4i_spdif_of_match), + .of_match_table = sun4i_spdif_of_match, .pm = &sun4i_spdif_pm, }, .probe = sun4i_spdif_probe, diff --git a/sound/soc/sunxi/sun50i-dmic.c b/sound/soc/sunxi/sun50i-dmic.c index c10439b9e0a2..2599683a582d 100644 --- a/sound/soc/sunxi/sun50i-dmic.c +++ b/sound/soc/sunxi/sun50i-dmic.c @@ -236,6 +236,7 @@ static int sun50i_dmic_soc_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops sun50i_dmic_dai_ops = { + .probe = sun50i_dmic_soc_dai_probe, .startup = sun50i_dmic_startup, .trigger = sun50i_dmic_trigger, .hw_params = sun50i_dmic_hw_params, @@ -260,7 +261,6 @@ static struct snd_soc_dai_driver sun50i_dmic_dai = { .formats = SUN50I_DMIC_FORMATS, .sig_bits = 21, }, - .probe = sun50i_dmic_soc_dai_probe, .ops = &sun50i_dmic_dai_ops, .name = "dmic", }; diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index a4073a746ae3..e713feca25fa 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -203,10 +203,6 @@ static int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = { - .trigger = tegra20_ac97_trigger, -}; - static int tegra20_ac97_probe(struct snd_soc_dai *dai) { struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); @@ -217,9 +213,13 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = { + .probe = tegra20_ac97_probe, + .trigger = tegra20_ac97_trigger, +}; + static struct snd_soc_dai_driver tegra20_ac97_dai = { .name = "tegra-ac97-pcm", - .probe = tegra20_ac97_probe, .playback = { .stream_name = "PCM Playback", .channels_min = 2, @@ -328,8 +328,7 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) goto err; } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(&pdev->dev, mem); + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(regs)) { ret = PTR_ERR(regs); goto err_clk_put; diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index d38b58305c6b..f11618e8f13e 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -310,6 +310,7 @@ static int tegra20_i2s_startup(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { + .probe = tegra20_i2s_probe, .set_fmt = tegra20_i2s_set_fmt, .hw_params = tegra20_i2s_hw_params, .trigger = tegra20_i2s_trigger, @@ -317,7 +318,6 @@ static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { }; static const struct snd_soc_dai_driver tegra20_i2s_dai_template = { - .probe = tegra20_i2s_probe, .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index d034803695a0..b0670aa4d967 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -241,6 +241,7 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { + .probe = tegra20_spdif_probe, .hw_params = tegra20_spdif_hw_params, .trigger = tegra20_spdif_trigger, .startup = tegra20_spdif_startup, @@ -248,7 +249,6 @@ static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { static struct snd_soc_dai_driver tegra20_spdif_dai = { .name = "tegra20-spdif", - .probe = tegra20_spdif_probe, .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c index 6868508585a0..9f9334e48049 100644 --- a/sound/soc/tegra/tegra210_admaif.c +++ b/sound/soc/tegra/tegra210_admaif.c @@ -419,11 +419,6 @@ static int tegra_admaif_trigger(struct snd_pcm_substream *substream, int cmd, } } -static const struct snd_soc_dai_ops tegra_admaif_dai_ops = { - .hw_params = tegra_admaif_hw_params, - .trigger = tegra_admaif_trigger, -}; - static int tegra210_admaif_pget_mono_to_stereo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -550,10 +545,15 @@ static int tegra_admaif_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops tegra_admaif_dai_ops = { + .probe = tegra_admaif_dai_probe, + .hw_params = tegra_admaif_hw_params, + .trigger = tegra_admaif_trigger, +}; + #define DAI(dai_name) \ { \ .name = dai_name, \ - .probe = tegra_admaif_dai_probe, \ .playback = { \ .stream_name = dai_name " Playback", \ .channels_min = 1, \ diff --git a/sound/soc/tegra/tegra210_sfc.c b/sound/soc/tegra/tegra210_sfc.c index e9df1ffc8a58..c2240babd601 100644 --- a/sound/soc/tegra/tegra210_sfc.c +++ b/sound/soc/tegra/tegra210_sfc.c @@ -2,7 +2,7 @@ // // tegra210_sfc.c - Tegra210 SFC driver // -// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. +// Copyright (c) 2021-2023 NVIDIA CORPORATION. All rights reserved. #include <linux/clk.h> #include <linux/device.h> @@ -42,6 +42,7 @@ static const int tegra210_sfc_rates[TEGRA210_SFC_NUM_RATES] = { 32000, 44100, 48000, + 64000, 88200, 96000, 176400, @@ -2857,6 +2858,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_8to32, coef_8to44, coef_8to48, + UNSUPP_CONV, coef_8to88, coef_8to96, UNSUPP_CONV, @@ -2872,6 +2874,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_11to32, coef_11to44, coef_11to48, + UNSUPP_CONV, coef_11to88, coef_11to96, UNSUPP_CONV, @@ -2887,6 +2890,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_16to32, coef_16to44, coef_16to48, + UNSUPP_CONV, coef_16to88, coef_16to96, coef_16to176, @@ -2902,6 +2906,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_22to32, coef_22to44, coef_22to48, + UNSUPP_CONV, coef_22to88, coef_22to96, coef_22to176, @@ -2917,6 +2922,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_24to32, coef_24to44, coef_24to48, + UNSUPP_CONV, coef_24to88, coef_24to96, coef_24to176, @@ -2932,6 +2938,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { BYPASS_CONV, coef_32to44, coef_32to48, + UNSUPP_CONV, coef_32to88, coef_32to96, coef_32to176, @@ -2947,6 +2954,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_44to32, BYPASS_CONV, coef_44to48, + UNSUPP_CONV, coef_44to88, coef_44to96, coef_44to176, @@ -2962,11 +2970,28 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_48to32, coef_48to44, BYPASS_CONV, + UNSUPP_CONV, coef_48to88, coef_48to96, coef_48to176, coef_48to192, }, + /* Convertions from 64 kHz */ + { + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + UNSUPP_CONV, + }, /* Convertions from 88.2 kHz */ { coef_88to8, @@ -2977,6 +3002,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_88to32, coef_88to44, coef_88to48, + UNSUPP_CONV, BYPASS_CONV, coef_88to96, coef_88to176, @@ -2991,6 +3017,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_96to32, coef_96to44, coef_96to48, + UNSUPP_CONV, coef_96to88, BYPASS_CONV, coef_96to176, @@ -3006,6 +3033,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_176to32, coef_176to44, coef_176to48, + UNSUPP_CONV, coef_176to88, coef_176to96, BYPASS_CONV, @@ -3021,6 +3049,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { coef_192to32, coef_192to44, coef_192to48, + UNSUPP_CONV, coef_192to88, coef_192to96, coef_192to176, diff --git a/sound/soc/tegra/tegra210_sfc.h b/sound/soc/tegra/tegra210_sfc.h index 5a6b66e297d8..a4c993d79403 100644 --- a/sound/soc/tegra/tegra210_sfc.h +++ b/sound/soc/tegra/tegra210_sfc.h @@ -2,7 +2,7 @@ /* * tegra210_sfc.h - Definitions for Tegra210 SFC driver * - * Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2021-2023 NVIDIA CORPORATION. All rights reserved. * */ @@ -47,7 +47,7 @@ #define TEGRA210_SFC_EN_SHIFT 0 #define TEGRA210_SFC_EN (1 << TEGRA210_SFC_EN_SHIFT) -#define TEGRA210_SFC_NUM_RATES 12 +#define TEGRA210_SFC_NUM_RATES 13 /* Fields in TEGRA210_SFC_COEF_RAM */ #define TEGRA210_SFC_COEF_RAM_EN BIT(0) diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 644280603095..81eaece51130 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -304,6 +304,7 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops tegra30_i2s_dai_ops = { + .probe = tegra30_i2s_probe, .set_fmt = tegra30_i2s_set_fmt, .hw_params = tegra30_i2s_hw_params, .trigger = tegra30_i2s_trigger, @@ -311,7 +312,6 @@ static const struct snd_soc_dai_ops tegra30_i2s_dai_ops = { }; static const struct snd_soc_dai_driver tegra30_i2s_dai_template = { - .probe = tegra30_i2s_probe, .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c index 983d69b951b0..544cb3da50eb 100644 --- a/sound/soc/ti/davinci-evm.c +++ b/sound/soc/ti/davinci-evm.c @@ -257,18 +257,7 @@ static struct platform_driver davinci_evm_driver = { }, }; -static int __init evm_init(void) -{ - return platform_driver_register(&davinci_evm_driver); -} - -static void __exit evm_exit(void) -{ - platform_driver_unregister(&davinci_evm_driver); -} - -module_init(evm_init); -module_exit(evm_exit); +module_platform_driver(davinci_evm_driver); MODULE_AUTHOR("Vladimir Barinov"); MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver"); diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c index 97dd1634b6be..07c8b2259208 100644 --- a/sound/soc/ti/davinci-i2s.c +++ b/sound/soc/ti/davinci-i2s.c @@ -601,16 +601,6 @@ static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, #define DAVINCI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S32_LE) -static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { - .shutdown = davinci_i2s_shutdown, - .prepare = davinci_i2s_prepare, - .trigger = davinci_i2s_trigger, - .hw_params = davinci_i2s_hw_params, - .set_fmt = davinci_i2s_set_dai_fmt, - .set_clkdiv = davinci_i2s_dai_set_clkdiv, - -}; - static int davinci_i2s_dai_probe(struct snd_soc_dai *dai) { struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); @@ -622,8 +612,18 @@ static int davinci_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { + .probe = davinci_i2s_dai_probe, + .shutdown = davinci_i2s_shutdown, + .prepare = davinci_i2s_prepare, + .trigger = davinci_i2s_trigger, + .hw_params = davinci_i2s_hw_params, + .set_fmt = davinci_i2s_set_dai_fmt, + .set_clkdiv = davinci_i2s_dai_set_clkdiv, + +}; + static struct snd_soc_dai_driver davinci_i2s_dai = { - .probe = davinci_i2s_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 172fea764a31..7e7d665a5504 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1616,18 +1616,6 @@ static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream, } } -static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { - .startup = davinci_mcasp_startup, - .shutdown = davinci_mcasp_shutdown, - .trigger = davinci_mcasp_trigger, - .delay = davinci_mcasp_delay, - .hw_params = davinci_mcasp_hw_params, - .set_fmt = davinci_mcasp_set_dai_fmt, - .set_clkdiv = davinci_mcasp_set_clkdiv, - .set_sysclk = davinci_mcasp_set_sysclk, - .set_tdm_slot = davinci_mcasp_set_tdm_slot, -}; - static int davinci_mcasp_iec958_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1716,6 +1704,19 @@ static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { + .probe = davinci_mcasp_dai_probe, + .startup = davinci_mcasp_startup, + .shutdown = davinci_mcasp_shutdown, + .trigger = davinci_mcasp_trigger, + .delay = davinci_mcasp_delay, + .hw_params = davinci_mcasp_hw_params, + .set_fmt = davinci_mcasp_set_dai_fmt, + .set_clkdiv = davinci_mcasp_set_clkdiv, + .set_sysclk = davinci_mcasp_set_sysclk, + .set_tdm_slot = davinci_mcasp_set_tdm_slot, +}; + #define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000 #define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \ @@ -1732,7 +1733,6 @@ static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver davinci_mcasp_dai[] = { { .name = "davinci-mcasp.0", - .probe = davinci_mcasp_dai_probe, .playback = { .stream_name = "IIS Playback", .channels_min = 1, @@ -1753,7 +1753,6 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { }, { .name = "davinci-mcasp.1", - .probe = davinci_mcasp_dai_probe, .playback = { .stream_name = "DIT Playback", .channels_min = 1, diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c index 825c70a443da..5b5eccf303ab 100644 --- a/sound/soc/ti/omap-dmic.c +++ b/sound/soc/ti/omap-dmic.c @@ -401,15 +401,6 @@ static int omap_dmic_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, return -EINVAL; } -static const struct snd_soc_dai_ops omap_dmic_dai_ops = { - .startup = omap_dmic_dai_startup, - .shutdown = omap_dmic_dai_shutdown, - .hw_params = omap_dmic_dai_hw_params, - .prepare = omap_dmic_dai_prepare, - .trigger = omap_dmic_dai_trigger, - .set_sysclk = omap_dmic_set_dai_sysclk, -}; - static int omap_dmic_probe(struct snd_soc_dai *dai) { struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); @@ -438,10 +429,19 @@ static int omap_dmic_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops omap_dmic_dai_ops = { + .probe = omap_dmic_probe, + .remove = omap_dmic_remove, + .startup = omap_dmic_dai_startup, + .shutdown = omap_dmic_dai_shutdown, + .hw_params = omap_dmic_dai_hw_params, + .prepare = omap_dmic_dai_prepare, + .trigger = omap_dmic_dai_trigger, + .set_sysclk = omap_dmic_set_dai_sysclk, +}; + static struct snd_soc_dai_driver omap_dmic_dai = { .name = "omap-dmic", - .probe = omap_dmic_probe, - .remove = omap_dmic_remove, .capture = { .channels_min = 2, .channels_max = 6, @@ -488,12 +488,10 @@ static int asoc_dmic_probe(struct platform_device *pdev) dmic->dma_data.filter_data = "up_link"; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - dmic->io_base = devm_ioremap_resource(&pdev->dev, res); + dmic->io_base = devm_platform_ioremap_resource_byname(pdev, "mpu"); if (IS_ERR(dmic->io_base)) return PTR_ERR(dmic->io_base); - ret = devm_snd_soc_register_component(&pdev->dev, &omap_dmic_component, &omap_dmic_dai, 1); diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c index 21fa7b978799..fdabed5133e8 100644 --- a/sound/soc/ti/omap-mcbsp.c +++ b/sound/soc/ti/omap-mcbsp.c @@ -70,8 +70,8 @@ static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) fck_src = clk_get(mcbsp->dev, src); if (IS_ERR(fck_src)) { - dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); - return -EINVAL; + dev_info(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); + return 0; } pm_runtime_put_sync(mcbsp->dev); @@ -1254,18 +1254,6 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, return err; } -static const struct snd_soc_dai_ops mcbsp_dai_ops = { - .startup = omap_mcbsp_dai_startup, - .shutdown = omap_mcbsp_dai_shutdown, - .prepare = omap_mcbsp_dai_prepare, - .trigger = omap_mcbsp_dai_trigger, - .delay = omap_mcbsp_dai_delay, - .hw_params = omap_mcbsp_dai_hw_params, - .set_fmt = omap_mcbsp_dai_set_dai_fmt, - .set_clkdiv = omap_mcbsp_dai_set_clkdiv, - .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, -}; - static int omap_mcbsp_probe(struct snd_soc_dai *dai) { struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai); @@ -1288,9 +1276,21 @@ static int omap_mcbsp_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops mcbsp_dai_ops = { + .probe = omap_mcbsp_probe, + .remove = omap_mcbsp_remove, + .startup = omap_mcbsp_dai_startup, + .shutdown = omap_mcbsp_dai_shutdown, + .prepare = omap_mcbsp_dai_prepare, + .trigger = omap_mcbsp_dai_trigger, + .delay = omap_mcbsp_dai_delay, + .hw_params = omap_mcbsp_dai_hw_params, + .set_fmt = omap_mcbsp_dai_set_dai_fmt, + .set_clkdiv = omap_mcbsp_dai_set_clkdiv, + .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, +}; + static struct snd_soc_dai_driver omap_mcbsp_dai = { - .probe = omap_mcbsp_probe, - .remove = omap_mcbsp_remove, .playback = { .channels_min = 1, .channels_max = 16, diff --git a/sound/soc/ti/omap-mcpdm.c b/sound/soc/ti/omap-mcpdm.c index 0b18a7bfd3fd..d7d9f708f1fd 100644 --- a/sound/soc/ti/omap-mcpdm.c +++ b/sound/soc/ti/omap-mcpdm.c @@ -404,13 +404,6 @@ static int omap_mcpdm_prepare(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops omap_mcpdm_dai_ops = { - .startup = omap_mcpdm_dai_startup, - .shutdown = omap_mcpdm_dai_shutdown, - .hw_params = omap_mcpdm_dai_hw_params, - .prepare = omap_mcpdm_prepare, -}; - static int omap_mcpdm_probe(struct snd_soc_dai *dai) { struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); @@ -457,6 +450,17 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops omap_mcpdm_dai_ops = { + .probe = omap_mcpdm_probe, + .remove = omap_mcpdm_remove, + .startup = omap_mcpdm_dai_startup, + .shutdown = omap_mcpdm_dai_shutdown, + .hw_params = omap_mcpdm_dai_hw_params, + .prepare = omap_mcpdm_prepare, + .probe_order = SND_SOC_COMP_ORDER_LATE, + .remove_order = SND_SOC_COMP_ORDER_EARLY, +}; + #ifdef CONFIG_PM_SLEEP static int omap_mcpdm_suspend(struct snd_soc_component *component) { @@ -502,10 +506,6 @@ static int omap_mcpdm_resume(struct snd_soc_component *component) #define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE static struct snd_soc_dai_driver omap_mcpdm_dai = { - .probe = omap_mcpdm_probe, - .remove = omap_mcpdm_remove, - .probe_order = SND_SOC_COMP_ORDER_LATE, - .remove_order = SND_SOC_COMP_ORDER_EARLY, .playback = { .channels_min = 1, .channels_max = 5, @@ -563,8 +563,7 @@ static int asoc_mcpdm_probe(struct platform_device *pdev) mcpdm->dma_data[0].filter_data = "dn_link"; mcpdm->dma_data[1].filter_data = "up_link"; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res); + mcpdm->io_base = devm_platform_ioremap_resource_byname(pdev, "mpu"); if (IS_ERR(mcpdm->io_base)) return PTR_ERR(mcpdm->io_base); diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index 4e8d5f7532ba..7c5188477b7c 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -355,30 +355,7 @@ static int uniphier_aio_prepare(struct snd_pcm_substream *substream, return 0; } -const struct snd_soc_dai_ops uniphier_aio_i2s_ops = { - .set_sysclk = uniphier_aio_set_sysclk, - .set_pll = uniphier_aio_set_pll, - .set_fmt = uniphier_aio_set_fmt, - .startup = uniphier_aio_startup, - .shutdown = uniphier_aio_shutdown, - .hw_params = uniphier_aio_hw_params, - .hw_free = uniphier_aio_hw_free, - .prepare = uniphier_aio_prepare, -}; -EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ops); - -const struct snd_soc_dai_ops uniphier_aio_spdif_ops = { - .set_sysclk = uniphier_aio_set_sysclk, - .set_pll = uniphier_aio_set_pll, - .startup = uniphier_aio_startup, - .shutdown = uniphier_aio_shutdown, - .hw_params = uniphier_aio_hw_params, - .hw_free = uniphier_aio_hw_free, - .prepare = uniphier_aio_prepare, -}; -EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ops); - -int uniphier_aio_dai_probe(struct snd_soc_dai *dai) +static int uniphier_aio_dai_probe(struct snd_soc_dai *dai) { struct uniphier_aio *aio = uniphier_priv(dai); int i; @@ -403,9 +380,8 @@ int uniphier_aio_dai_probe(struct snd_soc_dai *dai) return 0; } -EXPORT_SYMBOL_GPL(uniphier_aio_dai_probe); -int uniphier_aio_dai_remove(struct snd_soc_dai *dai) +static int uniphier_aio_dai_remove(struct snd_soc_dai *dai) { struct uniphier_aio *aio = uniphier_priv(dai); @@ -413,7 +389,138 @@ int uniphier_aio_dai_remove(struct snd_soc_dai *dai) return 0; } -EXPORT_SYMBOL_GPL(uniphier_aio_dai_remove); + +static int uniphier_aio_ld11_probe(struct snd_soc_dai *dai) +{ + int ret; + + ret = uniphier_aio_dai_probe(dai); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000); + if (ret < 0) + return ret; + ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800); + if (ret < 0) + return ret; + ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800); + if (ret < 0) + return ret; + + return 0; +} + +static int uniphier_aio_pxs2_probe(struct snd_soc_dai *dai) +{ + int ret; + + ret = uniphier_aio_dai_probe(dai); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000); + if (ret < 0) + return ret; + ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800); + if (ret < 0) + return ret; + ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800); + if (ret < 0) + return ret; + + return 0; +} + +const struct snd_soc_dai_ops uniphier_aio_i2s_ld11_ops = { + .probe = uniphier_aio_ld11_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .set_fmt = uniphier_aio_set_fmt, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ld11_ops); + +const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops = { + .probe = uniphier_aio_ld11_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ld11_ops); + +const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops2 = { + .probe = uniphier_aio_ld11_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, + .compress_new = snd_soc_new_compress, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ld11_ops2); + +const struct snd_soc_dai_ops uniphier_aio_i2s_pxs2_ops = { + .probe = uniphier_aio_pxs2_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .set_fmt = uniphier_aio_set_fmt, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_i2s_pxs2_ops); + +const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops = { + .probe = uniphier_aio_pxs2_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_spdif_pxs2_ops); + +const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops2 = { + .probe = uniphier_aio_pxs2_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, + .compress_new = snd_soc_new_compress, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_spdif_pxs2_ops2); static void uniphier_aio_dai_suspend(struct snd_soc_dai *dai) { diff --git a/sound/soc/uniphier/aio-ld11.c b/sound/soc/uniphier/aio-ld11.c index 7b3cf5d751f6..15dbded63804 100644 --- a/sound/soc/uniphier/aio-ld11.c +++ b/sound/soc/uniphier/aio-ld11.c @@ -188,36 +188,9 @@ static const struct uniphier_aio_pll uniphier_aio_pll_ld11[] = { [AUD_PLL_HSC0] = { .enable = true, }, }; -static int uniphier_aio_ld11_probe(struct snd_soc_dai *dai) -{ - int ret; - - ret = uniphier_aio_dai_probe(dai); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800); - if (ret < 0) - return ret; - - return 0; -} - static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { { .name = AUD_GNAME_HDMI, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_PCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -234,12 +207,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_PCMIN2, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .capture = { .stream_name = AUD_NAME_PCMIN2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -247,12 +218,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_GNAME_LINE, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_PCMOUT2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -267,12 +236,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_HPCMOUT1, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_HPCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -280,12 +247,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 8, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_PCMOUT3, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_PCMOUT3, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -293,12 +258,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_HIECOUT1, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_HIECOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -306,12 +269,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_ld11_ops, }, { .name = AUD_NAME_EPCMOUT2, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_EPCMOUT2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -321,12 +282,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_EPCMOUT3, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_EPCMOUT3, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -336,19 +295,16 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_HIECCOMPOUT1, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, - .compress_new = snd_soc_new_compress, .playback = { .stream_name = AUD_NAME_HIECCOMPOUT1, .channels_min = 1, .channels_max = 1, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_ld11_ops2, }, }; diff --git a/sound/soc/uniphier/aio-pxs2.c b/sound/soc/uniphier/aio-pxs2.c index 899904f7ffd6..305cb2a1253d 100644 --- a/sound/soc/uniphier/aio-pxs2.c +++ b/sound/soc/uniphier/aio-pxs2.c @@ -141,36 +141,9 @@ static const struct uniphier_aio_pll uniphier_aio_pll_pxs2[] = { [AUD_PLL_HSC0] = { .enable = true, }, }; -static int uniphier_aio_pxs2_probe(struct snd_soc_dai *dai) -{ - int ret; - - ret = uniphier_aio_dai_probe(dai); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800); - if (ret < 0) - return ret; - - return 0; -} - static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { { .name = AUD_GNAME_HDMI, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_HPCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -178,12 +151,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_pxs2_ops, }, { .name = AUD_GNAME_LINE, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_PCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -198,12 +169,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_pxs2_ops, }, { .name = AUD_GNAME_AUX, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_PCMOUT2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -218,12 +187,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_pxs2_ops, }, { .name = AUD_NAME_HIECOUT1, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_HIECOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -231,12 +198,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_pxs2_ops, }, { .name = AUD_NAME_IECOUT1, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_IECOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -244,31 +209,25 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_pxs2_ops, }, { .name = AUD_NAME_HIECCOMPOUT1, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, - .compress_new = snd_soc_new_compress, .playback = { .stream_name = AUD_NAME_HIECCOMPOUT1, .channels_min = 1, .channels_max = 1, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_pxs2_ops2, }, { .name = AUD_NAME_IECCOMPOUT1, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, - .compress_new = snd_soc_new_compress, .playback = { .stream_name = AUD_NAME_IECCOMPOUT1, .channels_min = 1, .channels_max = 1, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_pxs2_ops2, }, }; diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h index 0b03571aa9f0..09ccb47337fd 100644 --- a/sound/soc/uniphier/aio.h +++ b/sound/soc/uniphier/aio.h @@ -306,12 +306,14 @@ static inline struct uniphier_aio *uniphier_priv(struct snd_soc_dai *dai) int uniphier_aiodma_soc_register_platform(struct platform_device *pdev); extern const struct snd_compress_ops uniphier_aio_compress_ops; -int uniphier_aio_dai_probe(struct snd_soc_dai *dai); -int uniphier_aio_dai_remove(struct snd_soc_dai *dai); int uniphier_aio_probe(struct platform_device *pdev); int uniphier_aio_remove(struct platform_device *pdev); -extern const struct snd_soc_dai_ops uniphier_aio_i2s_ops; -extern const struct snd_soc_dai_ops uniphier_aio_spdif_ops; +extern const struct snd_soc_dai_ops uniphier_aio_i2s_ld11_ops; +extern const struct snd_soc_dai_ops uniphier_aio_i2s_pxs2_ops; +extern const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops; +extern const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops2; +extern const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops; +extern const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops2; u64 aio_rb_cnt(struct uniphier_aio_sub *sub); u64 aio_rbt_cnt_to_end(struct uniphier_aio_sub *sub); diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index 44e88dad8584..cde0dd8e2569 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -683,6 +683,7 @@ static int ux500_msp_dai_of_probe(struct snd_soc_dai *dai) static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = { { + .probe = ux500_msp_dai_of_probe, .set_sysclk = ux500_msp_dai_set_dai_sysclk, .set_fmt = ux500_msp_dai_set_dai_fmt, .set_tdm_slot = ux500_msp_dai_set_tdm_slot, @@ -695,7 +696,6 @@ static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = { }; static struct snd_soc_dai_driver ux500_msp_dai_drv = { - .probe = ux500_msp_dai_of_probe, .playback.channels_min = UX500_MSP_MIN_CHANNELS, .playback.channels_max = UX500_MSP_MAX_CHANNELS, .playback.rates = UX500_I2S_RATES, diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c index c434b69a83f1..0fea04acc3ea 100644 --- a/sound/sparc/amd7930.c +++ b/sound/sparc/amd7930.c @@ -37,7 +37,7 @@ #include <linux/interrupt.h> #include <linux/moduleparam.h> #include <linux/of.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> #include <linux/io.h> #include <sound/core.h> @@ -47,7 +47,6 @@ #include <sound/initval.h> #include <asm/irq.h> -#include <asm/prom.h> static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 31bac355ec4d..c2ad3fa2f25a 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -18,7 +18,7 @@ #include <linux/irq.h> #include <linux/io.h> #include <linux/of.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 376aed136a45..050e98f32d36 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -69,7 +69,7 @@ #include <sound/initval.h> #include <linux/of.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> #include <linux/atomic.h> #include <linux/module.h> diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index 7aec0a95c609..392b4d8e9e76 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -395,8 +395,8 @@ static int bcd2000_probe(struct usb_interface *interface, snd_card_set_dev(card, &interface->dev); - strncpy(card->driver, "snd-bcd2000", sizeof(card->driver)); - strncpy(card->shortname, "BCD2000", sizeof(card->shortname)); + strscpy(card->driver, "snd-bcd2000", sizeof(card->driver)); + strscpy(card->shortname, "BCD2000", sizeof(card->shortname)); usb_make_path(bcd2k->dev, usb_path, sizeof(usb_path)); snprintf(bcd2k->card->longname, sizeof(bcd2k->card->longname), "Behringer BCD2000 at %s", diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index a385e85c4650..8f65349a06d3 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -505,13 +505,18 @@ int snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, return -EPIPE; } - err = usb_submit_urb(ctx->urb, GFP_ATOMIC); + if (!atomic_read(&ep->chip->shutdown)) + err = usb_submit_urb(ctx->urb, GFP_ATOMIC); + else + err = -ENODEV; if (err < 0) { - usb_audio_err(ep->chip, - "Unable to submit urb #%d: %d at %s\n", - ctx->index, err, __func__); - if (!in_stream_lock) - notify_xrun(ep); + if (!atomic_read(&ep->chip->shutdown)) { + usb_audio_err(ep->chip, + "Unable to submit urb #%d: %d at %s\n", + ctx->index, err, __func__); + if (!in_stream_lock) + notify_xrun(ep); + } return -EPIPE; } @@ -575,12 +580,17 @@ static void snd_complete_urb(struct urb *urb) prepare_inbound_urb(ep, ctx); } - err = usb_submit_urb(urb, GFP_ATOMIC); + if (!atomic_read(&ep->chip->shutdown)) + err = usb_submit_urb(urb, GFP_ATOMIC); + else + err = -ENODEV; if (err == 0) return; - usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err); - notify_xrun(ep); + if (!atomic_read(&ep->chip->shutdown)) { + usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err); + notify_xrun(ep); + } exit_clear: clear_bit(ctx->index, &ep->active_mask); @@ -1603,11 +1613,15 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) goto __error; } - err = usb_submit_urb(urb, GFP_ATOMIC); + if (!atomic_read(&ep->chip->shutdown)) + err = usb_submit_urb(urb, GFP_ATOMIC); + else + err = -ENODEV; if (err < 0) { - usb_audio_err(ep->chip, - "cannot submit urb %d, error %d: %s\n", - i, err, usb_error_string(err)); + if (!atomic_read(&ep->chip->shutdown)) + usb_audio_err(ep->chip, + "cannot submit urb %d, error %d: %s\n", + i, err, usb_error_string(err)); goto __error; } set_bit(i, &ep->active_mask); diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index c09f68ce08b1..ba70f52f6860 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -44,7 +44,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending); void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep); -int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_release(struct snd_usb_endpoint *ep); void snd_usb_endpoint_free_all(struct snd_usb_audio *chip); diff --git a/sound/usb/media.c b/sound/usb/media.c index 840f42cb9272..d48db6f3ae65 100644 --- a/sound/usb/media.c +++ b/sound/usb/media.c @@ -35,7 +35,7 @@ int snd_media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm, { struct media_device *mdev; struct media_ctl *mctl; - struct device *pcm_dev = &pcm->streams[stream].dev; + struct device *pcm_dev = pcm->streams[stream].dev; u32 intf_type; int ret = 0; u16 mixer_pad; @@ -163,7 +163,7 @@ void snd_media_stop_pipeline(struct snd_usb_substream *subs) static int snd_media_mixer_init(struct snd_usb_audio *chip) { - struct device *ctl_dev = &chip->card->ctl_dev; + struct device *ctl_dev = chip->card->ctl_dev; struct media_intf_devnode *ctl_intf; struct usb_mixer_interface *mixer; struct media_device *mdev = chip->media_dev; diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index ee2835741479..a27e244650c8 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -990,7 +990,7 @@ static int parse_midi_2_0(struct snd_usb_midi2_interface *umidi) } } - return attach_legacy_rawmidi(umidi); + return 0; } /* is the given interface for MIDI 2.0? */ @@ -1059,12 +1059,6 @@ static void set_fallback_rawmidi_names(struct snd_usb_midi2_interface *umidi) usb_string(dev, dev->descriptor.iSerialNumber, ump->info.product_id, sizeof(ump->info.product_id)); -#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI) - if (ump->legacy_rmidi && !*ump->legacy_rmidi->name) - snprintf(ump->legacy_rmidi->name, - sizeof(ump->legacy_rmidi->name), - "%s (MIDI 1.0)", ump->info.name); -#endif } } @@ -1157,6 +1151,13 @@ int snd_usb_midi_v2_create(struct snd_usb_audio *chip, } set_fallback_rawmidi_names(umidi); + + err = attach_legacy_rawmidi(umidi); + if (err < 0) { + usb_audio_err(chip, "Failed to create legacy rawmidi\n"); + goto error; + } + return 0; error: diff --git a/sound/usb/stream.c b/sound/usb/stream.c index f10f4e6d3fb8..3d4add94e367 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -1093,6 +1093,7 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int i, altno, err, stream; struct audioformat *fp = NULL; struct snd_usb_power_domain *pd = NULL; + bool set_iface_first; int num, protocol; dev = chip->dev; @@ -1223,11 +1224,19 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, return err; } + set_iface_first = false; + if (protocol == UAC_VERSION_1 || + (chip->quirk_flags & QUIRK_FLAG_SET_IFACE_FIRST)) + set_iface_first = true; + /* try to set the interface... */ usb_set_interface(chip->dev, iface_no, 0); + if (set_iface_first) + usb_set_interface(chip->dev, iface_no, altno); snd_usb_init_pitch(chip, fp); snd_usb_init_sample_rate(chip, fp, fp->rate_max); - usb_set_interface(chip->dev, iface_no, altno); + if (!set_iface_first) + usb_set_interface(chip->dev, iface_no, altno); } return 0; } diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c index db917453a473..31b5dc0f34d2 100644 --- a/sound/xen/xen_snd_front_alsa.c +++ b/sound/xen/xen_snd_front_alsa.c @@ -602,56 +602,24 @@ static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream) return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr); } -static int alsa_pb_copy_user(struct snd_pcm_substream *substream, - int channel, unsigned long pos, void __user *src, - unsigned long count) +static int alsa_pb_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, struct iov_iter *src, + unsigned long count) { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); if (unlikely(pos + count > stream->buffer_sz)) return -EINVAL; - if (copy_from_user(stream->buffer + pos, src, count)) + if (copy_from_iter(stream->buffer + pos, count, src) != count) return -EFAULT; return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); } -static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, void *src, - unsigned long count) -{ - struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - - if (unlikely(pos + count > stream->buffer_sz)) - return -EINVAL; - - memcpy(stream->buffer + pos, src, count); - - return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); -} - -static int alsa_cap_copy_user(struct snd_pcm_substream *substream, - int channel, unsigned long pos, void __user *dst, - unsigned long count) -{ - struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); - int ret; - - if (unlikely(pos + count > stream->buffer_sz)) - return -EINVAL; - - ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count); - if (ret < 0) - return ret; - - return copy_to_user(dst, stream->buffer + pos, count) ? - -EFAULT : 0; -} - -static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream, - int channel, unsigned long pos, void *dst, - unsigned long count) +static int alsa_cap_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, struct iov_iter *dst, + unsigned long count) { struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); int ret; @@ -663,8 +631,8 @@ static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream, if (ret < 0) return ret; - memcpy(dst, stream->buffer + pos, count); - + if (copy_to_iter(stream->buffer + pos, count, dst) != count) + return -EFAULT; return 0; } @@ -697,8 +665,7 @@ static const struct snd_pcm_ops snd_drv_alsa_playback_ops = { .prepare = alsa_prepare, .trigger = alsa_trigger, .pointer = alsa_pointer, - .copy_user = alsa_pb_copy_user, - .copy_kernel = alsa_pb_copy_kernel, + .copy = alsa_pb_copy, .fill_silence = alsa_pb_fill_silence, }; @@ -710,8 +677,7 @@ static const struct snd_pcm_ops snd_drv_alsa_capture_ops = { .prepare = alsa_prepare, .trigger = alsa_trigger, .pointer = alsa_pointer, - .copy_user = alsa_cap_copy_user, - .copy_kernel = alsa_cap_copy_kernel, + .copy = alsa_cap_copy, }; static int new_pcm_instance(struct xen_snd_front_card_info *card_info, @@ -783,7 +749,7 @@ static int new_pcm_instance(struct xen_snd_front_card_info *card_info, pcm->info_flags = 0; /* we want to handle all PCM operations in non-atomic context */ pcm->nonatomic = true; - strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name)); + strscpy(pcm->name, "Virtual card PCM", sizeof(pcm->name)); if (instance_cfg->num_streams_pb) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, @@ -835,9 +801,9 @@ int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info) goto fail; } - strncpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver)); - strncpy(card->shortname, cfg->name_short, sizeof(card->shortname)); - strncpy(card->longname, cfg->name_long, sizeof(card->longname)); + strscpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver)); + strscpy(card->shortname, cfg->name_short, sizeof(card->shortname)); + strscpy(card->longname, cfg->name_long, sizeof(card->longname)); ret = snd_card_register(card); if (ret < 0) |