diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-06 09:08:38 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-06 09:08:38 -0700 |
commit | 126f7051b4daa3716d9af2851dcb55316e4c2b25 (patch) | |
tree | e2f29bf57f28110d8fe42faaded6f83ffc52ec06 /sound | |
parent | 135c5504a600ff9b06e321694fbcac78a9530cd4 (diff) | |
parent | d4d5a1cd298e67cb68cca8dc7dd1ea3942cce3ff (diff) | |
download | linux-126f7051b4daa3716d9af2851dcb55316e4c2b25.tar.bz2 |
Merge tag 'sound-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"We've got many code additions at this cycle as a result of quite a few
new drivers. Below are highlights:
Core stuff:
- Fix the long-standing issue with the device registration order; the
control device is now registered at last
- PCM locking code cleanups for RT kernels
- Fixes for possible races in ALSA timer resolution accesses
- TLV offset definitions in uapi
ASoC:
- Many fixes for the topology stuff, including fixes for v4 ABI
compatibility
- Lots of cleanups / quirks for Intel platforms based on Realtek
CODECs
- Continued componentization works, removing legacy CODEC stuff
- Conversion of OMAP DMA to the new, more standard SDMA-PCM driver
- Fixes and updates to Cirrus Logic SoC drivers
- New Qualcomm DSP support
- New drivers for Analog SSM2305, Atmel I2S controllers, Mediatek
MT6351, MT6797 and MT7622, Qualcomm DSPs, Realtek RT1305, RT1306
and RT5668 and TI TSCS454
HD-audio:
- Finally better support for some CA0132 boards, allowing Windows
firmware
- HP Spectre x360 support along with a bulk of COEF stuff
- Blacklisting power save default some known boards reported on
Fedora
USB-audio:
- Continued improvements on UAC3 support; now BADD is supported
- Fixes / improvements for Dell WD15 dock
- Allow DMA coherent pages for PCM buffers for ARCH, MIPS & co
Others:
- New Xen sound frontend driver support
- Cache implementation and other improvements for FireWire DICE
- Conversions to octal permissions in allover places"
* tag 'sound-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (386 commits)
ASoC: dapm: delete dapm_kcontrol_data paths list before freeing it
ALSA: usb-audio: remove redundant check on err
ASoC: topology: Move skl-tplg-interface.h to uapi
ASoC: topology: Move v4 manifest header data structures to uapi
ASoC: topology: Improve backwards compatibility with v4 topology files
ALSA: pci/hda: Remove unused, broken, header file
ASoC: TSCS454: Add Support
ASoC: Intel: kbl: Move codec sysclk config to codec_init function
ASoC: simple-card: set cpu dai clk in hw_params
ALSA: hda - Handle kzalloc() failure in snd_hda_attach_pcm_stream()
ALSA: oxygen: use match_string() helper
ASoC: dapm: use match_string() helper
ASoC: max98095: use match_string() helper
ASoC: max98088: use match_string() helper
ASoC: Intel: bytcr_rt5651: Set card long_name based on quirks
ASoC: mt6797-mt6351: add hostless phone call path
ASoC: mt6797: add Hostless DAI
ASoC: mt6797: add PCM interface
ASoC: mediatek: export mtk-afe symbols as needed
ASoC: codecs: PCM1789: include gpio/consumer.h
...
Diffstat (limited to 'sound')
300 files changed, 36649 insertions, 5022 deletions
diff --git a/sound/Kconfig b/sound/Kconfig index 6833db9002ec..1140e9988fc5 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -96,6 +96,8 @@ source "sound/x86/Kconfig" source "sound/synth/Kconfig" +source "sound/xen/Kconfig" + endif # SND endif # !UML diff --git a/sound/Makefile b/sound/Makefile index 99d8c31262c8..797ecdcd35e2 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_DMASOUND) += oss/dmasound/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 4563432badba..4b01a37c836e 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -1001,7 +1001,7 @@ static int snd_compress_proc_init(struct snd_compr *compr) compr->card->proc_root); if (!entry) return -ENOMEM; - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + entry->mode = S_IFDIR | 0555; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); return -ENOMEM; diff --git a/sound/core/device.c b/sound/core/device.c index cb0e46f66cc9..535102d564e3 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -240,6 +240,15 @@ void snd_device_free_all(struct snd_card *card) if (snd_BUG_ON(!card)) return; + list_for_each_entry_safe_reverse(dev, next, &card->devices, list) { + /* exception: free ctl and lowlevel stuff later */ + if (dev->type == SNDRV_DEV_CONTROL || + dev->type == SNDRV_DEV_LOWLEVEL) + continue; + __snd_device_free(dev); + } + + /* free all */ list_for_each_entry_safe_reverse(dev, next, &card->devices, list) __snd_device_free(dev); } diff --git a/sound/core/info.c b/sound/core/info.c index 4b36767af9e1..fe502bc5e6d2 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -454,7 +454,7 @@ static struct snd_info_entry *create_subdir(struct module *mod, entry = snd_info_create_module_entry(mod, name, NULL); if (!entry) return NULL; - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + entry->mode = S_IFDIR | 0555; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); return NULL; @@ -470,7 +470,7 @@ int __init snd_info_init(void) snd_proc_root = snd_info_create_entry("asound", NULL); if (!snd_proc_root) return -ENOMEM; - snd_proc_root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + snd_proc_root->mode = S_IFDIR | 0555; snd_proc_root->p = proc_mkdir("asound", NULL); if (!snd_proc_root->p) goto error; @@ -716,7 +716,7 @@ snd_info_create_entry(const char *name, struct snd_info_entry *parent) kfree(entry); return NULL; } - entry->mode = S_IFREG | S_IRUGO; + entry->mode = S_IFREG | 0444; entry->content = SNDRV_INFO_CONTENT_TEXT; mutex_init(&entry->access); INIT_LIST_HEAD(&entry->children); diff --git a/sound/core/init.c b/sound/core/init.c index 79b4df1c1dc7..4849c611c0fe 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -703,7 +703,7 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr); +static DEVICE_ATTR(id, 0644, card_id_show_attr, card_id_store_attr); static ssize_t card_number_show_attr(struct device *dev, @@ -713,7 +713,7 @@ card_number_show_attr(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%i\n", card->number); } -static DEVICE_ATTR(number, S_IRUGO, card_number_show_attr, NULL); +static DEVICE_ATTR(number, 0444, card_number_show_attr, NULL); static struct attribute *card_dev_attrs[] = { &dev_attr_id.attr, diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 379bf486ccc7..64d904bee8bb 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1247,7 +1247,7 @@ static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer) if (! entry) return; entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | 0644; entry->c.text.read = snd_mixer_oss_proc_read; entry->c.text.write = snd_mixer_oss_proc_write; entry->private_data = mixer; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 1980f68246cb..905a53c1cde5 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -3045,7 +3045,7 @@ static void snd_pcm_oss_proc_init(struct snd_pcm *pcm) continue; if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | 0644; entry->c.text.read = snd_pcm_oss_proc_read; entry->c.text.write = snd_pcm_oss_proc_write; entry->private_data = pstr; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 66ac89aad681..c352bfb973cc 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -530,7 +530,7 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) pcm->card->proc_root); if (!entry) return -ENOMEM; - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + entry->mode = S_IFDIR | 0555; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); return -ENOMEM; @@ -552,7 +552,7 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) if (entry) { entry->c.text.read = snd_pcm_xrun_debug_read; entry->c.text.write = snd_pcm_xrun_debug_write; - entry->mode |= S_IWUSR; + entry->mode |= 0200; entry->private_data = pstr; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -590,7 +590,7 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) substream->pstr->proc_root); if (!entry) return -ENOMEM; - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + entry->mode = S_IFDIR | 0555; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); return -ENOMEM; @@ -647,7 +647,7 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) entry->private_data = substream; entry->c.text.read = NULL; entry->c.text.write = snd_pcm_xrun_injection_write; - entry->mode = S_IFREG | S_IWUSR; + entry->mode = S_IFREG | 0200; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; @@ -1087,7 +1087,7 @@ static ssize_t show_pcm_class(struct device *dev, return snprintf(buf, PAGE_SIZE, "%s\n", str); } -static DEVICE_ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL); +static DEVICE_ATTR(pcm_class, 0444, show_pcm_class, NULL); static struct attribute *pcm_dev_attrs[] = { &dev_attr_pcm_class.attr, NULL diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 6491afbb5fd5..39d853bfa5ac 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -45,10 +45,7 @@ static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream, if (get_user(frames, src)) return -EFAULT; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - err = snd_pcm_playback_rewind(substream, frames); - else - err = snd_pcm_capture_rewind(substream, frames); + err = snd_pcm_rewind(substream, frames); if (put_user(err, src)) return -EFAULT; return err < 0 ? err : 0; @@ -62,10 +59,7 @@ static int snd_pcm_ioctl_forward_compat(struct snd_pcm_substream *substream, if (get_user(frames, src)) return -EFAULT; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - err = snd_pcm_playback_forward(substream, frames); - else - err = snd_pcm_capture_forward(substream, frames); + err = snd_pcm_forward(substream, frames); if (put_user(err, src)) return -EFAULT; return err < 0 ? err : 0; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index f4a19509cccf..44b5ae833082 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -191,10 +191,7 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, { snd_pcm_uframes_t avail; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - avail = snd_pcm_playback_avail(runtime); - else - avail = snd_pcm_capture_avail(runtime); + avail = snd_pcm_avail(substream); if (avail > runtime->avail_max) runtime->avail_max = avail; if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { @@ -1856,10 +1853,7 @@ static int wait_for_avail(struct snd_pcm_substream *substream, * This check must happen after been added to the waitqueue * and having current state be INTERRUPTIBLE. */ - if (is_playback) - avail = snd_pcm_playback_avail(runtime); - else - avail = snd_pcm_capture_avail(runtime); + avail = snd_pcm_avail(substream); if (avail >= runtime->twake) break; snd_pcm_stream_unlock_irq(substream); @@ -2175,10 +2169,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, runtime->twake = runtime->control->avail_min ? : 1; if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream); - if (is_playback) - avail = snd_pcm_playback_avail(runtime); - else - avail = snd_pcm_capture_avail(runtime); + avail = snd_pcm_avail(substream); while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t cont; diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h index 16f254732b2a..7a499d02df6c 100644 --- a/sound/core/pcm_local.h +++ b/sound/core/pcm_local.h @@ -36,6 +36,24 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr); +static inline snd_pcm_uframes_t +snd_pcm_avail(struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return snd_pcm_playback_avail(substream->runtime); + else + return snd_pcm_capture_avail(substream->runtime); +} + +static inline snd_pcm_uframes_t +snd_pcm_hw_avail(struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return snd_pcm_playback_hw_avail(substream->runtime); + else + return snd_pcm_capture_hw_avail(substream->runtime); +} + #ifdef CONFIG_SND_PCM_TIMER void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream); void snd_pcm_timer_init(struct snd_pcm_substream *substream); diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index ae33e456708c..4b5356a10315 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -201,7 +201,7 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream) if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) { entry->c.text.read = snd_pcm_lib_preallocate_proc_read; entry->c.text.write = snd_pcm_lib_preallocate_proc_write; - entry->mode |= S_IWUSR; + entry->mode |= 0200; entry->private_data = substream; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 0e875d5a9e86..04c6301394d0 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -99,6 +99,57 @@ static inline void down_write_nonblock(struct rw_semaphore *lock) cond_resched(); } +#define PCM_LOCK_DEFAULT 0 +#define PCM_LOCK_IRQ 1 +#define PCM_LOCK_IRQSAVE 2 + +static unsigned long __snd_pcm_stream_lock_mode(struct snd_pcm_substream *substream, + unsigned int mode) +{ + unsigned long flags = 0; + if (substream->pcm->nonatomic) { + down_read_nested(&snd_pcm_link_rwsem, SINGLE_DEPTH_NESTING); + mutex_lock(&substream->self_group.mutex); + } else { + switch (mode) { + case PCM_LOCK_DEFAULT: + read_lock(&snd_pcm_link_rwlock); + break; + case PCM_LOCK_IRQ: + read_lock_irq(&snd_pcm_link_rwlock); + break; + case PCM_LOCK_IRQSAVE: + read_lock_irqsave(&snd_pcm_link_rwlock, flags); + break; + } + spin_lock(&substream->self_group.lock); + } + return flags; +} + +static void __snd_pcm_stream_unlock_mode(struct snd_pcm_substream *substream, + unsigned int mode, unsigned long flags) +{ + if (substream->pcm->nonatomic) { + mutex_unlock(&substream->self_group.mutex); + up_read(&snd_pcm_link_rwsem); + } else { + spin_unlock(&substream->self_group.lock); + + switch (mode) { + case PCM_LOCK_DEFAULT: + read_unlock(&snd_pcm_link_rwlock); + break; + case PCM_LOCK_IRQ: + read_unlock_irq(&snd_pcm_link_rwlock); + break; + case PCM_LOCK_IRQSAVE: + read_unlock_irqrestore(&snd_pcm_link_rwlock, flags); + break; + } + } +} + /** * snd_pcm_stream_lock - Lock the PCM stream * @substream: PCM substream @@ -109,13 +160,7 @@ static inline void down_write_nonblock(struct rw_semaphore *lock) */ void snd_pcm_stream_lock(struct snd_pcm_substream *substream) { - if (substream->pcm->nonatomic) { - down_read_nested(&snd_pcm_link_rwsem, SINGLE_DEPTH_NESTING); - mutex_lock(&substream->self_group.mutex); - } else { - read_lock(&snd_pcm_link_rwlock); - spin_lock(&substream->self_group.lock); - } + __snd_pcm_stream_lock_mode(substream, PCM_LOCK_DEFAULT); } EXPORT_SYMBOL_GPL(snd_pcm_stream_lock); @@ -127,13 +172,7 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock); */ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) { - if (substream->pcm->nonatomic) { - mutex_unlock(&substream->self_group.mutex); - up_read(&snd_pcm_link_rwsem); - } else { - spin_unlock(&substream->self_group.lock); - read_unlock(&snd_pcm_link_rwlock); - } + __snd_pcm_stream_unlock_mode(substream, PCM_LOCK_DEFAULT, 0); } EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock); @@ -147,9 +186,7 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock); */ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) { - if (!substream->pcm->nonatomic) - local_irq_disable(); - snd_pcm_stream_lock(substream); + __snd_pcm_stream_lock_mode(substream, PCM_LOCK_IRQ); } EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); @@ -161,19 +198,13 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); */ void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream) { - snd_pcm_stream_unlock(substream); - if (!substream->pcm->nonatomic) - local_irq_enable(); + __snd_pcm_stream_unlock_mode(substream, PCM_LOCK_IRQ, 0); } EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq); unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream) { - unsigned long flags = 0; - if (!substream->pcm->nonatomic) - local_irq_save(flags); - snd_pcm_stream_lock(substream); - return flags; + return __snd_pcm_stream_lock_mode(substream, PCM_LOCK_IRQSAVE); } EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave); @@ -187,9 +218,7 @@ EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave); void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, unsigned long flags) { - snd_pcm_stream_unlock(substream); - if (!substream->pcm->nonatomic) - local_irq_restore(flags); + __snd_pcm_stream_unlock_mode(substream, PCM_LOCK_IRQSAVE, flags); } EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); @@ -857,6 +886,18 @@ static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream, return err; } +static inline snd_pcm_uframes_t +snd_pcm_calc_delay(struct snd_pcm_substream *substream) +{ + snd_pcm_uframes_t delay; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + delay = snd_pcm_playback_hw_avail(substream->runtime); + else + delay = snd_pcm_capture_avail(substream->runtime); + return delay + substream->runtime->delay; +} + int snd_pcm_status(struct snd_pcm_substream *substream, struct snd_pcm_status *status) { @@ -908,21 +949,9 @@ int snd_pcm_status(struct snd_pcm_substream *substream, _tstamp_end: status->appl_ptr = runtime->control->appl_ptr; status->hw_ptr = runtime->status->hw_ptr; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - status->avail = snd_pcm_playback_avail(runtime); - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING || - runtime->status->state == SNDRV_PCM_STATE_DRAINING) { - status->delay = runtime->buffer_size - status->avail; - status->delay += runtime->delay; - } else - status->delay = 0; - } else { - status->avail = snd_pcm_capture_avail(runtime); - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) - status->delay = status->avail + runtime->delay; - else - status->delay = 0; - } + status->avail = snd_pcm_avail(substream); + status->delay = snd_pcm_running(substream) ? + snd_pcm_calc_delay(substream) : 0; status->avail_max = runtime->avail_max; status->overrange = runtime->overrange; runtime->avail_max = 0; @@ -2610,10 +2639,9 @@ static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream, return ret < 0 ? 0 : frames; } -static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream, - snd_pcm_uframes_t frames) +static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream, + snd_pcm_uframes_t frames) { - struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t ret; if (frames == 0) @@ -2623,33 +2651,14 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst ret = do_pcm_hwsync(substream); if (!ret) ret = rewind_appl_ptr(substream, frames, - snd_pcm_playback_hw_avail(runtime)); + snd_pcm_hw_avail(substream)); snd_pcm_stream_unlock_irq(substream); return ret; } -static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substream, - snd_pcm_uframes_t frames) +static snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream, + snd_pcm_uframes_t frames) { - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_sframes_t ret; - - if (frames == 0) - return 0; - - snd_pcm_stream_lock_irq(substream); - ret = do_pcm_hwsync(substream); - if (!ret) - ret = rewind_appl_ptr(substream, frames, - snd_pcm_capture_hw_avail(runtime)); - snd_pcm_stream_unlock_irq(substream); - return ret; -} - -static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *substream, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t ret; if (frames == 0) @@ -2659,25 +2668,7 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs ret = do_pcm_hwsync(substream); if (!ret) ret = forward_appl_ptr(substream, frames, - snd_pcm_playback_avail(runtime)); - snd_pcm_stream_unlock_irq(substream); - return ret; -} - -static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *substream, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_sframes_t ret; - - if (frames == 0) - return 0; - - snd_pcm_stream_lock_irq(substream); - ret = do_pcm_hwsync(substream); - if (!ret) - ret = forward_appl_ptr(substream, frames, - snd_pcm_capture_avail(runtime)); + snd_pcm_avail(substream)); snd_pcm_stream_unlock_irq(substream); return ret; } @@ -2695,19 +2686,13 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream) static int snd_pcm_delay(struct snd_pcm_substream *substream, snd_pcm_sframes_t *delay) { - struct snd_pcm_runtime *runtime = substream->runtime; int err; snd_pcm_sframes_t n = 0; snd_pcm_stream_lock_irq(substream); err = do_pcm_hwsync(substream); - if (!err) { - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - n = snd_pcm_playback_hw_avail(runtime); - else - n = snd_pcm_capture_avail(runtime); - n += runtime->delay; - } + if (!err) + n = snd_pcm_calc_delay(substream); snd_pcm_stream_unlock_irq(substream); if (!err) *delay = n; @@ -2834,10 +2819,7 @@ static int snd_pcm_rewind_ioctl(struct snd_pcm_substream *substream, return -EFAULT; if (put_user(0, _frames)) return -EFAULT; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - result = snd_pcm_playback_rewind(substream, frames); - else - result = snd_pcm_capture_rewind(substream, frames); + result = snd_pcm_rewind(substream, frames); __put_user(result, _frames); return result < 0 ? result : 0; } @@ -2852,10 +2834,7 @@ static int snd_pcm_forward_ioctl(struct snd_pcm_substream *substream, return -EFAULT; if (put_user(0, _frames)) return -EFAULT; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - result = snd_pcm_playback_forward(substream, frames); - else - result = snd_pcm_capture_forward(substream, frames); + result = snd_pcm_forward(substream, frames); __put_user(result, _frames); return result < 0 ? result : 0; } @@ -2998,7 +2977,7 @@ int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, /* provided only for OSS; capture-only and no value returned */ if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) return -EINVAL; - result = snd_pcm_capture_forward(substream, *frames); + result = snd_pcm_forward(substream, *frames); return result < 0 ? result : 0; } case SNDRV_PCM_IOCTL_HW_PARAMS: @@ -3140,82 +3119,46 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from) return result; } -static __poll_t snd_pcm_playback_poll(struct file *file, poll_table * wait) +static __poll_t snd_pcm_poll(struct file *file, poll_table *wait) { struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; - __poll_t mask; + __poll_t mask, ok; snd_pcm_uframes_t avail; pcm_file = file->private_data; substream = pcm_file->substream; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ok = EPOLLOUT | EPOLLWRNORM; + else + ok = EPOLLIN | EPOLLRDNORM; if (PCM_RUNTIME_CHECK(substream)) - return EPOLLOUT | EPOLLWRNORM | EPOLLERR; - runtime = substream->runtime; - - poll_wait(file, &runtime->sleep, wait); + return ok | EPOLLERR; - snd_pcm_stream_lock_irq(substream); - avail = snd_pcm_playback_avail(runtime); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_RUNNING: - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_PAUSED: - if (avail >= runtime->control->avail_min) { - mask = EPOLLOUT | EPOLLWRNORM; - break; - } - /* Fall through */ - case SNDRV_PCM_STATE_DRAINING: - mask = 0; - break; - default: - mask = EPOLLOUT | EPOLLWRNORM | EPOLLERR; - break; - } - snd_pcm_stream_unlock_irq(substream); - return mask; -} - -static __poll_t snd_pcm_capture_poll(struct file *file, poll_table * wait) -{ - struct snd_pcm_file *pcm_file; - struct snd_pcm_substream *substream; - struct snd_pcm_runtime *runtime; - __poll_t mask; - snd_pcm_uframes_t avail; - - pcm_file = file->private_data; - - substream = pcm_file->substream; - if (PCM_RUNTIME_CHECK(substream)) - return EPOLLIN | EPOLLRDNORM | EPOLLERR; runtime = substream->runtime; - poll_wait(file, &runtime->sleep, wait); + mask = 0; snd_pcm_stream_lock_irq(substream); - avail = snd_pcm_capture_avail(runtime); + avail = snd_pcm_avail(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_PAUSED: - if (avail >= runtime->control->avail_min) { - mask = EPOLLIN | EPOLLRDNORM; - break; - } - mask = 0; + if (avail >= runtime->control->avail_min) + mask = ok; break; case SNDRV_PCM_STATE_DRAINING: - if (avail > 0) { - mask = EPOLLIN | EPOLLRDNORM; - break; + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + mask = ok; + if (!avail) + mask |= EPOLLERR; } - /* Fall through */ + break; default: - mask = EPOLLIN | EPOLLRDNORM | EPOLLERR; + mask = ok | EPOLLERR; break; } snd_pcm_stream_unlock_irq(substream); @@ -3707,7 +3650,7 @@ const struct file_operations snd_pcm_f_ops[2] = { .open = snd_pcm_playback_open, .release = snd_pcm_release, .llseek = no_llseek, - .poll = snd_pcm_playback_poll, + .poll = snd_pcm_poll, .unlocked_ioctl = snd_pcm_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, @@ -3721,7 +3664,7 @@ const struct file_operations snd_pcm_f_ops[2] = { .open = snd_pcm_capture_open, .release = snd_pcm_release, .llseek = no_llseek, - .poll = snd_pcm_capture_poll, + .poll = snd_pcm_poll, .unlocked_ioctl = snd_pcm_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index d21ece9f8d73..24d90abfc64d 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -669,7 +669,7 @@ int snd_seq_event_port_attach(int client, /* Set up the port */ memset(&portinfo, 0, sizeof(portinfo)); portinfo.addr.client = client; - strlcpy(portinfo.name, portname ? portname : "Unamed port", + strlcpy(portinfo.name, portname ? portname : "Unnamed port", sizeof(portinfo.name)); portinfo.capability = cap; diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 23167578231f..f587d0e27476 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -371,9 +371,7 @@ static int initialize_timer(struct snd_seq_timer *tmr) tmr->ticks = 1; if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { - unsigned long r = t->hw.resolution; - if (! r && t->hw.c_resolution) - r = t->hw.c_resolution(t); + unsigned long r = snd_timer_resolution(tmr->timeri); if (r) { tmr->ticks = (unsigned int)(1000000000uL / (r * freq)); if (! tmr->ticks) diff --git a/sound/core/timer.c b/sound/core/timer.c index 0ddcae495838..665089c45560 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -427,25 +427,35 @@ int snd_timer_close(struct snd_timer_instance *timeri) } EXPORT_SYMBOL(snd_timer_close); +static unsigned long snd_timer_hw_resolution(struct snd_timer *timer) +{ + if (timer->hw.c_resolution) + return timer->hw.c_resolution(timer); + else + return timer->hw.resolution; +} + unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) { struct snd_timer * timer; + unsigned long ret = 0; + unsigned long flags; if (timeri == NULL) return 0; timer = timeri->timer; if (timer) { - if (timer->hw.c_resolution) - return timer->hw.c_resolution(timer); - return timer->hw.resolution; + spin_lock_irqsave(&timer->lock, flags); + ret = snd_timer_hw_resolution(timer); + spin_unlock_irqrestore(&timer->lock, flags); } - return 0; + return ret; } EXPORT_SYMBOL(snd_timer_resolution); static void snd_timer_notify1(struct snd_timer_instance *ti, int event) { - struct snd_timer *timer; + struct snd_timer *timer = ti->timer; unsigned long resolution = 0; struct snd_timer_instance *ts; struct timespec tstamp; @@ -457,14 +467,14 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || event > SNDRV_TIMER_EVENT_PAUSE)) return; - if (event == SNDRV_TIMER_EVENT_START || - event == SNDRV_TIMER_EVENT_CONTINUE) - resolution = snd_timer_resolution(ti); + if (timer && + (event == SNDRV_TIMER_EVENT_START || + event == SNDRV_TIMER_EVENT_CONTINUE)) + resolution = snd_timer_hw_resolution(timer); if (ti->ccallback) ti->ccallback(ti, event, &tstamp, resolution); if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) return; - timer = ti->timer; if (timer == NULL) return; if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) @@ -771,10 +781,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) spin_lock_irqsave(&timer->lock, flags); /* remember the current resolution */ - if (timer->hw.c_resolution) - resolution = timer->hw.c_resolution(timer); - else - resolution = timer->hw.resolution; + resolution = snd_timer_hw_resolution(timer); /* loop for all active instances * Here we cannot use list_for_each_entry because the active_list of a @@ -1014,12 +1021,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam spin_lock_irqsave(&timer->lock, flags); if (event == SNDRV_TIMER_EVENT_MSTART || event == SNDRV_TIMER_EVENT_MCONTINUE || - event == SNDRV_TIMER_EVENT_MRESUME) { - if (timer->hw.c_resolution) - resolution = timer->hw.c_resolution(timer); - else - resolution = timer->hw.resolution; - } + event == SNDRV_TIMER_EVENT_MRESUME) + resolution = snd_timer_hw_resolution(timer); list_for_each_entry(ti, &timer->active_list_head, active_list) { if (ti->ccallback) ti->ccallback(ti, event, tstamp, resolution); @@ -1656,10 +1659,8 @@ static int snd_timer_user_gstatus(struct file *file, mutex_lock(®ister_mutex); t = snd_timer_find(&tid); if (t != NULL) { - if (t->hw.c_resolution) - gstatus.resolution = t->hw.c_resolution(t); - else - gstatus.resolution = t->hw.resolution; + spin_lock_irq(&t->lock); + gstatus.resolution = snd_timer_hw_resolution(t); if (t->hw.precise_resolution) { t->hw.precise_resolution(t, &gstatus.resolution_num, &gstatus.resolution_den); @@ -1667,6 +1668,7 @@ static int snd_timer_user_gstatus(struct file *file, gstatus.resolution_num = gstatus.resolution; gstatus.resolution_den = 1000000000uL; } + spin_unlock_irq(&t->lock); } else { err = -ENODEV; } diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 9e96186742d0..58fa3f94722a 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -421,13 +421,15 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, kctl->private_free = master_free; /* additional (constant) TLV read */ - if (tlv && - (tlv[0] == SNDRV_CTL_TLVT_DB_SCALE || - tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX || - tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX_MUTE)) { - kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; - memcpy(master->tlv, tlv, sizeof(master->tlv)); - kctl->tlv.p = master->tlv; + if (tlv) { + unsigned int type = tlv[SNDRV_CTL_TLVO_TYPE]; + if (type == SNDRV_CTL_TLVT_DB_SCALE || + type == SNDRV_CTL_TLVT_DB_MINMAX || + type == SNDRV_CTL_TLVT_DB_MINMAX_MUTE) { + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + memcpy(master->tlv, tlv, sizeof(master->tlv)); + kctl->tlv.p = master->tlv; + } } return kctl; diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index eab7f594ebe7..78a2fdc38531 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -768,20 +768,7 @@ static int loopback_close(struct snd_pcm_substream *substream) return 0; } -static const struct snd_pcm_ops loopback_playback_ops = { - .open = loopback_open, - .close = loopback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = loopback_hw_params, - .hw_free = loopback_hw_free, - .prepare = loopback_prepare, - .trigger = loopback_trigger, - .pointer = loopback_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - -static const struct snd_pcm_ops loopback_capture_ops = { +static const struct snd_pcm_ops loopback_pcm_ops = { .open = loopback_open, .close = loopback_close, .ioctl = snd_pcm_lib_ioctl, @@ -804,8 +791,8 @@ static int loopback_pcm_new(struct loopback *loopback, substreams, substreams, &pcm); if (err < 0) return err; - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_pcm_ops); pcm->private_data = loopback; pcm->info_flags = 0; diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 8fb9a54fe8ba..9af154db530a 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -1042,7 +1042,7 @@ static void dummy_proc_init(struct snd_dummy *chip) if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) { snd_info_set_text_ops(entry, chip, dummy_proc_read); entry->c.text.write = dummy_proc_write; - entry->mode |= S_IWUSR; + entry->mode |= 0200; entry->private_data = chip; } } diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index f32e81342247..b68e71ca7abd 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -41,11 +41,11 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; static struct platform_device *platform_devices[SNDRV_CARDS]; static int device_count; -module_param_array(index, int, NULL, S_IRUGO); +module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); -module_param_array(id, charp, NULL, S_IRUGO); +module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); -module_param_array(enable, bool, NULL, S_IRUGO); +module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); MODULE_AUTHOR("Matthias Koenig <mk@phasorlab.de>"); diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c index cd2c07fa2ef4..16b24091d799 100644 --- a/sound/drivers/opl4/opl4_proc.c +++ b/sound/drivers/opl4/opl4_proc.c @@ -104,7 +104,7 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4) if (entry) { if (opl4->hardware < OPL3_HW_OPL4_ML) { /* OPL4 can access 4 MB external ROM/SRAM */ - entry->mode |= S_IWUSR; + entry->mode |= 0200; entry->size = 4 * 1024 * 1024; } else { /* OPL4-ML has 1 MB internal ROM */ diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index ec8a94325ef6..3cdf0a88d71b 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -60,11 +60,11 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; static struct platform_device *platform_devices[SNDRV_CARDS]; static int device_count; -module_param_array(index, int, NULL, S_IRUGO); +module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); -module_param_array(id, charp, NULL, S_IRUGO); +module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); -module_param_array(enable, bool, NULL, S_IRUGO); +module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); MODULE_AUTHOR("Levent Guendogdu, Tobias Gehrig, Matthias Koenig"); diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c index ec24f96794f5..8096891af913 100644 --- a/sound/firewire/bebob/bebob_proc.c +++ b/sound/firewire/bebob/bebob_proc.c @@ -183,7 +183,7 @@ void snd_bebob_proc_init(struct snd_bebob *bebob) bebob->card->proc_root); if (root == NULL) return; - root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + root->mode = S_IFDIR | 0555; if (snd_info_register(root) < 0) { snd_info_free_entry(root); return; diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 55b4be9b0034..37062a233f6a 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,3 +1,4 @@ snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \ - dice-pcm.o dice-hwdep.o dice.o + dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \ + dice-alesis.o dice-extension.o dice-mytek.o obj-$(CONFIG_SND_DICE) += snd-dice.o diff --git a/sound/firewire/dice/dice-alesis.c b/sound/firewire/dice/dice-alesis.c new file mode 100644 index 000000000000..b2efb1c71a98 --- /dev/null +++ b/sound/firewire/dice/dice-alesis.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dice-alesis.c - a part of driver for DICE based devices + * + * Copyright (c) 2018 Takashi Sakamoto + */ + +#include "dice.h" + +static const unsigned int +alesis_io14_tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT] = { + {6, 6, 4}, /* Tx0 = Analog + S/PDIF. */ + {8, 4, 0}, /* Tx1 = ADAT1. */ +}; + +static const unsigned int +alesis_io26_tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT] = { + {10, 10, 8}, /* Tx0 = Analog + S/PDIF. */ + {16, 8, 0}, /* Tx1 = ADAT1 + ADAT2. */ +}; + +int snd_dice_detect_alesis_formats(struct snd_dice *dice) +{ + __be32 reg; + u32 data; + int i; + int err; + + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, ®, + sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg); + + if (data == 4 || data == 6) { + memcpy(dice->tx_pcm_chs, alesis_io14_tx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * + sizeof(unsigned int)); + } else { + memcpy(dice->rx_pcm_chs, alesis_io26_tx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * + sizeof(unsigned int)); + } + + for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i) + dice->rx_pcm_chs[0][i] = 8; + + dice->tx_midi_ports[0] = 1; + dice->rx_midi_ports[0] = 1; + + return 0; +} diff --git a/sound/firewire/dice/dice-extension.c b/sound/firewire/dice/dice-extension.c new file mode 100644 index 000000000000..a63fcbc875ad --- /dev/null +++ b/sound/firewire/dice/dice-extension.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dice-extension.c - a part of driver for DICE based devices + * + * Copyright (c) 2018 Takashi Sakamoto + */ + +#include "dice.h" + +/* For TCD2210/2220, TCAT defines extension of application protocol. */ + +#define DICE_EXT_APP_SPACE 0xffffe0200000uLL + +#define DICE_EXT_APP_CAPS_OFFSET 0x00 +#define DICE_EXT_APP_CAPS_SIZE 0x04 +#define DICE_EXT_APP_CMD_OFFSET 0x08 +#define DICE_EXT_APP_CMD_SIZE 0x0c +#define DICE_EXT_APP_MIXER_OFFSET 0x10 +#define DICE_EXT_APP_MIXER_SIZE 0x14 +#define DICE_EXT_APP_PEAK_OFFSET 0x18 +#define DICE_EXT_APP_PEAK_SIZE 0x1c +#define DICE_EXT_APP_ROUTER_OFFSET 0x20 +#define DICE_EXT_APP_ROUTER_SIZE 0x24 +#define DICE_EXT_APP_STREAM_OFFSET 0x28 +#define DICE_EXT_APP_STREAM_SIZE 0x2c +#define DICE_EXT_APP_CURRENT_OFFSET 0x30 +#define DICE_EXT_APP_CURRENT_SIZE 0x34 +#define DICE_EXT_APP_STANDALONE_OFFSET 0x38 +#define DICE_EXT_APP_STANDALONE_SIZE 0x3c +#define DICE_EXT_APP_APPLICATION_OFFSET 0x40 +#define DICE_EXT_APP_APPLICATION_SIZE 0x44 + +#define EXT_APP_STREAM_TX_NUMBER 0x0000 +#define EXT_APP_STREAM_RX_NUMBER 0x0004 +#define EXT_APP_STREAM_ENTRIES 0x0008 +#define EXT_APP_STREAM_ENTRY_SIZE 0x010c +#define EXT_APP_NUMBER_AUDIO 0x0000 +#define EXT_APP_NUMBER_MIDI 0x0004 +#define EXT_APP_NAMES 0x0008 +#define EXT_APP_NAMES_SIZE 256 +#define EXT_APP_AC3 0x0108 + +#define EXT_APP_CONFIG_LOW_ROUTER 0x0000 +#define EXT_APP_CONFIG_LOW_STREAM 0x1000 +#define EXT_APP_CONFIG_MIDDLE_ROUTER 0x2000 +#define EXT_APP_CONFIG_MIDDLE_STREAM 0x3000 +#define EXT_APP_CONFIG_HIGH_ROUTER 0x4000 +#define EXT_APP_CONFIG_HIGH_STREAM 0x5000 + +static inline int read_transaction(struct snd_dice *dice, u64 section_addr, + u32 offset, void *buf, size_t len) +{ + return snd_fw_transaction(dice->unit, + len == 4 ? TCODE_READ_QUADLET_REQUEST : + TCODE_READ_BLOCK_REQUEST, + section_addr + offset, buf, len, 0); +} + +static int read_stream_entries(struct snd_dice *dice, u64 section_addr, + u32 base_offset, unsigned int stream_count, + unsigned int mode, + unsigned int pcm_channels[MAX_STREAMS][3], + unsigned int midi_ports[MAX_STREAMS]) +{ + u32 entry_offset; + __be32 reg[2]; + int err; + int i; + + for (i = 0; i < stream_count; ++i) { + entry_offset = base_offset + i * EXT_APP_STREAM_ENTRY_SIZE; + err = read_transaction(dice, section_addr, + entry_offset + EXT_APP_NUMBER_AUDIO, + reg, sizeof(reg)); + if (err < 0) + return err; + pcm_channels[i][mode] = be32_to_cpu(reg[0]); + midi_ports[i] = max(midi_ports[i], be32_to_cpu(reg[1])); + } + + return 0; +} + +static int detect_stream_formats(struct snd_dice *dice, u64 section_addr) +{ + u32 base_offset; + __be32 reg[2]; + unsigned int stream_count; + int mode; + int err = 0; + + for (mode = 0; mode < SND_DICE_RATE_MODE_COUNT; ++mode) { + unsigned int cap; + + /* + * Some models report stream formats at highest mode, however + * they don't support the mode. Check clock capabilities. + */ + if (mode == 2) { + cap = CLOCK_CAP_RATE_176400 | CLOCK_CAP_RATE_192000; + } else if (mode == 1) { + cap = CLOCK_CAP_RATE_88200 | CLOCK_CAP_RATE_96000; + } else { + cap = CLOCK_CAP_RATE_32000 | CLOCK_CAP_RATE_44100 | + CLOCK_CAP_RATE_48000; + } + if (!(cap & dice->clock_caps)) + continue; + + base_offset = 0x2000 * mode + 0x1000; + + err = read_transaction(dice, section_addr, + base_offset + EXT_APP_STREAM_TX_NUMBER, + ®, sizeof(reg)); + if (err < 0) + break; + + base_offset += EXT_APP_STREAM_ENTRIES; + stream_count = be32_to_cpu(reg[0]); + err = read_stream_entries(dice, section_addr, base_offset, + stream_count, mode, + dice->tx_pcm_chs, + dice->tx_midi_ports); + if (err < 0) + break; + + base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE; + stream_count = be32_to_cpu(reg[1]); + err = read_stream_entries(dice, section_addr, base_offset, + stream_count, + mode, dice->rx_pcm_chs, + dice->rx_midi_ports); + if (err < 0) + break; + } + + return err; +} + +int snd_dice_detect_extension_formats(struct snd_dice *dice) +{ + __be32 *pointers; + unsigned int i; + u64 section_addr; + int err; + + pointers = kmalloc_array(9, sizeof(__be32) * 2, GFP_KERNEL); + if (pointers == NULL) + return -ENOMEM; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_EXT_APP_SPACE, pointers, + 9 * sizeof(__be32) * 2, 0); + if (err < 0) + goto end; + + /* Check two of them for offset have the same value or not. */ + for (i = 0; i < 9; ++i) { + int j; + + for (j = i + 1; j < 9; ++j) { + if (pointers[i * 2] == pointers[j * 2]) + goto end; + } + } + + section_addr = DICE_EXT_APP_SPACE + be32_to_cpu(pointers[12]) * 4; + err = detect_stream_formats(dice, section_addr); +end: + kfree(pointers); + return err; +} diff --git a/sound/firewire/dice/dice-interface.h b/sound/firewire/dice/dice-interface.h index 15a484b05298..9cad3d608229 100644 --- a/sound/firewire/dice/dice-interface.h +++ b/sound/firewire/dice/dice-interface.h @@ -175,13 +175,18 @@ #define GLOBAL_SAMPLE_RATE 0x05c /* + * Some old firmware versions do not have the following global registers. + * Windows drivers produced by TCAT lost backward compatibility in its + * early release because they can handle firmware only which supports the + * following registers. + */ + +/* * The version of the DICE driver specification that this device conforms to; * read-only. */ #define GLOBAL_VERSION 0x060 -/* Some old firmware versions do not have the following global registers: */ - /* * Supported sample rates and clock sources; read-only. */ diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index 8ff6da3c51f7..84eca8a51a02 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -101,27 +101,18 @@ int snd_dice_create_midi(struct snd_dice *dice) .close = midi_close, .trigger = midi_playback_trigger, }; - __be32 reg; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; unsigned int midi_in_ports, midi_out_ports; + int i; int err; - /* - * Use the number of MIDI conformant data channel at current sampling - * transfer frequency. - */ - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI, - ®, sizeof(reg)); - if (err < 0) - return err; - midi_in_ports = be32_to_cpu(reg); - - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI, - ®, sizeof(reg)); - if (err < 0) - return err; - midi_out_ports = be32_to_cpu(reg); + midi_in_ports = 0; + midi_out_ports = 0; + for (i = 0; i < MAX_STREAMS; ++i) { + midi_in_ports = max(midi_in_ports, dice->tx_midi_ports[i]); + midi_out_ports = max(midi_out_ports, dice->rx_midi_ports[i]); + } if (midi_in_ports + midi_out_ports == 0) return 0; diff --git a/sound/firewire/dice/dice-mytek.c b/sound/firewire/dice/dice-mytek.c new file mode 100644 index 000000000000..eb7d5492d10b --- /dev/null +++ b/sound/firewire/dice/dice-mytek.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dice-mytek.c - a part of driver for DICE based devices + * + * Copyright (c) 2018 Melvin Vermeeren + */ + +#include "dice.h" + +struct dice_mytek_spec { + unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; +}; + +static const struct dice_mytek_spec stereo_192_dsd_dac = { + /* AES, TOSLINK, SPDIF, ADAT inputs on device */ + .tx_pcm_chs = {{8, 8, 8}, {0, 0, 0} }, + /* PCM 44.1-192, native DSD64/DSD128 to device */ + .rx_pcm_chs = {{4, 4, 4}, {0, 0, 0} } +}; + +/* + * Mytek has a few other firewire-capable devices, though newer models appear + * to lack the port more often than not. As I don't have access to any of them + * they are missing here. An example is the Mytek 8x192 ADDA, which is DICE. + */ + +int snd_dice_detect_mytek_formats(struct snd_dice *dice) +{ + int i; + const struct dice_mytek_spec *dev; + + dev = &stereo_192_dsd_dac; + + memcpy(dice->tx_pcm_chs, dev->tx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int)); + memcpy(dice->rx_pcm_chs, dev->rx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int)); + + for (i = 0; i < MAX_STREAMS; ++i) { + dice->tx_midi_ports[i] = 0; + dice->rx_midi_ports[i] = 0; + } + + return 0; +} diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 7cb9e9713ac3..80351b29fe0d 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -9,43 +9,115 @@ #include "dice.h" +static int dice_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_substream *substream = rule->private; + struct snd_dice *dice = substream->private_data; + unsigned int index = substream->pcm->device; + + const struct snd_interval *c = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval rates = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int *pcm_channels; + enum snd_dice_rate_mode mode; + unsigned int i, rate; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_channels = dice->tx_pcm_chs[index]; + else + pcm_channels = dice->rx_pcm_chs[index]; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + + if (!snd_interval_test(c, pcm_channels[mode])) + continue; + + rates.min = min(rates.min, rate); + rates.max = max(rates.max, rate); + } + + return snd_interval_refine(r, &rates); +} + +static int dice_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_substream *substream = rule->private; + struct snd_dice *dice = substream->private_data; + unsigned int index = substream->pcm->device; + + const struct snd_interval *r = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval channels = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int *pcm_channels; + enum snd_dice_rate_mode mode; + unsigned int i, rate; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_channels = dice->tx_pcm_chs[index]; + else + pcm_channels = dice->rx_pcm_chs[index]; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + + if (!snd_interval_test(r, rate)) + continue; + + channels.min = min(channels.min, pcm_channels[mode]); + channels.max = max(channels.max, pcm_channels[mode]); + } + + return snd_interval_refine(c, &channels); +} + static int limit_channels_and_rates(struct snd_dice *dice, struct snd_pcm_runtime *runtime, enum amdtp_stream_direction dir, - unsigned int index, unsigned int size) + unsigned int index) { struct snd_pcm_hardware *hw = &runtime->hw; - struct amdtp_stream *stream; - unsigned int rate; - __be32 reg; - int err; - - /* - * Retrieve current Multi Bit Linear Audio data channel and limit to - * it. - */ - if (dir == AMDTP_IN_STREAM) { - stream = &dice->tx_stream[index]; - err = snd_dice_transaction_read_tx(dice, - size * index + TX_NUMBER_AUDIO, - ®, sizeof(reg)); - } else { - stream = &dice->rx_stream[index]; - err = snd_dice_transaction_read_rx(dice, - size * index + RX_NUMBER_AUDIO, - ®, sizeof(reg)); + unsigned int *pcm_channels; + unsigned int i; + + if (dir == AMDTP_IN_STREAM) + pcm_channels = dice->tx_pcm_chs[index]; + else + pcm_channels = dice->rx_pcm_chs[index]; + + hw->channels_min = UINT_MAX; + hw->channels_max = 0; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + enum snd_dice_rate_mode mode; + unsigned int rate, channels; + + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + hw->rates |= snd_pcm_rate_to_rate_bit(rate); + + channels = pcm_channels[mode]; + if (channels == 0) + continue; + hw->channels_min = min(hw->channels_min, channels); + hw->channels_max = max(hw->channels_max, channels); } - if (err < 0) - return err; - hw->channels_min = hw->channels_max = be32_to_cpu(reg); - - /* Retrieve current sampling transfer frequency and limit to it. */ - err = snd_dice_transaction_get_rate(dice, &rate); - if (err < 0) - return err; - - hw->rates = snd_pcm_rate_to_rate_bit(rate); snd_pcm_limit_hw_rates(runtime); return 0; @@ -56,36 +128,34 @@ static int init_hw_info(struct snd_dice *dice, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; + unsigned int index = substream->pcm->device; enum amdtp_stream_direction dir; struct amdtp_stream *stream; - __be32 reg[2]; - unsigned int count, size; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AM824_IN_PCM_FORMAT_BITS; dir = AMDTP_IN_STREAM; - stream = &dice->tx_stream[substream->pcm->device]; - err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, - sizeof(reg)); + stream = &dice->tx_stream[index]; } else { hw->formats = AM824_OUT_PCM_FORMAT_BITS; dir = AMDTP_OUT_STREAM; - stream = &dice->rx_stream[substream->pcm->device]; - err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, - sizeof(reg)); + stream = &dice->rx_stream[index]; } + err = limit_channels_and_rates(dice, substream->runtime, dir, + index); if (err < 0) return err; - count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); - if (substream->pcm->device >= count) - return -ENXIO; - - size = be32_to_cpu(reg[1]) * 4; - err = limit_channels_and_rates(dice, substream->runtime, dir, - substream->pcm->device, size); + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + dice_rate_constraint, substream, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dice_channels_constraint, substream, + SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; @@ -95,6 +165,8 @@ static int init_hw_info(struct snd_dice *dice, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + unsigned int source; + bool internal; int err; err = snd_dice_stream_lock_try(dice); @@ -105,6 +177,43 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; + err = snd_dice_transaction_get_clock_source(dice, &source); + if (err < 0) + goto err_locked; + switch (source) { + case CLOCK_SOURCE_AES1: + case CLOCK_SOURCE_AES2: + case CLOCK_SOURCE_AES3: + case CLOCK_SOURCE_AES4: + case CLOCK_SOURCE_AES_ANY: + case CLOCK_SOURCE_ADAT: + case CLOCK_SOURCE_TDIF: + case CLOCK_SOURCE_WC: + internal = false; + break; + default: + internal = true; + break; + } + + /* + * When source of clock is not internal or any PCM streams are running, + * available sampling rate is limited at current sampling rate. + */ + if (!internal || + amdtp_stream_pcm_running(&dice->tx_stream[0]) || + amdtp_stream_pcm_running(&dice->tx_stream[1]) || + amdtp_stream_pcm_running(&dice->rx_stream[0]) || + amdtp_stream_pcm_running(&dice->rx_stream[1])) { + unsigned int rate; + + err = snd_dice_transaction_get_rate(dice, &rate); + if (err < 0) + goto err_locked; + substream->runtime->hw.rate_min = rate; + substream->runtime->hw.rate_max = rate; + } + snd_pcm_set_sync(substream); end: return err; @@ -318,37 +427,19 @@ int snd_dice_create_pcm(struct snd_dice *dice) .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; - __be32 reg; struct snd_pcm *pcm; - unsigned int i, max_capture, max_playback, capture, playback; + unsigned int capture, playback; + int i, j; int err; - /* Check whether PCM substreams are required. */ - if (dice->force_two_pcms) { - max_capture = max_playback = 2; - } else { - max_capture = max_playback = 0; - err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, - sizeof(reg)); - if (err < 0) - return err; - max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); - - err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, - sizeof(reg)); - if (err < 0) - return err; - max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); - } - for (i = 0; i < MAX_STREAMS; i++) { capture = playback = 0; - if (i < max_capture) - capture = 1; - if (i < max_playback) - playback = 1; - if (capture == 0 && playback == 0) - break; + for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) { + if (dice->tx_pcm_chs[i][j] > 0) + capture = 1; + if (dice->rx_pcm_chs[i][j] > 0) + playback = 1; + } err = snd_pcm_new(dice->card, "DICE", i, playback, capture, &pcm); diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c index f5c1d1bced59..bb870fc73f99 100644 --- a/sound/firewire/dice/dice-proc.c +++ b/sound/firewire/dice/dice-proc.c @@ -148,12 +148,12 @@ static void dice_proc_read(struct snd_info_entry *entry, >> CLOCK_RATE_SHIFT)); snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); - snd_iprintf(buffer, " version: %u.%u.%u.%u\n", - (buf.global.version >> 24) & 0xff, - (buf.global.version >> 16) & 0xff, - (buf.global.version >> 8) & 0xff, - (buf.global.version >> 0) & 0xff); if (quadlets >= 90) { + snd_iprintf(buffer, " version: %u.%u.%u.%u\n", + (buf.global.version >> 24) & 0xff, + (buf.global.version >> 16) & 0xff, + (buf.global.version >> 8) & 0xff, + (buf.global.version >> 0) & 0xff); snd_iprintf(buffer, " clock caps:"); for (i = 0; i <= 6; ++i) if (buf.global.clock_caps & (1 << i)) @@ -243,10 +243,74 @@ static void dice_proc_read(struct snd_info_entry *entry, } } -void snd_dice_create_proc(struct snd_dice *dice) +static void dice_proc_read_formation(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + static const char *const rate_labels[] = { + [SND_DICE_RATE_MODE_LOW] = "low", + [SND_DICE_RATE_MODE_MIDDLE] = "middle", + [SND_DICE_RATE_MODE_HIGH] = "high", + }; + struct snd_dice *dice = entry->private_data; + int i, j; + + snd_iprintf(buffer, "Output stream from unit:\n"); + for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i) + snd_iprintf(buffer, "\t%s", rate_labels[i]); + snd_iprintf(buffer, "\tMIDI\n"); + for (i = 0; i < MAX_STREAMS; ++i) { + snd_iprintf(buffer, "Tx %u:", i); + for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) + snd_iprintf(buffer, "\t%u", dice->tx_pcm_chs[i][j]); + snd_iprintf(buffer, "\t%u\n", dice->tx_midi_ports[i]); + } + + snd_iprintf(buffer, "Input stream to unit:\n"); + for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i) + snd_iprintf(buffer, "\t%s", rate_labels[i]); + snd_iprintf(buffer, "\n"); + for (i = 0; i < MAX_STREAMS; ++i) { + snd_iprintf(buffer, "Rx %u:", i); + for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) + snd_iprintf(buffer, "\t%u", dice->rx_pcm_chs[i][j]); + snd_iprintf(buffer, "\t%u\n", dice->rx_midi_ports[i]); + } +} + +static void add_node(struct snd_dice *dice, struct snd_info_entry *root, + const char *name, + void (*op)(struct snd_info_entry *entry, + struct snd_info_buffer *buffer)) { struct snd_info_entry *entry; - if (!snd_card_proc_new(dice->card, "dice", &entry)) - snd_info_set_text_ops(entry, dice, dice_proc_read); + entry = snd_info_create_card_entry(dice->card, name, root); + if (!entry) + return; + + snd_info_set_text_ops(entry, dice, op); + if (snd_info_register(entry) < 0) + snd_info_free_entry(entry); +} + +void snd_dice_create_proc(struct snd_dice *dice) +{ + struct snd_info_entry *root; + + /* + * All nodes are automatically removed at snd_card_disconnect(), + * by following to link list. + */ + root = snd_info_create_card_entry(dice->card, "firewire", + dice->card->proc_root); + if (!root) + return; + root->mode = S_IFDIR | 0555; + if (snd_info_register(root) < 0) { + snd_info_free_entry(root); + return; + } + + add_node(dice, root, "dice", dice_proc_read); + add_node(dice, root, "formation", dice_proc_read_formation); } diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 928a255bfc35..c3c892c5c7ff 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -30,13 +30,43 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { [6] = 192000, }; +int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, + enum snd_dice_rate_mode *mode) +{ + /* Corresponding to each entry in snd_dice_rates. */ + static const enum snd_dice_rate_mode modes[] = { + [0] = SND_DICE_RATE_MODE_LOW, + [1] = SND_DICE_RATE_MODE_LOW, + [2] = SND_DICE_RATE_MODE_LOW, + [3] = SND_DICE_RATE_MODE_MIDDLE, + [4] = SND_DICE_RATE_MODE_MIDDLE, + [5] = SND_DICE_RATE_MODE_HIGH, + [6] = SND_DICE_RATE_MODE_HIGH, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { + if (!(dice->clock_caps & BIT(i))) + continue; + if (snd_dice_rates[i] != rate) + continue; + + *mode = modes[i]; + return 0; + } + + return -EINVAL; +} + /* * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE * to GLOBAL_STATUS. Especially, just after powering on, these are different. */ -static int ensure_phase_lock(struct snd_dice *dice) +static int ensure_phase_lock(struct snd_dice *dice, unsigned int rate) { __be32 reg, nominal; + u32 data; + int i; int err; err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, @@ -44,9 +74,21 @@ static int ensure_phase_lock(struct snd_dice *dice) if (err < 0) return err; + data = be32_to_cpu(reg); + + data &= ~CLOCK_RATE_MASK; + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + if (snd_dice_rates[i] == rate) + break; + } + if (i == ARRAY_SIZE(snd_dice_rates)) + return -EINVAL; + data |= i << CLOCK_RATE_SHIFT; + if (completion_done(&dice->clock_accepted)) reinit_completion(&dice->clock_accepted); + reg = cpu_to_be32(data); err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, ®, sizeof(reg)); if (err < 0) @@ -192,6 +234,7 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, unsigned int rate, struct reg_params *params) { __be32 reg[2]; + enum snd_dice_rate_mode mode; unsigned int i, pcm_chs, midi_ports; struct amdtp_stream *streams; struct fw_iso_resources *resources; @@ -206,12 +249,23 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, resources = dice->rx_resources; } + err = snd_dice_stream_get_rate_mode(dice, rate, &mode); + if (err < 0) + return err; + for (i = 0; i < params->count; i++) { + unsigned int pcm_cache; + unsigned int midi_cache; + if (dir == AMDTP_IN_STREAM) { + pcm_cache = dice->tx_pcm_chs[i][mode]; + midi_cache = dice->tx_midi_ports[i]; err = snd_dice_transaction_read_tx(dice, params->size * i + TX_NUMBER_AUDIO, reg, sizeof(reg)); } else { + pcm_cache = dice->rx_pcm_chs[i][mode]; + midi_cache = dice->rx_midi_ports[i]; err = snd_dice_transaction_read_rx(dice, params->size * i + RX_NUMBER_AUDIO, reg, sizeof(reg)); @@ -221,6 +275,14 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, pcm_chs = be32_to_cpu(reg[0]); midi_ports = be32_to_cpu(reg[1]); + /* These are important for developer of this driver. */ + if (pcm_chs != pcm_cache || midi_ports != midi_cache) { + dev_info(&dice->unit->device, + "cache mismatch: pcm: %u:%u, midi: %u:%u\n", + pcm_chs, pcm_cache, midi_ports, midi_cache); + return -EPROTO; + } + err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports); if (err < 0) return err; @@ -256,6 +318,68 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, return err; } +static int start_duplex_streams(struct snd_dice *dice, unsigned int rate) +{ + struct reg_params tx_params, rx_params; + int i; + int err; + + err = get_register_params(dice, &tx_params, &rx_params); + if (err < 0) + return err; + + /* Stop transmission. */ + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + snd_dice_transaction_clear_enable(dice); + release_resources(dice); + + err = ensure_phase_lock(dice, rate); + if (err < 0) { + dev_err(&dice->unit->device, "fail to ensure phase lock\n"); + return err; + } + + /* Likely to have changed stream formats. */ + err = get_register_params(dice, &tx_params, &rx_params); + if (err < 0) + return err; + + /* Start both streams. */ + err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); + if (err < 0) + goto error; + err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); + if (err < 0) + goto error; + + err = snd_dice_transaction_set_enable(dice); + if (err < 0) { + dev_err(&dice->unit->device, "fail to enable interface\n"); + goto error; + } + + for (i = 0; i < MAX_STREAMS; i++) { + if ((i < tx_params.count && + !amdtp_stream_wait_callback(&dice->tx_stream[i], + CALLBACK_TIMEOUT)) || + (i < rx_params.count && + !amdtp_stream_wait_callback(&dice->rx_stream[i], + CALLBACK_TIMEOUT))) { + err = -ETIMEDOUT; + goto error; + } + } + + return 0; +error: + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + snd_dice_transaction_clear_enable(dice); + release_resources(dice); + return err; +} + /* * MEMO: After this function, there're two states of streams: * - None streams are running. @@ -265,17 +389,13 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) { unsigned int curr_rate; unsigned int i; - struct reg_params tx_params, rx_params; - bool need_to_start; + enum snd_dice_rate_mode mode; int err; if (dice->substreams_counter == 0) return -EIO; - err = get_register_params(dice, &tx_params, &rx_params); - if (err < 0) - return err; - + /* Check sampling transmission frequency. */ err = snd_dice_transaction_get_rate(dice, &curr_rate); if (err < 0) { dev_err(&dice->unit->device, @@ -285,72 +405,36 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) if (rate == 0) rate = curr_rate; if (rate != curr_rate) - return -EINVAL; + goto restart; - /* Judge to need to restart streams. */ - for (i = 0; i < MAX_STREAMS; i++) { - if (i < tx_params.count) { - if (amdtp_streaming_error(&dice->tx_stream[i]) || - !amdtp_stream_running(&dice->tx_stream[i])) - break; - } - if (i < rx_params.count) { - if (amdtp_streaming_error(&dice->rx_stream[i]) || - !amdtp_stream_running(&dice->rx_stream[i])) - break; - } + /* Check error of packet streaming. */ + for (i = 0; i < MAX_STREAMS; ++i) { + if (amdtp_streaming_error(&dice->tx_stream[i])) + break; + if (amdtp_streaming_error(&dice->rx_stream[i])) + break; } - need_to_start = (i < MAX_STREAMS); - - if (need_to_start) { - /* Stop transmission. */ - snd_dice_transaction_clear_enable(dice); - stop_streams(dice, AMDTP_IN_STREAM, &tx_params); - stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); - release_resources(dice); - - err = ensure_phase_lock(dice); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to ensure phase lock\n"); - return err; - } + if (i < MAX_STREAMS) + goto restart; - /* Start both streams. */ - err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); - if (err < 0) - goto error; - err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); - if (err < 0) - goto error; - - err = snd_dice_transaction_set_enable(dice); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to enable interface\n"); - goto error; - } - - for (i = 0; i < MAX_STREAMS; i++) { - if ((i < tx_params.count && - !amdtp_stream_wait_callback(&dice->tx_stream[i], - CALLBACK_TIMEOUT)) || - (i < rx_params.count && - !amdtp_stream_wait_callback(&dice->rx_stream[i], - CALLBACK_TIMEOUT))) { - err = -ETIMEDOUT; - goto error; - } - } + /* Check required streams are running or not. */ + err = snd_dice_stream_get_rate_mode(dice, rate, &mode); + if (err < 0) + return err; + for (i = 0; i < MAX_STREAMS; ++i) { + if (dice->tx_pcm_chs[i][mode] > 0 && + !amdtp_stream_running(&dice->tx_stream[i])) + break; + if (dice->rx_pcm_chs[i][mode] > 0 && + !amdtp_stream_running(&dice->rx_stream[i])) + break; } + if (i < MAX_STREAMS) + goto restart; - return err; -error: - snd_dice_transaction_clear_enable(dice); - stop_streams(dice, AMDTP_IN_STREAM, &tx_params); - stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); - release_resources(dice); - return err; + return 0; +restart: + return start_duplex_streams(dice, rate); } /* @@ -484,6 +568,69 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice) } } +int snd_dice_stream_detect_current_formats(struct snd_dice *dice) +{ + unsigned int rate; + enum snd_dice_rate_mode mode; + __be32 reg[2]; + struct reg_params tx_params, rx_params; + int i; + int err; + + /* If extended protocol is available, detect detail spec. */ + err = snd_dice_detect_extension_formats(dice); + if (err >= 0) + return err; + + /* + * Available stream format is restricted at current mode of sampling + * clock. + */ + err = snd_dice_transaction_get_rate(dice, &rate); + if (err < 0) + return err; + + err = snd_dice_stream_get_rate_mode(dice, rate, &mode); + if (err < 0) + return err; + + /* + * Just after owning the unit (GLOBAL_OWNER), the unit can return + * invalid stream formats. Selecting clock parameters have an effect + * for the unit to refine it. + */ + err = ensure_phase_lock(dice, rate); + if (err < 0) + return err; + + err = get_register_params(dice, &tx_params, &rx_params); + if (err < 0) + return err; + + for (i = 0; i < tx_params.count; ++i) { + err = snd_dice_transaction_read_tx(dice, + tx_params.size * i + TX_NUMBER_AUDIO, + reg, sizeof(reg)); + if (err < 0) + return err; + dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]); + dice->tx_midi_ports[i] = max_t(unsigned int, + be32_to_cpu(reg[1]), dice->tx_midi_ports[i]); + } + for (i = 0; i < rx_params.count; ++i) { + err = snd_dice_transaction_read_rx(dice, + rx_params.size * i + RX_NUMBER_AUDIO, + reg, sizeof(reg)); + if (err < 0) + return err; + dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]); + dice->rx_midi_ports[i] = max_t(unsigned int, + be32_to_cpu(reg[1]), dice->rx_midi_ports[i]); + } + + return 0; +} + static void dice_lock_changed(struct snd_dice *dice) { dice->dev_lock_changed = true; diff --git a/sound/firewire/dice/dice-tcelectronic.c b/sound/firewire/dice/dice-tcelectronic.c new file mode 100644 index 000000000000..a8875d24ba2a --- /dev/null +++ b/sound/firewire/dice/dice-tcelectronic.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dice-tc_electronic.c - a part of driver for DICE based devices + * + * Copyright (c) 2018 Takashi Sakamoto + */ + +#include "dice.h" + +struct dice_tc_spec { + unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + bool has_midi; +}; + +static const struct dice_tc_spec desktop_konnekt6 = { + .tx_pcm_chs = {{6, 6, 2}, {0, 0, 0} }, + .rx_pcm_chs = {{6, 6, 4}, {0, 0, 0} }, + .has_midi = false, +}; + +static const struct dice_tc_spec impact_twin = { + .tx_pcm_chs = {{14, 10, 6}, {0, 0, 0} }, + .rx_pcm_chs = {{14, 10, 6}, {0, 0, 0} }, + .has_midi = true, +}; + +static const struct dice_tc_spec konnekt_8 = { + .tx_pcm_chs = {{4, 4, 3}, {0, 0, 0} }, + .rx_pcm_chs = {{4, 4, 3}, {0, 0, 0} }, + .has_midi = true, +}; + +static const struct dice_tc_spec konnekt_24d = { + .tx_pcm_chs = {{16, 16, 6}, {0, 0, 0} }, + .rx_pcm_chs = {{16, 16, 6}, {0, 0, 0} }, + .has_midi = true, +}; + +static const struct dice_tc_spec konnekt_live = { + .tx_pcm_chs = {{16, 16, 16}, {0, 0, 0} }, + .rx_pcm_chs = {{16, 16, 16}, {0, 0, 0} }, + .has_midi = true, +}; + +static const struct dice_tc_spec studio_konnekt_48 = { + .tx_pcm_chs = {{16, 16, 8}, {16, 16, 7} }, + .rx_pcm_chs = {{16, 16, 8}, {14, 14, 7} }, + .has_midi = true, +}; + +static const struct dice_tc_spec digital_konnekt_x32 = { + .tx_pcm_chs = {{16, 16, 4}, {0, 0, 0} }, + .rx_pcm_chs = {{16, 16, 4}, {0, 0, 0} }, + .has_midi = false, +}; + +int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice) +{ + static const struct { + u32 model_id; + const struct dice_tc_spec *spec; + } *entry, entries[] = { + {0x00000020, &konnekt_24d}, + {0x00000021, &konnekt_8}, + {0x00000022, &studio_konnekt_48}, + {0x00000023, &konnekt_live}, + {0x00000024, &desktop_konnekt6}, + {0x00000027, &impact_twin}, + {0x00000030, &digital_konnekt_x32}, + }; + struct fw_csr_iterator it; + int key, val, model_id; + int i; + + model_id = 0; + fw_csr_iterator_init(&it, dice->unit->directory); + while (fw_csr_iterator_next(&it, &key, &val)) { + if (key == CSR_MODEL) { + model_id = val; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(entries); ++i) { + entry = entries + i; + if (entry->model_id == model_id) + break; + } + if (i == ARRAY_SIZE(entries)) + return -ENODEV; + + memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int)); + memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int)); + + if (entry->spec->has_midi) { + dice->tx_midi_ports[0] = 1; + dice->rx_midi_ports[0] = 1; + } + + return 0; +} diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c index 0f0350320ae8..b7e138b5abcf 100644 --- a/sound/firewire/dice/dice-transaction.c +++ b/sound/firewire/dice/dice-transaction.c @@ -265,7 +265,7 @@ int snd_dice_transaction_reinit(struct snd_dice *dice) static int get_subaddrs(struct snd_dice *dice) { static const int min_values[10] = { - 10, 0x64 / 4, + 10, 0x60 / 4, 10, 0x18 / 4, 10, 0x18 / 4, 0, 0, @@ -301,33 +301,40 @@ static int get_subaddrs(struct snd_dice *dice) } } - /* - * Check that the implemented DICE driver specification major version - * number matches. - */ - err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, - DICE_PRIVATE_SPACE + - be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, - &version, sizeof(version), 0); - if (err < 0) - goto end; + if (be32_to_cpu(pointers[1]) > 0x18) { + /* + * Check that the implemented DICE driver specification major + * version number matches. + */ + err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, + DICE_PRIVATE_SPACE + + be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, + &version, sizeof(version), 0); + if (err < 0) + goto end; - if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { - dev_err(&dice->unit->device, - "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); - err = -ENODEV; - goto end; + if ((version & cpu_to_be32(0xff000000)) != + cpu_to_be32(0x01000000)) { + dev_err(&dice->unit->device, + "unknown DICE version: 0x%08x\n", + be32_to_cpu(version)); + err = -ENODEV; + goto end; + } + + /* Set up later. */ + dice->clock_caps = 1; } dice->global_offset = be32_to_cpu(pointers[0]) * 4; dice->tx_offset = be32_to_cpu(pointers[2]) * 4; dice->rx_offset = be32_to_cpu(pointers[4]) * 4; - dice->sync_offset = be32_to_cpu(pointers[6]) * 4; - dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4; - /* Set up later. */ - if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) - dice->clock_caps = 1; + /* Old firmware doesn't support these fields. */ + if (pointers[7]) + dice->sync_offset = be32_to_cpu(pointers[6]) * 4; + if (pointers[9]) + dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4; end: kfree(pointers); return err; diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 96bb01b6b751..774eb2205668 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -15,40 +15,15 @@ MODULE_LICENSE("GPL v2"); #define OUI_LOUD 0x000ff2 #define OUI_FOCUSRITE 0x00130e #define OUI_TCELECTRONIC 0x000166 +#define OUI_ALESIS 0x000595 +#define OUI_MAUDIO 0x000d6c +#define OUI_MYTEK 0x001ee8 #define DICE_CATEGORY_ID 0x04 #define WEISS_CATEGORY_ID 0x00 #define LOUD_CATEGORY_ID 0x10 -/* - * Some models support several isochronous channels, while these streams are not - * always available. In this case, add the model name to this list. - */ -static bool force_two_pcm_support(struct fw_unit *unit) -{ - static const char *const models[] = { - /* TC Electronic models. */ - "StudioKonnekt48", - /* Focusrite models. */ - "SAFFIRE_PRO_40", - "LIQUID_SAFFIRE_56", - "SAFFIRE_PRO_40_1", - }; - char model[32]; - unsigned int i; - int err; - - err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model)); - if (err < 0) - return false; - - for (i = 0; i < ARRAY_SIZE(models); i++) { - if (strcmp(models[i], model) == 0) - break; - } - - return i < ARRAY_SIZE(models); -} +#define MODEL_ALESIS_IO_BOTH 0x000001 static int check_dice_category(struct fw_unit *unit) { @@ -75,11 +50,6 @@ static int check_dice_category(struct fw_unit *unit) } } - if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) { - if (force_two_pcm_support(unit)) - return 0; - } - if (vendor == OUI_WEISS) category = WEISS_CATEGORY_ID; else if (vendor == OUI_LOUD) @@ -186,9 +156,6 @@ static void do_registration(struct work_struct *work) if (err < 0) return; - if (force_two_pcm_support(dice->unit)) - dice->force_two_pcms = true; - err = snd_dice_transaction_init(dice); if (err < 0) goto error; @@ -199,6 +166,10 @@ static void do_registration(struct work_struct *work) dice_card_strings(dice); + err = dice->detect_formats(dice); + if (err < 0) + goto error; + err = snd_dice_stream_init_duplex(dice); if (err < 0) goto error; @@ -239,14 +210,17 @@ error: "Sound card registration failed: %d\n", err); } -static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) +static int dice_probe(struct fw_unit *unit, + const struct ieee1394_device_id *entry) { struct snd_dice *dice; int err; - err = check_dice_category(unit); - if (err < 0) - return -ENODEV; + if (!entry->driver_data) { + err = check_dice_category(unit); + if (err < 0) + return -ENODEV; + } /* Allocate this independent of sound card instance. */ dice = kzalloc(sizeof(struct snd_dice), GFP_KERNEL); @@ -256,6 +230,13 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice->unit = fw_unit_get(unit); dev_set_drvdata(&unit->device, dice); + if (!entry->driver_data) { + dice->detect_formats = snd_dice_stream_detect_current_formats; + } else { + dice->detect_formats = + (snd_dice_detect_formats_t)entry->driver_data; + } + spin_lock_init(&dice->lock); mutex_init(&dice->mutex); init_completion(&dice->clock_accepted); @@ -313,16 +294,97 @@ static void dice_bus_reset(struct fw_unit *unit) #define DICE_INTERFACE 0x000001 static const struct ieee1394_device_id dice_id_table[] = { + /* M-Audio Profire 2626 has a different value in version field. */ { - .match_flags = IEEE1394_MATCH_VERSION, - .version = DICE_INTERFACE, + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_MAUDIO, + .model_id = 0x000010, + .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats, }, - /* M-Audio Profire 610/2626 has a different value in version field. */ + /* M-Audio Profire 610 has a different value in version field. */ { .match_flags = IEEE1394_MATCH_VENDOR_ID | - IEEE1394_MATCH_SPECIFIER_ID, - .vendor_id = 0x000d6c, - .specifier_id = 0x000d6c, + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_MAUDIO, + .model_id = 0x000011, + .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats, + }, + /* TC Electronic Konnekt 24D. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000020, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* TC Electronic Konnekt 8. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000021, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* TC Electronic Studio Konnekt 48. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000022, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* TC Electronic Konnekt Live. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000023, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* TC Electronic Desktop Konnekt 6. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000024, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* TC Electronic Impact Twin. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000027, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* TC Electronic Digital Konnekt x32. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000030, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* Alesis iO14/iO26. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_ALESIS, + .model_id = MODEL_ALESIS_IO_BOTH, + .driver_data = (kernel_ulong_t)snd_dice_detect_alesis_formats, + }, + /* Mytek Stereo 192 DSD-DAC. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_MYTEK, + .model_id = 0x000002, + .driver_data = (kernel_ulong_t)snd_dice_detect_mytek_formats, + }, + { + .match_flags = IEEE1394_MATCH_VERSION, + .version = DICE_INTERFACE, }, { } }; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index da00e75e09d4..83353a3559e8 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -63,6 +63,16 @@ */ #define MAX_STREAMS 2 +enum snd_dice_rate_mode { + SND_DICE_RATE_MODE_LOW = 0, + SND_DICE_RATE_MODE_MIDDLE, + SND_DICE_RATE_MODE_HIGH, + SND_DICE_RATE_MODE_COUNT, +}; + +struct snd_dice; +typedef int (*snd_dice_detect_formats_t)(struct snd_dice *dice); + struct snd_dice { struct snd_card *card; struct fw_unit *unit; @@ -80,6 +90,11 @@ struct snd_dice { unsigned int rsrv_offset; unsigned int clock_caps; + unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + unsigned int tx_midi_ports[MAX_STREAMS]; + unsigned int rx_midi_ports[MAX_STREAMS]; + snd_dice_detect_formats_t detect_formats; struct fw_address_handler notification_handler; int owner_generation; @@ -98,8 +113,6 @@ struct snd_dice { bool global_enabled; struct completion clock_accepted; unsigned int substreams_counter; - - bool force_two_pcms; }; enum snd_dice_addr_type { @@ -190,11 +203,14 @@ void snd_dice_transaction_destroy(struct snd_dice *dice); #define SND_DICE_RATES_COUNT 7 extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; +int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, + enum snd_dice_rate_mode *mode); int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate); void snd_dice_stream_stop_duplex(struct snd_dice *dice); int snd_dice_stream_init_duplex(struct snd_dice *dice); void snd_dice_stream_destroy_duplex(struct snd_dice *dice); void snd_dice_stream_update_duplex(struct snd_dice *dice); +int snd_dice_stream_detect_current_formats(struct snd_dice *dice); int snd_dice_stream_lock_try(struct snd_dice *dice); void snd_dice_stream_lock_release(struct snd_dice *dice); @@ -207,4 +223,9 @@ void snd_dice_create_proc(struct snd_dice *dice); int snd_dice_create_midi(struct snd_dice *dice); +int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice); +int snd_dice_detect_alesis_formats(struct snd_dice *dice); +int snd_dice_detect_extension_formats(struct snd_dice *dice); +int snd_dice_detect_mytek_formats(struct snd_dice *dice); + #endif diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c index a1d601f31165..6996d5a6ff5f 100644 --- a/sound/firewire/digi00x/digi00x-proc.c +++ b/sound/firewire/digi00x/digi00x-proc.c @@ -79,7 +79,7 @@ void snd_dg00x_proc_init(struct snd_dg00x *dg00x) if (root == NULL) return; - root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + root->mode = S_IFDIR | 0555; if (snd_info_register(root) < 0) { snd_info_free_entry(root); return; diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c index 69441d121f71..40ccbfd8ef89 100644 --- a/sound/firewire/fireface/ff-proc.c +++ b/sound/firewire/fireface/ff-proc.c @@ -52,7 +52,7 @@ void snd_ff_proc_init(struct snd_ff *ff) ff->card->proc_root); if (root == NULL) return; - root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + root->mode = S_IFDIR | 0555; if (snd_info_register(root) < 0) { snd_info_free_entry(root); return; diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c index 9c21f31b8b21..779ecec5af62 100644 --- a/sound/firewire/fireworks/fireworks_proc.c +++ b/sound/firewire/fireworks/fireworks_proc.c @@ -219,7 +219,7 @@ void snd_efw_proc_init(struct snd_efw *efw) efw->card->proc_root); if (root == NULL) return; - root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + root->mode = S_IFDIR | 0555; if (snd_info_register(root) < 0) { snd_info_free_entry(root); return; diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 46092fa3ff9b..3919e186a30b 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -569,18 +569,20 @@ static int isight_create_mixer(struct isight *isight) return err; isight->gain_max = be32_to_cpu(value); - isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX; - isight->gain_tlv[1] = 2 * sizeof(unsigned int); + isight->gain_tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_MINMAX; + isight->gain_tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int); err = reg_read(isight, REG_GAIN_DB_START, &value); if (err < 0) return err; - isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100; + isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MIN] = + (s32)be32_to_cpu(value) * 100; err = reg_read(isight, REG_GAIN_DB_END, &value); if (err < 0) return err; - isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100; + isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX] = + (s32)be32_to_cpu(value) * 100; ctl = snd_ctl_new1(&gain_control, isight); if (ctl) diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c index 4edc064999ed..ab6830a6d242 100644 --- a/sound/firewire/motu/motu-proc.c +++ b/sound/firewire/motu/motu-proc.c @@ -107,7 +107,7 @@ void snd_motu_proc_init(struct snd_motu *motu) motu->card->proc_root); if (root == NULL) return; - root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + root->mode = S_IFDIR | 0555; if (snd_info_register(root) < 0) { snd_info_free_entry(root); return; diff --git a/sound/firewire/oxfw/oxfw-proc.c b/sound/firewire/oxfw/oxfw-proc.c index 8ba4f9f262b8..27dac071bc73 100644 --- a/sound/firewire/oxfw/oxfw-proc.c +++ b/sound/firewire/oxfw/oxfw-proc.c @@ -103,7 +103,7 @@ void snd_oxfw_proc_init(struct snd_oxfw *oxfw) oxfw->card->proc_root); if (root == NULL) return; - root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + root->mode = S_IFDIR | 0555; if (snd_info_register(root) < 0) { snd_info_free_entry(root); return; diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 413ab6313bb6..1e5b2c802635 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -49,7 +49,6 @@ static bool detect_loud_models(struct fw_unit *unit) "Tapco LINK.firewire 4x6", "U.420"}; char model[32]; - unsigned int i; int err; err = fw_csr_string(unit->directory, CSR_MODEL, @@ -57,12 +56,7 @@ static bool detect_loud_models(struct fw_unit *unit) if (err < 0) return false; - for (i = 0; i < ARRAY_SIZE(models); i++) { - if (strcmp(models[i], model) == 0) - break; - } - - return (i < ARRAY_SIZE(models)); + return match_string(models, ARRAY_SIZE(models), model) >= 0; } static int name_card(struct snd_oxfw *oxfw) diff --git a/sound/firewire/tascam/tascam-proc.c b/sound/firewire/tascam/tascam-proc.c index bfd4a4c06914..fee3bf32a0da 100644 --- a/sound/firewire/tascam/tascam-proc.c +++ b/sound/firewire/tascam/tascam-proc.c @@ -78,7 +78,7 @@ void snd_tscm_proc_init(struct snd_tscm *tscm) tscm->card->proc_root); if (root == NULL) return; - root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + root->mode = S_IFDIR | 0555; if (snd_info_register(root) < 0) { snd_info_free_entry(root); return; diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index 47a358fab132..419e285e0226 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -65,10 +65,10 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg) { struct hdac_device *codec = dev_to_hdac_dev(dev); unsigned int verb = get_verb(reg); + const unsigned int *v; int i; - for (i = 0; i < codec->vendor_verbs.used; i++) { - unsigned int *v = snd_array_elem(&codec->vendor_verbs, i); + snd_array_for_each(&codec->vendor_verbs, i, v) { if (verb == *v) return true; } diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c index d09e456107ad..de6ef1b1cf0e 100644 --- a/sound/isa/cmi8328.c +++ b/sound/isa/cmi8328.c @@ -192,7 +192,7 @@ static int snd_cmi8328_mixer(struct snd_wss *chip) } /* find index of an item in "-1"-ended array */ -int array_find(int array[], int item) +static int array_find(int array[], int item) { int i; @@ -203,7 +203,7 @@ int array_find(int array[], int item) return -1; } /* the same for long */ -int array_find_l(long array[], long item) +static int array_find_l(long array[], long item) { int i; diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index 45e561c425bf..6c584d9b6c42 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -757,9 +757,9 @@ static int snd_msnd_pinnacle_cfg_reset(int cfg) static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -module_param_array(index, int, NULL, S_IRUGO); +module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for msnd_pinnacle soundcard."); -module_param_array(id, charp, NULL, S_IRUGO); +module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for msnd_pinnacle soundcard."); static long io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; @@ -801,22 +801,22 @@ MODULE_LICENSE("GPL"); MODULE_FIRMWARE(INITCODEFILE); MODULE_FIRMWARE(PERMCODEFILE); -module_param_hw_array(io, long, ioport, NULL, S_IRUGO); +module_param_hw_array(io, long, ioport, NULL, 0444); MODULE_PARM_DESC(io, "IO port #"); -module_param_hw_array(irq, int, irq, NULL, S_IRUGO); -module_param_hw_array(mem, long, iomem, NULL, S_IRUGO); -module_param_array(write_ndelay, int, NULL, S_IRUGO); -module_param(calibrate_signal, int, S_IRUGO); +module_param_hw_array(irq, int, irq, NULL, 0444); +module_param_hw_array(mem, long, iomem, NULL, 0444); +module_param_array(write_ndelay, int, NULL, 0444); +module_param(calibrate_signal, int, 0444); #ifndef MSND_CLASSIC -module_param_array(digital, int, NULL, S_IRUGO); -module_param_hw_array(cfg, long, ioport, NULL, S_IRUGO); -module_param_array(reset, int, 0, S_IRUGO); -module_param_hw_array(mpu_io, long, ioport, NULL, S_IRUGO); -module_param_hw_array(mpu_irq, int, irq, NULL, S_IRUGO); -module_param_hw_array(ide_io0, long, ioport, NULL, S_IRUGO); -module_param_hw_array(ide_io1, long, ioport, NULL, S_IRUGO); -module_param_hw_array(ide_irq, int, irq, NULL, S_IRUGO); -module_param_hw_array(joystick_io, long, ioport, NULL, S_IRUGO); +module_param_array(digital, int, NULL, 0444); +module_param_hw_array(cfg, long, ioport, NULL, 0444); +module_param_array(reset, int, 0, 0444); +module_param_hw_array(mpu_io, long, ioport, NULL, 0444); +module_param_hw_array(mpu_irq, int, irq, NULL, 0444); +module_param_hw_array(ide_io0, long, ioport, NULL, 0444); +module_param_hw_array(ide_io1, long, ioport, NULL, 0444); +module_param_hw_array(ide_irq, int, irq, NULL, 0444); +module_param_hw_array(joystick_io, long, ioport, NULL, 0444); #endif diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index c09d9b914efe..a985e9183be9 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c @@ -592,7 +592,7 @@ static int snd_sc6000_probe(struct device *devptr, unsigned int dev) *vport = devm_ioport_map(devptr, port[dev], 0x10); if (*vport == NULL) { snd_printk(KERN_ERR PFX - "I/O port cannot be iomaped.\n"); + "I/O port cannot be iomapped.\n"); err = -EBUSY; goto err_unmap1; } @@ -607,7 +607,7 @@ static int snd_sc6000_probe(struct device *devptr, unsigned int dev) vmss_port = devm_ioport_map(devptr, mss_port[dev], 4); if (!vmss_port) { snd_printk(KERN_ERR PFX - "MSS port I/O cannot be iomaped.\n"); + "MSS port I/O cannot be iomapped.\n"); err = -EBUSY; goto err_unmap2; } diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 6320bf084e47..e120a11c69e8 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -448,7 +448,7 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97) if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) { snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read); #ifdef CONFIG_SND_DEBUG - entry->mode |= S_IWUSR; + entry->mode |= 0200; entry->c.text.write = snd_ac97_proc_regs_write; #endif if (snd_info_register(entry) < 0) { @@ -474,7 +474,7 @@ void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus) sprintf(name, "codec97#%d", bus->num); if ((entry = snd_info_create_card_entry(bus->card, name, bus->card->proc_root)) != NULL) { - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + entry->mode = S_IFDIR | 0555; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 0bf2c04eeada..d9c54c08e2db 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -258,7 +258,7 @@ snd_ad1889_ac97_ready(struct snd_ad1889 *chip) while (!(ad1889_readw(chip, AD_AC97_ACIC) & AD_AC97_ACIC_ACRDY) && --retry) - mdelay(1); + usleep_range(1000, 2000); if (!retry) { dev_err(chip->card->dev, "[%s] Link is not ready.\n", __func__); @@ -872,7 +872,7 @@ snd_ad1889_init(struct snd_ad1889 *chip) ad1889_writew(chip, AD_DS_CCS, AD_DS_CCS_CLKEN); /* turn on clock */ ad1889_readw(chip, AD_DS_CCS); /* flush posted write */ - mdelay(10); + usleep_range(10000, 11000); /* enable Master and Target abort interrupts */ ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PMAE | AD_DMA_DISR_PTAE); diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 720361455c60..64e0961f93ba 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -69,27 +69,27 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; static bool enable_hpi_hwdep = 1; -module_param_array(index, int, NULL, S_IRUGO); +module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard."); -module_param_array(id, charp, NULL, S_IRUGO); +module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard."); -module_param_array(enable, bool, NULL, S_IRUGO); +module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard."); -module_param(enable_hpi_hwdep, bool, S_IRUGO|S_IWUSR); +module_param(enable_hpi_hwdep, bool, 0644); MODULE_PARM_DESC(enable_hpi_hwdep, "ALSA enable HPI hwdep for AudioScience soundcard "); /* identify driver */ #ifdef KERNEL_ALSA_BUILD static char *build_info = "Built using headers from kernel source"; -module_param(build_info, charp, S_IRUGO); +module_param(build_info, charp, 0444); MODULE_PARM_DESC(build_info, "Built using headers from kernel source"); #else static char *build_info = "Built within ALSA source"; -module_param(build_info, charp, S_IRUGO); +module_param(build_info, charp, 0444); MODULE_PARM_DESC(build_info, "Built within ALSA source"); #endif diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index b1a2a7ea4172..7d049569012c 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -46,14 +46,14 @@ MODULE_FIRMWARE("asihpi/dsp8900.bin"); #endif static int prealloc_stream_buf; -module_param(prealloc_stream_buf, int, S_IRUGO); +module_param(prealloc_stream_buf, int, 0444); MODULE_PARM_DESC(prealloc_stream_buf, "Preallocate size for per-adapter stream buffer"); /* Allow the debug level to be changed after module load. E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel */ -module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR); +module_param(hpi_debug_level, int, 0644); MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5"); /* List of adapters found */ diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index 9b2b8b38122f..a2c85cc37972 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -431,7 +431,7 @@ int snd_ca0106_proc_init(struct snd_ca0106 *emu) if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) { snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read32); entry->c.text.write = snd_ca0106_proc_reg_write32; - entry->mode |= S_IWUSR; + entry->mode |= 0200; } if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry)) snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read16); @@ -440,12 +440,12 @@ int snd_ca0106_proc_init(struct snd_ca0106 *emu) if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) { snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read1); entry->c.text.write = snd_ca0106_proc_reg_write; - entry->mode |= S_IWUSR; + entry->mode |= 0200; } if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) { entry->c.text.write = snd_ca0106_proc_i2c_write; entry->private_data = emu; - entry->mode |= S_IWUSR; + entry->mode |= 0200; } if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry)) snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2); diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 26a657870664..452cc79b44af 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -1139,7 +1139,7 @@ static int save_mixer_state(struct cmipci *cm) struct snd_ctl_elem_value *val; unsigned int i; - val = kmalloc(sizeof(*val), GFP_ATOMIC); + val = kmalloc(sizeof(*val), GFP_KERNEL); if (!val) return -ENOMEM; for (i = 0; i < CM_SAVED_MIXERS; i++) { diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 655fbea1692c..4910d3f46d4b 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -58,7 +58,7 @@ MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable CS46xx soundcard."); module_param_array(external_amp, bool, NULL, 0444); -MODULE_PARM_DESC(external_amp, "Force to enable external amplifer."); +MODULE_PARM_DESC(external_amp, "Force to enable external amplifier."); module_param_array(thinkpad, bool, NULL, 0444); MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control."); module_param_array(mmap_valid, bool, NULL, 0444); diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 0020fd0efc46..ed1251c5f449 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2849,7 +2849,7 @@ static int snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip) entry->private_data = chip; entry->c.ops = &snd_cs46xx_proc_io_ops; entry->size = region->size; - entry->mode = S_IFREG | S_IRUSR; + entry->mode = S_IFREG | 0400; } } #ifdef CONFIG_SND_CS46XX_NEW_DSP diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index aa61615288ff..c44eadef64ae 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -798,7 +798,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; + entry->mode = S_IFDIR | 0555; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -814,7 +814,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) if ((entry = snd_info_create_card_entry(card, "spos_symbols", ins->proc_dsp_dir)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | 0644; entry->c.text.read = cs46xx_dsp_proc_symbol_table_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -826,7 +826,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) if ((entry = snd_info_create_card_entry(card, "spos_modules", ins->proc_dsp_dir)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | 0644; entry->c.text.read = cs46xx_dsp_proc_modules_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -838,7 +838,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) if ((entry = snd_info_create_card_entry(card, "parameter", ins->proc_dsp_dir)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | 0644; entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -850,7 +850,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) if ((entry = snd_info_create_card_entry(card, "sample", ins->proc_dsp_dir)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | 0644; entry->c.text.read = cs46xx_dsp_proc_sample_dump_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -862,7 +862,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) if ((entry = snd_info_create_card_entry(card, "task_tree", ins->proc_dsp_dir)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | 0644; entry->c.text.read = cs46xx_dsp_proc_task_tree_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -874,7 +874,7 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) if ((entry = snd_info_create_card_entry(card, "scb_info", ins->proc_dsp_dir)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | 0644; entry->c.text.read = cs46xx_dsp_proc_scb_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index 7488e1b7a770..abb01ce66983 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -271,7 +271,7 @@ void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip, entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = scb_info; - entry->mode = S_IFREG | S_IRUGO | S_IWUSR; + entry->mode = S_IFREG | 0644; entry->c.text.read = cs46xx_dsp_proc_scb_info_read; diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index 08e874e9a7f6..2099e9ce441a 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -17,7 +17,7 @@ static bool use_system_timer; MODULE_PARM_DESC(use_system_timer, "Force to use system-timer"); -module_param(use_system_timer, bool, S_IRUGO); +module_param(use_system_timer, bool, 0444); struct ct_timer_ops { void (*init)(struct ct_timer_instance *); diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index f2f32779de98..b2874220be1b 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -26,9 +26,9 @@ MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}"); static unsigned int reference_rate = 48000; static unsigned int multiple = 2; MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)"); -module_param(reference_rate, uint, S_IRUGO); +module_param(reference_rate, uint, 0444); MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)"); -module_param(multiple, uint, S_IRUGO); +module_param(multiple, uint, 0444); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 0935a5c8741f..358ef7dcf410 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -59,7 +59,7 @@ static int get_firmware(const struct firmware **fw_entry, dev_dbg(chip->card->dev, "firmware requested: %s\n", card_fw[fw_index].data); snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data); - err = request_firmware(fw_entry, name, pci_device(chip)); + err = request_firmware(fw_entry, name, &chip->pci->dev); if (err < 0) dev_err(chip->card->dev, "get_firmware(): Firmware not available (%d)\n", err); diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index 152ce158583c..44b390a667d5 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -559,10 +559,4 @@ static inline int monitor_index(const struct echoaudio *chip, int out, int in) return out * num_busses_in(chip) + in; } - -#ifndef pci_device -#define pci_device(chip) (&chip->pci->dev) -#endif - - #endif /* _ECHOAUDIO_H_ */ diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 2c2b12a06177..611589cbdad6 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1070,7 +1070,7 @@ static int snd_emu10k1x_proc_init(struct emu10k1x *emu) if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) { snd_info_set_text_ops(entry, emu, snd_emu10k1x_proc_reg_read); entry->c.text.write = snd_emu10k1x_proc_reg_write; - entry->mode |= S_IWUSR; + entry->mode |= 0200; entry->private_data = emu; } diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index a2b56b188be4..b45a01bb73e5 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -170,7 +170,7 @@ static char *audigy_outs[32] = { /* 0x0f */ "Rear Right", /* 0x10 */ "AC97 Front Left", /* 0x11 */ "AC97 Front Right", - /* 0x12 */ "ADC Caputre Left", + /* 0x12 */ "ADC Capture Left", /* 0x13 */ "ADC Capture Right", /* 0x14 */ NULL, /* 0x15 */ NULL, @@ -421,14 +421,10 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu, snd_fx8010_irq_handler_t *handler, unsigned char gpr_running, void *private_data, - struct snd_emu10k1_fx8010_irq **r_irq) + struct snd_emu10k1_fx8010_irq *irq) { - struct snd_emu10k1_fx8010_irq *irq; unsigned long flags; - irq = kmalloc(sizeof(*irq), GFP_ATOMIC); - if (irq == NULL) - return -ENOMEM; irq->handler = handler; irq->gpr_running = gpr_running; irq->private_data = private_data; @@ -443,8 +439,6 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu, emu->fx8010.irq_handlers = irq; } spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags); - if (r_irq) - *r_irq = irq; return 0; } @@ -468,7 +462,6 @@ int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu, tmp->next = tmp->next->next; } spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags); - kfree(irq); return 0; } diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index cefe613ef7b7..d39458ab251f 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1724,7 +1724,7 @@ static int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substre case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: - snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL; + snd_emu10k1_fx8010_unregister_irq_handler(emu, &pcm->irq); snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); pcm->tram_shift = 0; diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index bde0d1954f56..b57008031792 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -135,7 +135,7 @@ static void snd_emu10k1_proc_read(struct snd_info_entry *entry, /* 15 */ "Rear Right", /* 16 */ "AC97 Front Left", /* 17 */ "AC97 Front Right", - /* 18 */ "ADC Caputre Left", + /* 18 */ "ADC Capture Left", /* 19 */ "ADC Capture Right", /* 20 */ "???", /* 21 */ "???", @@ -574,32 +574,32 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu) if (! snd_card_proc_new(emu->card, "io_regs", &entry)) { snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read); entry->c.text.write = snd_emu_proc_io_reg_write; - entry->mode |= S_IWUSR; + entry->mode |= 0200; } if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) { snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a); entry->c.text.write = snd_emu_proc_ptr_reg_write00; - entry->mode |= S_IWUSR; + entry->mode |= 0200; } if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) { snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b); entry->c.text.write = snd_emu_proc_ptr_reg_write00; - entry->mode |= S_IWUSR; + entry->mode |= 0200; } if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) { snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a); entry->c.text.write = snd_emu_proc_ptr_reg_write20; - entry->mode |= S_IWUSR; + entry->mode |= 0200; } if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) { snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b); entry->c.text.write = snd_emu_proc_ptr_reg_write20; - entry->mode |= S_IWUSR; + entry->mode |= 0200; } if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) { snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c); entry->c.text.write = snd_emu_proc_ptr_reg_write20; - entry->mode |= S_IWUSR; + entry->mode |= 0200; } #endif @@ -621,35 +621,35 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu) if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; + entry->mode = S_IFREG | 0444 /*| S_IWUSR*/; entry->size = emu->audigy ? A_TOTAL_SIZE_GPR : TOTAL_SIZE_GPR; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; } if (! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; + entry->mode = S_IFREG | 0444 /*| S_IWUSR*/; entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_DATA : TOTAL_SIZE_TANKMEM_DATA ; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; } if (! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; + entry->mode = S_IFREG | 0444 /*| S_IWUSR*/; entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_ADDR : TOTAL_SIZE_TANKMEM_ADDR ; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; } if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; + entry->mode = S_IFREG | 0444 /*| S_IWUSR*/; entry->size = emu->audigy ? A_TOTAL_SIZE_CODE : TOTAL_SIZE_CODE; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; } if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = emu; - entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; + entry->mode = S_IFREG | 0444 /*| S_IWUSR*/; entry->c.text.read = snd_emu10k1_proc_acode_read; } return 0; diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index 5865f3b90b34..dbc7d8d0e1c4 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -248,13 +248,13 @@ __found_pages: static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr) { if (addr & ~emu->dma_mask) { - dev_err(emu->card->dev, + dev_err_ratelimited(emu->card->dev, "max memory size is 0x%lx (addr = 0x%lx)!!\n", emu->dma_mask, (unsigned long)addr); return 0; } if (addr & (EMUPAGESIZE-1)) { - dev_err(emu->card->dev, "page is not aligned\n"); + dev_err_ratelimited(emu->card->dev, "page is not aligned\n"); return 0; } return 1; @@ -345,7 +345,7 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst else addr = snd_pcm_sgbuf_get_addr(substream, ofs); if (! is_valid_page(emu, addr)) { - dev_err(emu->card->dev, + dev_err_ratelimited(emu->card->dev, "emu: failure page = %d\n", idx); mutex_unlock(&hdr->block_mutex); return NULL; diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index f7a492c382d9..4235907b7858 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -127,11 +127,15 @@ comment "Set to Y if you want auto-loading the codec driver" config SND_HDA_CODEC_HDMI tristate "Build HDMI/DisplayPort HD-audio codec support" + select SND_DYNAMIC_MINORS help Say Y or M here to include HDMI and DisplayPort HD-audio codec support in snd-hda-intel driver. This includes all AMD/ATI, Intel and Nvidia HDMI/DisplayPort codecs. + Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS + to assure the multiple streams for DP-MST support. + comment "Set to Y if you want auto-loading the codec driver" depends on SND_HDA=y && SND_HDA_CODEC_HDMI=m diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index d3ea73171a3d..b9a6b66aeb0e 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -793,11 +793,11 @@ EXPORT_SYMBOL_GPL(snd_hda_add_verbs); */ void snd_hda_apply_verbs(struct hda_codec *codec) { + const struct hda_verb **v; int i; - for (i = 0; i < codec->verbs.used; i++) { - struct hda_verb **v = snd_array_elem(&codec->verbs, i); + + snd_array_for_each(&codec->verbs, i, v) snd_hda_sequence_write(codec, *v); - } } EXPORT_SYMBOL_GPL(snd_hda_apply_verbs); @@ -890,10 +890,10 @@ EXPORT_SYMBOL_GPL(snd_hda_apply_fixup); static bool pin_config_match(struct hda_codec *codec, const struct hda_pintbl *pins) { + const struct hda_pincfg *pin; int i; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + snd_array_for_each(&codec->init_pins, i, pin) { hda_nid_t nid = pin->nid; u32 cfg = pin->cfg; const struct hda_pintbl *t_pins; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5bc3a7468e17..08151f3c0b13 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -481,9 +481,10 @@ static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec, struct snd_array *array, hda_nid_t nid) { + struct hda_pincfg *pin; int i; - for (i = 0; i < array->used; i++) { - struct hda_pincfg *pin = snd_array_elem(array, i); + + snd_array_for_each(array, i, pin) { if (pin->nid == nid) return pin; } @@ -618,14 +619,15 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target); */ void snd_hda_shutup_pins(struct hda_codec *codec) { + const struct hda_pincfg *pin; int i; + /* don't shut up pins when unloading the driver; otherwise it breaks * the default pin setup at the next load of the driver */ if (codec->bus->shutdown) return; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + snd_array_for_each(&codec->init_pins, i, pin) { /* use read here for syncing after issuing each verb */ snd_hda_codec_read(codec, pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0); @@ -638,13 +640,14 @@ EXPORT_SYMBOL_GPL(snd_hda_shutup_pins); /* Restore the pin controls cleared previously via snd_hda_shutup_pins() */ static void restore_shutup_pins(struct hda_codec *codec) { + const struct hda_pincfg *pin; int i; + if (!codec->pins_shutup) return; if (codec->bus->shutdown) return; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + snd_array_for_each(&codec->init_pins, i, pin) { snd_hda_codec_write(codec, pin->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin->ctrl); @@ -697,8 +700,7 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid) struct hda_cvt_setup *p; int i; - for (i = 0; i < codec->cvt_setups.used; i++) { - p = snd_array_elem(&codec->cvt_setups, i); + snd_array_for_each(&codec->cvt_setups, i, p) { if (p->nid == nid) return p; } @@ -1076,8 +1078,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, /* make other inactive cvts with the same stream-tag dirty */ type = get_wcaps_type(get_wcaps(codec, nid)); list_for_each_codec(c, codec->bus) { - for (i = 0; i < c->cvt_setups.used; i++) { - p = snd_array_elem(&c->cvt_setups, i); + snd_array_for_each(&c->cvt_setups, i, p) { if (!p->active && p->stream_tag == stream_tag && get_wcaps_type(get_wcaps(c, p->nid)) == type) p->dirty = 1; @@ -1140,12 +1141,11 @@ static void really_cleanup_stream(struct hda_codec *codec, static void purify_inactive_streams(struct hda_codec *codec) { struct hda_codec *c; + struct hda_cvt_setup *p; int i; list_for_each_codec(c, codec->bus) { - for (i = 0; i < c->cvt_setups.used; i++) { - struct hda_cvt_setup *p; - p = snd_array_elem(&c->cvt_setups, i); + snd_array_for_each(&c->cvt_setups, i, p) { if (p->dirty) really_cleanup_stream(c, p); } @@ -1156,10 +1156,10 @@ static void purify_inactive_streams(struct hda_codec *codec) /* clean up all streams; called from suspend */ static void hda_cleanup_all_streams(struct hda_codec *codec) { + struct hda_cvt_setup *p; int i; - for (i = 0; i < codec->cvt_setups.used; i++) { - struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i); + snd_array_for_each(&codec->cvt_setups, i, p) { if (p->stream_tag) really_cleanup_stream(codec, p); } @@ -1493,10 +1493,10 @@ static void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv) val1 = ((int)val1) * ((int)val2); if (min_mute || (caps & AC_AMPCAP_MIN_MUTE)) val2 |= TLV_DB_SCALE_MUTE; - tlv[0] = SNDRV_CTL_TLVT_DB_SCALE; - tlv[1] = 2 * sizeof(unsigned int); - tlv[2] = val1; - tlv[3] = val2; + tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE; + tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int); + tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = val1; + tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = val2; } /** @@ -1544,10 +1544,10 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; step = (step + 1) * 25; - tlv[0] = SNDRV_CTL_TLVT_DB_SCALE; - tlv[1] = 2 * sizeof(unsigned int); - tlv[2] = -nums * step; - tlv[3] = step; + tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE; + tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int); + tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = -nums * step; + tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = step; } EXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv); @@ -1845,10 +1845,10 @@ static int init_slave_0dB(struct snd_kcontrol *slave, } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) tlv = kctl->tlv.p; - if (!tlv || tlv[0] != SNDRV_CTL_TLVT_DB_SCALE) + if (!tlv || tlv[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE) return 0; - step = tlv[3]; + step = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP]; step &= ~TLV_DB_SCALE_MUTE; if (!step) return 0; @@ -1860,7 +1860,7 @@ static int init_slave_0dB(struct snd_kcontrol *slave, } arg->step = step; - val = -tlv[2] / step; + val = -tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] / step; if (val > 0) { put_kctl_with_value(slave, val); return val; @@ -2175,6 +2175,8 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol, int idx = kcontrol->private_value; struct hda_spdif_out *spdif; + if (WARN_ON(codec->spdif_out.used <= idx)) + return -EINVAL; mutex_lock(&codec->spdif_mutex); spdif = snd_array_elem(&codec->spdif_out, idx); ucontrol->value.iec958.status[0] = spdif->status & 0xff; @@ -2282,6 +2284,8 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, unsigned short val; int change; + if (WARN_ON(codec->spdif_out.used <= idx)) + return -EINVAL; mutex_lock(&codec->spdif_mutex); spdif = snd_array_elem(&codec->spdif_out, idx); nid = spdif->nid; @@ -2308,6 +2312,8 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, int idx = kcontrol->private_value; struct hda_spdif_out *spdif; + if (WARN_ON(codec->spdif_out.used <= idx)) + return -EINVAL; mutex_lock(&codec->spdif_mutex); spdif = snd_array_elem(&codec->spdif_out, idx); ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE; @@ -2336,6 +2342,8 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, unsigned short val; int change; + if (WARN_ON(codec->spdif_out.used <= idx)) + return -EINVAL; mutex_lock(&codec->spdif_mutex); spdif = snd_array_elem(&codec->spdif_out, idx); nid = spdif->nid; @@ -2461,10 +2469,10 @@ EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls); struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, hda_nid_t nid) { + struct hda_spdif_out *spdif; int i; - for (i = 0; i < codec->spdif_out.used; i++) { - struct hda_spdif_out *spdif = - snd_array_elem(&codec->spdif_out, i); + + snd_array_for_each(&codec->spdif_out, i, spdif) { if (spdif->nid == nid) return spdif; } @@ -2483,6 +2491,8 @@ void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) { struct hda_spdif_out *spdif; + if (WARN_ON(codec->spdif_out.used <= idx)) + return; mutex_lock(&codec->spdif_mutex); spdif = snd_array_elem(&codec->spdif_out, idx); spdif->nid = (u16)-1; @@ -2503,6 +2513,8 @@ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) struct hda_spdif_out *spdif; unsigned short val; + if (WARN_ON(codec->spdif_out.used <= idx)) + return; mutex_lock(&codec->spdif_mutex); spdif = snd_array_elem(&codec->spdif_out, idx); if (spdif->nid != nid) { diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index d1eb14842340..a12e594d4e3b 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -748,8 +748,10 @@ int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec, return err; strlcpy(pcm->name, cpcm->name, sizeof(pcm->name)); apcm = kzalloc(sizeof(*apcm), GFP_KERNEL); - if (apcm == NULL) + if (apcm == NULL) { + snd_device_free(chip->card, pcm); return -ENOMEM; + } apcm->chip = chip; apcm->pcm = pcm; apcm->codec = codec; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 5cc65093d941..db773e219aaa 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -264,10 +264,10 @@ static struct nid_path *get_nid_path(struct hda_codec *codec, int anchor_nid) { struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; int i; - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); + snd_array_for_each(&spec->paths, i, path) { if (path->depth <= 0) continue; if ((!from_nid || path->path[0] == from_nid) && @@ -325,10 +325,10 @@ EXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx); static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { struct hda_gen_spec *spec = codec->spec; + const struct nid_path *path; int i; - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); + snd_array_for_each(&spec->paths, i, path) { if (path->path[0] == nid) return true; } @@ -351,11 +351,11 @@ static bool is_reachable_path(struct hda_codec *codec, static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) { struct hda_gen_spec *spec = codec->spec; + const struct nid_path *path; int i; val &= AMP_VAL_COMPARE_MASK; - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); + snd_array_for_each(&spec->paths, i, path) { if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) return true; } @@ -638,13 +638,13 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, { struct hda_gen_spec *spec = codec->spec; int type = get_wcaps_type(get_wcaps(codec, nid)); + const struct nid_path *path; int i, n; if (nid == codec->core.afg) return true; - for (n = 0; n < spec->paths.used; n++) { - struct nid_path *path = snd_array_elem(&spec->paths, n); + snd_array_for_each(&spec->paths, n, path) { if (!path->active) continue; if (codec->power_save_node) { @@ -2065,7 +2065,7 @@ static int parse_output_paths(struct hda_codec *codec) snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, HDA_OUTPUT, spec->vmaster_tlv); if (spec->dac_min_mute) - spec->vmaster_tlv[3] |= TLV_DB_SCALE_MUTE; + spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE; } } @@ -2696,10 +2696,10 @@ static const struct snd_kcontrol_new out_jack_mode_enum = { static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx) { struct hda_gen_spec *spec = codec->spec; + const struct snd_kcontrol_new *kctl; int i; - for (i = 0; i < spec->kctls.used; i++) { - struct snd_kcontrol_new *kctl = snd_array_elem(&spec->kctls, i); + snd_array_for_each(&spec->kctls, i, kctl) { if (!strcmp(kctl->name, name) && kctl->index == idx) return true; } @@ -4021,8 +4021,7 @@ static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid, struct nid_path *path; int n; - for (n = 0; n < spec->paths.used; n++) { - path = snd_array_elem(&spec->paths, n); + snd_array_for_each(&spec->paths, n, path) { if (!path->depth) continue; if (path->path[0] == nid || @@ -5831,10 +5830,10 @@ static void init_digital(struct hda_codec *codec) */ static void clear_unsol_on_unused_pins(struct hda_codec *codec) { + const struct hda_pincfg *pin; int i; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + snd_array_for_each(&codec->init_pins, i, pin) { hda_nid_t nid = pin->nid; if (is_jack_detectable(codec, nid) && !snd_hda_jack_tbl_get(codec, nid)) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index a0c93b9c9a28..1ae1850b3bfd 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2209,7 +2209,18 @@ static struct snd_pci_quirk power_save_blacklist[] = { /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ SND_PCI_QUIRK(0x1849, 0x0c0c, "Asrock B85M-ITX", 0), /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ + SND_PCI_QUIRK(0x1849, 0x7662, "Asrock H81M-HDS", 0), + /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0), + /* https://bugzilla.redhat.com/show_bug.cgi?id=1581607 */ + SND_PCI_QUIRK(0x1558, 0x3501, "Clevo W35xSS_370SS", 0), + /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ + /* Note the P55A-UD3 and Z87-D3HP share the subsys id for the HDA dev */ + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P55A-UD3 / Z87-D3HP", 0), + /* https://bugzilla.kernel.org/show_bug.cgi?id=199607 */ + SND_PCI_QUIRK(0x8086, 0x2057, "Intel NUC5i7RYB", 0), + /* https://bugzilla.redhat.com/show_bug.cgi?id=1520902 */ + SND_PCI_QUIRK(0x8086, 0x2068, "Intel NUC7i3BNB", 0), /* https://bugzilla.redhat.com/show_bug.cgi?id=1572975 */ SND_PCI_QUIRK(0x17aa, 0x36a7, "Lenovo C50 All in one", 0), /* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */ diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index 9b7efece4484..6ec79c58d48d 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -80,10 +80,10 @@ static ssize_t pin_configs_show(struct hda_codec *codec, struct snd_array *list, char *buf) { + const struct hda_pincfg *pin; int i, len = 0; mutex_lock(&codec->user_mutex); - for (i = 0; i < list->used; i++) { - struct hda_pincfg *pin = snd_array_elem(list, i); + snd_array_for_each(list, i, pin) { len += sprintf(buf + len, "0x%02x 0x%08x\n", pin->nid, pin->cfg); } @@ -217,10 +217,10 @@ static ssize_t init_verbs_show(struct device *dev, char *buf) { struct hda_codec *codec = dev_get_drvdata(dev); + const struct hda_verb *v; int i, len = 0; mutex_lock(&codec->user_mutex); - for (i = 0; i < codec->init_verbs.used; i++) { - struct hda_verb *v = snd_array_elem(&codec->init_verbs, i); + snd_array_for_each(&codec->init_verbs, i, v) { len += snprintf(buf + len, PAGE_SIZE - len, "0x%02x 0x%03x 0x%04x\n", v->nid, v->verb, v->param); @@ -267,10 +267,10 @@ static ssize_t hints_show(struct device *dev, char *buf) { struct hda_codec *codec = dev_get_drvdata(dev); + const struct hda_hint *hint; int i, len = 0; mutex_lock(&codec->user_mutex); - for (i = 0; i < codec->hints.used; i++) { - struct hda_hint *hint = snd_array_elem(&codec->hints, i); + snd_array_for_each(&codec->hints, i, hint) { len += snprintf(buf + len, PAGE_SIZE - len, "%s = %s\n", hint->key, hint->val); } @@ -280,10 +280,10 @@ static ssize_t hints_show(struct device *dev, static struct hda_hint *get_hint(struct hda_codec *codec, const char *key) { + struct hda_hint *hint; int i; - for (i = 0; i < codec->hints.used; i++) { - struct hda_hint *hint = snd_array_elem(&codec->hints, i); + snd_array_for_each(&codec->hints, i, hint) { if (!strcmp(hint->key, key)) return hint; } @@ -783,13 +783,13 @@ void snd_hda_sysfs_init(struct hda_codec *codec) void snd_hda_sysfs_clear(struct hda_codec *codec) { #ifdef CONFIG_SND_HDA_RECONFIG + struct hda_hint *hint; int i; /* clear init verbs */ snd_array_free(&codec->init_verbs); /* clear hints */ - for (i = 0; i < codec->hints.used; i++) { - struct hda_hint *hint = snd_array_elem(&codec->hints, i); + snd_array_for_each(&codec->hints, i, hint) { kfree(hint->key); /* we don't need to free hint->val */ } snd_array_free(&codec->hints); diff --git a/sound/pci/hda/hp_x360_helper.c b/sound/pci/hda/hp_x360_helper.c new file mode 100644 index 000000000000..969542c57358 --- /dev/null +++ b/sound/pci/hda/hp_x360_helper.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Fixes for HP X360 laptops with top B&O speakers + * to be included from codec driver + */ + +static void alc295_fixup_hp_top_speakers(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const struct hda_pintbl pincfgs[] = { + { 0x17, 0x90170110 }, + { } + }; + static const struct coef_fw alc295_hp_speakers_coefs[] = { + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0600), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0xc0c0), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0008), WRITE_COEF(0x28, 0xb000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x002e), WRITE_COEF(0x28, 0x0800), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x00c1), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0320), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0039), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003b), WRITE_COEF(0x28, 0xffff), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003c), WRITE_COEF(0x28, 0xffd0), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0080), WRITE_COEF(0x28, 0x0880), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x0dfe), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0018), WRITE_COEF(0x28, 0x0219), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x005d), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x9142), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c0), WRITE_COEF(0x28, 0x01ce), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c1), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c2), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c3), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c4), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c5), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c6), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c7), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c8), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c9), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ca), WRITE_COEF(0x28, 0x01c0), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cb), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cc), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cd), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ce), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cf), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d0), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d1), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d2), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d3), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0062), WRITE_COEF(0x28, 0x8000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0063), WRITE_COEF(0x28, 0x5f5f), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0064), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0065), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0066), WRITE_COEF(0x28, 0x4004), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0067), WRITE_COEF(0x28, 0x0802), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0068), WRITE_COEF(0x28, 0x890f), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0069), WRITE_COEF(0x28, 0xe021), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0070), WRITE_COEF(0x28, 0x8012), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0071), WRITE_COEF(0x28, 0x3450), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0072), WRITE_COEF(0x28, 0x0123), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0073), WRITE_COEF(0x28, 0x4543), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0074), WRITE_COEF(0x28, 0x2100), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0075), WRITE_COEF(0x28, 0x4321), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0076), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x8200), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0051), WRITE_COEF(0x28, 0x0707), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0052), WRITE_COEF(0x28, 0x4090), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0090), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x721f), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0012), WRITE_COEF(0x28, 0xebeb), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x009e), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0060), WRITE_COEF(0x28, 0x2213), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x3000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0500), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0040), WRITE_COEF(0x28, 0x800c), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0046), WRITE_COEF(0x28, 0xc22e), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x004b), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024), + WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x82ec), WRITE_COEF(0x29, 0xb024), + }; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); + alc295_fixup_disable_dac3(codec, fix, action); + break; + case HDA_FIXUP_ACT_INIT: + alc_process_coef_fw(codec, alc295_hp_speakers_coefs); + break; + } +} diff --git a/sound/pci/hda/local.h b/sound/pci/hda/local.h deleted file mode 100644 index 3b8b7d78f9e0..000000000000 --- a/sound/pci/hda/local.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - */ - -#ifndef __HDAC_LOCAL_H -#define __HDAC_LOCAL_H - -int hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm); - -#define get_wcaps(codec, nid) \ - hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) -/* get the widget type from widget capability bits */ -static inline int get_wcaps_type(unsigned int wcaps) -{ - if (!wcaps) - return -1; /* invalid type */ - return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; -} - -#define get_pin_caps(codec, nid) \ - hdac_read_parm(codec, nid, AC_PAR_PIN_CAP) - -static inline -unsigned int get_pin_cfg(struct hdac_device *codec, hda_nid_t nid) -{ - unsigned int val; - - if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val)) - return -1; - return val; -} - -#define get_amp_caps(codec, nid, dir) \ - hdac_read_parm(codec, nid, (dir) == HDA_OUTPUT ? \ - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP) - -#define get_power_caps(codec, nid) \ - hdac_read_parm(codec, nid, AC_PAR_POWER_STATE) - -#endif /* __HDAC_LOCAL_H */ diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 768ea8651993..292e2c592c17 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -28,6 +28,9 @@ #include <linux/module.h> #include <linux/firmware.h> #include <linux/kernel.h> +#include <linux/types.h> +#include <linux/io.h> +#include <linux/pci.h> #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" @@ -39,9 +42,15 @@ /* Enable this to see controls for tuning purpose. */ /*#define ENABLE_TUNING_CONTROLS*/ +#ifdef ENABLE_TUNING_CONTROLS +#include <sound/tlv.h> +#endif + #define FLOAT_ZERO 0x00000000 #define FLOAT_ONE 0x3f800000 #define FLOAT_TWO 0x40000000 +#define FLOAT_THREE 0x40400000 +#define FLOAT_EIGHT 0x41000000 #define FLOAT_MINUS_5 0xc0a00000 #define UNSOL_TAG_DSP 0x16 @@ -72,16 +81,22 @@ #define SCP_GET 1 #define EFX_FILE "ctefx.bin" +#define SBZ_EFX_FILE "ctefx-sbz.bin" +#define R3DI_EFX_FILE "ctefx-r3di.bin" #ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP MODULE_FIRMWARE(EFX_FILE); +MODULE_FIRMWARE(SBZ_EFX_FILE); +MODULE_FIRMWARE(R3DI_EFX_FILE); #endif -static char *dirstr[2] = { "Playback", "Capture" }; +static const char *const dirstr[2] = { "Playback", "Capture" }; +#define NUM_OF_OUTPUTS 3 enum { SPEAKER_OUT, - HEADPHONE_OUT + HEADPHONE_OUT, + SURROUND_OUT }; enum { @@ -89,6 +104,15 @@ enum { LINE_MIC_IN }; +/* Strings for Input Source Enum Control */ +static const char *const in_src_str[3] = {"Rear Mic", "Line", "Front Mic" }; +#define IN_SRC_NUM_OF_INPUTS 3 +enum { + REAR_MIC, + REAR_LINE_IN, + FRONT_MIC, +}; + enum { #define VNODE_START_NID 0x80 VNID_SPK = VNODE_START_NID, /* Speaker vnid */ @@ -122,13 +146,28 @@ enum { VOICEFX = IN_EFFECT_END_NID, PLAY_ENHANCEMENT, CRYSTAL_VOICE, - EFFECT_END_NID + EFFECT_END_NID, + OUTPUT_SOURCE_ENUM, + INPUT_SOURCE_ENUM, + XBASS_XOVER, + EQ_PRESET_ENUM, + SMART_VOLUME_ENUM, + MIC_BOOST_ENUM #define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) }; /* Effects values size*/ #define EFFECT_VALS_MAX_COUNT 12 +/* + * Default values for the effect slider controls, they are in order of their + * effect NID's. Surround, Crystalizer, Dialog Plus, Smart Volume, and then + * X-bass. + */ +static const unsigned int effect_slider_defaults[] = {67, 65, 50, 74, 50}; +/* Amount of effect level sliders for ca0132_alt controls. */ +#define EFFECT_LEVEL_SLIDERS 5 + /* Latency introduced by DSP blocks in milliseconds. */ #define DSP_CAPTURE_INIT_LATENCY 0 #define DSP_CRYSTAL_VOICE_LATENCY 124 @@ -150,7 +189,7 @@ struct ct_effect { #define EFX_DIR_OUT 0 #define EFX_DIR_IN 1 -static struct ct_effect ca0132_effects[EFFECTS_COUNT] = { +static const struct ct_effect ca0132_effects[EFFECTS_COUNT] = { { .name = "Surround", .nid = SURROUND, .mid = 0x96, @@ -277,7 +316,7 @@ struct ct_tuning_ctl { unsigned int def_val;/*effect default values*/ }; -static struct ct_tuning_ctl ca0132_tuning_ctls[] = { +static const struct ct_tuning_ctl ca0132_tuning_ctls[] = { { .name = "Wedge Angle", .parent_nid = VOICE_FOCUS, .nid = WEDGE_ANGLE, @@ -392,14 +431,14 @@ struct ct_voicefx_preset { unsigned int vals[VOICEFX_MAX_PARAM_COUNT]; }; -static struct ct_voicefx ca0132_voicefx = { +static const struct ct_voicefx ca0132_voicefx = { .name = "VoiceFX Capture Switch", .nid = VOICEFX, .mid = 0x95, .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18} }; -static struct ct_voicefx_preset ca0132_voicefx_presets[] = { +static const struct ct_voicefx_preset ca0132_voicefx_presets[] = { { .name = "Neutral", .vals = { 0x00000000, 0x43C80000, 0x44AF0000, 0x44FA0000, 0x3F800000, 0x3F800000, @@ -472,6 +511,161 @@ static struct ct_voicefx_preset ca0132_voicefx_presets[] = { } }; +/* ca0132 EQ presets, taken from Windows Sound Blaster Z Driver */ + +#define EQ_PRESET_MAX_PARAM_COUNT 11 + +struct ct_eq { + char *name; + hda_nid_t nid; + int mid; + int reqs[EQ_PRESET_MAX_PARAM_COUNT]; /*effect module request*/ +}; + +struct ct_eq_preset { + char *name; /*preset name*/ + unsigned int vals[EQ_PRESET_MAX_PARAM_COUNT]; +}; + +static const struct ct_eq ca0132_alt_eq_enum = { + .name = "FX: Equalizer Preset Switch", + .nid = EQ_PRESET_ENUM, + .mid = 0x96, + .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} +}; + + +static const struct ct_eq_preset ca0132_alt_eq_presets[] = { + { .name = "Flat", + .vals = { 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000 } + }, + { .name = "Acoustic", + .vals = { 0x00000000, 0x00000000, 0x3F8CCCCD, + 0x40000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x40000000, + 0x40000000, 0x40000000 } + }, + { .name = "Classical", + .vals = { 0x00000000, 0x00000000, 0x40C00000, + 0x40C00000, 0x40466666, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x40466666, 0x40466666 } + }, + { .name = "Country", + .vals = { 0x00000000, 0xBF99999A, 0x00000000, + 0x3FA66666, 0x3FA66666, 0x3F8CCCCD, + 0x00000000, 0x00000000, 0x40000000, + 0x40466666, 0x40800000 } + }, + { .name = "Dance", + .vals = { 0x00000000, 0xBF99999A, 0x40000000, + 0x40466666, 0x40866666, 0xBF99999A, + 0xBF99999A, 0x00000000, 0x00000000, + 0x40800000, 0x40800000 } + }, + { .name = "Jazz", + .vals = { 0x00000000, 0x00000000, 0x00000000, + 0x3F8CCCCD, 0x40800000, 0x40800000, + 0x40800000, 0x00000000, 0x3F8CCCCD, + 0x40466666, 0x40466666 } + }, + { .name = "New Age", + .vals = { 0x00000000, 0x00000000, 0x40000000, + 0x40000000, 0x00000000, 0x00000000, + 0x00000000, 0x3F8CCCCD, 0x40000000, + 0x40000000, 0x40000000 } + }, + { .name = "Pop", + .vals = { 0x00000000, 0xBFCCCCCD, 0x00000000, + 0x40000000, 0x40000000, 0x00000000, + 0xBF99999A, 0xBF99999A, 0x00000000, + 0x40466666, 0x40C00000 } + }, + { .name = "Rock", + .vals = { 0x00000000, 0xBF99999A, 0xBF99999A, + 0x3F8CCCCD, 0x40000000, 0xBF99999A, + 0xBF99999A, 0x00000000, 0x00000000, + 0x40800000, 0x40800000 } + }, + { .name = "Vocal", + .vals = { 0x00000000, 0xC0000000, 0xBF99999A, + 0xBF99999A, 0x00000000, 0x40466666, + 0x40800000, 0x40466666, 0x00000000, + 0x00000000, 0x3F8CCCCD } + } +}; + +/* DSP command sequences for ca0132_alt_select_out */ +#define ALT_OUT_SET_MAX_COMMANDS 9 /* Max number of commands in sequence */ +struct ca0132_alt_out_set { + char *name; /*preset name*/ + unsigned char commands; + unsigned int mids[ALT_OUT_SET_MAX_COMMANDS]; + unsigned int reqs[ALT_OUT_SET_MAX_COMMANDS]; + unsigned int vals[ALT_OUT_SET_MAX_COMMANDS]; +}; + +static const struct ca0132_alt_out_set alt_out_presets[] = { + { .name = "Line Out", + .commands = 7, + .mids = { 0x96, 0x96, 0x96, 0x8F, + 0x96, 0x96, 0x96 }, + .reqs = { 0x19, 0x17, 0x18, 0x01, + 0x1F, 0x15, 0x3A }, + .vals = { 0x3F000000, 0x42A00000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000 } + }, + { .name = "Headphone", + .commands = 7, + .mids = { 0x96, 0x96, 0x96, 0x8F, + 0x96, 0x96, 0x96 }, + .reqs = { 0x19, 0x17, 0x18, 0x01, + 0x1F, 0x15, 0x3A }, + .vals = { 0x3F000000, 0x42A00000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000 } + }, + { .name = "Surround", + .commands = 8, + .mids = { 0x96, 0x8F, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96 }, + .reqs = { 0x18, 0x01, 0x1F, 0x15, + 0x3A, 0x1A, 0x1B, 0x1C }, + .vals = { 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000 } + } +}; + +/* + * DSP volume setting structs. Req 1 is left volume, req 2 is right volume, + * and I don't know what the third req is, but it's always zero. I assume it's + * some sort of update or set command to tell the DSP there's new volume info. + */ +#define DSP_VOL_OUT 0 +#define DSP_VOL_IN 1 + +struct ct_dsp_volume_ctl { + hda_nid_t vnid; + int mid; /* module ID*/ + unsigned int reqs[3]; /* scp req ID */ +}; + +static const struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = { + { .vnid = VNID_SPK, + .mid = 0x32, + .reqs = {3, 4, 2} + }, + { .vnid = VNID_MIC, + .mid = 0x37, + .reqs = {2, 3, 1} + } +}; + enum hda_cmd_vendor_io { /* for DspIO node */ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, @@ -698,11 +892,12 @@ enum dsp_download_state { */ struct ca0132_spec { - struct snd_kcontrol_new *mixers[5]; + const struct snd_kcontrol_new *mixers[5]; unsigned int num_mixers; const struct hda_verb *base_init_verbs; const struct hda_verb *base_exit_verbs; const struct hda_verb *chip_init_verbs; + const struct hda_verb *sbz_init_verbs; struct hda_verb *spec_init_verbs; struct auto_pin_cfg autocfg; @@ -719,6 +914,7 @@ struct ca0132_spec { hda_nid_t shared_mic_nid; hda_nid_t shared_out_nid; hda_nid_t unsol_tag_hp; + hda_nid_t unsol_tag_front_hp; /* for desktop ca0132 codecs */ hda_nid_t unsol_tag_amic1; /* chip access */ @@ -734,6 +930,9 @@ struct ca0132_spec { unsigned int scp_resp_header; unsigned int scp_resp_data[4]; unsigned int scp_resp_count; + bool alt_firmware_present; + bool startup_check_entered; + bool dsp_reload; /* mixer and effects related */ unsigned char dmic_ctl; @@ -746,6 +945,17 @@ struct ca0132_spec { long effects_switch[EFFECTS_COUNT]; long voicefx_val; long cur_mic_boost; + /* ca0132_alt control related values */ + unsigned char in_enum_val; + unsigned char out_enum_val; + unsigned char mic_boost_enum_val; + unsigned char smart_volume_setting; + long fx_ctl_val[EFFECT_LEVEL_SLIDERS]; + long xbass_xover_freq; + long eq_preset_val; + unsigned int tlv[4]; + struct hda_vmaster_mute_hook vmaster_mute; + struct hda_codec *codec; struct delayed_work unsol_hp_work; @@ -754,6 +964,25 @@ struct ca0132_spec { #ifdef ENABLE_TUNING_CONTROLS long cur_ctl_vals[TUNING_CTLS_COUNT]; #endif + /* + * Sound Blaster Z PCI region 2 iomem, used for input and output + * switching, and other unknown commands. + */ + void __iomem *mem_base; + + /* + * Whether or not to use the alt functions like alt_select_out, + * alt_select_in, etc. Only used on desktop codecs for now, because of + * surround sound support. + */ + bool use_alt_functions; + + /* + * Whether or not to use alt controls: volume effect sliders, EQ + * presets, smart volume presets, and new control names with FX prefix. + * Renames PlayEnhancement and CrystalVoice too. + */ + bool use_alt_controls; }; /* @@ -762,6 +991,8 @@ struct ca0132_spec { enum { QUIRK_NONE, QUIRK_ALIENWARE, + QUIRK_SBZ, + QUIRK_R3DI, }; static const struct hda_pintbl alienware_pincfgs[] = { @@ -778,10 +1009,44 @@ static const struct hda_pintbl alienware_pincfgs[] = { {} }; +/* Sound Blaster Z pin configs taken from Windows Driver */ +static const struct hda_pintbl sbz_pincfgs[] = { + { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */ + { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */ + { 0x0d, 0x014510f0 }, /* Digital Out */ + { 0x0e, 0x01c510f0 }, /* SPDIF In */ + { 0x0f, 0x0221701f }, /* Port A -- BackPanel HP */ + { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */ + { 0x11, 0x01017014 }, /* Port B -- LineMicIn2 / Rear L/R */ + { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */ + { 0x13, 0x908700f0 }, /* What U Hear In*/ + { 0x18, 0x50d000f0 }, /* N/A */ + {} +}; + +/* Recon3D integrated pin configs taken from Windows Driver */ +static const struct hda_pintbl r3di_pincfgs[] = { + { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */ + { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */ + { 0x0d, 0x014510f0 }, /* Digital Out */ + { 0x0e, 0x41c520f0 }, /* SPDIF In */ + { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */ + { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */ + { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */ + { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */ + { 0x13, 0x908700f0 }, /* What U Hear In*/ + { 0x18, 0x500000f0 }, /* N/A */ + {} +}; + static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE), SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE), SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE), + SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ), + SND_PCI_QUIRK(0x1102, 0x0023, "Sound Blaster Z", QUIRK_SBZ), + SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI), + SND_PCI_QUIRK(0x1458, 0xA036, "Recon3Di", QUIRK_R3DI), {} }; @@ -965,6 +1230,29 @@ exit: } /* + * Write given value to the given address through the chip I/O widget. + * not protected by the Mutex + */ +static int chipio_write_no_mutex(struct hda_codec *codec, + unsigned int chip_addx, const unsigned int data) +{ + int err; + + + /* write the address, and if successful proceed to write data */ + err = chipio_write_address(codec, chip_addx); + if (err < 0) + goto exit; + + err = chipio_write_data(codec, data); + if (err < 0) + goto exit; + +exit: + return err; +} + +/* * Write multiple values to the given address through the chip I/O widget. * protected by the Mutex */ @@ -1058,6 +1346,81 @@ static void chipio_set_control_param(struct hda_codec *codec, } /* + * Set chip parameters through the chip I/O widget. NO MUTEX. + */ +static void chipio_set_control_param_no_mutex(struct hda_codec *codec, + enum control_param_id param_id, int param_val) +{ + int val; + + if ((param_id < 32) && (param_val < 8)) { + val = (param_val << 5) | (param_id); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_SET, val); + } else { + if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) { + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_EX_ID_SET, + param_id); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_EX_VALUE_SET, + param_val); + } + } +} +/* + * Connect stream to a source point, and then connect + * that source point to a destination point. + */ +static void chipio_set_stream_source_dest(struct hda_codec *codec, + int streamid, int source_point, int dest_point) +{ + chipio_set_control_param_no_mutex(codec, + CONTROL_PARAM_STREAM_ID, streamid); + chipio_set_control_param_no_mutex(codec, + CONTROL_PARAM_STREAM_SOURCE_CONN_POINT, source_point); + chipio_set_control_param_no_mutex(codec, + CONTROL_PARAM_STREAM_DEST_CONN_POINT, dest_point); +} + +/* + * Set number of channels in the selected stream. + */ +static void chipio_set_stream_channels(struct hda_codec *codec, + int streamid, unsigned int channels) +{ + chipio_set_control_param_no_mutex(codec, + CONTROL_PARAM_STREAM_ID, streamid); + chipio_set_control_param_no_mutex(codec, + CONTROL_PARAM_STREAMS_CHANNELS, channels); +} + +/* + * Enable/Disable audio stream. + */ +static void chipio_set_stream_control(struct hda_codec *codec, + int streamid, int enable) +{ + chipio_set_control_param_no_mutex(codec, + CONTROL_PARAM_STREAM_ID, streamid); + chipio_set_control_param_no_mutex(codec, + CONTROL_PARAM_STREAM_CONTROL, enable); +} + + +/* + * Set sampling rate of the connection point. NO MUTEX. + */ +static void chipio_set_conn_rate_no_mutex(struct hda_codec *codec, + int connid, enum ca0132_sample_rate rate) +{ + chipio_set_control_param_no_mutex(codec, + CONTROL_PARAM_CONN_POINT_ID, connid); + chipio_set_control_param_no_mutex(codec, + CONTROL_PARAM_CONN_POINT_SAMPLE_RATE, rate); +} + +/* * Set sampling rate of the connection point. */ static void chipio_set_conn_rate(struct hda_codec *codec, @@ -1420,8 +1783,8 @@ static int dspio_send_scp_message(struct hda_codec *codec, * Returns zero or a negative error code. */ static int dspio_scp(struct hda_codec *codec, - int mod_id, int req, int dir, void *data, unsigned int len, - void *reply, unsigned int *reply_len) + int mod_id, int src_id, int req, int dir, const void *data, + unsigned int len, void *reply, unsigned int *reply_len) { int status = 0; struct scp_msg scp_send, scp_reply; @@ -1445,7 +1808,7 @@ static int dspio_scp(struct hda_codec *codec, return -EINVAL; } - scp_send.hdr = make_scp_header(mod_id, 0x20, (dir == SCP_GET), req, + scp_send.hdr = make_scp_header(mod_id, src_id, (dir == SCP_GET), req, 0, 0, 0, len/sizeof(unsigned int)); if (data != NULL && len > 0) { len = min((unsigned int)(sizeof(scp_send.data)), len); @@ -1502,15 +1865,24 @@ static int dspio_scp(struct hda_codec *codec, * Set DSP parameters */ static int dspio_set_param(struct hda_codec *codec, int mod_id, - int req, void *data, unsigned int len) + int src_id, int req, const void *data, unsigned int len) { - return dspio_scp(codec, mod_id, req, SCP_SET, data, len, NULL, NULL); + return dspio_scp(codec, mod_id, src_id, req, SCP_SET, data, len, NULL, + NULL); } static int dspio_set_uint_param(struct hda_codec *codec, int mod_id, - int req, unsigned int data) + int req, const unsigned int data) { - return dspio_set_param(codec, mod_id, req, &data, sizeof(unsigned int)); + return dspio_set_param(codec, mod_id, 0x20, req, &data, + sizeof(unsigned int)); +} + +static int dspio_set_uint_param_no_source(struct hda_codec *codec, int mod_id, + int req, const unsigned int data) +{ + return dspio_set_param(codec, mod_id, 0x00, req, &data, + sizeof(unsigned int)); } /* @@ -1522,8 +1894,9 @@ static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan) unsigned int size = sizeof(dma_chan); codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n"); - status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN, - SCP_GET, NULL, 0, dma_chan, &size); + status = dspio_scp(codec, MASTERCONTROL, 0x20, + MASTERCONTROL_ALLOC_DMA_CHAN, SCP_GET, NULL, 0, + dma_chan, &size); if (status < 0) { codec_dbg(codec, "dspio_alloc_dma_chan: SCP Failed\n"); @@ -1552,8 +1925,9 @@ static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan) codec_dbg(codec, " dspio_free_dma_chan() -- begin\n"); codec_dbg(codec, "dspio_free_dma_chan: chan=%d\n", dma_chan); - status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN, - SCP_SET, &dma_chan, sizeof(dma_chan), NULL, &dummy); + status = dspio_scp(codec, MASTERCONTROL, 0x20, + MASTERCONTROL_ALLOC_DMA_CHAN, SCP_SET, &dma_chan, + sizeof(dma_chan), NULL, &dummy); if (status < 0) { codec_dbg(codec, "dspio_free_dma_chan: SCP Failed\n"); @@ -2575,14 +2949,16 @@ exit: */ static void dspload_post_setup(struct hda_codec *codec) { + struct ca0132_spec *spec = codec->spec; codec_dbg(codec, "---- dspload_post_setup ------\n"); + if (!spec->use_alt_functions) { + /*set DSP speaker to 2.0 configuration*/ + chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080); + chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000); - /*set DSP speaker to 2.0 configuration*/ - chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080); - chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000); - - /*update write pointer*/ - chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002); + /*update write pointer*/ + chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002); + } } /** @@ -2690,6 +3066,170 @@ static bool dspload_wait_loaded(struct hda_codec *codec) } /* + * Setup GPIO for the other variants of Core3D. + */ + +/* + * Sets up the GPIO pins so that they are discoverable. If this isn't done, + * the card shows as having no GPIO pins. + */ +static void ca0132_gpio_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + switch (spec->quirk) { + case QUIRK_SBZ: + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); + snd_hda_codec_write(codec, 0x01, 0, 0x790, 0x23); + break; + case QUIRK_R3DI: + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5B); + break; + } + +} + +/* Sets the GPIO for audio output. */ +static void ca0132_gpio_setup(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + switch (spec->quirk) { + case QUIRK_SBZ: + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DIRECTION, 0x07); + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_MASK, 0x07); + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 0x04); + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 0x06); + break; + case QUIRK_R3DI: + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DIRECTION, 0x1E); + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_MASK, 0x1F); + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 0x0C); + break; + } +} + +/* + * GPIO control functions for the Recon3D integrated. + */ + +enum r3di_gpio_bit { + /* Bit 1 - Switch between front/rear mic. 0 = rear, 1 = front */ + R3DI_MIC_SELECT_BIT = 1, + /* Bit 2 - Switch between headphone/line out. 0 = Headphone, 1 = Line */ + R3DI_OUT_SELECT_BIT = 2, + /* + * I dunno what this actually does, but it stays on until the dsp + * is downloaded. + */ + R3DI_GPIO_DSP_DOWNLOADING = 3, + /* + * Same as above, no clue what it does, but it comes on after the dsp + * is downloaded. + */ + R3DI_GPIO_DSP_DOWNLOADED = 4 +}; + +enum r3di_mic_select { + /* Set GPIO bit 1 to 0 for rear mic */ + R3DI_REAR_MIC = 0, + /* Set GPIO bit 1 to 1 for front microphone*/ + R3DI_FRONT_MIC = 1 +}; + +enum r3di_out_select { + /* Set GPIO bit 2 to 0 for headphone */ + R3DI_HEADPHONE_OUT = 0, + /* Set GPIO bit 2 to 1 for speaker */ + R3DI_LINE_OUT = 1 +}; +enum r3di_dsp_status { + /* Set GPIO bit 3 to 1 until DSP is downloaded */ + R3DI_DSP_DOWNLOADING = 0, + /* Set GPIO bit 4 to 1 once DSP is downloaded */ + R3DI_DSP_DOWNLOADED = 1 +}; + + +static void r3di_gpio_mic_set(struct hda_codec *codec, + enum r3di_mic_select cur_mic) +{ + unsigned int cur_gpio; + + /* Get the current GPIO Data setup */ + cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0); + + switch (cur_mic) { + case R3DI_REAR_MIC: + cur_gpio &= ~(1 << R3DI_MIC_SELECT_BIT); + break; + case R3DI_FRONT_MIC: + cur_gpio |= (1 << R3DI_MIC_SELECT_BIT); + break; + } + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, cur_gpio); +} + +static void r3di_gpio_out_set(struct hda_codec *codec, + enum r3di_out_select cur_out) +{ + unsigned int cur_gpio; + + /* Get the current GPIO Data setup */ + cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0); + + switch (cur_out) { + case R3DI_HEADPHONE_OUT: + cur_gpio &= ~(1 << R3DI_OUT_SELECT_BIT); + break; + case R3DI_LINE_OUT: + cur_gpio |= (1 << R3DI_OUT_SELECT_BIT); + break; + } + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, cur_gpio); +} + +static void r3di_gpio_dsp_status_set(struct hda_codec *codec, + enum r3di_dsp_status dsp_status) +{ + unsigned int cur_gpio; + + /* Get the current GPIO Data setup */ + cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0); + + switch (dsp_status) { + case R3DI_DSP_DOWNLOADING: + cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADING); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, cur_gpio); + break; + case R3DI_DSP_DOWNLOADED: + /* Set DOWNLOADING bit to 0. */ + cur_gpio &= ~(1 << R3DI_GPIO_DSP_DOWNLOADING); + + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, cur_gpio); + + cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADED); + break; + } + + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, cur_gpio); +} + +/* * PCM callbacks */ static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -2852,6 +3392,24 @@ static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info, .tlv = { .c = ca0132_volume_tlv }, \ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } +/* + * Creates a mixer control that uses defaults of HDA_CODEC_VOL except for the + * volume put, which is used for setting the DSP volume. This was done because + * the ca0132 functions were taking too much time and causing lag. + */ +#define CA0132_ALT_CODEC_VOL_MONO(xname, nid, channel, dir) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .subdevice = HDA_SUBDEV_AMP_FLAG, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ + .info = snd_hda_mixer_amp_volume_info, \ + .get = snd_hda_mixer_amp_volume_get, \ + .put = ca0132_alt_volume_put, \ + .tlv = { .c = snd_hda_mixer_amp_tlv }, \ + .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) } + #define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2864,9 +3422,88 @@ static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info, /* stereo */ #define CA0132_CODEC_VOL(xname, nid, dir) \ CA0132_CODEC_VOL_MONO(xname, nid, 3, dir) +#define CA0132_ALT_CODEC_VOL(xname, nid, dir) \ + CA0132_ALT_CODEC_VOL_MONO(xname, nid, 3, dir) #define CA0132_CODEC_MUTE(xname, nid, dir) \ CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir) +/* lookup tables */ +/* + * Lookup table with decibel values for the DSP. When volume is changed in + * Windows, the DSP is also sent the dB value in floating point. In Windows, + * these values have decimal points, probably because the Windows driver + * actually uses floating point. We can't here, so I made a lookup table of + * values -90 to 9. -90 is the lowest decibel value for both the ADC's and the + * DAC's, and 9 is the maximum. + */ +static const unsigned int float_vol_db_lookup[] = { +0xC2B40000, 0xC2B20000, 0xC2B00000, 0xC2AE0000, 0xC2AC0000, 0xC2AA0000, +0xC2A80000, 0xC2A60000, 0xC2A40000, 0xC2A20000, 0xC2A00000, 0xC29E0000, +0xC29C0000, 0xC29A0000, 0xC2980000, 0xC2960000, 0xC2940000, 0xC2920000, +0xC2900000, 0xC28E0000, 0xC28C0000, 0xC28A0000, 0xC2880000, 0xC2860000, +0xC2840000, 0xC2820000, 0xC2800000, 0xC27C0000, 0xC2780000, 0xC2740000, +0xC2700000, 0xC26C0000, 0xC2680000, 0xC2640000, 0xC2600000, 0xC25C0000, +0xC2580000, 0xC2540000, 0xC2500000, 0xC24C0000, 0xC2480000, 0xC2440000, +0xC2400000, 0xC23C0000, 0xC2380000, 0xC2340000, 0xC2300000, 0xC22C0000, +0xC2280000, 0xC2240000, 0xC2200000, 0xC21C0000, 0xC2180000, 0xC2140000, +0xC2100000, 0xC20C0000, 0xC2080000, 0xC2040000, 0xC2000000, 0xC1F80000, +0xC1F00000, 0xC1E80000, 0xC1E00000, 0xC1D80000, 0xC1D00000, 0xC1C80000, +0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000, +0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000, +0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000, +0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000, +0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000, +0x40C00000, 0x40E00000, 0x41000000, 0x41100000 +}; + +/* + * This table counts from float 0 to 1 in increments of .01, which is + * useful for a few different sliders. + */ +static const unsigned int float_zero_to_one_lookup[] = { +0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD, +0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE, +0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B, +0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F, +0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1, +0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333, +0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85, +0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7, +0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14, +0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D, +0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666, +0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F, +0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8, +0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1, +0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A, +0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333, +0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000 +}; + +/* + * This table counts from float 10 to 1000, which is the range of the x-bass + * crossover slider in Windows. + */ +static const unsigned int float_xbass_xover_lookup[] = { +0x41200000, 0x41A00000, 0x41F00000, 0x42200000, 0x42480000, 0x42700000, +0x428C0000, 0x42A00000, 0x42B40000, 0x42C80000, 0x42DC0000, 0x42F00000, +0x43020000, 0x430C0000, 0x43160000, 0x43200000, 0x432A0000, 0x43340000, +0x433E0000, 0x43480000, 0x43520000, 0x435C0000, 0x43660000, 0x43700000, +0x437A0000, 0x43820000, 0x43870000, 0x438C0000, 0x43910000, 0x43960000, +0x439B0000, 0x43A00000, 0x43A50000, 0x43AA0000, 0x43AF0000, 0x43B40000, +0x43B90000, 0x43BE0000, 0x43C30000, 0x43C80000, 0x43CD0000, 0x43D20000, +0x43D70000, 0x43DC0000, 0x43E10000, 0x43E60000, 0x43EB0000, 0x43F00000, +0x43F50000, 0x43FA0000, 0x43FF0000, 0x44020000, 0x44048000, 0x44070000, +0x44098000, 0x440C0000, 0x440E8000, 0x44110000, 0x44138000, 0x44160000, +0x44188000, 0x441B0000, 0x441D8000, 0x44200000, 0x44228000, 0x44250000, +0x44278000, 0x442A0000, 0x442C8000, 0x442F0000, 0x44318000, 0x44340000, +0x44368000, 0x44390000, 0x443B8000, 0x443E0000, 0x44408000, 0x44430000, +0x44458000, 0x44480000, 0x444A8000, 0x444D0000, 0x444F8000, 0x44520000, +0x44548000, 0x44570000, 0x44598000, 0x445C0000, 0x445E8000, 0x44610000, +0x44638000, 0x44660000, 0x44688000, 0x446B0000, 0x446D8000, 0x44700000, +0x44728000, 0x44750000, 0x44778000, 0x447A0000 +}; + /* The following are for tuning of products */ #ifdef ENABLE_TUNING_CONTROLS @@ -2942,7 +3579,7 @@ static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid, break; snd_hda_power_up(codec); - dspio_set_param(codec, ca0132_tuning_ctls[i].mid, + dspio_set_param(codec, ca0132_tuning_ctls[i].mid, 0x20, ca0132_tuning_ctls[i].req, &(lookup[idx]), sizeof(unsigned int)); snd_hda_power_down(codec); @@ -3068,8 +3705,8 @@ static int equalizer_ctl_put(struct snd_kcontrol *kcontrol, return 1; } -static const DECLARE_TLV_DB_SCALE(voice_focus_db_scale, 2000, 100, 0); -static const DECLARE_TLV_DB_SCALE(eq_db_scale, -2400, 100, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(voice_focus_db_scale, 2000, 100, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(eq_db_scale, -2400, 100, 0); static int add_tuning_control(struct hda_codec *codec, hda_nid_t pnid, hda_nid_t nid, @@ -3207,7 +3844,7 @@ static int ca0132_select_out(struct hda_codec *codec) pin_ctl & ~PIN_HP); /* enable speaker node */ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); snd_hda_set_pin_ctl(codec, spec->out_pins[0], pin_ctl | PIN_OUT); } else { @@ -3251,13 +3888,209 @@ exit: return err < 0 ? err : 0; } +/* + * This function behaves similarly to the ca0132_select_out funciton above, + * except with a few differences. It adds the ability to select the current + * output with an enumerated control "output source" if the auto detect + * mute switch is set to off. If the auto detect mute switch is enabled, it + * will detect either headphone or lineout(SPEAKER_OUT) from jack detection. + * It also adds the ability to auto-detect the front headphone port. The only + * way to select surround is to disable auto detect, and set Surround with the + * enumerated control. + */ +static int ca0132_alt_select_out(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int pin_ctl; + int jack_present; + int auto_jack; + unsigned int i; + unsigned int tmp; + int err; + /* Default Headphone is rear headphone */ + hda_nid_t headphone_nid = spec->out_pins[1]; + + codec_dbg(codec, "%s\n", __func__); + + snd_hda_power_up_pm(codec); + + auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; + + /* + * If headphone rear or front is plugged in, set to headphone. + * If neither is plugged in, set to rear line out. Only if + * hp/speaker auto detect is enabled. + */ + if (auto_jack) { + jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp) || + snd_hda_jack_detect(codec, spec->unsol_tag_front_hp); + + if (jack_present) + spec->cur_out_type = HEADPHONE_OUT; + else + spec->cur_out_type = SPEAKER_OUT; + } else + spec->cur_out_type = spec->out_enum_val; + + /* Begin DSP output switch */ + tmp = FLOAT_ONE; + err = dspio_set_uint_param(codec, 0x96, 0x3A, tmp); + if (err < 0) + goto exit; + + switch (spec->cur_out_type) { + case SPEAKER_OUT: + codec_dbg(codec, "%s speaker\n", __func__); + /*speaker out config*/ + switch (spec->quirk) { + case QUIRK_SBZ: + writew(0x0007, spec->mem_base + 0x320); + writew(0x0104, spec->mem_base + 0x320); + writew(0x0101, spec->mem_base + 0x320); + chipio_set_control_param(codec, 0x0D, 0x18); + break; + case QUIRK_R3DI: + chipio_set_control_param(codec, 0x0D, 0x24); + r3di_gpio_out_set(codec, R3DI_LINE_OUT); + break; + } + + /* disable headphone node */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, spec->out_pins[1], + pin_ctl & ~PIN_HP); + /* enable line-out node */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, spec->out_pins[0], + pin_ctl | PIN_OUT); + /* Enable EAPD */ + snd_hda_codec_write(codec, spec->out_pins[0], 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x01); + + /* If PlayEnhancement is enabled, set different source */ + if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) + dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE); + else + dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT); + break; + case HEADPHONE_OUT: + codec_dbg(codec, "%s hp\n", __func__); + /* Headphone out config*/ + switch (spec->quirk) { + case QUIRK_SBZ: + writew(0x0107, spec->mem_base + 0x320); + writew(0x0104, spec->mem_base + 0x320); + writew(0x0001, spec->mem_base + 0x320); + chipio_set_control_param(codec, 0x0D, 0x12); + break; + case QUIRK_R3DI: + chipio_set_control_param(codec, 0x0D, 0x21); + r3di_gpio_out_set(codec, R3DI_HEADPHONE_OUT); + break; + } + + snd_hda_codec_write(codec, spec->out_pins[0], 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x00); + + /* disable speaker*/ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, spec->out_pins[0], + pin_ctl & ~PIN_HP); + + /* enable headphone, either front or rear */ + + if (snd_hda_jack_detect(codec, spec->unsol_tag_front_hp)) + headphone_nid = spec->out_pins[2]; + else if (snd_hda_jack_detect(codec, spec->unsol_tag_hp)) + headphone_nid = spec->out_pins[1]; + + pin_ctl = snd_hda_codec_read(codec, headphone_nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, headphone_nid, + pin_ctl | PIN_HP); + + if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) + dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE); + else + dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO); + break; + case SURROUND_OUT: + codec_dbg(codec, "%s surround\n", __func__); + /* Surround out config*/ + switch (spec->quirk) { + case QUIRK_SBZ: + writew(0x0007, spec->mem_base + 0x320); + writew(0x0104, spec->mem_base + 0x320); + writew(0x0101, spec->mem_base + 0x320); + chipio_set_control_param(codec, 0x0D, 0x18); + break; + case QUIRK_R3DI: + chipio_set_control_param(codec, 0x0D, 0x24); + r3di_gpio_out_set(codec, R3DI_LINE_OUT); + break; + } + /* enable line out node */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, spec->out_pins[0], + pin_ctl | PIN_OUT); + /* Disable headphone out */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, spec->out_pins[1], + pin_ctl & ~PIN_HP); + /* Enable EAPD on line out */ + snd_hda_codec_write(codec, spec->out_pins[0], 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x01); + /* enable center/lfe out node */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[2], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, spec->out_pins[2], + pin_ctl | PIN_OUT); + /* Now set rear surround node as out. */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[3], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, spec->out_pins[3], + pin_ctl | PIN_OUT); + + if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) + dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE); + else + dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT); + break; + } + + /* run through the output dsp commands for line-out */ + for (i = 0; i < alt_out_presets[spec->cur_out_type].commands; i++) { + err = dspio_set_uint_param(codec, + alt_out_presets[spec->cur_out_type].mids[i], + alt_out_presets[spec->cur_out_type].reqs[i], + alt_out_presets[spec->cur_out_type].vals[i]); + + if (err < 0) + goto exit; + } + +exit: + snd_hda_power_down_pm(codec); + + return err < 0 ? err : 0; +} + static void ca0132_unsol_hp_delayed(struct work_struct *work) { struct ca0132_spec *spec = container_of( to_delayed_work(work), struct ca0132_spec, unsol_hp_work); struct hda_jack_tbl *jack; - ca0132_select_out(spec->codec); + if (spec->use_alt_functions) + ca0132_alt_select_out(spec->codec); + else + ca0132_select_out(spec->codec); + jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp); if (jack) { jack->block_report = 0; @@ -3268,6 +4101,10 @@ static void ca0132_unsol_hp_delayed(struct work_struct *work) static void ca0132_set_dmic(struct hda_codec *codec, int enable); static int ca0132_mic_boost_set(struct hda_codec *codec, long val); static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val); +static void resume_mic1(struct hda_codec *codec, unsigned int oldval); +static int stop_mic1(struct hda_codec *codec); +static int ca0132_cvoice_switch_set(struct hda_codec *codec); +static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val); /* * Select the active VIP source @@ -3310,6 +4147,71 @@ static int ca0132_set_vipsource(struct hda_codec *codec, int val) return 1; } +static int ca0132_alt_set_vipsource(struct hda_codec *codec, int val) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + + if (spec->dsp_state != DSP_DOWNLOADED) + return 0; + + codec_dbg(codec, "%s\n", __func__); + + chipio_set_stream_control(codec, 0x03, 0); + chipio_set_stream_control(codec, 0x04, 0); + + /* if CrystalVoice is off, vipsource should be 0 */ + if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] || + (val == 0) || spec->in_enum_val == REAR_LINE_IN) { + codec_dbg(codec, "%s: off.", __func__); + chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0); + + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x80, 0x05, tmp); + + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); + if (spec->quirk == QUIRK_R3DI) + chipio_set_conn_rate(codec, 0x0F, SR_96_000); + + + if (spec->in_enum_val == REAR_LINE_IN) + tmp = FLOAT_ZERO; + else { + if (spec->quirk == QUIRK_SBZ) + tmp = FLOAT_THREE; + else + tmp = FLOAT_ONE; + } + + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + } else { + codec_dbg(codec, "%s: on.", __func__); + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000); + if (spec->quirk == QUIRK_R3DI) + chipio_set_conn_rate(codec, 0x0F, SR_16_000); + + if (spec->effects_switch[VOICE_FOCUS - EFFECT_START_NID]) + tmp = FLOAT_TWO; + else + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x05, tmp); + + msleep(20); + chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val); + } + + chipio_set_stream_control(codec, 0x03, 1); + chipio_set_stream_control(codec, 0x04, 1); + + return 1; +} + /* * Select the active microphone. * If autodetect is enabled, mic will be selected based on jack detection. @@ -3363,6 +4265,125 @@ static int ca0132_select_mic(struct hda_codec *codec) } /* + * Select the active input. + * Mic detection isn't used, because it's kind of pointless on the SBZ. + * The front mic has no jack-detection, so the only way to switch to it + * is to do it manually in alsamixer. + */ +static int ca0132_alt_select_in(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + + codec_dbg(codec, "%s\n", __func__); + + snd_hda_power_up_pm(codec); + + chipio_set_stream_control(codec, 0x03, 0); + chipio_set_stream_control(codec, 0x04, 0); + + spec->cur_mic_type = spec->in_enum_val; + + switch (spec->cur_mic_type) { + case REAR_MIC: + switch (spec->quirk) { + case QUIRK_SBZ: + writew(0x0000, spec->mem_base + 0x320); + tmp = FLOAT_THREE; + break; + case QUIRK_R3DI: + r3di_gpio_mic_set(codec, R3DI_REAR_MIC); + tmp = FLOAT_ONE; + break; + default: + tmp = FLOAT_ONE; + break; + } + + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); + if (spec->quirk == QUIRK_R3DI) + chipio_set_conn_rate(codec, 0x0F, SR_96_000); + + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + chipio_set_stream_control(codec, 0x03, 1); + chipio_set_stream_control(codec, 0x04, 1); + + if (spec->quirk == QUIRK_SBZ) { + chipio_write(codec, 0x18B098, 0x0000000C); + chipio_write(codec, 0x18B09C, 0x0000000C); + } + ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); + break; + case REAR_LINE_IN: + ca0132_mic_boost_set(codec, 0); + switch (spec->quirk) { + case QUIRK_SBZ: + writew(0x0000, spec->mem_base + 0x320); + break; + case QUIRK_R3DI: + r3di_gpio_mic_set(codec, R3DI_REAR_MIC); + break; + } + + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); + if (spec->quirk == QUIRK_R3DI) + chipio_set_conn_rate(codec, 0x0F, SR_96_000); + + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + if (spec->quirk == QUIRK_SBZ) { + chipio_write(codec, 0x18B098, 0x00000000); + chipio_write(codec, 0x18B09C, 0x00000000); + } + + chipio_set_stream_control(codec, 0x03, 1); + chipio_set_stream_control(codec, 0x04, 1); + break; + case FRONT_MIC: + switch (spec->quirk) { + case QUIRK_SBZ: + writew(0x0100, spec->mem_base + 0x320); + writew(0x0005, spec->mem_base + 0x320); + tmp = FLOAT_THREE; + break; + case QUIRK_R3DI: + r3di_gpio_mic_set(codec, R3DI_FRONT_MIC); + tmp = FLOAT_ONE; + break; + default: + tmp = FLOAT_ONE; + break; + } + + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); + if (spec->quirk == QUIRK_R3DI) + chipio_set_conn_rate(codec, 0x0F, SR_96_000); + + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + chipio_set_stream_control(codec, 0x03, 1); + chipio_set_stream_control(codec, 0x04, 1); + + if (spec->quirk == QUIRK_SBZ) { + chipio_write(codec, 0x18B098, 0x0000000C); + chipio_write(codec, 0x18B09C, 0x000000CC); + } + ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); + break; + } + ca0132_cvoice_switch_set(codec); + + snd_hda_power_down_pm(codec); + return 0; + +} + +/* * Check if VNODE settings take effect immediately. */ static bool ca0132_is_vnode_effective(struct hda_codec *codec, @@ -3418,7 +4439,7 @@ static int ca0132_voicefx_set(struct hda_codec *codec, int enable) static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) { struct ca0132_spec *spec = codec->spec; - unsigned int on; + unsigned int on, tmp; int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; int err = 0; int idx = nid - EFFECT_START_NID; @@ -3442,6 +4463,46 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) /* Voice Focus applies to 2-ch Mic, Digital Mic */ if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC)) val = 0; + + /* If Voice Focus on SBZ, set to two channel. */ + if ((nid == VOICE_FOCUS) && (spec->quirk == QUIRK_SBZ) + && (spec->cur_mic_type != REAR_LINE_IN)) { + if (spec->effects_switch[CRYSTAL_VOICE - + EFFECT_START_NID]) { + + if (spec->effects_switch[VOICE_FOCUS - + EFFECT_START_NID]) { + tmp = FLOAT_TWO; + val = 1; + } else + tmp = FLOAT_ONE; + + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + } + } + /* + * For SBZ noise reduction, there's an extra command + * to module ID 0x47. No clue why. + */ + if ((nid == NOISE_REDUCTION) && (spec->quirk == QUIRK_SBZ) + && (spec->cur_mic_type != REAR_LINE_IN)) { + if (spec->effects_switch[CRYSTAL_VOICE - + EFFECT_START_NID]) { + if (spec->effects_switch[NOISE_REDUCTION - + EFFECT_START_NID]) + tmp = FLOAT_ONE; + else + tmp = FLOAT_ZERO; + } else + tmp = FLOAT_ZERO; + + dspio_set_uint_param(codec, 0x47, 0x00, tmp); + } + + /* If rear line in disable effects. */ + if (spec->use_alt_functions && + spec->in_enum_val == REAR_LINE_IN) + val = 0; } codec_dbg(codec, "ca0132_effect_set: nid=0x%x, val=%ld\n", @@ -3469,6 +4530,9 @@ static int ca0132_pe_switch_set(struct hda_codec *codec) codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n", spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]); + if (spec->use_alt_functions) + ca0132_alt_select_out(codec); + i = OUT_EFFECT_START_NID - EFFECT_START_NID; nid = OUT_EFFECT_START_NID; /* PE affects all out effects */ @@ -3526,7 +4590,10 @@ static int ca0132_cvoice_switch_set(struct hda_codec *codec) /* set correct vipsource */ oldval = stop_mic1(codec); - ret |= ca0132_set_vipsource(codec, 1); + if (spec->use_alt_functions) + ret |= ca0132_alt_set_vipsource(codec, 1); + else + ret |= ca0132_set_vipsource(codec, 1); resume_mic1(codec, oldval); return ret; } @@ -3546,6 +4613,16 @@ static int ca0132_mic_boost_set(struct hda_codec *codec, long val) return ret; } +static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val) +{ + struct ca0132_spec *spec = codec->spec; + int ret = 0; + + ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0, + HDA_INPUT, 0, HDA_AMP_VOLMASK, val); + return ret; +} + static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -3560,8 +4637,12 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, if (nid == VNID_HP_SEL) { auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; - if (!auto_jack) - ca0132_select_out(codec); + if (!auto_jack) { + if (spec->use_alt_functions) + ca0132_alt_select_out(codec); + else + ca0132_select_out(codec); + } return 1; } @@ -3574,7 +4655,10 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, } if (nid == VNID_HP_ASEL) { - ca0132_select_out(codec); + if (spec->use_alt_functions) + ca0132_alt_select_out(codec); + else + ca0132_select_out(codec); return 1; } @@ -3602,6 +4686,432 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, return ret; } /* End of control change helpers. */ +/* + * Below I've added controls to mess with the effect levels, I've only enabled + * them on the Sound Blaster Z, but they would probably also work on the + * Chromebook. I figured they were probably tuned specifically for it, and left + * out for a reason. + */ + +/* Sets DSP effect level from the sliders above the controls */ +static int ca0132_alt_slider_ctl_set(struct hda_codec *codec, hda_nid_t nid, + const unsigned int *lookup, int idx) +{ + int i = 0; + unsigned int y; + /* + * For X_BASS, req 2 is actually crossover freq instead of + * effect level + */ + if (nid == X_BASS) + y = 2; + else + y = 1; + + snd_hda_power_up(codec); + if (nid == XBASS_XOVER) { + for (i = 0; i < OUT_EFFECTS_COUNT; i++) + if (ca0132_effects[i].nid == X_BASS) + break; + + dspio_set_param(codec, ca0132_effects[i].mid, 0x20, + ca0132_effects[i].reqs[1], + &(lookup[idx - 1]), sizeof(unsigned int)); + } else { + /* Find the actual effect structure */ + for (i = 0; i < OUT_EFFECTS_COUNT; i++) + if (nid == ca0132_effects[i].nid) + break; + + dspio_set_param(codec, ca0132_effects[i].mid, 0x20, + ca0132_effects[i].reqs[y], + &(lookup[idx]), sizeof(unsigned int)); + } + + snd_hda_power_down(codec); + + return 0; +} + +static int ca0132_alt_xbass_xover_slider_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + long *valp = ucontrol->value.integer.value; + + *valp = spec->xbass_xover_freq; + return 0; +} + +static int ca0132_alt_slider_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + long *valp = ucontrol->value.integer.value; + int idx = nid - OUT_EFFECT_START_NID; + + *valp = spec->fx_ctl_val[idx]; + return 0; +} + +/* + * The X-bass crossover starts at 10hz, so the min is 1. The + * frequency is set in multiples of 10. + */ +static int ca0132_alt_xbass_xover_slider_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 = 1; + uinfo->value.integer.max = 100; + uinfo->value.integer.step = 1; + + return 0; +} + +static int ca0132_alt_effect_slider_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int chs = get_amp_channels(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chs == 3 ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + uinfo->value.integer.step = 1; + + return 0; +} + +static int ca0132_alt_xbass_xover_slider_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + long *valp = ucontrol->value.integer.value; + int idx; + + /* any change? */ + if (spec->xbass_xover_freq == *valp) + return 0; + + spec->xbass_xover_freq = *valp; + + idx = *valp; + ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx); + + return 0; +} + +static int ca0132_alt_effect_slider_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + long *valp = ucontrol->value.integer.value; + int idx; + + idx = nid - EFFECT_START_NID; + /* any change? */ + if (spec->fx_ctl_val[idx] == *valp) + return 0; + + spec->fx_ctl_val[idx] = *valp; + + idx = *valp; + ca0132_alt_slider_ctl_set(codec, nid, float_zero_to_one_lookup, idx); + + return 0; +} + + +/* + * Mic Boost Enum for alternative ca0132 codecs. I didn't like that the original + * only has off or full 30 dB, and didn't like making a volume slider that has + * traditional 0-100 in alsamixer that goes in big steps. I like enum better. + */ +#define MIC_BOOST_NUM_OF_STEPS 4 +#define MIC_BOOST_ENUM_MAX_STRLEN 10 + +static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + char *sfx = "dB"; + char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = MIC_BOOST_NUM_OF_STEPS; + if (uinfo->value.enumerated.item >= MIC_BOOST_NUM_OF_STEPS) + uinfo->value.enumerated.item = MIC_BOOST_NUM_OF_STEPS - 1; + sprintf(namestr, "%d %s", (uinfo->value.enumerated.item * 10), sfx); + strcpy(uinfo->value.enumerated.name, namestr); + return 0; +} + +static int ca0132_alt_mic_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->mic_boost_enum_val; + return 0; +} + +static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = MIC_BOOST_NUM_OF_STEPS; + + if (sel >= items) + return 0; + + codec_dbg(codec, "ca0132_alt_mic_boost: boost=%d\n", + sel); + + spec->mic_boost_enum_val = sel; + + if (spec->in_enum_val != REAR_LINE_IN) + ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); + + return 1; +} + + +/* + * Input Select Control for alternative ca0132 codecs. This exists because + * front microphone has no auto-detect, and we need a way to set the rear + * as line-in + */ +static int ca0132_alt_input_source_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 = IN_SRC_NUM_OF_INPUTS; + if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS) + uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1; + strcpy(uinfo->value.enumerated.name, + in_src_str[uinfo->value.enumerated.item]); + return 0; +} + +static int ca0132_alt_input_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->in_enum_val; + return 0; +} + +static int ca0132_alt_input_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = IN_SRC_NUM_OF_INPUTS; + + if (sel >= items) + return 0; + + codec_dbg(codec, "ca0132_alt_input_select: sel=%d, preset=%s\n", + sel, in_src_str[sel]); + + spec->in_enum_val = sel; + + ca0132_alt_select_in(codec); + + return 1; +} + +/* Sound Blaster Z Output Select Control */ +static int ca0132_alt_output_select_get_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 = NUM_OF_OUTPUTS; + if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS) + uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1; + strcpy(uinfo->value.enumerated.name, + alt_out_presets[uinfo->value.enumerated.item].name); + return 0; +} + +static int ca0132_alt_output_select_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->out_enum_val; + return 0; +} + +static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = NUM_OF_OUTPUTS; + unsigned int auto_jack; + + if (sel >= items) + return 0; + + codec_dbg(codec, "ca0132_alt_output_select: sel=%d, preset=%s\n", + sel, alt_out_presets[sel].name); + + spec->out_enum_val = sel; + + auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; + + if (!auto_jack) + ca0132_alt_select_out(codec); + + return 1; +} + +/* + * Smart Volume output setting control. Three different settings, Normal, + * which takes the value from the smart volume slider. The two others, loud + * and night, disregard the slider value and have uneditable values. + */ +#define NUM_OF_SVM_SETTINGS 3 +static const char *const out_svm_set_enum_str[3] = {"Normal", "Loud", "Night" }; + +static int ca0132_alt_svm_setting_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 = NUM_OF_SVM_SETTINGS; + if (uinfo->value.enumerated.item >= NUM_OF_SVM_SETTINGS) + uinfo->value.enumerated.item = NUM_OF_SVM_SETTINGS - 1; + strcpy(uinfo->value.enumerated.name, + out_svm_set_enum_str[uinfo->value.enumerated.item]); + return 0; +} + +static int ca0132_alt_svm_setting_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->smart_volume_setting; + return 0; +} + +static int ca0132_alt_svm_setting_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = NUM_OF_SVM_SETTINGS; + unsigned int idx = SMART_VOLUME - EFFECT_START_NID; + unsigned int tmp; + + if (sel >= items) + return 0; + + codec_dbg(codec, "ca0132_alt_svm_setting: sel=%d, preset=%s\n", + sel, out_svm_set_enum_str[sel]); + + spec->smart_volume_setting = sel; + + switch (sel) { + case 0: + tmp = FLOAT_ZERO; + break; + case 1: + tmp = FLOAT_ONE; + break; + case 2: + tmp = FLOAT_TWO; + break; + default: + tmp = FLOAT_ZERO; + break; + } + /* Req 2 is the Smart Volume Setting req. */ + dspio_set_uint_param(codec, ca0132_effects[idx].mid, + ca0132_effects[idx].reqs[2], tmp); + return 1; +} + +/* Sound Blaster Z EQ preset controls */ +static int ca0132_alt_eq_preset_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int items = ARRAY_SIZE(ca0132_alt_eq_presets); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = items; + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + strcpy(uinfo->value.enumerated.name, + ca0132_alt_eq_presets[uinfo->value.enumerated.item].name); + return 0; +} + +static int ca0132_alt_eq_preset_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->eq_preset_val; + return 0; +} + +static int ca0132_alt_eq_preset_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + int i, err = 0; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = ARRAY_SIZE(ca0132_alt_eq_presets); + + if (sel >= items) + return 0; + + codec_dbg(codec, "%s: sel=%d, preset=%s\n", __func__, sel, + ca0132_alt_eq_presets[sel].name); + /* + * Idx 0 is default. + * Default needs to qualify with CrystalVoice state. + */ + for (i = 0; i < EQ_PRESET_MAX_PARAM_COUNT; i++) { + err = dspio_set_uint_param(codec, ca0132_alt_eq_enum.mid, + ca0132_alt_eq_enum.reqs[i], + ca0132_alt_eq_presets[sel].vals[i]); + if (err < 0) + break; + } + + if (err >= 0) + spec->eq_preset_val = sel; + + return 1; +} static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -3753,10 +5263,15 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol, /* mic boost */ if (nid == spec->input_pins[0]) { spec->cur_mic_boost = *valp; + if (spec->use_alt_functions) { + if (spec->in_enum_val != REAR_LINE_IN) + changed = ca0132_mic_boost_set(codec, *valp); + } else { + /* Mic boost does not apply to Digital Mic */ + if (spec->cur_mic_type != DIGITAL_MIC) + changed = ca0132_mic_boost_set(codec, *valp); + } - /* Mic boost does not apply to Digital Mic */ - if (spec->cur_mic_type != DIGITAL_MIC) - changed = ca0132_mic_boost_set(codec, *valp); goto exit; } @@ -3768,6 +5283,41 @@ exit: /* * Volume related */ +/* + * Sets the internal DSP decibel level to match the DAC for output, and the + * ADC for input. Currently only the SBZ sets dsp capture volume level, and + * all alternative codecs set DSP playback volume. + */ +static void ca0132_alt_dsp_volume_put(struct hda_codec *codec, hda_nid_t nid) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int dsp_dir; + unsigned int lookup_val; + + if (nid == VNID_SPK) + dsp_dir = DSP_VOL_OUT; + else + dsp_dir = DSP_VOL_IN; + + lookup_val = spec->vnode_lvol[nid - VNODE_START_NID]; + + dspio_set_uint_param(codec, + ca0132_alt_vol_ctls[dsp_dir].mid, + ca0132_alt_vol_ctls[dsp_dir].reqs[0], + float_vol_db_lookup[lookup_val]); + + lookup_val = spec->vnode_rvol[nid - VNODE_START_NID]; + + dspio_set_uint_param(codec, + ca0132_alt_vol_ctls[dsp_dir].mid, + ca0132_alt_vol_ctls[dsp_dir].reqs[1], + float_vol_db_lookup[lookup_val]); + + dspio_set_uint_param(codec, + ca0132_alt_vol_ctls[dsp_dir].mid, + ca0132_alt_vol_ctls[dsp_dir].reqs[2], FLOAT_ZERO); +} + static int ca0132_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -3869,6 +5419,51 @@ static int ca0132_volume_put(struct snd_kcontrol *kcontrol, return changed; } +/* + * This function is the same as the one above, because using an if statement + * inside of the above volume control for the DSP volume would cause too much + * lag. This is a lot more smooth. + */ +static int ca0132_alt_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + int ch = get_amp_channels(kcontrol); + long *valp = ucontrol->value.integer.value; + hda_nid_t vnid = 0; + int changed = 1; + + switch (nid) { + case 0x02: + vnid = VNID_SPK; + break; + case 0x07: + vnid = VNID_MIC; + break; + } + + /* store the left and right volume */ + if (ch & 1) { + spec->vnode_lvol[vnid - VNODE_START_NID] = *valp; + valp++; + } + if (ch & 2) { + spec->vnode_rvol[vnid - VNODE_START_NID] = *valp; + valp++; + } + + snd_hda_power_up(codec); + ca0132_alt_dsp_volume_put(codec, vnid); + mutex_lock(&codec->control_mutex); + changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); + mutex_unlock(&codec->control_mutex); + snd_hda_power_down(codec); + + return changed; +} + static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { @@ -3907,14 +5502,59 @@ static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, return err; } +/* Add volume slider control for effect level */ +static int ca0132_alt_add_effect_slider(struct hda_codec *codec, hda_nid_t nid, + const char *pfx, int dir) +{ + char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int type = dir ? HDA_INPUT : HDA_OUTPUT; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type); + + sprintf(namestr, "FX: %s %s Volume", pfx, dirstr[dir]); + + knew.tlv.c = 0; + knew.tlv.p = 0; + + switch (nid) { + case XBASS_XOVER: + knew.info = ca0132_alt_xbass_xover_slider_info; + knew.get = ca0132_alt_xbass_xover_slider_ctl_get; + knew.put = ca0132_alt_xbass_xover_slider_put; + break; + default: + knew.info = ca0132_alt_effect_slider_info; + knew.get = ca0132_alt_slider_ctl_get; + knew.put = ca0132_alt_effect_slider_put; + knew.private_value = + HDA_COMPOSE_AMP_VAL(nid, 1, 0, type); + break; + } + + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); +} + +/* + * Added FX: prefix for the alternative codecs, because otherwise the surround + * effect would conflict with the Surround sound volume control. Also seems more + * clear as to what the switches do. Left alone for others. + */ static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, int dir) { + struct ca0132_spec *spec = codec->spec; char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int type = dir ? HDA_INPUT : HDA_OUTPUT; struct snd_kcontrol_new knew = CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type); - sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); + /* If using alt_controls, add FX: prefix. But, don't add FX: + * prefix to OutFX or InFX enable controls. + */ + if ((spec->use_alt_controls) && (nid <= IN_EFFECT_END_NID)) + sprintf(namestr, "FX: %s %s Switch", pfx, dirstr[dir]); + else + sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); + return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } @@ -3929,11 +5569,141 @@ static int add_voicefx(struct hda_codec *codec) return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec)); } +/* Create the EQ Preset control */ +static int add_ca0132_alt_eq_presets(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO(ca0132_alt_eq_enum.name, + EQ_PRESET_ENUM, 1, 0, HDA_OUTPUT); + knew.info = ca0132_alt_eq_preset_info; + knew.get = ca0132_alt_eq_preset_get; + knew.put = ca0132_alt_eq_preset_put; + return snd_hda_ctl_add(codec, EQ_PRESET_ENUM, + snd_ctl_new1(&knew, codec)); +} + +/* + * Add enumerated control for the three different settings of the smart volume + * output effect. Normal just uses the slider value, and loud and night are + * their own things that ignore that value. + */ +static int ca0132_alt_add_svm_enum(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("FX: Smart Volume Setting", + SMART_VOLUME_ENUM, 1, 0, HDA_OUTPUT); + knew.info = ca0132_alt_svm_setting_info; + knew.get = ca0132_alt_svm_setting_get; + knew.put = ca0132_alt_svm_setting_put; + return snd_hda_ctl_add(codec, SMART_VOLUME_ENUM, + snd_ctl_new1(&knew, codec)); + +} + +/* + * Create an Output Select enumerated control for codecs with surround + * out capabilities. + */ +static int ca0132_alt_add_output_enum(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("Output Select", + OUTPUT_SOURCE_ENUM, 1, 0, HDA_OUTPUT); + knew.info = ca0132_alt_output_select_get_info; + knew.get = ca0132_alt_output_select_get; + knew.put = ca0132_alt_output_select_put; + return snd_hda_ctl_add(codec, OUTPUT_SOURCE_ENUM, + snd_ctl_new1(&knew, codec)); +} + +/* + * Create an Input Source enumerated control for the alternate ca0132 codecs + * because the front microphone has no auto-detect, and Line-in has to be set + * somehow. + */ +static int ca0132_alt_add_input_enum(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("Input Source", + INPUT_SOURCE_ENUM, 1, 0, HDA_INPUT); + knew.info = ca0132_alt_input_source_info; + knew.get = ca0132_alt_input_source_get; + knew.put = ca0132_alt_input_source_put; + return snd_hda_ctl_add(codec, INPUT_SOURCE_ENUM, + snd_ctl_new1(&knew, codec)); +} + +/* + * Add mic boost enumerated control. Switches through 0dB to 30dB. This adds + * more control than the original mic boost, which is either full 30dB or off. + */ +static int ca0132_alt_add_mic_boost_enum(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("Mic Boost Capture Switch", + MIC_BOOST_ENUM, 1, 0, HDA_INPUT); + knew.info = ca0132_alt_mic_boost_info; + knew.get = ca0132_alt_mic_boost_get; + knew.put = ca0132_alt_mic_boost_put; + return snd_hda_ctl_add(codec, MIC_BOOST_ENUM, + snd_ctl_new1(&knew, codec)); + +} + +/* + * Need to create slave controls for the alternate codecs that have surround + * capabilities. + */ +static const char * const ca0132_alt_slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", NULL, +}; + +/* + * Also need special channel map, because the default one is incorrect. + * I think this has to do with the pin for rear surround being 0x11, + * and the center/lfe being 0x10. Usually the pin order is the opposite. + */ +const struct snd_pcm_chmap_elem ca0132_alt_chmaps[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, + SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { } +}; + +/* Add the correct chmap for streams with 6 channels. */ +static void ca0132_alt_add_chmap_ctls(struct hda_codec *codec) +{ + int err = 0; + struct hda_pcm *pcm; + + list_for_each_entry(pcm, &codec->pcm_list_head, list) { + struct hda_pcm_stream *hinfo = + &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK]; + struct snd_pcm_chmap *chmap; + const struct snd_pcm_chmap_elem *elem; + + elem = ca0132_alt_chmaps; + if (hinfo->channels_max == 6) { + err = snd_pcm_add_chmap_ctls(pcm->pcm, + SNDRV_PCM_STREAM_PLAYBACK, + elem, hinfo->channels_max, 0, &chmap); + if (err < 0) + codec_dbg(codec, "snd_pcm_add_chmap_ctls failed!"); + } + } +} + /* * When changing Node IDs for Mixer Controls below, make sure to update * Node IDs in ca0132_config() as well. */ -static struct snd_kcontrol_new ca0132_mixer[] = { +static const struct snd_kcontrol_new ca0132_mixer[] = { CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT), CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT), CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT), @@ -3955,10 +5725,55 @@ static struct snd_kcontrol_new ca0132_mixer[] = { { } /* end */ }; +/* + * SBZ specific control mixer. Removes auto-detect for mic, and adds surround + * controls. Also sets both the Front Playback and Capture Volume controls to + * alt so they set the DSP's decibel level. + */ +static const struct snd_kcontrol_new sbz_mixer[] = { + CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT), + CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT), + CA0132_ALT_CODEC_VOL("Capture Volume", 0x07, HDA_INPUT), + CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT), + HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT), + HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT), + CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch", + VNID_HP_ASEL, 1, HDA_OUTPUT), + { } /* end */ +}; + +/* + * Same as the Sound Blaster Z, except doesn't use the alt volume for capture + * because it doesn't set decibel levels for the DSP for capture. + */ +static const struct snd_kcontrol_new r3di_mixer[] = { + CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT), + CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT), + CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT), + CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT), + HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT), + HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT), + CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch", + VNID_HP_ASEL, 1, HDA_OUTPUT), + { } /* end */ +}; + static int ca0132_build_controls(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - int i, num_fx; + int i, num_fx, num_sliders; int err = 0; /* Add Mixer controls */ @@ -3967,29 +5782,94 @@ static int ca0132_build_controls(struct hda_codec *codec) if (err < 0) return err; } + /* Setup vmaster with surround slaves for desktop ca0132 devices */ + if (spec->use_alt_functions) { + snd_hda_set_vmaster_tlv(codec, spec->dacs[0], HDA_OUTPUT, + spec->tlv); + snd_hda_add_vmaster(codec, "Master Playback Volume", + spec->tlv, ca0132_alt_slave_pfxs, + "Playback Volume"); + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, ca0132_alt_slave_pfxs, + "Playback Switch", + true, &spec->vmaster_mute.sw_kctl); + + } /* Add in and out effects controls. * VoiceFX, PE and CrystalVoice are added separately. */ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; for (i = 0; i < num_fx; i++) { + /* SBZ breaks if Echo Cancellation is used */ + if (spec->quirk == QUIRK_SBZ) { + if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID + + OUT_EFFECTS_COUNT)) + continue; + } + err = add_fx_switch(codec, ca0132_effects[i].nid, ca0132_effects[i].name, ca0132_effects[i].direct); if (err < 0) return err; } + /* + * If codec has use_alt_controls set to true, add effect level sliders, + * EQ presets, and Smart Volume presets. Also, change names to add FX + * prefix, and change PlayEnhancement and CrystalVoice to match. + */ + if (spec->use_alt_controls) { + ca0132_alt_add_svm_enum(codec); + add_ca0132_alt_eq_presets(codec); + err = add_fx_switch(codec, PLAY_ENHANCEMENT, + "Enable OutFX", 0); + if (err < 0) + return err; - err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0); - if (err < 0) - return err; + err = add_fx_switch(codec, CRYSTAL_VOICE, + "Enable InFX", 1); + if (err < 0) + return err; - err = add_fx_switch(codec, CRYSTAL_VOICE, "CrystalVoice", 1); - if (err < 0) - return err; + num_sliders = OUT_EFFECTS_COUNT - 1; + for (i = 0; i < num_sliders; i++) { + err = ca0132_alt_add_effect_slider(codec, + ca0132_effects[i].nid, + ca0132_effects[i].name, + ca0132_effects[i].direct); + if (err < 0) + return err; + } + + err = ca0132_alt_add_effect_slider(codec, XBASS_XOVER, + "X-Bass Crossover", EFX_DIR_OUT); + if (err < 0) + return err; + } else { + err = add_fx_switch(codec, PLAY_ENHANCEMENT, + "PlayEnhancement", 0); + if (err < 0) + return err; + + err = add_fx_switch(codec, CRYSTAL_VOICE, + "CrystalVoice", 1); + if (err < 0) + return err; + } add_voicefx(codec); + /* + * If the codec uses alt_functions, you need the enumerated controls + * to select the new outputs and inputs, plus add the new mic boost + * setting control. + */ + if (spec->use_alt_functions) { + ca0132_alt_add_output_enum(codec); + ca0132_alt_add_input_enum(codec); + ca0132_alt_add_mic_boost_enum(codec); + } #ifdef ENABLE_TUNING_CONTROLS add_tuning_ctls(codec); #endif @@ -4014,6 +5894,10 @@ static int ca0132_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + if (spec->use_alt_functions) + ca0132_alt_add_chmap_ctls(codec); + return 0; } @@ -4068,6 +5952,11 @@ static int ca0132_build_pcms(struct hda_codec *codec) info = snd_hda_codec_pcm_new(codec, "CA0132 Analog"); if (!info) return -ENOMEM; + if (spec->use_alt_functions) { + info->own_chmap = true; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap + = ca0132_alt_chmaps; + } info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = @@ -4076,12 +5965,16 @@ static int ca0132_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; - info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2"); - if (!info) - return -ENOMEM; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1]; + /* With the DSP enabled, desktops don't use this ADC. */ + if (spec->use_alt_functions) { + info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2"); + if (!info) + return -ENOMEM; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + ca0132_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1]; + } info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear"); if (!info) @@ -4288,6 +6181,196 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec) } /* + * Recon3Di r3di_setup_defaults sub functions. + */ + +static void r3di_dsp_scp_startup(struct hda_codec *codec) +{ + unsigned int tmp; + + tmp = 0x00000000; + dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp); + + tmp = 0x00000001; + dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp); + + tmp = 0x00000004; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + + tmp = 0x00000005; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + + tmp = 0x00000000; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + +} + +static void r3di_dsp_initial_mic_setup(struct hda_codec *codec) +{ + unsigned int tmp; + + /* Mic 1 Setup */ + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); + /* This ConnPointID is unique to Recon3Di. Haven't seen it elsewhere */ + chipio_set_conn_rate(codec, 0x0F, SR_96_000); + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + /* Mic 2 Setup, even though it isn't connected on SBZ */ + chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000); + chipio_set_conn_rate(codec, 0x0F, SR_96_000); + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x80, 0x01, tmp); +} + +/* + * Initialize Sound Blaster Z analog microphones. + */ +static void sbz_init_analog_mics(struct hda_codec *codec) +{ + unsigned int tmp; + + /* Mic 1 Setup */ + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); + tmp = FLOAT_THREE; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + /* Mic 2 Setup, even though it isn't connected on SBZ */ + chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000); + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x80, 0x01, tmp); + +} + +/* + * Sets the source of stream 0x14 to connpointID 0x48, and the destination + * connpointID to 0x91. If this isn't done, the destination is 0x71, and + * you get no sound. I'm guessing this has to do with the Sound Blaster Z + * having an updated DAC, which changes the destination to that DAC. + */ +static void sbz_connect_streams(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + + codec_dbg(codec, "Connect Streams entered, mutex locked and loaded.\n"); + + chipio_set_stream_channels(codec, 0x0C, 6); + chipio_set_stream_control(codec, 0x0C, 1); + + /* This value is 0x43 for 96khz, and 0x83 for 192khz. */ + chipio_write_no_mutex(codec, 0x18a020, 0x00000043); + + /* Setup stream 0x14 with it's source and destination points */ + chipio_set_stream_source_dest(codec, 0x14, 0x48, 0x91); + chipio_set_conn_rate_no_mutex(codec, 0x48, SR_96_000); + chipio_set_conn_rate_no_mutex(codec, 0x91, SR_96_000); + chipio_set_stream_channels(codec, 0x14, 2); + chipio_set_stream_control(codec, 0x14, 1); + + codec_dbg(codec, "Connect Streams exited, mutex released.\n"); + + mutex_unlock(&spec->chipio_mutex); + +} + +/* + * Write data through ChipIO to setup proper stream destinations. + * Not sure how it exactly works, but it seems to direct data + * to different destinations. Example is f8 to c0, e0 to c0. + * All I know is, if you don't set these, you get no sound. + */ +static void sbz_chipio_startup_data(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + codec_dbg(codec, "Startup Data entered, mutex locked and loaded.\n"); + + /* These control audio output */ + chipio_write_no_mutex(codec, 0x190060, 0x0001f8c0); + chipio_write_no_mutex(codec, 0x190064, 0x0001f9c1); + chipio_write_no_mutex(codec, 0x190068, 0x0001fac6); + chipio_write_no_mutex(codec, 0x19006c, 0x0001fbc7); + /* Signal to update I think */ + chipio_write_no_mutex(codec, 0x19042c, 0x00000001); + + chipio_set_stream_channels(codec, 0x0C, 6); + chipio_set_stream_control(codec, 0x0C, 1); + /* No clue what these control */ + chipio_write_no_mutex(codec, 0x190030, 0x0001e0c0); + chipio_write_no_mutex(codec, 0x190034, 0x0001e1c1); + chipio_write_no_mutex(codec, 0x190038, 0x0001e4c2); + chipio_write_no_mutex(codec, 0x19003c, 0x0001e5c3); + chipio_write_no_mutex(codec, 0x190040, 0x0001e2c4); + chipio_write_no_mutex(codec, 0x190044, 0x0001e3c5); + chipio_write_no_mutex(codec, 0x190048, 0x0001e8c6); + chipio_write_no_mutex(codec, 0x19004c, 0x0001e9c7); + chipio_write_no_mutex(codec, 0x190050, 0x0001ecc8); + chipio_write_no_mutex(codec, 0x190054, 0x0001edc9); + chipio_write_no_mutex(codec, 0x190058, 0x0001eaca); + chipio_write_no_mutex(codec, 0x19005c, 0x0001ebcb); + + chipio_write_no_mutex(codec, 0x19042c, 0x00000001); + + codec_dbg(codec, "Startup Data exited, mutex released.\n"); + mutex_unlock(&spec->chipio_mutex); +} + +/* + * Sound Blaster Z uses these after DSP is loaded. Weird SCP commands + * without a 0x20 source like normal. + */ +static void sbz_dsp_scp_startup(struct hda_codec *codec) +{ + unsigned int tmp; + + tmp = 0x00000003; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + + tmp = 0x00000000; + dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp); + + tmp = 0x00000001; + dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp); + + tmp = 0x00000004; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + + tmp = 0x00000005; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + + tmp = 0x00000000; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + +} + +static void sbz_dsp_initial_mic_setup(struct hda_codec *codec) +{ + unsigned int tmp; + + chipio_set_stream_control(codec, 0x03, 0); + chipio_set_stream_control(codec, 0x04, 0); + + chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); + + tmp = FLOAT_THREE; + dspio_set_uint_param(codec, 0x80, 0x00, tmp); + + chipio_set_stream_control(codec, 0x03, 1); + chipio_set_stream_control(codec, 0x04, 1); + + chipio_write(codec, 0x18b098, 0x0000000c); + chipio_write(codec, 0x18b09C, 0x0000000c); +} + +/* * Setup default parameters for DSP */ static void ca0132_setup_defaults(struct hda_codec *codec) @@ -4332,16 +6415,159 @@ static void ca0132_setup_defaults(struct hda_codec *codec) } /* + * Setup default parameters for Recon3Di DSP. + */ + +static void r3di_setup_defaults(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + int num_fx; + int idx, i; + + if (spec->dsp_state != DSP_DOWNLOADED) + return; + + r3di_dsp_scp_startup(codec); + + r3di_dsp_initial_mic_setup(codec); + + /*remove DSP headroom*/ + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x3C, tmp); + + /* set WUH source */ + tmp = FLOAT_TWO; + dspio_set_uint_param(codec, 0x31, 0x00, tmp); + chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); + + /* Set speaker source? */ + dspio_set_uint_param(codec, 0x32, 0x00, tmp); + + r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED); + + /* Setup effect defaults */ + num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; + for (idx = 0; idx < num_fx; idx++) { + for (i = 0; i <= ca0132_effects[idx].params; i++) { + dspio_set_uint_param(codec, + ca0132_effects[idx].mid, + ca0132_effects[idx].reqs[i], + ca0132_effects[idx].def_vals[i]); + } + } + +} + +/* + * Setup default parameters for the Sound Blaster Z DSP. A lot more going on + * than the Chromebook setup. + */ +static void sbz_setup_defaults(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp, stream_format; + int num_fx; + int idx, i; + + if (spec->dsp_state != DSP_DOWNLOADED) + return; + + sbz_dsp_scp_startup(codec); + + sbz_init_analog_mics(codec); + + sbz_connect_streams(codec); + + sbz_chipio_startup_data(codec); + + chipio_set_stream_control(codec, 0x03, 1); + chipio_set_stream_control(codec, 0x04, 1); + + /* + * Sets internal input loopback to off, used to have a switch to + * enable input loopback, but turned out to be way too buggy. + */ + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x37, 0x08, tmp); + dspio_set_uint_param(codec, 0x37, 0x10, tmp); + + /*remove DSP headroom*/ + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x3C, tmp); + + /* set WUH source */ + tmp = FLOAT_TWO; + dspio_set_uint_param(codec, 0x31, 0x00, tmp); + chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); + + /* Set speaker source? */ + dspio_set_uint_param(codec, 0x32, 0x00, tmp); + + sbz_dsp_initial_mic_setup(codec); + + + /* out, in effects + voicefx */ + num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; + for (idx = 0; idx < num_fx; idx++) { + for (i = 0; i <= ca0132_effects[idx].params; i++) { + dspio_set_uint_param(codec, + ca0132_effects[idx].mid, + ca0132_effects[idx].reqs[i], + ca0132_effects[idx].def_vals[i]); + } + } + + /* + * Have to make a stream to bind the sound output to, otherwise + * you'll get dead audio. Before I did this, it would bind to an + * audio input, and would never work + */ + stream_format = snd_hdac_calc_stream_format(48000, 2, + SNDRV_PCM_FORMAT_S32_LE, 32, 0); + + snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id, + 0, stream_format); + + snd_hda_codec_cleanup_stream(codec, spec->dacs[0]); + + snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id, + 0, stream_format); + + snd_hda_codec_cleanup_stream(codec, spec->dacs[0]); +} + +/* * Initialization of flags in chip */ static void ca0132_init_flags(struct hda_codec *codec) { - chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0); - chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_COMMON_MODE, 0); - chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_COMMON_MODE, 0); - chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0); - chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0); - chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1); + struct ca0132_spec *spec = codec->spec; + + if (spec->use_alt_functions) { + chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, 1); + chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, 1); + chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, 1); + chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, 1); + chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, 1); + chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0); + chipio_set_control_flag(codec, CONTROL_FLAG_SPDIF2OUT, 0); + chipio_set_control_flag(codec, + CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0); + chipio_set_control_flag(codec, + CONTROL_FLAG_PORT_A_10KOHM_LOAD, 1); + } else { + chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0); + chipio_set_control_flag(codec, + CONTROL_FLAG_PORT_A_COMMON_MODE, 0); + chipio_set_control_flag(codec, + CONTROL_FLAG_PORT_D_COMMON_MODE, 0); + chipio_set_control_flag(codec, + CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0); + chipio_set_control_flag(codec, + CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0); + chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1); + } } /* @@ -4349,6 +6575,16 @@ static void ca0132_init_flags(struct hda_codec *codec) */ static void ca0132_init_params(struct hda_codec *codec) { + struct ca0132_spec *spec = codec->spec; + + if (spec->use_alt_functions) { + chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); + chipio_set_conn_rate(codec, 0x0B, SR_48_000); + chipio_set_control_param(codec, CONTROL_PARAM_SPDIF1_SOURCE, 0); + chipio_set_control_param(codec, 0, 0); + chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0); + } + chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6); chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6); } @@ -4370,11 +6606,49 @@ static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k) static bool ca0132_download_dsp_images(struct hda_codec *codec) { bool dsp_loaded = false; + struct ca0132_spec *spec = codec->spec; const struct dsp_image_seg *dsp_os_image; const struct firmware *fw_entry; - - if (request_firmware(&fw_entry, EFX_FILE, codec->card->dev) != 0) - return false; + /* + * Alternate firmwares for different variants. The Recon3Di apparently + * can use the default firmware, but I'll leave the option in case + * it needs it again. + */ + switch (spec->quirk) { + case QUIRK_SBZ: + if (request_firmware(&fw_entry, SBZ_EFX_FILE, + codec->card->dev) != 0) { + codec_dbg(codec, "SBZ alt firmware not detected. "); + spec->alt_firmware_present = false; + } else { + codec_dbg(codec, "Sound Blaster Z firmware selected."); + spec->alt_firmware_present = true; + } + break; + case QUIRK_R3DI: + if (request_firmware(&fw_entry, R3DI_EFX_FILE, + codec->card->dev) != 0) { + codec_dbg(codec, "Recon3Di alt firmware not detected."); + spec->alt_firmware_present = false; + } else { + codec_dbg(codec, "Recon3Di firmware selected."); + spec->alt_firmware_present = true; + } + break; + default: + spec->alt_firmware_present = false; + break; + } + /* + * Use default ctefx.bin if no alt firmware is detected, or if none + * exists for your particular codec. + */ + if (!spec->alt_firmware_present) { + codec_dbg(codec, "Default firmware selected."); + if (request_firmware(&fw_entry, EFX_FILE, + codec->card->dev) != 0) + return false; + } dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) { @@ -4402,13 +6676,17 @@ static void ca0132_download_dsp(struct hda_codec *codec) return; /* don't retry failures */ chipio_enable_clocks(codec); - spec->dsp_state = DSP_DOWNLOADING; - if (!ca0132_download_dsp_images(codec)) - spec->dsp_state = DSP_DOWNLOAD_FAILED; - else - spec->dsp_state = DSP_DOWNLOADED; + if (spec->dsp_state != DSP_DOWNLOADED) { + spec->dsp_state = DSP_DOWNLOADING; - if (spec->dsp_state == DSP_DOWNLOADED) + if (!ca0132_download_dsp_images(codec)) + spec->dsp_state = DSP_DOWNLOAD_FAILED; + else + spec->dsp_state = DSP_DOWNLOADED; + } + + /* For codecs using alt functions, this is already done earlier */ + if (spec->dsp_state == DSP_DOWNLOADED && (!spec->use_alt_functions)) ca0132_set_dsp_msr(codec, true); } @@ -4454,6 +6732,10 @@ static void ca0132_init_unsol(struct hda_codec *codec) amic_callback); snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP, ca0132_process_dsp_response); + /* Front headphone jack detection */ + if (spec->use_alt_functions) + snd_hda_jack_detect_enable_callback(codec, + spec->unsol_tag_front_hp, hp_callback); } /* @@ -4476,7 +6758,8 @@ static struct hda_verb ca0132_base_exit_verbs[] = { {} }; -/* Other verbs tables. Sends after DSP download. */ +/* Other verbs tables. Sends after DSP download. */ + static struct hda_verb ca0132_init_verbs0[] = { /* chip init verbs */ {0x15, 0x70D, 0xF0}, @@ -4506,8 +6789,27 @@ static struct hda_verb ca0132_init_verbs0[] = { {0x15, 0x546, 0xC9}, {0x15, 0x53B, 0xCE}, {0x15, 0x5E8, 0xC9}, - {0x15, 0x717, 0x0D}, - {0x15, 0x718, 0x20}, + {} +}; + +/* Extra init verbs for SBZ */ +static struct hda_verb sbz_init_verbs[] = { + {0x15, 0x70D, 0x20}, + {0x15, 0x70E, 0x19}, + {0x15, 0x707, 0x00}, + {0x15, 0x539, 0xCE}, + {0x15, 0x546, 0xC9}, + {0x15, 0x70D, 0xB7}, + {0x15, 0x70E, 0x09}, + {0x15, 0x707, 0x10}, + {0x15, 0x70D, 0xAF}, + {0x15, 0x70E, 0x09}, + {0x15, 0x707, 0x01}, + {0x15, 0x707, 0x05}, + {0x15, 0x70D, 0x73}, + {0x15, 0x70E, 0x09}, + {0x15, 0x707, 0x14}, + {0x15, 0x6FF, 0xC4}, {} }; @@ -4521,7 +6823,11 @@ static void ca0132_init_chip(struct hda_codec *codec) mutex_init(&spec->chipio_mutex); spec->cur_out_type = SPEAKER_OUT; - spec->cur_mic_type = DIGITAL_MIC; + if (!spec->use_alt_functions) + spec->cur_mic_type = DIGITAL_MIC; + else + spec->cur_mic_type = REAR_MIC; + spec->cur_mic_boost = 0; for (i = 0; i < VNODES_COUNT; i++) { @@ -4539,6 +6845,15 @@ static void ca0132_init_chip(struct hda_codec *codec) on = (unsigned int)ca0132_effects[i].reqs[0]; spec->effects_switch[i] = on ? 1 : 0; } + /* + * Sets defaults for the effect slider controls, only for alternative + * ca0132 codecs. Also sets x-bass crossover frequency to 80hz. + */ + if (spec->use_alt_controls) { + spec->xbass_xover_freq = 8; + for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++) + spec->fx_ctl_val[i] = effect_slider_defaults[i]; + } spec->voicefx_val = 0; spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1; @@ -4549,6 +6864,120 @@ static void ca0132_init_chip(struct hda_codec *codec) #endif } +/* + * Recon3Di exit specific commands. + */ +/* prevents popping noise on shutdown */ +static void r3di_gpio_shutdown(struct hda_codec *codec) +{ + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x00); +} + +/* + * Sound Blaster Z exit specific commands. + */ +static void sbz_region2_exit(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int i; + + for (i = 0; i < 4; i++) + writeb(0x0, spec->mem_base + 0x100); + for (i = 0; i < 8; i++) + writeb(0xb3, spec->mem_base + 0x304); + /* + * I believe these are GPIO, with the right most hex digit being the + * gpio pin, and the second digit being on or off. We see this more in + * the input/output select functions. + */ + writew(0x0000, spec->mem_base + 0x320); + writew(0x0001, spec->mem_base + 0x320); + writew(0x0104, spec->mem_base + 0x320); + writew(0x0005, spec->mem_base + 0x320); + writew(0x0007, spec->mem_base + 0x320); +} + +static void sbz_set_pin_ctl_default(struct hda_codec *codec) +{ + hda_nid_t pins[5] = {0x0B, 0x0C, 0x0E, 0x12, 0x13}; + unsigned int i; + + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40); + + for (i = 0; i < 5; i++) + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00); +} + +static void sbz_clear_unsolicited(struct hda_codec *codec) +{ + hda_nid_t pins[7] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13}; + unsigned int i; + + for (i = 0; i < 7; i++) { + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, 0x00); + } +} + +/* On shutdown, sends commands in sets of three */ +static void sbz_gpio_shutdown_commands(struct hda_codec *codec, int dir, + int mask, int data) +{ + if (dir >= 0) + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DIRECTION, dir); + if (mask >= 0) + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_MASK, mask); + + if (data >= 0) + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, data); +} + +static void sbz_exit_chip(struct hda_codec *codec) +{ + chipio_set_stream_control(codec, 0x03, 0); + chipio_set_stream_control(codec, 0x04, 0); + + /* Mess with GPIO */ + sbz_gpio_shutdown_commands(codec, 0x07, 0x07, -1); + sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x05); + sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x01); + + chipio_set_stream_control(codec, 0x14, 0); + chipio_set_stream_control(codec, 0x0C, 0); + + chipio_set_conn_rate(codec, 0x41, SR_192_000); + chipio_set_conn_rate(codec, 0x91, SR_192_000); + + chipio_write(codec, 0x18a020, 0x00000083); + + sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x03); + sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x07); + sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x06); + + chipio_set_stream_control(codec, 0x0C, 0); + + chipio_set_control_param(codec, 0x0D, 0x24); + + sbz_clear_unsolicited(codec); + sbz_set_pin_ctl_default(codec); + + snd_hda_codec_write(codec, 0x0B, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x00); + + if (dspload_is_loaded(codec)) + dsp_reset(codec); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x00); + + sbz_region2_exit(codec); +} + static void ca0132_exit_chip(struct hda_codec *codec) { /* put any chip cleanup stuffs here. */ @@ -4557,28 +6986,264 @@ static void ca0132_exit_chip(struct hda_codec *codec) dsp_reset(codec); } +/* + * This fixes a problem that was hard to reproduce. Very rarely, I would + * boot up, and there would be no sound, but the DSP indicated it had loaded + * properly. I did a few memory dumps to see if anything was different, and + * there were a few areas of memory uninitialized with a1a2a3a4. This function + * checks if those areas are uninitialized, and if they are, it'll attempt to + * reload the card 3 times. Usually it fixes by the second. + */ +static void sbz_dsp_startup_check(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int dsp_data_check[4]; + unsigned int cur_address = 0x390; + unsigned int i; + unsigned int failure = 0; + unsigned int reload = 3; + + if (spec->startup_check_entered) + return; + + spec->startup_check_entered = true; + + for (i = 0; i < 4; i++) { + chipio_read(codec, cur_address, &dsp_data_check[i]); + cur_address += 0x4; + } + for (i = 0; i < 4; i++) { + if (dsp_data_check[i] == 0xa1a2a3a4) + failure = 1; + } + + codec_dbg(codec, "Startup Check: %d ", failure); + if (failure) + codec_info(codec, "DSP not initialized properly. Attempting to fix."); + /* + * While the failure condition is true, and we haven't reached our + * three reload limit, continue trying to reload the driver and + * fix the issue. + */ + while (failure && (reload != 0)) { + codec_info(codec, "Reloading... Tries left: %d", reload); + sbz_exit_chip(codec); + spec->dsp_state = DSP_DOWNLOAD_INIT; + codec->patch_ops.init(codec); + failure = 0; + for (i = 0; i < 4; i++) { + chipio_read(codec, cur_address, &dsp_data_check[i]); + cur_address += 0x4; + } + for (i = 0; i < 4; i++) { + if (dsp_data_check[i] == 0xa1a2a3a4) + failure = 1; + } + reload--; + } + + if (!failure && reload < 3) + codec_info(codec, "DSP fixed."); + + if (!failure) + return; + + codec_info(codec, "DSP failed to initialize properly. Either try a full shutdown or a suspend to clear the internal memory."); +} + +/* + * This is for the extra volume verbs 0x797 (left) and 0x798 (right). These add + * extra precision for decibel values. If you had the dB value in floating point + * you would take the value after the decimal point, multiply by 64, and divide + * by 2. So for 8.59, it's (59 * 64) / 100. Useful if someone wanted to + * implement fixed point or floating point dB volumes. For now, I'll set them + * to 0 just incase a value has lingered from a boot into Windows. + */ +static void ca0132_alt_vol_setup(struct hda_codec *codec) +{ + snd_hda_codec_write(codec, 0x02, 0, 0x797, 0x00); + snd_hda_codec_write(codec, 0x02, 0, 0x798, 0x00); + snd_hda_codec_write(codec, 0x03, 0, 0x797, 0x00); + snd_hda_codec_write(codec, 0x03, 0, 0x798, 0x00); + snd_hda_codec_write(codec, 0x04, 0, 0x797, 0x00); + snd_hda_codec_write(codec, 0x04, 0, 0x798, 0x00); + snd_hda_codec_write(codec, 0x07, 0, 0x797, 0x00); + snd_hda_codec_write(codec, 0x07, 0, 0x798, 0x00); +} + +/* + * Extra commands that don't really fit anywhere else. + */ +static void sbz_pre_dsp_setup(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + writel(0x00820680, spec->mem_base + 0x01C); + writel(0x00820680, spec->mem_base + 0x01C); + + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfc); + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfd); + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfe); + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xff); + + chipio_write(codec, 0x18b0a4, 0x000000c2); + + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44); +} + +/* + * Extra commands that don't really fit anywhere else. + */ +static void r3di_pre_dsp_setup(struct hda_codec *codec) +{ + chipio_write(codec, 0x18b0a4, 0x000000c2); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x1E); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x1C); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x5B); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x00); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x40); + + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x04); +} + + +/* + * These are sent before the DSP is downloaded. Not sure + * what they do, or if they're necessary. Could possibly + * be removed. Figure they're better to leave in. + */ +static void sbz_region2_startup(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + writel(0x00000000, spec->mem_base + 0x400); + writel(0x00000000, spec->mem_base + 0x408); + writel(0x00000000, spec->mem_base + 0x40C); + writel(0x00880680, spec->mem_base + 0x01C); + writel(0x00000083, spec->mem_base + 0xC0C); + writel(0x00000030, spec->mem_base + 0xC00); + writel(0x00000000, spec->mem_base + 0xC04); + writel(0x00000003, spec->mem_base + 0xC0C); + writel(0x00000003, spec->mem_base + 0xC0C); + writel(0x00000003, spec->mem_base + 0xC0C); + writel(0x00000003, spec->mem_base + 0xC0C); + writel(0x000000C1, spec->mem_base + 0xC08); + writel(0x000000F1, spec->mem_base + 0xC08); + writel(0x00000001, spec->mem_base + 0xC08); + writel(0x000000C7, spec->mem_base + 0xC08); + writel(0x000000C1, spec->mem_base + 0xC08); + writel(0x00000080, spec->mem_base + 0xC04); +} + +/* + * Extra init functions for alternative ca0132 codecs. Done + * here so they don't clutter up the main ca0132_init function + * anymore than they have to. + */ +static void ca0132_alt_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_alt_vol_setup(codec); + + switch (spec->quirk) { + case QUIRK_SBZ: + codec_dbg(codec, "SBZ alt_init"); + ca0132_gpio_init(codec); + sbz_pre_dsp_setup(codec); + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_sequence_write(codec, spec->sbz_init_verbs); + break; + case QUIRK_R3DI: + codec_dbg(codec, "R3DI alt_init"); + ca0132_gpio_init(codec); + ca0132_gpio_setup(codec); + r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADING); + r3di_pre_dsp_setup(codec); + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x6FF, 0xC4); + break; + } +} + static int ca0132_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i; + bool dsp_loaded; + + /* + * If the DSP is already downloaded, and init has been entered again, + * there's only two reasons for it. One, the codec has awaken from a + * suspended state, and in that case dspload_is_loaded will return + * false, and the init will be ran again. The other reason it gets + * re entered is on startup for some reason it triggers a suspend and + * resume state. In this case, it will check if the DSP is downloaded, + * and not run the init function again. For codecs using alt_functions, + * it will check if the DSP is loaded properly. + */ + if (spec->dsp_state == DSP_DOWNLOADED) { + dsp_loaded = dspload_is_loaded(codec); + if (!dsp_loaded) { + spec->dsp_reload = true; + spec->dsp_state = DSP_DOWNLOAD_INIT; + } else { + if (spec->quirk == QUIRK_SBZ) + sbz_dsp_startup_check(codec); + return 0; + } + } if (spec->dsp_state != DSP_DOWNLOAD_FAILED) spec->dsp_state = DSP_DOWNLOAD_INIT; spec->curr_chip_addx = INVALID_CHIP_ADDRESS; + if (spec->quirk == QUIRK_SBZ) + sbz_region2_startup(codec); + snd_hda_power_up_pm(codec); ca0132_init_unsol(codec); - ca0132_init_params(codec); ca0132_init_flags(codec); + snd_hda_sequence_write(codec, spec->base_init_verbs); + + if (spec->quirk != QUIRK_NONE) + ca0132_alt_init(codec); + ca0132_download_dsp(codec); + ca0132_refresh_widget_caps(codec); - ca0132_setup_defaults(codec); - ca0132_init_analog_mic2(codec); - ca0132_init_dmic(codec); + + if (spec->quirk == QUIRK_SBZ) + writew(0x0107, spec->mem_base + 0x320); + + switch (spec->quirk) { + case QUIRK_R3DI: + r3di_setup_defaults(codec); + break; + case QUIRK_NONE: + case QUIRK_ALIENWARE: + ca0132_setup_defaults(codec); + ca0132_init_analog_mic2(codec); + ca0132_init_dmic(codec); + break; + } for (i = 0; i < spec->num_outputs; i++) init_output(codec, spec->out_pins[i], spec->dacs[0]); @@ -4590,14 +7255,45 @@ static int ca0132_init(struct hda_codec *codec) init_input(codec, cfg->dig_in_pin, spec->dig_in); - snd_hda_sequence_write(codec, spec->chip_init_verbs); - snd_hda_sequence_write(codec, spec->spec_init_verbs); + if (!spec->use_alt_functions) { + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_EX_ID_SET, 0x0D); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20); + } - ca0132_select_out(codec); - ca0132_select_mic(codec); + if (spec->quirk == QUIRK_SBZ) + ca0132_gpio_setup(codec); + + snd_hda_sequence_write(codec, spec->spec_init_verbs); + switch (spec->quirk) { + case QUIRK_SBZ: + sbz_setup_defaults(codec); + ca0132_alt_select_out(codec); + ca0132_alt_select_in(codec); + break; + case QUIRK_R3DI: + ca0132_alt_select_out(codec); + ca0132_alt_select_in(codec); + break; + default: + ca0132_select_out(codec); + ca0132_select_mic(codec); + break; + } snd_hda_jack_report_sync(codec); + /* + * Re set the PlayEnhancement switch on a resume event, because the + * controls will not be reloaded. + */ + if (spec->dsp_reload) { + spec->dsp_reload = false; + ca0132_pe_switch_set(codec); + } + snd_hda_power_down_pm(codec); return 0; @@ -4609,19 +7305,39 @@ static void ca0132_free(struct hda_codec *codec) cancel_delayed_work_sync(&spec->unsol_hp_work); snd_hda_power_up(codec); - snd_hda_sequence_write(codec, spec->base_exit_verbs); - ca0132_exit_chip(codec); + switch (spec->quirk) { + case QUIRK_SBZ: + sbz_exit_chip(codec); + break; + case QUIRK_R3DI: + r3di_gpio_shutdown(codec); + snd_hda_sequence_write(codec, spec->base_exit_verbs); + ca0132_exit_chip(codec); + break; + default: + snd_hda_sequence_write(codec, spec->base_exit_verbs); + ca0132_exit_chip(codec); + break; + } snd_hda_power_down(codec); + if (spec->mem_base) + iounmap(spec->mem_base); kfree(spec->spec_init_verbs); kfree(codec->spec); } +static void ca0132_reboot_notify(struct hda_codec *codec) +{ + codec->patch_ops.free(codec); +} + static const struct hda_codec_ops ca0132_patch_ops = { .build_controls = ca0132_build_controls, .build_pcms = ca0132_build_pcms, .init = ca0132_init, .free = ca0132_free, .unsol_event = snd_hda_jack_unsol_event, + .reboot_notify = ca0132_reboot_notify, }; static void ca0132_config(struct hda_codec *codec) @@ -4635,9 +7351,14 @@ static void ca0132_config(struct hda_codec *codec) spec->multiout.dac_nids = spec->dacs; spec->multiout.num_dacs = 3; - spec->multiout.max_channels = 2; - if (spec->quirk == QUIRK_ALIENWARE) { + if (!spec->use_alt_functions) + spec->multiout.max_channels = 2; + else + spec->multiout.max_channels = 6; + + switch (spec->quirk) { + case QUIRK_ALIENWARE: codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n"); snd_hda_apply_pincfgs(codec, alienware_pincfgs); @@ -4657,7 +7378,71 @@ static void ca0132_config(struct hda_codec *codec) spec->input_pins[2] = 0x13; spec->shared_mic_nid = 0x7; spec->unsol_tag_amic1 = 0x11; - } else { + break; + case QUIRK_SBZ: + codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__); + snd_hda_apply_pincfgs(codec, sbz_pincfgs); + + spec->num_outputs = 2; + spec->out_pins[0] = 0x0B; /* Line out */ + spec->out_pins[1] = 0x0F; /* Rear headphone out */ + spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/ + spec->out_pins[3] = 0x11; /* Rear surround */ + spec->shared_out_nid = 0x2; + spec->unsol_tag_hp = spec->out_pins[1]; + spec->unsol_tag_front_hp = spec->out_pins[2]; + + spec->adcs[0] = 0x7; /* Rear Mic / Line-in */ + spec->adcs[1] = 0x8; /* Front Mic, but only if no DSP */ + spec->adcs[2] = 0xa; /* what u hear */ + + spec->num_inputs = 2; + spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */ + spec->input_pins[1] = 0x13; /* What U Hear */ + spec->shared_mic_nid = 0x7; + spec->unsol_tag_amic1 = spec->input_pins[0]; + + /* SPDIF I/O */ + spec->dig_out = 0x05; + spec->multiout.dig_out_nid = spec->dig_out; + cfg->dig_out_pins[0] = 0x0c; + cfg->dig_outs = 1; + cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF; + spec->dig_in = 0x09; + cfg->dig_in_pin = 0x0e; + cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; + break; + case QUIRK_R3DI: + codec_dbg(codec, "%s: QUIRK_R3DI applied.\n", __func__); + snd_hda_apply_pincfgs(codec, r3di_pincfgs); + + spec->num_outputs = 2; + spec->out_pins[0] = 0x0B; /* Line out */ + spec->out_pins[1] = 0x0F; /* Rear headphone out */ + spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/ + spec->out_pins[3] = 0x11; /* Rear surround */ + spec->shared_out_nid = 0x2; + spec->unsol_tag_hp = spec->out_pins[1]; + spec->unsol_tag_front_hp = spec->out_pins[2]; + + spec->adcs[0] = 0x07; /* Rear Mic / Line-in */ + spec->adcs[1] = 0x08; /* Front Mic, but only if no DSP */ + spec->adcs[2] = 0x0a; /* what u hear */ + + spec->num_inputs = 2; + spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */ + spec->input_pins[1] = 0x13; /* What U Hear */ + spec->shared_mic_nid = 0x7; + spec->unsol_tag_amic1 = spec->input_pins[0]; + + /* SPDIF I/O */ + spec->dig_out = 0x05; + spec->multiout.dig_out_nid = spec->dig_out; + cfg->dig_out_pins[0] = 0x0c; + cfg->dig_outs = 1; + cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF; + break; + default: spec->num_outputs = 2; spec->out_pins[0] = 0x0b; /* speaker out */ spec->out_pins[1] = 0x10; /* headphone out */ @@ -4684,6 +7469,7 @@ static void ca0132_config(struct hda_codec *codec) spec->dig_in = 0x09; cfg->dig_in_pin = 0x0e; cfg->dig_in_type = HDA_PCM_TYPE_SPDIF; + break; } } @@ -4694,6 +7480,8 @@ static int ca0132_prepare_verbs(struct hda_codec *codec) struct ca0132_spec *spec = codec->spec; spec->chip_init_verbs = ca0132_init_verbs0; + if (spec->quirk == QUIRK_SBZ) + spec->sbz_init_verbs = sbz_init_verbs; spec->spec_init_verbs = kzalloc(sizeof(struct hda_verb) * NUM_SPEC_VERBS, GFP_KERNEL); if (!spec->spec_init_verbs) return -ENOMEM; @@ -4757,9 +7545,46 @@ static int patch_ca0132(struct hda_codec *codec) else spec->quirk = QUIRK_NONE; + /* Setup BAR Region 2 for Sound Blaster Z */ + if (spec->quirk == QUIRK_SBZ) { + spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20); + if (spec->mem_base == NULL) { + codec_warn(codec, "pci_iomap failed!"); + codec_info(codec, "perhaps this is not an SBZ?"); + spec->quirk = QUIRK_NONE; + } + } + spec->dsp_state = DSP_DOWNLOAD_INIT; spec->num_mixers = 1; - spec->mixers[0] = ca0132_mixer; + + /* Set which mixers each quirk uses. */ + switch (spec->quirk) { + case QUIRK_SBZ: + spec->mixers[0] = sbz_mixer; + snd_hda_codec_set_name(codec, "Sound Blaster Z"); + break; + case QUIRK_R3DI: + spec->mixers[0] = r3di_mixer; + snd_hda_codec_set_name(codec, "Recon3Di"); + break; + default: + spec->mixers[0] = ca0132_mixer; + break; + } + + /* Setup whether or not to use alt functions/controls */ + switch (spec->quirk) { + case QUIRK_SBZ: + case QUIRK_R3DI: + spec->use_alt_controls = true; + spec->use_alt_functions = true; + break; + default: + spec->use_alt_controls = false; + spec->use_alt_functions = false; + break; + } spec->base_init_verbs = ca0132_base_init_verbs; spec->base_exit_verbs = ca0132_base_exit_verbs; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 5b4dbcec6de8..dbf9910c5269 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -588,6 +588,7 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct conexant_spec *spec = codec->spec; + struct snd_kcontrol_new *kctl; int i; if (action != HDA_FIXUP_ACT_PROBE) @@ -606,9 +607,7 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec, snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50); /* override mic boost control */ - for (i = 0; i < spec->gen.kctls.used; i++) { - struct snd_kcontrol_new *kctl = - snd_array_elem(&spec->gen.kctls, i); + snd_array_for_each(&spec->gen.kctls, i, kctl) { if (!strcmp(kctl->name, "Mic Boost Volume")) { kctl->put = olpc_xo_mic_boost_put; break; @@ -965,6 +964,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), @@ -998,6 +998,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = { { .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" }, { .id = CXT_FIXUP_HP_DOCK, .name = "hp-dock" }, { .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" }, + { .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" }, {} }; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 7d7eb1354eee..8840daf9c6a3 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -510,7 +510,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) snd_info_set_text_ops(entry, per_pin, print_eld_info); entry->c.text.write = write_eld_info; - entry->mode |= S_IWUSR; + entry->mode |= 0200; per_pin->proc_entry = entry; return 0; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 01a6643fc7d4..d64dcb9a4c99 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2830,6 +2830,7 @@ static int find_ext_mic_pin(struct hda_codec *codec); static void alc286_shutup(struct hda_codec *codec) { + const struct hda_pincfg *pin; int i; int mic_pin = find_ext_mic_pin(codec); /* don't shut up pins when unloading the driver; otherwise it breaks @@ -2837,8 +2838,7 @@ static void alc286_shutup(struct hda_codec *codec) */ if (codec->bus->shutdown) return; - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + snd_array_for_each(&codec->init_pins, i, pin) { /* use read here for syncing after issuing each verb */ if (pin->nid != mic_pin) snd_hda_codec_read(codec, pin->nid, 0, @@ -3653,30 +3653,37 @@ static void alc269_fixup_hp_mute_led(struct hda_codec *codec, } } -static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_hp_mute_led_micx(struct hda_codec *codec, + const struct hda_fixup *fix, + int action, hda_nid_t pin) { struct alc_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mute_led_polarity = 0; - spec->mute_led_nid = 0x18; + spec->mute_led_nid = pin; spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute_enum = 1; codec->power_filter = led_power_filter; } } +static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x18); +} + static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->mute_led_polarity = 0; - spec->mute_led_nid = 0x19; - spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; - spec->gen.vmaster_mute_enum = 1; - codec->power_filter = led_power_filter; - } + alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x19); +} + +static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b); } /* update LED status via GPIO */ @@ -5387,6 +5394,9 @@ static void alc274_fixup_bind_dacs(struct hda_codec *codec, /* for dell wmi mic mute led */ #include "dell_wmi_helper.c" +/* for alc295_fixup_hp_top_speakers */ +#include "hp_x360_helper.c" + enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -5413,6 +5423,7 @@ enum { ALC269_FIXUP_HP_MUTE_LED, ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC269_FIXUP_HP_MUTE_LED_MIC2, + ALC269_FIXUP_HP_MUTE_LED_MIC3, ALC269_FIXUP_HP_GPIO_LED, ALC269_FIXUP_HP_GPIO_MIC1_LED, ALC269_FIXUP_HP_LINE1_MIC1_LED, @@ -5506,6 +5517,7 @@ enum { ALC298_FIXUP_TPT470_DOCK, ALC255_FIXUP_DUMMY_LINEOUT_VERB, ALC255_FIXUP_DELL_HEADSET_MIC, + ALC295_FIXUP_HP_X360, }; static const struct hda_fixup alc269_fixups[] = { @@ -5672,6 +5684,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_hp_mute_led_mic2, }, + [ALC269_FIXUP_HP_MUTE_LED_MIC3] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_hp_mute_led_mic3, + }, [ALC269_FIXUP_HP_GPIO_LED] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_hp_gpio_led, @@ -6375,6 +6391,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MIC }, + [ALC295_FIXUP_HP_X360] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc295_fixup_hp_top_speakers, + .chained = true, + .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC3 + } }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -6494,6 +6516,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC), SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), + SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), SND_PCI_QUIRK(0x103c, 0x82bf, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x82c0, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), @@ -6580,7 +6603,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x17aa, 0x3138, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), - SND_PCI_QUIRK(0x17aa, 0x3112, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), @@ -6752,6 +6774,11 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x1b, 0x01111010}, {0x1e, 0x01451130}, {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, + {0x12, 0x90a60140}, + {0x14, 0x90170110}, + {0x19, 0x02a11030}, + {0x21, 0x02211020}), SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60140}, {0x14, 0x90170110}, diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 5101f40f6fbd..93b8cfc6636f 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -662,7 +662,7 @@ static void wm_proc_init(struct snd_ice1712 *ice) struct snd_info_entry *entry; if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) { snd_info_set_text_ops(entry, ice, wm_proc_regs_read); - entry->mode |= S_IWUSR; + entry->mode |= 0200; entry->c.text.write = wm_proc_regs_write; } } diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c index 8dabd4d0211d..d7366ade5a25 100644 --- a/sound/pci/ice1712/prodigy_hifi.c +++ b/sound/pci/ice1712/prodigy_hifi.c @@ -926,7 +926,7 @@ static void wm_proc_init(struct snd_ice1712 *ice) struct snd_info_entry *entry; if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) { snd_info_set_text_ops(entry, ice, wm_proc_regs_read); - entry->mode |= S_IWUSR; + entry->mode |= 0200; entry->c.text.write = wm_proc_regs_write; } } diff --git a/sound/pci/lola/lola_proc.c b/sound/pci/lola/lola_proc.c index c241dc06dd92..904e3c4f4dfe 100644 --- a/sound/pci/lola/lola_proc.c +++ b/sound/pci/lola/lola_proc.c @@ -214,7 +214,7 @@ void lola_proc_debug_new(struct lola *chip) snd_info_set_text_ops(entry, chip, lola_proc_codec_read); if (!snd_card_proc_new(chip->card, "codec_rw", &entry)) { snd_info_set_text_ops(entry, chip, lola_proc_codec_rw_read); - entry->mode |= S_IWUSR; + entry->mode |= 0200; entry->c.text.write = lola_proc_codec_rw_write; } if (!snd_card_proc_new(chip->card, "regs", &entry)) diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 4ca12665ff73..81af21ac1439 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -1052,10 +1052,10 @@ static int add_controls(struct oxygen *chip, [CONTROL_CD_CAPTURE_SWITCH] = "CD Capture Switch", [CONTROL_AUX_CAPTURE_SWITCH] = "Aux Capture Switch", }; - unsigned int i, j; + unsigned int i; struct snd_kcontrol_new template; struct snd_kcontrol *ctl; - int err; + int j, err; for (i = 0; i < count; ++i) { template = controls[i]; @@ -1086,11 +1086,11 @@ static int add_controls(struct oxygen *chip, err = snd_ctl_add(chip->card, ctl); if (err < 0) return err; - for (j = 0; j < CONTROL_COUNT; ++j) - if (!strcmp(ctl->id.name, known_ctl_names[j])) { - chip->controls[j] = ctl; - ctl->private_free = oxygen_any_ctl_free; - } + j = match_string(known_ctl_names, CONTROL_COUNT, ctl->id.name); + if (j >= 0) { + chip->controls[j] = ctl; + ctl->private_free = oxygen_any_ctl_free; + } } return 0; } diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index f9ae72f28ddc..e57da4036231 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1465,7 +1465,7 @@ static void pcxhr_proc_init(struct snd_pcxhr *chip) !snd_card_proc_new(chip->card, "gpio", &entry)) { snd_info_set_text_ops(entry, chip, pcxhr_proc_gpio_read); entry->c.text.write = pcxhr_proc_gpo_write; - entry->mode |= S_IWUSR; + entry->mode |= 0200; } if (!snd_card_proc_new(chip->card, "ltc", &entry)) snd_info_set_text_ops(entry, chip, pcxhr_proc_ltc); diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 8d92492183d2..06389a5385d7 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o +snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index f41560ecbcd1..ccddc6650b9c 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -33,17 +33,19 @@ #include <linux/gpio.h> #include <linux/module.h> #include <linux/i2c.h> +#include <linux/input.h> #include <linux/acpi.h> +#include "acp.h" #include "../codecs/da7219.h" #include "../codecs/da7219-aad.h" -#define CZ_PLAT_CLK 24000000 -#define MCLK_RATE 24576000 +#define CZ_PLAT_CLK 25000000 #define DUAL_CHANNEL 2 static struct snd_soc_jack cz_jack; static struct clk *da7219_dai_clk; +extern int bt_uart_enable; static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) { @@ -62,7 +64,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) } ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL, - CZ_PLAT_CLK, MCLK_RATE); + CZ_PLAT_CLK, DA7219_PLL_FREQ_OUT_98304); if (ret < 0) { dev_err(rtd->dev, "can't set codec pll: %d\n", ret); return ret; @@ -80,13 +82,17 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) return ret; } + snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + da7219_aad_jack_det(component, &cz_jack); return 0; } -static int cz_da7219_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int da7219_clk_enable(struct snd_pcm_substream *substream) { int ret = 0; struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -100,11 +106,9 @@ static int cz_da7219_hw_params(struct snd_pcm_substream *substream, return ret; } -static int cz_da7219_hw_free(struct snd_pcm_substream *substream) +static void da7219_clk_disable(void) { clk_disable_unprepare(da7219_dai_clk); - - return 0; } static const unsigned int channels[] = { @@ -127,9 +131,12 @@ static const struct snd_pcm_hw_constraint_list constraints_channels = { .mask = 0, }; -static int cz_fe_startup(struct snd_pcm_substream *substream) +static int cz_da7219_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); /* * On this platform for PCM device we support stereo @@ -141,23 +148,58 @@ static int cz_fe_startup(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); - return 0; + machine->i2s_instance = I2S_BT_INSTANCE; + return da7219_clk_enable(substream); +} + +static void cz_da7219_shutdown(struct snd_pcm_substream *substream) +{ + da7219_clk_disable(); +} + +static int cz_max_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); + + machine->i2s_instance = I2S_SP_INSTANCE; + return da7219_clk_enable(substream); +} + +static void cz_max_shutdown(struct snd_pcm_substream *substream) +{ + da7219_clk_disable(); +} + +static int cz_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); + + machine->i2s_instance = I2S_SP_INSTANCE; + return da7219_clk_enable(substream); +} + +static void cz_dmic_shutdown(struct snd_pcm_substream *substream) +{ + da7219_clk_disable(); } -static struct snd_soc_ops cz_da7219_cap_ops = { - .hw_params = cz_da7219_hw_params, - .hw_free = cz_da7219_hw_free, - .startup = cz_fe_startup, +static const struct snd_soc_ops cz_da7219_cap_ops = { + .startup = cz_da7219_startup, + .shutdown = cz_da7219_shutdown, }; -static struct snd_soc_ops cz_max_play_ops = { - .hw_params = cz_da7219_hw_params, - .hw_free = cz_da7219_hw_free, +static const struct snd_soc_ops cz_max_play_ops = { + .startup = cz_max_startup, + .shutdown = cz_max_shutdown, }; -static struct snd_soc_ops cz_dmic_cap_ops = { - .hw_params = cz_da7219_hw_params, - .hw_free = cz_da7219_hw_free, +static const struct snd_soc_ops cz_dmic_cap_ops = { + .startup = cz_dmic_startup, + .shutdown = cz_dmic_shutdown, }; static struct snd_soc_dai_link cz_dai_7219_98357[] = { @@ -240,10 +282,16 @@ static int cz_probe(struct platform_device *pdev) { int ret; struct snd_soc_card *card; + struct acp_platform_info *machine; + machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info), + GFP_KERNEL); + if (!machine) + return -ENOMEM; card = &cz_card; cz_card.dev = &pdev->dev; platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); ret = devm_snd_soc_register_card(&pdev->dev, &cz_card); if (ret) { dev_err(&pdev->dev, @@ -251,6 +299,8 @@ static int cz_probe(struct platform_device *pdev) cz_card.name, ret); return ret; } + bt_uart_enable = !device_property_read_bool(&pdev->dev, + "bt-pad-enable"); return 0; } diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 540088d317f2..77203841c535 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -37,12 +37,14 @@ #define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) #define MIN_BUFFER MAX_BUFFER -#define ST_PLAYBACK_MAX_PERIOD_SIZE 8192 +#define ST_PLAYBACK_MAX_PERIOD_SIZE 4096 #define ST_CAPTURE_MAX_PERIOD_SIZE ST_PLAYBACK_MAX_PERIOD_SIZE #define ST_MAX_BUFFER (ST_PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) #define ST_MIN_BUFFER ST_MAX_BUFFER #define DRV_NAME "acp_audio_dma" +bool bt_uart_enable = true; +EXPORT_SYMBOL(bt_uart_enable); static const struct snd_pcm_hardware acp_pcm_hardware_playback = { .info = SNDRV_PCM_INFO_INTERLEAVED | @@ -130,7 +132,8 @@ static void acp_reg_write(u32 val, void __iomem *acp_mmio, u32 reg) writel(val, acp_mmio + (reg * 4)); } -/* Configure a given dma channel parameters - enable/disable, +/* + * Configure a given dma channel parameters - enable/disable, * number of descriptors, priority */ static void config_acp_dma_channel(void __iomem *acp_mmio, u8 ch_num, @@ -149,11 +152,12 @@ static void config_acp_dma_channel(void __iomem *acp_mmio, u8 ch_num, & dscr_strt_idx), acp_mmio, mmACP_DMA_DSCR_STRT_IDX_0 + ch_num); - /* program a DMA channel with the number of descriptors to be + /* + * program a DMA channel with the number of descriptors to be * processed in the transfer - */ + */ acp_reg_write(ACP_DMA_DSCR_CNT_0__DMAChDscrCnt_MASK & num_dscrs, - acp_mmio, mmACP_DMA_DSCR_CNT_0 + ch_num); + acp_mmio, mmACP_DMA_DSCR_CNT_0 + ch_num); /* set DMA channel priority */ acp_reg_write(priority_level, acp_mmio, mmACP_DMA_PRIO_0 + ch_num); @@ -180,13 +184,15 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio, acp_reg_write(descr_info->xfer_val, acp_mmio, mmACP_SRBM_Targ_Idx_Data); } -/* Initialize the DMA descriptor information for transfer between +/* + * Initialize the DMA descriptor information for transfer between * system memory <-> ACP SRAM */ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, - u32 size, int direction, u32 pte_offset, - u16 ch, u32 sram_bank, - u16 dma_dscr_idx, u32 asic_type) + u32 size, int direction, + u32 pte_offset, u16 ch, + u32 sram_bank, u16 dma_dscr_idx, + u32 asic_type) { u16 i; acp_dma_dscr_transfer_t dmadscr[NUM_DSCRS_PER_CHANNEL]; @@ -195,58 +201,58 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, dmadscr[i].xfer_val = 0; if (direction == SNDRV_PCM_STREAM_PLAYBACK) { dma_dscr_idx = dma_dscr_idx + i; - dmadscr[i].dest = sram_bank + (i * (size/2)); + dmadscr[i].dest = sram_bank + (i * (size / 2)); dmadscr[i].src = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS - + (pte_offset * SZ_4K) + (i * (size/2)); + + (pte_offset * SZ_4K) + (i * (size / 2)); switch (asic_type) { case CHIP_STONEY: dmadscr[i].xfer_val |= - (ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM << 16) | + (ACP_DMA_ATTR_DAGB_GARLIC_TO_SHAREDMEM << 16) | (size / 2); break; default: dmadscr[i].xfer_val |= - (ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) | + (ACP_DMA_ATTR_DAGB_ONION_TO_SHAREDMEM << 16) | (size / 2); } } else { dma_dscr_idx = dma_dscr_idx + i; - dmadscr[i].src = sram_bank + (i * (size/2)); + dmadscr[i].src = sram_bank + (i * (size / 2)); dmadscr[i].dest = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS + - (pte_offset * SZ_4K) + (i * (size/2)); + (pte_offset * SZ_4K) + (i * (size / 2)); switch (asic_type) { case CHIP_STONEY: dmadscr[i].xfer_val |= BIT(22) | - (ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC << 16) | + (ACP_DMA_ATTR_SHARED_MEM_TO_DAGB_GARLIC << 16) | (size / 2); break; default: dmadscr[i].xfer_val |= BIT(22) | - (ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) | + (ACP_DMA_ATTR_SHAREDMEM_TO_DAGB_ONION << 16) | (size / 2); } } config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx, - &dmadscr[i]); + &dmadscr[i]); } config_acp_dma_channel(acp_mmio, ch, - dma_dscr_idx - 1, - NUM_DSCRS_PER_CHANNEL, - ACP_DMA_PRIORITY_LEVEL_NORMAL); + dma_dscr_idx - 1, + NUM_DSCRS_PER_CHANNEL, + ACP_DMA_PRIORITY_LEVEL_NORMAL); } -/* Initialize the DMA descriptor information for transfer between +/* + * Initialize the DMA descriptor information for transfer between * ACP SRAM <-> I2S */ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, u32 size, - int direction, u32 sram_bank, - u16 destination, u16 ch, - u16 dma_dscr_idx, u32 asic_type) + int direction, u32 sram_bank, + u16 destination, u16 ch, + u16 dma_dscr_idx, u32 asic_type) { - u16 i; acp_dma_dscr_transfer_t dmadscr[NUM_DSCRS_PER_CHANNEL]; @@ -254,7 +260,7 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, u32 size, dmadscr[i].xfer_val = 0; if (direction == SNDRV_PCM_STREAM_PLAYBACK) { dma_dscr_idx = dma_dscr_idx + i; - dmadscr[i].src = sram_bank + (i * (size/2)); + dmadscr[i].src = sram_bank + (i * (size / 2)); /* dmadscr[i].dest is unused by hardware. */ dmadscr[i].dest = 0; dmadscr[i].xfer_val |= BIT(22) | (destination << 16) | @@ -269,12 +275,12 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, u32 size, (destination << 16) | (size / 2); } config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx, - &dmadscr[i]); + &dmadscr[i]); } /* Configure the DMA channel with the above descriptore */ config_acp_dma_channel(acp_mmio, ch, dma_dscr_idx - 1, - NUM_DSCRS_PER_CHANNEL, - ACP_DMA_PRIORITY_LEVEL_NORMAL); + NUM_DSCRS_PER_CHANNEL, + ACP_DMA_PRIORITY_LEVEL_NORMAL); } /* Create page table entries in ACP SRAM for the allocated memory */ @@ -291,7 +297,7 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, for (page_idx = 0; page_idx < (num_of_pages); page_idx++) { /* Load the low address of page int ACP SRAM through SRBM */ acp_reg_write((offset + (page_idx * 8)), - acp_mmio, mmACP_SRBM_Targ_Idx_Addr); + acp_mmio, mmACP_SRBM_Targ_Idx_Addr); addr = page_to_phys(pg); low = lower_32_bits(addr); @@ -301,7 +307,7 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, /* Load the High address of page int ACP SRAM through SRBM */ acp_reg_write((offset + (page_idx * 8) + 4), - acp_mmio, mmACP_SRBM_Targ_Idx_Addr); + acp_mmio, mmACP_SRBM_Targ_Idx_Addr); /* page enable in ACP */ high |= BIT(31); @@ -313,59 +319,25 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, } static void config_acp_dma(void __iomem *acp_mmio, - struct audio_substream_data *audio_config, - u32 asic_type) + struct audio_substream_data *rtd, + u32 asic_type) { - u32 pte_offset, sram_bank; - u16 ch1, ch2, destination, dma_dscr_idx; - - if (audio_config->direction == SNDRV_PCM_STREAM_PLAYBACK) { - pte_offset = ACP_PLAYBACK_PTE_OFFSET; - ch1 = SYSRAM_TO_ACP_CH_NUM; - ch2 = ACP_TO_I2S_DMA_CH_NUM; - sram_bank = ACP_SHARED_RAM_BANK_1_ADDRESS; - destination = TO_ACP_I2S_1; - - } else { - pte_offset = ACP_CAPTURE_PTE_OFFSET; - ch1 = SYSRAM_TO_ACP_CH_NUM; - ch2 = ACP_TO_I2S_DMA_CH_NUM; - switch (asic_type) { - case CHIP_STONEY: - sram_bank = ACP_SHARED_RAM_BANK_3_ADDRESS; - break; - default: - sram_bank = ACP_SHARED_RAM_BANK_5_ADDRESS; - } - destination = FROM_ACP_I2S_1; - } - - acp_pte_config(acp_mmio, audio_config->pg, audio_config->num_of_pages, - pte_offset); - if (audio_config->direction == SNDRV_PCM_STREAM_PLAYBACK) - dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12; - else - dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH14; - + acp_pte_config(acp_mmio, rtd->pg, rtd->num_of_pages, + rtd->pte_offset); /* Configure System memory <-> ACP SRAM DMA descriptors */ - set_acp_sysmem_dma_descriptors(acp_mmio, audio_config->size, - audio_config->direction, pte_offset, - ch1, sram_bank, dma_dscr_idx, asic_type); - - if (audio_config->direction == SNDRV_PCM_STREAM_PLAYBACK) - dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH13; - else - dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH15; + set_acp_sysmem_dma_descriptors(acp_mmio, rtd->size, + rtd->direction, rtd->pte_offset, + rtd->ch1, rtd->sram_bank, + rtd->dma_dscr_idx_1, asic_type); /* Configure ACP SRAM <-> I2S DMA descriptors */ - set_acp_to_i2s_dma_descriptors(acp_mmio, audio_config->size, - audio_config->direction, sram_bank, - destination, ch2, dma_dscr_idx, - asic_type); + set_acp_to_i2s_dma_descriptors(acp_mmio, rtd->size, + rtd->direction, rtd->sram_bank, + rtd->destination, rtd->ch2, + rtd->dma_dscr_idx_2, asic_type); } /* Start a given DMA channel transfer */ -static void acp_dma_start(void __iomem *acp_mmio, - u16 ch_num, bool is_circular) +static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num) { u32 dma_ctrl; @@ -375,7 +347,8 @@ static void acp_dma_start(void __iomem *acp_mmio, /* Invalidating the DAGB cache */ acp_reg_write(1, acp_mmio, mmACP_DAGB_ATU_CTRL); - /* configure the DMA channel and start the DMA transfer + /* + * configure the DMA channel and start the DMA transfer * set dmachrun bit to start the transfer and enable the * interrupt on completion of the dma transfer */ @@ -385,6 +358,9 @@ static void acp_dma_start(void __iomem *acp_mmio, case ACP_TO_I2S_DMA_CH_NUM: case ACP_TO_SYSRAM_CH_NUM: case I2S_TO_ACP_DMA_CH_NUM: + case ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM: + case ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM: + case I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM: dma_ctrl |= ACP_DMA_CNTL_0__DMAChIOCEn_MASK; break; default: @@ -392,11 +368,8 @@ static void acp_dma_start(void __iomem *acp_mmio, break; } - /* enable for ACP SRAM to/from I2S DMA channel */ - if (is_circular == true) - dma_ctrl |= ACP_DMA_CNTL_0__Circular_DMA_En_MASK; - else - dma_ctrl &= ~ACP_DMA_CNTL_0__Circular_DMA_En_MASK; + /* circular for both DMA channel */ + dma_ctrl |= ACP_DMA_CNTL_0__Circular_DMA_En_MASK; acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num); } @@ -410,9 +383,10 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num) dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num); - /* clear the dma control register fields before writing zero + /* + * clear the dma control register fields before writing zero * in reset bit - */ + */ dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRun_MASK; dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChIOCEn_MASK; @@ -420,9 +394,10 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num) dma_ch_sts = acp_reg_read(acp_mmio, mmACP_DMA_CH_STS); if (dma_ch_sts & BIT(ch_num)) { - /* set the reset bit for this channel to stop the dma - * transfer - */ + /* + * set the reset bit for this channel to stop the dma + * transfer + */ dma_ctrl |= ACP_DMA_CNTL_0__DMAChRst_MASK; acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num); } @@ -431,13 +406,14 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num) while (true) { dma_ch_sts = acp_reg_read(acp_mmio, mmACP_DMA_CH_STS); if (!(dma_ch_sts & BIT(ch_num))) { - /* clear the reset flag after successfully stopping - * the dma transfer and break from the loop - */ + /* + * clear the reset flag after successfully stopping + * the dma transfer and break from the loop + */ dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRst_MASK; acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 - + ch_num); + + ch_num); break; } if (--count == 0) { @@ -450,7 +426,7 @@ static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num) } static void acp_set_sram_bank_state(void __iomem *acp_mmio, u16 bank, - bool power_on) + bool power_on) { u32 val, req_reg, sts_reg, sts_reg_mask; u32 loops = 1000; @@ -530,7 +506,7 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type) while (true) { val = acp_reg_read(acp_mmio, mmACP_STATUS); - if (val & (u32) 0x1) + if (val & (u32)0x1) break; if (--count == 0) { pr_err("Failed to reset ACP\n"); @@ -544,13 +520,20 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type) val &= ~ACP_SOFT_RESET__SoftResetAud_MASK; acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET); + /* For BT instance change pins from UART to BT */ + if (!bt_uart_enable) { + val = acp_reg_read(acp_mmio, mmACP_BT_UART_PAD_SEL); + val |= ACP_BT_UART_PAD_SELECT_MASK; + acp_reg_write(val, acp_mmio, mmACP_BT_UART_PAD_SEL); + } + /* initiailize Onion control DAGB register */ acp_reg_write(ACP_ONION_CNTL_DEFAULT, acp_mmio, - mmACP_AXI2DAGB_ONION_CNTL); + mmACP_AXI2DAGB_ONION_CNTL); /* initiailize Garlic control DAGB registers */ acp_reg_write(ACP_GARLIC_CNTL_DEFAULT, acp_mmio, - mmACP_AXI2DAGB_GARLIC_CNTL); + mmACP_AXI2DAGB_GARLIC_CNTL); sram_pte_offset = ACP_DAGB_GRP_SRAM_BASE_ADDRESS | ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBSnoopSel_MASK | @@ -558,17 +541,18 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type) ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBGrpEnable_MASK; acp_reg_write(sram_pte_offset, acp_mmio, mmACP_DAGB_BASE_ADDR_GRP_1); acp_reg_write(ACP_PAGE_SIZE_4K_ENABLE, acp_mmio, - mmACP_DAGB_PAGE_SIZE_GRP_1); + mmACP_DAGB_PAGE_SIZE_GRP_1); acp_reg_write(ACP_SRAM_BASE_ADDRESS, acp_mmio, - mmACP_DMA_DESC_BASE_ADDR); + mmACP_DMA_DESC_BASE_ADDR); /* Num of descriptiors in SRAM 0x4, means 256 descriptors;(64 * 4) */ acp_reg_write(0x4, acp_mmio, mmACP_DMA_DESC_MAX_NUM_DSCR); acp_reg_write(ACP_EXTERNAL_INTR_CNTL__DMAIOCMask_MASK, - acp_mmio, mmACP_EXTERNAL_INTR_CNTL); + acp_mmio, mmACP_EXTERNAL_INTR_CNTL); - /* When ACP_TILE_P1 is turned on, all SRAM banks get turned on. + /* + * When ACP_TILE_P1 is turned on, all SRAM banks get turned on. * Now, turn off all of them. This can't be done in 'poweron' of * ACP pm domain, as this requires ACP to be initialized. * For Stoney, Memory gating is disabled,i.e SRAM Banks @@ -606,7 +590,7 @@ static int acp_deinit(void __iomem *acp_mmio) } udelay(100); } - /** Disable ACP clock */ + /* Disable ACP clock */ val = acp_reg_read(acp_mmio, mmACP_CONTROL); val &= ~ACP_CONTROL__ClkEn_MASK; acp_reg_write(val, acp_mmio, mmACP_CONTROL); @@ -615,7 +599,7 @@ static int acp_deinit(void __iomem *acp_mmio) while (true) { val = acp_reg_read(acp_mmio, mmACP_STATUS); - if (!(val & (u32) 0x1)) + if (!(val & (u32)0x1)) break; if (--count == 0) { pr_err("Failed to reset ACP\n"); @@ -629,7 +613,6 @@ static int acp_deinit(void __iomem *acp_mmio) /* ACP DMA irq handler routine for playback, capture usecases */ static irqreturn_t dma_irq_handler(int irq, void *arg) { - u16 dscr_idx; u32 intr_flag, ext_intr_status; struct audio_drv_data *irq_data; void __iomem *acp_mmio; @@ -646,41 +629,45 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) if ((intr_flag & BIT(ACP_TO_I2S_DMA_CH_NUM)) != 0) { valid_irq = true; - if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_13) == - PLAYBACK_START_DMA_DESCR_CH13) - dscr_idx = PLAYBACK_END_DMA_DESCR_CH12; - else - dscr_idx = PLAYBACK_START_DMA_DESCR_CH12; - config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM, dscr_idx, - 1, 0); - acp_dma_start(acp_mmio, SYSRAM_TO_ACP_CH_NUM, false); - snd_pcm_period_elapsed(irq_data->play_i2ssp_stream); - acp_reg_write((intr_flag & BIT(ACP_TO_I2S_DMA_CH_NUM)) << 16, - acp_mmio, mmACP_EXTERNAL_INTR_STAT); + acp_mmio, mmACP_EXTERNAL_INTR_STAT); } - if ((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) != 0) { + if ((intr_flag & BIT(ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM)) != 0) { valid_irq = true; - if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_15) == - CAPTURE_START_DMA_DESCR_CH15) - dscr_idx = CAPTURE_END_DMA_DESCR_CH14; - else - dscr_idx = CAPTURE_START_DMA_DESCR_CH14; - config_acp_dma_channel(acp_mmio, ACP_TO_SYSRAM_CH_NUM, dscr_idx, - 1, 0); - acp_dma_start(acp_mmio, ACP_TO_SYSRAM_CH_NUM, false); + snd_pcm_period_elapsed(irq_data->play_i2sbt_stream); + acp_reg_write((intr_flag & + BIT(ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM)) << 16, + acp_mmio, mmACP_EXTERNAL_INTR_STAT); + } + if ((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) != 0) { + valid_irq = true; + snd_pcm_period_elapsed(irq_data->capture_i2ssp_stream); acp_reg_write((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) << 16, - acp_mmio, mmACP_EXTERNAL_INTR_STAT); + acp_mmio, mmACP_EXTERNAL_INTR_STAT); } if ((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) != 0) { valid_irq = true; - snd_pcm_period_elapsed(irq_data->capture_i2ssp_stream); acp_reg_write((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) << 16, - acp_mmio, mmACP_EXTERNAL_INTR_STAT); + acp_mmio, mmACP_EXTERNAL_INTR_STAT); + } + + if ((intr_flag & BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) != 0) { + valid_irq = true; + snd_pcm_period_elapsed(irq_data->capture_i2sbt_stream); + acp_reg_write((intr_flag & + BIT(I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM)) << 16, + acp_mmio, mmACP_EXTERNAL_INTR_STAT); + } + + if ((intr_flag & BIT(ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM)) != 0) { + valid_irq = true; + acp_reg_write((intr_flag & + BIT(ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM)) << 16, + acp_mmio, mmACP_EXTERNAL_INTR_STAT); } if (valid_irq) @@ -695,11 +682,12 @@ static int acp_dma_open(struct snd_pcm_substream *substream) int ret = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *prtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); + struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, + DRV_NAME); struct audio_drv_data *intr_data = dev_get_drvdata(component->dev); struct audio_substream_data *adata = kzalloc(sizeof(struct audio_substream_data), GFP_KERNEL); - if (adata == NULL) + if (!adata) return -ENOMEM; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -731,17 +719,19 @@ static int acp_dma_open(struct snd_pcm_substream *substream) adata->acp_mmio = intr_data->acp_mmio; runtime->private_data = adata; - /* Enable ACP irq, when neither playback or capture streams are + /* + * Enable ACP irq, when neither playback or capture streams are * active by the time when a new stream is being opened. * This enablement is not required for another stream, if current * stream is not closed - */ - if (!intr_data->play_i2ssp_stream && !intr_data->capture_i2ssp_stream) + */ + if (!intr_data->play_i2ssp_stream && !intr_data->capture_i2ssp_stream && + !intr_data->play_i2sbt_stream && !intr_data->capture_i2sbt_stream) acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - intr_data->play_i2ssp_stream = substream; - /* For Stoney, Memory gating is disabled,i.e SRAM Banks + /* + * For Stoney, Memory gating is disabled,i.e SRAM Banks * won't be turned off. The default state for SRAM banks is ON. * Setting SRAM bank state code skipped for STONEY platform. */ @@ -751,7 +741,6 @@ static int acp_dma_open(struct snd_pcm_substream *substream) bank, true); } } else { - intr_data->capture_i2ssp_stream = substream; if (intr_data->asic_type != CHIP_STONEY) { for (bank = 5; bank <= 8; bank++) acp_set_sram_bank_state(intr_data->acp_mmio, @@ -772,8 +761,11 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime; struct audio_substream_data *rtd; struct snd_soc_pcm_runtime *prtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); + struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, + DRV_NAME); struct audio_drv_data *adata = dev_get_drvdata(component->dev); + struct snd_soc_card *card = prtd->card; + struct acp_platform_info *pinfo = snd_soc_card_get_drvdata(card); runtime = substream->runtime; rtd = runtime->private_data; @@ -781,14 +773,111 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, if (WARN_ON(!rtd)) return -EINVAL; + rtd->i2s_instance = pinfo->i2s_instance; if (adata->asic_type == CHIP_STONEY) { - val = acp_reg_read(adata->acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - val |= ACP_I2S_SP_16BIT_RESOLUTION_EN; - else - val |= ACP_I2S_MIC_16BIT_RESOLUTION_EN; - acp_reg_write(val, adata->acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN); + val = acp_reg_read(adata->acp_mmio, + mmACP_I2S_16BIT_RESOLUTION_EN); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + val |= ACP_I2S_BT_16BIT_RESOLUTION_EN; + break; + case I2S_SP_INSTANCE: + default: + val |= ACP_I2S_SP_16BIT_RESOLUTION_EN; + } + } else { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + val |= ACP_I2S_BT_16BIT_RESOLUTION_EN; + break; + case I2S_SP_INSTANCE: + default: + val |= ACP_I2S_MIC_16BIT_RESOLUTION_EN; + } + } + acp_reg_write(val, adata->acp_mmio, + mmACP_I2S_16BIT_RESOLUTION_EN); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + rtd->pte_offset = ACP_ST_BT_PLAYBACK_PTE_OFFSET; + rtd->ch1 = SYSRAM_TO_ACP_BT_INSTANCE_CH_NUM; + rtd->ch2 = ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM; + rtd->sram_bank = ACP_SRAM_BANK_3_ADDRESS; + rtd->destination = TO_BLUETOOTH; + rtd->dma_dscr_idx_1 = PLAYBACK_START_DMA_DESCR_CH8; + rtd->dma_dscr_idx_2 = PLAYBACK_START_DMA_DESCR_CH9; + rtd->byte_cnt_high_reg_offset = + mmACP_I2S_BT_TRANSMIT_BYTE_CNT_HIGH; + rtd->byte_cnt_low_reg_offset = + mmACP_I2S_BT_TRANSMIT_BYTE_CNT_LOW; + adata->play_i2sbt_stream = substream; + break; + case I2S_SP_INSTANCE: + default: + switch (adata->asic_type) { + case CHIP_STONEY: + rtd->pte_offset = ACP_ST_PLAYBACK_PTE_OFFSET; + break; + default: + rtd->pte_offset = ACP_PLAYBACK_PTE_OFFSET; + } + rtd->ch1 = SYSRAM_TO_ACP_CH_NUM; + rtd->ch2 = ACP_TO_I2S_DMA_CH_NUM; + rtd->sram_bank = ACP_SRAM_BANK_1_ADDRESS; + rtd->destination = TO_ACP_I2S_1; + rtd->dma_dscr_idx_1 = PLAYBACK_START_DMA_DESCR_CH12; + rtd->dma_dscr_idx_2 = PLAYBACK_START_DMA_DESCR_CH13; + rtd->byte_cnt_high_reg_offset = + mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH; + rtd->byte_cnt_low_reg_offset = + mmACP_I2S_TRANSMIT_BYTE_CNT_LOW; + adata->play_i2ssp_stream = substream; + } + } else { + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + rtd->pte_offset = ACP_ST_BT_CAPTURE_PTE_OFFSET; + rtd->ch1 = ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM; + rtd->ch2 = I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM; + rtd->sram_bank = ACP_SRAM_BANK_4_ADDRESS; + rtd->destination = FROM_BLUETOOTH; + rtd->dma_dscr_idx_1 = CAPTURE_START_DMA_DESCR_CH10; + rtd->dma_dscr_idx_2 = CAPTURE_START_DMA_DESCR_CH11; + rtd->byte_cnt_high_reg_offset = + mmACP_I2S_BT_RECEIVE_BYTE_CNT_HIGH; + rtd->byte_cnt_low_reg_offset = + mmACP_I2S_BT_RECEIVE_BYTE_CNT_LOW; + adata->capture_i2sbt_stream = substream; + break; + case I2S_SP_INSTANCE: + default: + rtd->pte_offset = ACP_CAPTURE_PTE_OFFSET; + rtd->ch1 = ACP_TO_SYSRAM_CH_NUM; + rtd->ch2 = I2S_TO_ACP_DMA_CH_NUM; + switch (adata->asic_type) { + case CHIP_STONEY: + rtd->pte_offset = ACP_ST_CAPTURE_PTE_OFFSET; + rtd->sram_bank = ACP_SRAM_BANK_2_ADDRESS; + break; + default: + rtd->pte_offset = ACP_CAPTURE_PTE_OFFSET; + rtd->sram_bank = ACP_SRAM_BANK_5_ADDRESS; + } + rtd->destination = FROM_ACP_I2S_1; + rtd->dma_dscr_idx_1 = CAPTURE_START_DMA_DESCR_CH14; + rtd->dma_dscr_idx_2 = CAPTURE_START_DMA_DESCR_CH15; + rtd->byte_cnt_high_reg_offset = + mmACP_I2S_RECEIVED_BYTE_CNT_HIGH; + rtd->byte_cnt_low_reg_offset = + mmACP_I2S_RECEIVED_BYTE_CNT_LOW; + adata->capture_i2ssp_stream = substream; + } } + size = params_buffer_bytes(params); status = snd_pcm_lib_malloc_pages(substream, size); if (status < 0) @@ -797,7 +886,7 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); pg = virt_to_page(substream->dma_buffer.area); - if (pg != NULL) { + if (pg) { acp_set_sram_bank_state(rtd->acp_mmio, 0, true); /* Save for runtime private data */ rtd->pg = pg; @@ -822,26 +911,15 @@ static int acp_dma_hw_free(struct snd_pcm_substream *substream) return snd_pcm_lib_free_pages(substream); } -static u64 acp_get_byte_count(void __iomem *acp_mmio, int stream) +static u64 acp_get_byte_count(struct audio_substream_data *rtd) { - union acp_dma_count playback_dma_count; - union acp_dma_count capture_dma_count; - u64 bytescount = 0; + union acp_dma_count byte_count; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - playback_dma_count.bcount.high = acp_reg_read(acp_mmio, - mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH); - playback_dma_count.bcount.low = acp_reg_read(acp_mmio, - mmACP_I2S_TRANSMIT_BYTE_CNT_LOW); - bytescount = playback_dma_count.bytescount; - } else { - capture_dma_count.bcount.high = acp_reg_read(acp_mmio, - mmACP_I2S_RECEIVED_BYTE_CNT_HIGH); - capture_dma_count.bcount.low = acp_reg_read(acp_mmio, - mmACP_I2S_RECEIVED_BYTE_CNT_LOW); - bytescount = capture_dma_count.bytescount; - } - return bytescount; + byte_count.bcount.high = acp_reg_read(rtd->acp_mmio, + rtd->byte_cnt_high_reg_offset); + byte_count.bcount.low = acp_reg_read(rtd->acp_mmio, + rtd->byte_cnt_low_reg_offset); + return byte_count.bytescount; } static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) @@ -857,15 +935,10 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) return -EINVAL; buffersize = frames_to_bytes(runtime, runtime->buffer_size); - bytescount = acp_get_byte_count(rtd->acp_mmio, substream->stream); + bytescount = acp_get_byte_count(rtd); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (bytescount > rtd->i2ssp_renderbytescount) - bytescount = bytescount - rtd->i2ssp_renderbytescount; - } else { - if (bytescount > rtd->i2ssp_capturebytescount) - bytescount = bytescount - rtd->i2ssp_capturebytescount; - } + if (bytescount > rtd->bytescount) + bytescount -= rtd->bytescount; pos = do_div(bytescount, buffersize); return bytes_to_frames(runtime, pos); } @@ -883,34 +956,25 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) if (!rtd) return -EINVAL; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, - PLAYBACK_START_DMA_DESCR_CH12, - NUM_DSCRS_PER_CHANNEL, 0); - config_acp_dma_channel(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM, - PLAYBACK_START_DMA_DESCR_CH13, - NUM_DSCRS_PER_CHANNEL, 0); - } else { - config_acp_dma_channel(rtd->acp_mmio, ACP_TO_SYSRAM_CH_NUM, - CAPTURE_START_DMA_DESCR_CH14, - NUM_DSCRS_PER_CHANNEL, 0); - config_acp_dma_channel(rtd->acp_mmio, I2S_TO_ACP_DMA_CH_NUM, - CAPTURE_START_DMA_DESCR_CH15, - NUM_DSCRS_PER_CHANNEL, 0); - } + + config_acp_dma_channel(rtd->acp_mmio, + rtd->ch1, + rtd->dma_dscr_idx_1, + NUM_DSCRS_PER_CHANNEL, 0); + config_acp_dma_channel(rtd->acp_mmio, + rtd->ch2, + rtd->dma_dscr_idx_2, + NUM_DSCRS_PER_CHANNEL, 0); return 0; } static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) { int ret; - u32 loops = 4000; u64 bytescount = 0; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *prtd = substream->private_data; struct audio_substream_data *rtd = runtime->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); if (!rtd) return -EINVAL; @@ -918,59 +982,40 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: - bytescount = acp_get_byte_count(rtd->acp_mmio, - substream->stream); + bytescount = acp_get_byte_count(rtd); + if (rtd->bytescount == 0) + rtd->bytescount = bytescount; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (rtd->i2ssp_renderbytescount == 0) - rtd->i2ssp_renderbytescount = bytescount; - acp_dma_start(rtd->acp_mmio, - SYSRAM_TO_ACP_CH_NUM, false); - while (acp_reg_read(rtd->acp_mmio, mmACP_DMA_CH_STS) & - BIT(SYSRAM_TO_ACP_CH_NUM)) { - if (!loops--) { - dev_err(component->dev, - "acp dma start timeout\n"); - return -ETIMEDOUT; - } - cpu_relax(); - } - - acp_dma_start(rtd->acp_mmio, - ACP_TO_I2S_DMA_CH_NUM, true); - + acp_dma_start(rtd->acp_mmio, rtd->ch1); + acp_dma_start(rtd->acp_mmio, rtd->ch2); } else { - if (rtd->i2ssp_capturebytescount == 0) - rtd->i2ssp_capturebytescount = bytescount; - acp_dma_start(rtd->acp_mmio, - I2S_TO_ACP_DMA_CH_NUM, true); + acp_dma_start(rtd->acp_mmio, rtd->ch2); + acp_dma_start(rtd->acp_mmio, rtd->ch1); } ret = 0; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: - /* Need to stop only circular DMA channels : - * ACP_TO_I2S_DMA_CH_NUM / I2S_TO_ACP_DMA_CH_NUM. Non-circular - * channels will stopped automatically after its transfer - * completes : SYSRAM_TO_ACP_CH_NUM / ACP_TO_SYSRAM_CH_NUM + /* For playback, non circular dma should be stopped first + * i.e Sysram to acp dma transfer channel(rtd->ch1) should be + * stopped before stopping cirular dma which is acp sram to i2s + * fifo dma transfer channel(rtd->ch2). Where as in Capture + * scenario, i2s fifo to acp sram dma channel(rtd->ch2) stopped + * first before stopping acp sram to sysram which is circular + * dma(rtd->ch1). */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ret = acp_dma_stop(rtd->acp_mmio, - SYSRAM_TO_ACP_CH_NUM); - ret = acp_dma_stop(rtd->acp_mmio, - ACP_TO_I2S_DMA_CH_NUM); - rtd->i2ssp_renderbytescount = 0; + acp_dma_stop(rtd->acp_mmio, rtd->ch1); + ret = acp_dma_stop(rtd->acp_mmio, rtd->ch2); } else { - ret = acp_dma_stop(rtd->acp_mmio, - I2S_TO_ACP_DMA_CH_NUM); - ret = acp_dma_stop(rtd->acp_mmio, - ACP_TO_SYSRAM_CH_NUM); - rtd->i2ssp_capturebytescount = 0; + acp_dma_stop(rtd->acp_mmio, rtd->ch2); + ret = acp_dma_stop(rtd->acp_mmio, rtd->ch1); } + rtd->bytescount = 0; break; default: ret = -EINVAL; - } return ret; } @@ -978,26 +1023,27 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) static int acp_dma_new(struct snd_soc_pcm_runtime *rtd) { int ret; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, + DRV_NAME); struct audio_drv_data *adata = dev_get_drvdata(component->dev); switch (adata->asic_type) { case CHIP_STONEY: ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, - SNDRV_DMA_TYPE_DEV, - NULL, ST_MIN_BUFFER, - ST_MAX_BUFFER); + SNDRV_DMA_TYPE_DEV, + NULL, ST_MIN_BUFFER, + ST_MAX_BUFFER); break; default: ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, - SNDRV_DMA_TYPE_DEV, - NULL, MIN_BUFFER, - MAX_BUFFER); + SNDRV_DMA_TYPE_DEV, + NULL, MIN_BUFFER, + MAX_BUFFER); break; } if (ret < 0) dev_err(component->dev, - "buffer preallocation failer error:%d\n", ret); + "buffer preallocation failure error:%d\n", ret); return ret; } @@ -1007,38 +1053,55 @@ static int acp_dma_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; struct snd_soc_pcm_runtime *prtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, DRV_NAME); + struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, + DRV_NAME); struct audio_drv_data *adata = dev_get_drvdata(component->dev); - kfree(rtd); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - adata->play_i2ssp_stream = NULL; - /* For Stoney, Memory gating is disabled,i.e SRAM Banks - * won't be turned off. The default state for SRAM banks is ON. - * Setting SRAM bank state code skipped for STONEY platform. - * added condition checks for Carrizo platform only - */ - if (adata->asic_type != CHIP_STONEY) { - for (bank = 1; bank <= 4; bank++) - acp_set_sram_bank_state(adata->acp_mmio, bank, - false); + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + adata->play_i2sbt_stream = NULL; + break; + case I2S_SP_INSTANCE: + default: + adata->play_i2ssp_stream = NULL; + /* + * For Stoney, Memory gating is disabled,i.e SRAM Banks + * won't be turned off. The default state for SRAM banks + * is ON.Setting SRAM bank state code skipped for STONEY + * platform. Added condition checks for Carrizo platform + * only. + */ + if (adata->asic_type != CHIP_STONEY) { + for (bank = 1; bank <= 4; bank++) + acp_set_sram_bank_state(adata->acp_mmio, + bank, false); + } } } else { - adata->capture_i2ssp_stream = NULL; - if (adata->asic_type != CHIP_STONEY) { - for (bank = 5; bank <= 8; bank++) - acp_set_sram_bank_state(adata->acp_mmio, bank, - false); + switch (rtd->i2s_instance) { + case I2S_BT_INSTANCE: + adata->capture_i2sbt_stream = NULL; + break; + case I2S_SP_INSTANCE: + default: + adata->capture_i2ssp_stream = NULL; + if (adata->asic_type != CHIP_STONEY) { + for (bank = 5; bank <= 8; bank++) + acp_set_sram_bank_state(adata->acp_mmio, + bank, false); + } } } - /* Disable ACP irq, when the current stream is being closed and + /* + * Disable ACP irq, when the current stream is being closed and * another stream is also not active. - */ - if (!adata->play_i2ssp_stream && !adata->capture_i2ssp_stream) + */ + if (!adata->play_i2ssp_stream && !adata->capture_i2ssp_stream && + !adata->play_i2sbt_stream && !adata->capture_i2sbt_stream) acp_reg_write(0, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); - + kfree(rtd); return 0; } @@ -1054,7 +1117,7 @@ static const struct snd_pcm_ops acp_dma_ops = { .prepare = acp_dma_prepare, }; -static struct snd_soc_component_driver acp_asoc_platform = { +static const struct snd_soc_component_driver acp_asoc_platform = { .name = DRV_NAME, .ops = &acp_dma_ops, .pcm_new = acp_dma_new, @@ -1073,8 +1136,8 @@ static int acp_audio_probe(struct platform_device *pdev) } audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data), - GFP_KERNEL); - if (audio_drv_data == NULL) + GFP_KERNEL); + if (!audio_drv_data) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1082,13 +1145,16 @@ static int acp_audio_probe(struct platform_device *pdev) if (IS_ERR(audio_drv_data->acp_mmio)) return PTR_ERR(audio_drv_data->acp_mmio); - /* The following members gets populated in device 'open' + /* + * The following members gets populated in device 'open' * function. Till then interrupts are disabled in 'acp_init' * and device doesn't generate any interrupts. */ audio_drv_data->play_i2ssp_stream = NULL; audio_drv_data->capture_i2ssp_stream = NULL; + audio_drv_data->play_i2sbt_stream = NULL; + audio_drv_data->capture_i2sbt_stream = NULL; audio_drv_data->asic_type = *pdata; @@ -1099,7 +1165,7 @@ static int acp_audio_probe(struct platform_device *pdev) } status = devm_request_irq(&pdev->dev, res->start, dma_irq_handler, - 0, "ACP_IRQ", &pdev->dev); + 0, "ACP_IRQ", &pdev->dev); if (status) { dev_err(&pdev->dev, "ACP IRQ request failed\n"); return status; @@ -1115,7 +1181,7 @@ static int acp_audio_probe(struct platform_device *pdev) } status = devm_snd_soc_register_component(&pdev->dev, - &acp_asoc_platform, NULL, 0); + &acp_asoc_platform, NULL, 0); if (status != 0) { dev_err(&pdev->dev, "Fail to register ALSA platform device\n"); return status; @@ -1145,6 +1211,7 @@ static int acp_pcm_resume(struct device *dev) { u16 bank; int status; + struct audio_substream_data *rtd; struct audio_drv_data *adata = dev_get_drvdata(dev); status = acp_init(adata->acp_mmio, adata->asic_type); @@ -1154,28 +1221,40 @@ static int acp_pcm_resume(struct device *dev) } if (adata->play_i2ssp_stream && adata->play_i2ssp_stream->runtime) { - /* For Stoney, Memory gating is disabled,i.e SRAM Banks + /* + * For Stoney, Memory gating is disabled,i.e SRAM Banks * won't be turned off. The default state for SRAM banks is ON. * Setting SRAM bank state code skipped for STONEY platform. */ if (adata->asic_type != CHIP_STONEY) { for (bank = 1; bank <= 4; bank++) acp_set_sram_bank_state(adata->acp_mmio, bank, - true); + true); } - config_acp_dma(adata->acp_mmio, - adata->play_i2ssp_stream->runtime->private_data, - adata->asic_type); + rtd = adata->play_i2ssp_stream->runtime->private_data; + config_acp_dma(adata->acp_mmio, rtd, adata->asic_type); } - if (adata->capture_i2ssp_stream && adata->capture_i2ssp_stream->runtime) { + if (adata->capture_i2ssp_stream && + adata->capture_i2ssp_stream->runtime) { if (adata->asic_type != CHIP_STONEY) { for (bank = 5; bank <= 8; bank++) acp_set_sram_bank_state(adata->acp_mmio, bank, - true); + true); + } + rtd = adata->capture_i2ssp_stream->runtime->private_data; + config_acp_dma(adata->acp_mmio, rtd, adata->asic_type); + } + if (adata->asic_type != CHIP_CARRIZO) { + if (adata->play_i2sbt_stream && + adata->play_i2sbt_stream->runtime) { + rtd = adata->play_i2sbt_stream->runtime->private_data; + config_acp_dma(adata->acp_mmio, rtd, adata->asic_type); + } + if (adata->capture_i2sbt_stream && + adata->capture_i2sbt_stream->runtime) { + rtd = adata->capture_i2sbt_stream->runtime->private_data; + config_acp_dma(adata->acp_mmio, rtd, adata->asic_type); } - config_acp_dma(adata->acp_mmio, - adata->capture_i2ssp_stream->runtime->private_data, - adata->asic_type); } acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); return 0; diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index ba01510eb818..9cd3e96c84d4 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -10,17 +10,30 @@ #define ACP_PLAYBACK_PTE_OFFSET 10 #define ACP_CAPTURE_PTE_OFFSET 0 +/* Playback and Capture Offset for Stoney */ +#define ACP_ST_PLAYBACK_PTE_OFFSET 0x04 +#define ACP_ST_CAPTURE_PTE_OFFSET 0x00 +#define ACP_ST_BT_PLAYBACK_PTE_OFFSET 0x08 +#define ACP_ST_BT_CAPTURE_PTE_OFFSET 0x0c + #define ACP_GARLIC_CNTL_DEFAULT 0x00000FB4 #define ACP_ONION_CNTL_DEFAULT 0x00000FB4 #define ACP_PHYSICAL_BASE 0x14000 -/* Playback SRAM address (as a destination in dma descriptor) */ -#define ACP_SHARED_RAM_BANK_1_ADDRESS 0x4002000 - -/* Capture SRAM address (as a source in dma descriptor) */ -#define ACP_SHARED_RAM_BANK_5_ADDRESS 0x400A000 -#define ACP_SHARED_RAM_BANK_3_ADDRESS 0x4006000 +/* + * In case of I2S SP controller instance, Stoney uses SRAM bank 1 for + * playback and SRAM Bank 2 for capture where as in case of BT I2S + * Instance, Stoney uses SRAM Bank 3 for playback & SRAM Bank 4 will + * be used for capture. Carrizo uses I2S SP controller instance. SRAM Banks + * 1, 2, 3, 4 will be used for playback & SRAM Banks 5, 6, 7, 8 will be used + * for capture scenario. + */ +#define ACP_SRAM_BANK_1_ADDRESS 0x4002000 +#define ACP_SRAM_BANK_2_ADDRESS 0x4004000 +#define ACP_SRAM_BANK_3_ADDRESS 0x4006000 +#define ACP_SRAM_BANK_4_ADDRESS 0x4008000 +#define ACP_SRAM_BANK_5_ADDRESS 0x400A000 #define ACP_DMA_RESET_TIME 10000 #define ACP_CLOCK_EN_TIME_OUT_VALUE 0x000000FF @@ -35,8 +48,13 @@ #define TO_ACP_I2S_1 0x2 #define TO_ACP_I2S_2 0x4 +#define TO_BLUETOOTH 0x3 #define FROM_ACP_I2S_1 0xa #define FROM_ACP_I2S_2 0xb +#define FROM_BLUETOOTH 0xb + +#define I2S_SP_INSTANCE 0x01 +#define I2S_BT_INSTANCE 0x02 #define ACP_TILE_ON_MASK 0x03 #define ACP_TILE_OFF_MASK 0x02 @@ -57,6 +75,14 @@ #define ACP_TO_SYSRAM_CH_NUM 14 #define I2S_TO_ACP_DMA_CH_NUM 15 +/* Playback DMA Channels for I2S BT instance */ +#define SYSRAM_TO_ACP_BT_INSTANCE_CH_NUM 8 +#define ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM 9 + +/* Capture DMA Channels for I2S BT Instance */ +#define ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM 10 +#define I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM 11 + #define NUM_DSCRS_PER_CHANNEL 2 #define PLAYBACK_START_DMA_DESCR_CH12 0 @@ -69,9 +95,23 @@ #define CAPTURE_START_DMA_DESCR_CH15 6 #define CAPTURE_END_DMA_DESCR_CH15 7 +/* I2S BT Instance DMA Descriptors */ +#define PLAYBACK_START_DMA_DESCR_CH8 8 +#define PLAYBACK_END_DMA_DESCR_CH8 9 +#define PLAYBACK_START_DMA_DESCR_CH9 10 +#define PLAYBACK_END_DMA_DESCR_CH9 11 + +#define CAPTURE_START_DMA_DESCR_CH10 12 +#define CAPTURE_END_DMA_DESCR_CH10 13 +#define CAPTURE_START_DMA_DESCR_CH11 14 +#define CAPTURE_END_DMA_DESCR_CH11 15 + #define mmACP_I2S_16BIT_RESOLUTION_EN 0x5209 #define ACP_I2S_MIC_16BIT_RESOLUTION_EN 0x01 #define ACP_I2S_SP_16BIT_RESOLUTION_EN 0x02 +#define ACP_I2S_BT_16BIT_RESOLUTION_EN 0x04 +#define ACP_BT_UART_PAD_SELECT_MASK 0x1 + enum acp_dma_priority_level { /* 0x0 Specifies the DMA channel is given normal priority */ ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0, @@ -84,20 +124,39 @@ struct audio_substream_data { struct page *pg; unsigned int order; u16 num_of_pages; + u16 i2s_instance; u16 direction; + u16 ch1; + u16 ch2; + u16 destination; + u16 dma_dscr_idx_1; + u16 dma_dscr_idx_2; + u32 pte_offset; + u32 sram_bank; + u32 byte_cnt_high_reg_offset; + u32 byte_cnt_low_reg_offset; uint64_t size; - u64 i2ssp_renderbytescount; - u64 i2ssp_capturebytescount; + u64 bytescount; void __iomem *acp_mmio; }; struct audio_drv_data { struct snd_pcm_substream *play_i2ssp_stream; struct snd_pcm_substream *capture_i2ssp_stream; + struct snd_pcm_substream *play_i2sbt_stream; + struct snd_pcm_substream *capture_i2sbt_stream; void __iomem *acp_mmio; u32 asic_type; }; +/* + * this structure used for platform data transfer between machine driver + * and dma driver + */ +struct acp_platform_info { + u16 i2s_instance; +}; + union acp_dma_count { struct { u32 low; @@ -115,23 +174,25 @@ enum { }; enum { - ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION = 0x0, - ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC = 0x1, - ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM = 0x8, - ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM = 0x9, - ACP_DMA_ATTRIBUTES_FORCE_SIZE = 0xF + ACP_DMA_ATTR_SHAREDMEM_TO_DAGB_ONION = 0x0, + ACP_DMA_ATTR_SHARED_MEM_TO_DAGB_GARLIC = 0x1, + ACP_DMA_ATTR_DAGB_ONION_TO_SHAREDMEM = 0x8, + ACP_DMA_ATTR_DAGB_GARLIC_TO_SHAREDMEM = 0x9, + ACP_DMA_ATTR_FORCE_SIZE = 0xF }; typedef struct acp_dma_dscr_transfer { /* Specifies the source memory location for the DMA data transfer. */ u32 src; - /* Specifies the destination memory location to where the data will + /* + * Specifies the destination memory location to where the data will * be transferred. - */ + */ u32 dest; - /* Specifies the number of bytes need to be transferred - * from source to destination memory.Transfer direction & IOC enable - */ + /* + * Specifies the number of bytes need to be transferred + * from source to destination memory.Transfer direction & IOC enable + */ u32 xfer_val; /* Reserved for future use */ u32 reserved; diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index dcee145dd179..64b784e96f84 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -88,4 +88,13 @@ config SND_ATMEL_SOC_TSE850_PCM5142 help Say Y if you want to add support for the ASoC driver for the Axentia TSE-850 with a PCM5142 codec. + +config SND_ATMEL_SOC_I2S + tristate "Atmel ASoC driver for boards using I2S" + depends on OF && (ARCH_AT91 || COMPILE_TEST) + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Say Y or M if you want to add support for Atmel ASoc driver for boards + using I2S. endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 4440646416e8..cd87cb4bcff5 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -3,10 +3,12 @@ snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o +snd-soc-atmel-i2s-objs := atmel-i2s.o obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o +obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o # AT91 Machine Support snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c new file mode 100644 index 000000000000..5d3b5af9fd92 --- /dev/null +++ b/sound/soc/atmel/atmel-i2s.c @@ -0,0 +1,765 @@ +/* + * Driver for Atmel I2S controller + * + * Copyright (C) 2015 Atmel Corporation + * + * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/mfd/syscon.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> + +#define ATMEL_I2SC_MAX_TDM_CHANNELS 8 + +/* + * ---- I2S Controller Register map ---- + */ +#define ATMEL_I2SC_CR 0x0000 /* Control Register */ +#define ATMEL_I2SC_MR 0x0004 /* Mode Register */ +#define ATMEL_I2SC_SR 0x0008 /* Status Register */ +#define ATMEL_I2SC_SCR 0x000c /* Status Clear Register */ +#define ATMEL_I2SC_SSR 0x0010 /* Status Set Register */ +#define ATMEL_I2SC_IER 0x0014 /* Interrupt Enable Register */ +#define ATMEL_I2SC_IDR 0x0018 /* Interrupt Disable Register */ +#define ATMEL_I2SC_IMR 0x001c /* Interrupt Mask Register */ +#define ATMEL_I2SC_RHR 0x0020 /* Receiver Holding Register */ +#define ATMEL_I2SC_THR 0x0024 /* Transmitter Holding Register */ +#define ATMEL_I2SC_VERSION 0x0028 /* Version Register */ + +/* + * ---- Control Register (Write-only) ---- + */ +#define ATMEL_I2SC_CR_RXEN BIT(0) /* Receiver Enable */ +#define ATMEL_I2SC_CR_RXDIS BIT(1) /* Receiver Disable */ +#define ATMEL_I2SC_CR_CKEN BIT(2) /* Clock Enable */ +#define ATMEL_I2SC_CR_CKDIS BIT(3) /* Clock Disable */ +#define ATMEL_I2SC_CR_TXEN BIT(4) /* Transmitter Enable */ +#define ATMEL_I2SC_CR_TXDIS BIT(5) /* Transmitter Disable */ +#define ATMEL_I2SC_CR_SWRST BIT(7) /* Software Reset */ + +/* + * ---- Mode Register (Read/Write) ---- + */ +#define ATMEL_I2SC_MR_MODE_MASK GENMASK(0, 0) +#define ATMEL_I2SC_MR_MODE_SLAVE (0 << 0) +#define ATMEL_I2SC_MR_MODE_MASTER (1 << 0) + +#define ATMEL_I2SC_MR_DATALENGTH_MASK GENMASK(4, 2) +#define ATMEL_I2SC_MR_DATALENGTH_32_BITS (0 << 2) +#define ATMEL_I2SC_MR_DATALENGTH_24_BITS (1 << 2) +#define ATMEL_I2SC_MR_DATALENGTH_20_BITS (2 << 2) +#define ATMEL_I2SC_MR_DATALENGTH_18_BITS (3 << 2) +#define ATMEL_I2SC_MR_DATALENGTH_16_BITS (4 << 2) +#define ATMEL_I2SC_MR_DATALENGTH_16_BITS_COMPACT (5 << 2) +#define ATMEL_I2SC_MR_DATALENGTH_8_BITS (6 << 2) +#define ATMEL_I2SC_MR_DATALENGTH_8_BITS_COMPACT (7 << 2) + +#define ATMEL_I2SC_MR_FORMAT_MASK GENMASK(7, 6) +#define ATMEL_I2SC_MR_FORMAT_I2S (0 << 6) +#define ATMEL_I2SC_MR_FORMAT_LJ (1 << 6) /* Left Justified */ +#define ATMEL_I2SC_MR_FORMAT_TDM (2 << 6) +#define ATMEL_I2SC_MR_FORMAT_TDMLJ (3 << 6) + +/* Left audio samples duplicated to right audio channel */ +#define ATMEL_I2SC_MR_RXMONO BIT(8) + +/* Receiver uses one DMA channel ... */ +#define ATMEL_I2SC_MR_RXDMA_MASK GENMASK(9, 9) +#define ATMEL_I2SC_MR_RXDMA_SINGLE (0 << 9) /* for all audio channels */ +#define ATMEL_I2SC_MR_RXDMA_MULTIPLE (1 << 9) /* per audio channel */ + +/* I2SDO output of I2SC is internally connected to I2SDI input */ +#define ATMEL_I2SC_MR_RXLOOP BIT(10) + +/* Left audio samples duplicated to right audio channel */ +#define ATMEL_I2SC_MR_TXMONO BIT(12) + +/* Transmitter uses one DMA channel ... */ +#define ATMEL_I2SC_MR_TXDMA_MASK GENMASK(13, 13) +#define ATMEL_I2SC_MR_TXDMA_SINGLE (0 << 13) /* for all audio channels */ +#define ATMEL_I2SC_MR_TXDME_MULTIPLE (1 << 13) /* per audio channel */ + +/* x sample transmitted when underrun */ +#define ATMEL_I2SC_MR_TXSAME_MASK GENMASK(14, 14) +#define ATMEL_I2SC_MR_TXSAME_ZERO (0 << 14) /* Zero sample */ +#define ATMEL_I2SC_MR_TXSAME_PREVIOUS (1 << 14) /* Previous sample */ + +/* Audio Clock to I2SC Master Clock ratio */ +#define ATMEL_I2SC_MR_IMCKDIV_MASK GENMASK(21, 16) +#define ATMEL_I2SC_MR_IMCKDIV(div) \ + (((div) << 16) & ATMEL_I2SC_MR_IMCKDIV_MASK) + +/* Master Clock to fs ratio */ +#define ATMEL_I2SC_MR_IMCKFS_MASK GENMASK(29, 24) +#define ATMEL_I2SC_MR_IMCKFS(fs) \ + (((fs) << 24) & ATMEL_I2SC_MR_IMCKFS_MASK) + +/* Master Clock mode */ +#define ATMEL_I2SC_MR_IMCKMODE_MASK GENMASK(30, 30) +/* 0: No master clock generated (selected clock drives I2SCK pin) */ +#define ATMEL_I2SC_MR_IMCKMODE_I2SCK (0 << 30) +/* 1: master clock generated (internally generated clock drives I2SMCK pin) */ +#define ATMEL_I2SC_MR_IMCKMODE_I2SMCK (1 << 30) + +/* Slot Width */ +/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */ +/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */ +#define ATMEL_I2SC_MR_IWS BIT(31) + +/* + * ---- Status Registers ---- + */ +#define ATMEL_I2SC_SR_RXEN BIT(0) /* Receiver Enabled */ +#define ATMEL_I2SC_SR_RXRDY BIT(1) /* Receive Ready */ +#define ATMEL_I2SC_SR_RXOR BIT(2) /* Receive Overrun */ + +#define ATMEL_I2SC_SR_TXEN BIT(4) /* Transmitter Enabled */ +#define ATMEL_I2SC_SR_TXRDY BIT(5) /* Transmit Ready */ +#define ATMEL_I2SC_SR_TXUR BIT(6) /* Transmit Underrun */ + +/* Receive Overrun Channel */ +#define ATMEL_I2SC_SR_RXORCH_MASK GENMASK(15, 8) +#define ATMEL_I2SC_SR_RXORCH(ch) (1 << (((ch) & 0x7) + 8)) + +/* Transmit Underrun Channel */ +#define ATMEL_I2SC_SR_TXURCH_MASK GENMASK(27, 20) +#define ATMEL_I2SC_SR_TXURCH(ch) (1 << (((ch) & 0x7) + 20)) + +/* + * ---- Interrupt Enable/Disable/Mask Registers ---- + */ +#define ATMEL_I2SC_INT_RXRDY ATMEL_I2SC_SR_RXRDY +#define ATMEL_I2SC_INT_RXOR ATMEL_I2SC_SR_RXOR +#define ATMEL_I2SC_INT_TXRDY ATMEL_I2SC_SR_TXRDY +#define ATMEL_I2SC_INT_TXUR ATMEL_I2SC_SR_TXUR + +static const struct regmap_config atmel_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ATMEL_I2SC_VERSION, +}; + +struct atmel_i2s_gck_param { + int fs; + unsigned long mck; + int imckdiv; + int imckfs; +}; + +#define I2S_MCK_12M288 12288000UL +#define I2S_MCK_11M2896 11289600UL + +/* mck = (32 * (imckfs+1) / (imckdiv+1)) * fs */ +static const struct atmel_i2s_gck_param gck_params[] = { + /* mck = 12.288MHz */ + { 8000, I2S_MCK_12M288, 0, 47}, /* mck = 1536 fs */ + { 16000, I2S_MCK_12M288, 1, 47}, /* mck = 768 fs */ + { 24000, I2S_MCK_12M288, 3, 63}, /* mck = 512 fs */ + { 32000, I2S_MCK_12M288, 3, 47}, /* mck = 384 fs */ + { 48000, I2S_MCK_12M288, 7, 63}, /* mck = 256 fs */ + { 64000, I2S_MCK_12M288, 7, 47}, /* mck = 192 fs */ + { 96000, I2S_MCK_12M288, 7, 31}, /* mck = 128 fs */ + {192000, I2S_MCK_12M288, 7, 15}, /* mck = 64 fs */ + + /* mck = 11.2896MHz */ + { 11025, I2S_MCK_11M2896, 1, 63}, /* mck = 1024 fs */ + { 22050, I2S_MCK_11M2896, 3, 63}, /* mck = 512 fs */ + { 44100, I2S_MCK_11M2896, 7, 63}, /* mck = 256 fs */ + { 88200, I2S_MCK_11M2896, 7, 31}, /* mck = 128 fs */ + {176400, I2S_MCK_11M2896, 7, 15}, /* mck = 64 fs */ +}; + +struct atmel_i2s_dev; + +struct atmel_i2s_caps { + int (*mck_init)(struct atmel_i2s_dev *, struct device_node *np); +}; + +struct atmel_i2s_dev { + struct device *dev; + struct regmap *regmap; + struct clk *pclk; + struct clk *gclk; + struct clk *aclk; + struct snd_dmaengine_dai_dma_data playback; + struct snd_dmaengine_dai_dma_data capture; + unsigned int fmt; + const struct atmel_i2s_gck_param *gck_param; + const struct atmel_i2s_caps *caps; +}; + +static irqreturn_t atmel_i2s_interrupt(int irq, void *dev_id) +{ + struct atmel_i2s_dev *dev = dev_id; + unsigned int sr, imr, pending, ch, mask; + irqreturn_t ret = IRQ_NONE; + + regmap_read(dev->regmap, ATMEL_I2SC_SR, &sr); + regmap_read(dev->regmap, ATMEL_I2SC_IMR, &imr); + pending = sr & imr; + + if (!pending) + return IRQ_NONE; + + if (pending & ATMEL_I2SC_INT_RXOR) { + mask = ATMEL_I2SC_SR_RXOR; + + for (ch = 0; ch < ATMEL_I2SC_MAX_TDM_CHANNELS; ++ch) { + if (sr & ATMEL_I2SC_SR_RXORCH(ch)) { + mask |= ATMEL_I2SC_SR_RXORCH(ch); + dev_err(dev->dev, + "RX overrun on channel %d\n", ch); + } + } + regmap_write(dev->regmap, ATMEL_I2SC_SCR, mask); + ret = IRQ_HANDLED; + } + + if (pending & ATMEL_I2SC_INT_TXUR) { + mask = ATMEL_I2SC_SR_TXUR; + + for (ch = 0; ch < ATMEL_I2SC_MAX_TDM_CHANNELS; ++ch) { + if (sr & ATMEL_I2SC_SR_TXURCH(ch)) { + mask |= ATMEL_I2SC_SR_TXURCH(ch); + dev_err(dev->dev, + "TX underrun on channel %d\n", ch); + } + } + regmap_write(dev->regmap, ATMEL_I2SC_SCR, mask); + ret = IRQ_HANDLED; + } + + return ret; +} + +#define ATMEL_I2S_RATES SNDRV_PCM_RATE_8000_192000 + +#define ATMEL_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static int atmel_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + dev->fmt = fmt; + return 0; +} + +static int atmel_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + unsigned int rhr, sr = 0; + + if (is_playback) { + regmap_read(dev->regmap, ATMEL_I2SC_SR, &sr); + if (sr & ATMEL_I2SC_SR_RXRDY) { + /* + * The RX Ready flag should not be set. However if here, + * we flush (read) the Receive Holding Register to start + * from a clean state. + */ + dev_dbg(dev->dev, "RXRDY is set\n"); + regmap_read(dev->regmap, ATMEL_I2SC_RHR, &rhr); + } + } + + return 0; +} + +static int atmel_i2s_get_gck_param(struct atmel_i2s_dev *dev, int fs) +{ + int i, best; + + if (!dev->gclk || !dev->aclk) { + dev_err(dev->dev, "cannot generate the I2S Master Clock\n"); + return -EINVAL; + } + + /* + * Find the best possible settings to generate the I2S Master Clock + * from the PLL Audio. + */ + dev->gck_param = NULL; + best = INT_MAX; + for (i = 0; i < ARRAY_SIZE(gck_params); ++i) { + const struct atmel_i2s_gck_param *gck_param = &gck_params[i]; + int val = abs(fs - gck_param->fs); + + if (val < best) { + best = val; + dev->gck_param = gck_param; + } + } + + return 0; +} + +static int atmel_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + unsigned int mr = 0; + int ret; + + switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + mr |= ATMEL_I2SC_MR_FORMAT_I2S; + break; + + default: + dev_err(dev->dev, "unsupported bus format\n"); + return -EINVAL; + } + + switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* codec is slave, so cpu is master */ + mr |= ATMEL_I2SC_MR_MODE_MASTER; + ret = atmel_i2s_get_gck_param(dev, params_rate(params)); + if (ret) + return ret; + break; + + case SND_SOC_DAIFMT_CBM_CFM: + /* codec is master, so cpu is slave */ + mr |= ATMEL_I2SC_MR_MODE_SLAVE; + dev->gck_param = NULL; + break; + + default: + dev_err(dev->dev, "unsupported master/slave mode\n"); + return -EINVAL; + } + + switch (params_channels(params)) { + case 1: + if (is_playback) + mr |= ATMEL_I2SC_MR_TXMONO; + else + mr |= ATMEL_I2SC_MR_RXMONO; + break; + case 2: + break; + default: + dev_err(dev->dev, "unsupported number of audio channels\n"); + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + mr |= ATMEL_I2SC_MR_DATALENGTH_8_BITS; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + mr |= ATMEL_I2SC_MR_DATALENGTH_16_BITS; + break; + + case SNDRV_PCM_FORMAT_S18_3LE: + mr |= ATMEL_I2SC_MR_DATALENGTH_18_BITS | ATMEL_I2SC_MR_IWS; + break; + + case SNDRV_PCM_FORMAT_S20_3LE: + mr |= ATMEL_I2SC_MR_DATALENGTH_20_BITS | ATMEL_I2SC_MR_IWS; + break; + + case SNDRV_PCM_FORMAT_S24_3LE: + mr |= ATMEL_I2SC_MR_DATALENGTH_24_BITS | ATMEL_I2SC_MR_IWS; + break; + + case SNDRV_PCM_FORMAT_S24_LE: + mr |= ATMEL_I2SC_MR_DATALENGTH_24_BITS; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + mr |= ATMEL_I2SC_MR_DATALENGTH_32_BITS; + break; + + default: + dev_err(dev->dev, "unsupported size/endianness for audio samples\n"); + return -EINVAL; + } + + return regmap_write(dev->regmap, ATMEL_I2SC_MR, mr); +} + +static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev, + bool enabled) +{ + unsigned int mr, mr_mask; + unsigned long aclk_rate; + int ret; + + mr = 0; + mr_mask = (ATMEL_I2SC_MR_IMCKDIV_MASK | + ATMEL_I2SC_MR_IMCKFS_MASK | + ATMEL_I2SC_MR_IMCKMODE_MASK); + + if (!enabled) { + /* Disable the I2S Master Clock generator. */ + ret = regmap_write(dev->regmap, ATMEL_I2SC_CR, + ATMEL_I2SC_CR_CKDIS); + if (ret) + return ret; + + /* Reset the I2S Master Clock generator settings. */ + ret = regmap_update_bits(dev->regmap, ATMEL_I2SC_MR, + mr_mask, mr); + if (ret) + return ret; + + /* Disable/unprepare the PMC generated clock. */ + clk_disable_unprepare(dev->gclk); + + /* Disable/unprepare the PLL audio clock. */ + clk_disable_unprepare(dev->aclk); + return 0; + } + + if (!dev->gck_param) + return -EINVAL; + + aclk_rate = dev->gck_param->mck * (dev->gck_param->imckdiv + 1); + + /* Fist change the PLL audio clock frequency ... */ + ret = clk_set_rate(dev->aclk, aclk_rate); + if (ret) + return ret; + + /* + * ... then set the PMC generated clock rate to the very same frequency + * to set the gclk parent to aclk. + */ + ret = clk_set_rate(dev->gclk, aclk_rate); + if (ret) + return ret; + + /* Prepare and enable the PLL audio clock first ... */ + ret = clk_prepare_enable(dev->aclk); + if (ret) + return ret; + + /* ... then prepare and enable the PMC generated clock. */ + ret = clk_prepare_enable(dev->gclk); + if (ret) + return ret; + + /* Update the Mode Register to generate the I2S Master Clock. */ + mr |= ATMEL_I2SC_MR_IMCKDIV(dev->gck_param->imckdiv); + mr |= ATMEL_I2SC_MR_IMCKFS(dev->gck_param->imckfs); + mr |= ATMEL_I2SC_MR_IMCKMODE_I2SMCK; + ret = regmap_update_bits(dev->regmap, ATMEL_I2SC_MR, mr_mask, mr); + if (ret) + return ret; + + /* Finally enable the I2S Master Clock generator. */ + return regmap_write(dev->regmap, ATMEL_I2SC_CR, + ATMEL_I2SC_CR_CKEN); +} + +static int atmel_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + bool is_master, mck_enabled; + unsigned int cr, mr; + int err; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cr = is_playback ? ATMEL_I2SC_CR_TXEN : ATMEL_I2SC_CR_RXEN; + mck_enabled = true; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + cr = is_playback ? ATMEL_I2SC_CR_TXDIS : ATMEL_I2SC_CR_RXDIS; + mck_enabled = false; + break; + default: + return -EINVAL; + } + + /* Read the Mode Register to retrieve the master/slave state. */ + err = regmap_read(dev->regmap, ATMEL_I2SC_MR, &mr); + if (err) + return err; + is_master = (mr & ATMEL_I2SC_MR_MODE_MASK) == ATMEL_I2SC_MR_MODE_MASTER; + + /* If master starts, enable the audio clock. */ + if (is_master && mck_enabled) + err = atmel_i2s_switch_mck_generator(dev, true); + if (err) + return err; + + err = regmap_write(dev->regmap, ATMEL_I2SC_CR, cr); + if (err) + return err; + + /* If master stops, disable the audio clock. */ + if (is_master && !mck_enabled) + err = atmel_i2s_switch_mck_generator(dev, false); + + 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); + + snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture); + return 0; +} + +static struct snd_soc_dai_driver atmel_i2s_dai = { + .probe = atmel_i2s_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = ATMEL_I2S_RATES, + .formats = ATMEL_I2S_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = ATMEL_I2S_RATES, + .formats = ATMEL_I2S_FORMATS, + }, + .ops = &atmel_i2s_dai_ops, + .symmetric_rates = 1, +}; + +static const struct snd_soc_component_driver atmel_i2s_component = { + .name = "atmel-i2s", +}; + +static int atmel_i2s_sama5d2_mck_init(struct atmel_i2s_dev *dev, + struct device_node *np) +{ + struct clk *muxclk; + int err; + + if (!dev->gclk) + return 0; + + /* muxclk is optional, so we return error for probe defer only */ + muxclk = devm_clk_get(dev->dev, "muxclk"); + if (IS_ERR(muxclk)) { + err = PTR_ERR(muxclk); + if (err == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_warn(dev->dev, + "failed to get the I2S clock control: %d\n", err); + return 0; + } + + return clk_set_parent(muxclk, dev->gclk); +} + +static const struct atmel_i2s_caps atmel_i2s_sama5d2_caps = { + .mck_init = atmel_i2s_sama5d2_mck_init, +}; + +static const struct of_device_id atmel_i2s_dt_ids[] = { + { + .compatible = "atmel,sama5d2-i2s", + .data = (void *)&atmel_i2s_sama5d2_caps, + }, + + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_i2s_dt_ids); + +static int atmel_i2s_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + struct atmel_i2s_dev *dev; + struct resource *mem; + struct regmap *regmap; + void __iomem *base; + int irq; + int err = -ENXIO; + unsigned int pcm_flags = 0; + unsigned int version; + + /* Get memory for driver data. */ + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* Get hardware capabilities. */ + match = of_match_node(atmel_i2s_dt_ids, np); + if (match) + dev->caps = match->data; + + /* Map I/O registers. */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(&pdev->dev, base, + &atmel_i2s_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Request IRQ. */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + err = devm_request_irq(&pdev->dev, irq, atmel_i2s_interrupt, 0, + dev_name(&pdev->dev), dev); + if (err) + return err; + + /* Get the peripheral clock. */ + dev->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(dev->pclk)) { + err = PTR_ERR(dev->pclk); + dev_err(&pdev->dev, + "failed to get the peripheral clock: %d\n", err); + return err; + } + + /* Get audio clocks to generate the I2S Master Clock (I2S_MCK) */ + dev->aclk = devm_clk_get(&pdev->dev, "aclk"); + dev->gclk = devm_clk_get(&pdev->dev, "gclk"); + if (IS_ERR(dev->aclk) && IS_ERR(dev->gclk)) { + if (PTR_ERR(dev->aclk) == -EPROBE_DEFER || + PTR_ERR(dev->gclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + /* Master Mode not supported */ + dev->aclk = NULL; + dev->gclk = NULL; + } else if (IS_ERR(dev->gclk)) { + err = PTR_ERR(dev->gclk); + dev_err(&pdev->dev, + "failed to get the PMC generated clock: %d\n", err); + return err; + } else if (IS_ERR(dev->aclk)) { + err = PTR_ERR(dev->aclk); + dev_err(&pdev->dev, + "failed to get the PLL audio clock: %d\n", err); + return err; + } + + dev->dev = &pdev->dev; + dev->regmap = regmap; + platform_set_drvdata(pdev, dev); + + /* Do hardware specific settings to initialize I2S_MCK generator */ + if (dev->caps && dev->caps->mck_init) { + err = dev->caps->mck_init(dev, np); + if (err) + return err; + } + + /* Enable the peripheral clock. */ + err = clk_prepare_enable(dev->pclk); + if (err) + return err; + + /* Get IP version. */ + regmap_read(dev->regmap, ATMEL_I2SC_VERSION, &version); + dev_info(&pdev->dev, "hw version: %#x\n", version); + + /* Enable error interrupts. */ + regmap_write(dev->regmap, ATMEL_I2SC_IER, + ATMEL_I2SC_INT_RXOR | ATMEL_I2SC_INT_TXUR); + + err = devm_snd_soc_register_component(&pdev->dev, + &atmel_i2s_component, + &atmel_i2s_dai, 1); + if (err) { + dev_err(&pdev->dev, "failed to register DAI: %d\n", err); + clk_disable_unprepare(dev->pclk); + return err; + } + + /* Prepare DMA config. */ + dev->playback.addr = (dma_addr_t)mem->start + ATMEL_I2SC_THR; + dev->playback.maxburst = 1; + dev->capture.addr = (dma_addr_t)mem->start + ATMEL_I2SC_RHR; + dev->capture.maxburst = 1; + + if (of_property_match_string(np, "dma-names", "rx-tx") == 0) + pcm_flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX; + err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, pcm_flags); + if (err) { + dev_err(&pdev->dev, "failed to register PCM: %d\n", err); + clk_disable_unprepare(dev->pclk); + return err; + } + + return 0; +} + +static int atmel_i2s_remove(struct platform_device *pdev) +{ + struct atmel_i2s_dev *dev = platform_get_drvdata(pdev); + + clk_disable_unprepare(dev->pclk); + + return 0; +} + +static struct platform_driver atmel_i2s_driver = { + .driver = { + .name = "atmel_i2s", + .of_match_table = of_match_ptr(atmel_i2s_dt_ids), + }, + .probe = atmel_i2s_probe, + .remove = atmel_i2s_remove, +}; +module_platform_driver(atmel_i2s_driver); + +MODULE_DESCRIPTION("Atmel I2S Controller driver"); +MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index a1e2c5682dcd..d3b69682d9c2 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -820,7 +820,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, if (ret < 0) { printk(KERN_WARNING "atmel_ssc_dai: request_irq failure\n"); - pr_debug("Atmel_ssc_dai: Stoping clock\n"); + pr_debug("Atmel_ssc_dai: Stopping clock\n"); clk_disable(ssc_p->ssc->clk); return ret; } @@ -1002,8 +1002,7 @@ static const struct snd_soc_component_driver atmel_ssc_component = { static int asoc_ssc_init(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct ssc_device *ssc = platform_get_drvdata(pdev); + struct ssc_device *ssc = dev_get_drvdata(dev); int ret; ret = snd_soc_register_component(dev, &atmel_ssc_component, @@ -1033,8 +1032,7 @@ err: static void asoc_ssc_exit(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct ssc_device *ssc = platform_get_drvdata(pdev); + struct ssc_device *ssc = dev_get_drvdata(dev); if (ssc->pdata->use_dma) atmel_pcm_dma_platform_unregister(dev); diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig index edf367100ebd..02f50b7a966f 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -11,9 +11,8 @@ config SND_BCM2835_SOC_I2S config SND_SOC_CYGNUS tristate "SoC platform audio for Broadcom Cygnus chips" depends on ARCH_BCM_CYGNUS || COMPILE_TEST - depends on HAS_DMA help Say Y if you want to add support for ASoC audio on Broadcom Cygnus chips (bcm958300, bcm958305, bcm911360) - If you don't know what to do here, say N.
\ No newline at end of file + If you don't know what to do here, say N. diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig index c7cd60f009e9..e09199124c36 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -9,6 +9,23 @@ config SND_EP93XX_SOC config SND_EP93XX_SOC_I2S tristate +if SND_EP93XX_SOC_I2S + +config SND_EP93XX_SOC_I2S_WATCHDOG + bool "IRQ based underflow watchdog workaround" + default y + help + I2S controller on EP93xx seems to have undocumented HW issue. + Underflow of internal I2S controller FIFO could confuse the + state machine and the whole stream can be shifted by one byte + until I2S is disabled. This option enables IRQ based watchdog + which disables and re-enables I2S in case of underflow and + fills FIFO with zeroes. + + If you are unsure how to answer this question, answer Y. + +endif # if SND_EP93XX_SOC_I2S + config SND_EP93XX_SOC_AC97 tristate select AC97_BUS diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c index c53bd6f2c2d7..3d011abaa266 100644 --- a/sound/soc/cirrus/edb93xx.c +++ b/sound/soc/cirrus/edb93xx.c @@ -67,7 +67,7 @@ static struct snd_soc_dai_link edb93xx_dai = { .cpu_dai_name = "ep93xx-i2s", .codec_name = "spi0.0", .codec_dai_name = "cs4271-hifi", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ops = &edb93xx_ops, }; diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 934f8aefdd90..0918c5da575a 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -35,8 +35,12 @@ #define EP93XX_I2S_TXCLKCFG 0x00 #define EP93XX_I2S_RXCLKCFG 0x04 +#define EP93XX_I2S_GLSTS 0x08 #define EP93XX_I2S_GLCTRL 0x0C +#define EP93XX_I2S_I2STX0LFT 0x10 +#define EP93XX_I2S_I2STX0RT 0x14 + #define EP93XX_I2S_TXLINCTRLDATA 0x28 #define EP93XX_I2S_TXCTRL 0x2C #define EP93XX_I2S_TXWRDLEN 0x30 @@ -51,7 +55,17 @@ #define EP93XX_I2S_WRDLEN_24 (1 << 0) #define EP93XX_I2S_WRDLEN_32 (2 << 0) -#define EP93XX_I2S_LINCTRLDATA_R_JUST (1 << 2) /* Right justify */ +#define EP93XX_I2S_RXLINCTRLDATA_R_JUST BIT(1) /* Right justify */ + +#define EP93XX_I2S_TXLINCTRLDATA_R_JUST BIT(2) /* Right justify */ + +/* + * Transmit empty interrupt level select: + * 0 - Generate interrupt when FIFO is half empty + * 1 - Generate interrupt when FIFO is empty + */ +#define EP93XX_I2S_TXCTRL_TXEMPTY_LVL BIT(0) +#define EP93XX_I2S_TXCTRL_TXUFIE BIT(1) /* Transmit interrupt enable */ #define EP93XX_I2S_CLKCFG_LRS (1 << 0) /* lrclk polarity */ #define EP93XX_I2S_CLKCFG_CKP (1 << 1) /* Bit clock polarity */ @@ -59,6 +73,8 @@ #define EP93XX_I2S_CLKCFG_MASTER (1 << 3) /* Master mode */ #define EP93XX_I2S_CLKCFG_NBCG (1 << 4) /* Not bit clock gating */ +#define EP93XX_I2S_GLSTS_TX0_FIFO_FULL BIT(12) + struct ep93xx_i2s_info { struct clk *mclk; struct clk *sclk; @@ -96,7 +112,6 @@ static inline unsigned ep93xx_i2s_read_reg(struct ep93xx_i2s_info *info, static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream) { unsigned base_reg; - int i; if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 && (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) { @@ -109,27 +124,36 @@ static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream) ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1); } - /* Enable fifos */ + /* Enable fifo */ if (stream == SNDRV_PCM_STREAM_PLAYBACK) base_reg = EP93XX_I2S_TX0EN; else base_reg = EP93XX_I2S_RX0EN; - for (i = 0; i < 3; i++) - ep93xx_i2s_write_reg(info, base_reg + (i * 4), 1); + ep93xx_i2s_write_reg(info, base_reg, 1); + + /* Enable TX IRQs (FIFO empty or underflow) */ + if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG) && + stream == SNDRV_PCM_STREAM_PLAYBACK) + ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL, + EP93XX_I2S_TXCTRL_TXEMPTY_LVL | + EP93XX_I2S_TXCTRL_TXUFIE); } static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) { unsigned base_reg; - int i; - /* Disable fifos */ + /* Disable IRQs */ + if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG) && + stream == SNDRV_PCM_STREAM_PLAYBACK) + ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCTRL, 0); + + /* Disable fifo */ if (stream == SNDRV_PCM_STREAM_PLAYBACK) base_reg = EP93XX_I2S_TX0EN; else base_reg = EP93XX_I2S_RX0EN; - for (i = 0; i < 3; i++) - ep93xx_i2s_write_reg(info, base_reg + (i * 4), 0); + ep93xx_i2s_write_reg(info, base_reg, 0); if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 && (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) { @@ -143,6 +167,37 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) } } +/* + * According to documentation I2S controller can handle underflow conditions + * just fine, but in reality the state machine is sometimes confused so that + * the whole stream is shifted by one byte. The watchdog below disables the TX + * FIFO, fills the buffer with zeroes and re-enables the FIFO. State machine + * is being reset and by filling the buffer we get some time before next + * underflow happens. + */ +static irqreturn_t ep93xx_i2s_interrupt(int irq, void *dev_id) +{ + struct ep93xx_i2s_info *info = dev_id; + + /* Disable FIFO */ + ep93xx_i2s_write_reg(info, EP93XX_I2S_TX0EN, 0); + /* + * Fill TX FIFO with zeroes, this way we can defer next IRQs as much as + * possible and get more time for DMA to catch up. Actually there are + * only 8 samples in this FIFO, so even on 8kHz maximum deferral here is + * 1ms. + */ + while (!(ep93xx_i2s_read_reg(info, EP93XX_I2S_GLSTS) & + EP93XX_I2S_GLSTS_TX0_FIFO_FULL)) { + ep93xx_i2s_write_reg(info, EP93XX_I2S_I2STX0LFT, 0); + ep93xx_i2s_write_reg(info, EP93XX_I2S_I2STX0RT, 0); + } + /* Re-enable FIFO */ + ep93xx_i2s_write_reg(info, EP93XX_I2S_TX0EN, 1); + + return IRQ_HANDLED; +} + static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai) { struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); @@ -170,25 +225,25 @@ static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(cpu_dai); - unsigned int clk_cfg, lin_ctrl; + unsigned int clk_cfg; + unsigned int txlin_ctrl = 0; + unsigned int rxlin_ctrl = 0; clk_cfg = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXCLKCFG); - lin_ctrl = ep93xx_i2s_read_reg(info, EP93XX_I2S_RXLINCTRLDATA); switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: clk_cfg |= EP93XX_I2S_CLKCFG_REL; - lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST; break; case SND_SOC_DAIFMT_LEFT_J: clk_cfg &= ~EP93XX_I2S_CLKCFG_REL; - lin_ctrl &= ~EP93XX_I2S_LINCTRLDATA_R_JUST; break; case SND_SOC_DAIFMT_RIGHT_J: clk_cfg &= ~EP93XX_I2S_CLKCFG_REL; - lin_ctrl |= EP93XX_I2S_LINCTRLDATA_R_JUST; + rxlin_ctrl |= EP93XX_I2S_RXLINCTRLDATA_R_JUST; + txlin_ctrl |= EP93XX_I2S_TXLINCTRLDATA_R_JUST; break; default: @@ -213,32 +268,32 @@ static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: /* Negative bit clock, lrclk low on left word */ - clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL); + clk_cfg &= ~(EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_LRS); break; case SND_SOC_DAIFMT_NB_IF: /* Negative bit clock, lrclk low on right word */ clk_cfg &= ~EP93XX_I2S_CLKCFG_CKP; - clk_cfg |= EP93XX_I2S_CLKCFG_REL; + clk_cfg |= EP93XX_I2S_CLKCFG_LRS; break; case SND_SOC_DAIFMT_IB_NF: /* Positive bit clock, lrclk low on left word */ clk_cfg |= EP93XX_I2S_CLKCFG_CKP; - clk_cfg &= ~EP93XX_I2S_CLKCFG_REL; + clk_cfg &= ~EP93XX_I2S_CLKCFG_LRS; break; case SND_SOC_DAIFMT_IB_IF: /* Positive bit clock, lrclk low on right word */ - clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_REL; + clk_cfg |= EP93XX_I2S_CLKCFG_CKP | EP93XX_I2S_CLKCFG_LRS; break; } /* Write new register values */ ep93xx_i2s_write_reg(info, EP93XX_I2S_RXCLKCFG, clk_cfg); ep93xx_i2s_write_reg(info, EP93XX_I2S_TXCLKCFG, clk_cfg); - ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, lin_ctrl); - ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, lin_ctrl); + ep93xx_i2s_write_reg(info, EP93XX_I2S_RXLINCTRLDATA, rxlin_ctrl); + ep93xx_i2s_write_reg(info, EP93XX_I2S_TXLINCTRLDATA, txlin_ctrl); return 0; } @@ -392,6 +447,17 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) if (IS_ERR(info->regs)) return PTR_ERR(info->regs); + if (IS_ENABLED(CONFIG_SND_EP93XX_SOC_I2S_WATCHDOG)) { + int irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return irq < 0 ? irq : -ENODEV; + + err = devm_request_irq(&pdev->dev, irq, ep93xx_i2s_interrupt, 0, + pdev->name, info); + if (err) + return err; + } + info->mclk = clk_get(&pdev->dev, "mclk"); if (IS_ERR(info->mclk)) { err = PTR_ERR(info->mclk); diff --git a/sound/soc/cirrus/snappercl15.c b/sound/soc/cirrus/snappercl15.c index 2334ec19e7eb..11ff7b2672b2 100644 --- a/sound/soc/cirrus/snappercl15.c +++ b/sound/soc/cirrus/snappercl15.c @@ -72,7 +72,7 @@ static struct snd_soc_dai_link snappercl15_dai = { .codec_dai_name = "tlv320aic23-hifi", .codec_name = "tlv320aic23-codec.0-001a", .platform_name = "ep93xx-i2s", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ops = &snappercl15_ops, }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9548f63ca531..63cf62e9c9aa 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -106,6 +106,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9877 if I2C select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C + select SND_SOC_MT6351 if MTK_PMIC_WRAP select SND_SOC_NAU8540 if I2C select SND_SOC_NAU8810 if I2C select SND_SOC_NAU8824 if I2C @@ -126,6 +127,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT274 if I2C select SND_SOC_RT286 if I2C select SND_SOC_RT298 if I2C + select SND_SOC_RT1305 if I2C select SND_SOC_RT5514 if I2C select SND_SOC_RT5616 if I2C select SND_SOC_RT5631 if I2C @@ -136,12 +138,14 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT5660 if I2C select SND_SOC_RT5663 if I2C select SND_SOC_RT5665 if I2C + select SND_SOC_RT5668 if I2C select SND_SOC_RT5670 if I2C select SND_SOC_RT5677 if I2C && SPI_MASTER select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE select SND_SOC_SIRF_AUDIO_CODEC select SND_SOC_SPDIF + select SND_SOC_SSM2305 select SND_SOC_SSM2518 if I2C select SND_SOC_SSM2602_SPI if SPI_MASTER select SND_SOC_SSM2602_I2C if I2C @@ -168,6 +172,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C select SND_SOC_TSCS42XX if I2C + select SND_SOC_TSCS454 if I2C select SND_SOC_TS3A227E if I2C select SND_SOC_TWL4030 if TWL4030_CORE select SND_SOC_TWL6040 if TWL6040_CORE @@ -770,8 +775,10 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5660=y default y if SND_SOC_RT5663=y default y if SND_SOC_RT5665=y + default y if SND_SOC_RT5668=y default y if SND_SOC_RT5670=y default y if SND_SOC_RT5677=y + default y if SND_SOC_RT1305=y default m if SND_SOC_RT5514=m default m if SND_SOC_RT5616=m default m if SND_SOC_RT5640=m @@ -781,8 +788,10 @@ config SND_SOC_RL6231 default m if SND_SOC_RT5660=m default m if SND_SOC_RT5663=m default m if SND_SOC_RT5665=m + default m if SND_SOC_RT5668=m default m if SND_SOC_RT5670=m default m if SND_SOC_RT5677=m + default m if SND_SOC_RT1305=m config SND_SOC_RL6347A tristate @@ -805,6 +814,9 @@ config SND_SOC_RT298 tristate depends on I2C +config SND_SOC_RT1305 + tristate + config SND_SOC_RT5514 tristate @@ -844,6 +856,9 @@ config SND_SOC_RT5663 config SND_SOC_RT5665 tristate +config SND_SOC_RT5668 + tristate + config SND_SOC_RT5670 tristate @@ -883,6 +898,12 @@ config SND_SOC_SIRF_AUDIO_CODEC config SND_SOC_SPDIF tristate "S/PDIF CODEC" +config SND_SOC_SSM2305 + tristate "Analog Devices SSM2305 Class-D Amplifier" + help + Enable support for Analog Devices SSM2305 filterless + high-efficiency mono Class-D audio power amplifiers. + config SND_SOC_SSM2518 tristate @@ -1011,6 +1032,13 @@ config SND_SOC_TSCS42XX help Add support for Tempo Semiconductor's TSCS42xx audio CODEC. +config SND_SOC_TSCS454 + tristate "Tempo Semiconductor TSCS454 CODEC" + depends on I2C + select REGMAP_I2C + help + Add support for Tempo Semiconductor's TSCS454 audio CODEC. + config SND_SOC_TWL4030 select MFD_TWL4030_AUDIO tristate @@ -1111,7 +1139,7 @@ config SND_SOC_WM8776 depends on SND_SOC_I2C_AND_SPI config SND_SOC_WM8782 - tristate + tristate "Wolfson Microelectronics WM8782 ADC" config SND_SOC_WM8804 tristate @@ -1247,6 +1275,9 @@ config SND_SOC_MC13783 config SND_SOC_ML26124 tristate +config SND_SOC_MT6351 + tristate "MediaTek MT6351 Codec" + config SND_SOC_NAU8540 tristate "Nuvoton Technology Corporation NAU85L40 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index e849d1495308..e023fdf85221 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -102,6 +102,7 @@ snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o +snd-soc-mt6351-objs := mt6351.o snd-soc-nau8540-objs := nau8540.o snd-soc-nau8810-objs := nau8810.o snd-soc-nau8824-objs := nau8824.o @@ -126,6 +127,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-rl6231-objs := rl6231.o snd-soc-rl6347a-objs := rl6347a.o +snd-soc-rt1305-objs := rt1305.o snd-soc-rt274-objs := rt274.o snd-soc-rt286-objs := rt286.o snd-soc-rt298-objs := rt298.o @@ -140,6 +142,7 @@ snd-soc-rt5659-objs := rt5659.o snd-soc-rt5660-objs := rt5660.o snd-soc-rt5663-objs := rt5663.o snd-soc-rt5665-objs := rt5665.o +snd-soc-rt5668-objs := rt5668.o snd-soc-rt5670-objs := rt5670.o snd-soc-rt5677-objs := rt5677.o snd-soc-rt5677-spi-objs := rt5677-spi.o @@ -153,6 +156,7 @@ snd-soc-si476x-objs := si476x.o snd-soc-sirf-audio-codec-objs := sirf-audio-codec.o snd-soc-spdif-tx-objs := spdif_transmitter.o snd-soc-spdif-rx-objs := spdif_receiver.o +snd-soc-ssm2305-objs := ssm2305.o snd-soc-ssm2518-objs := ssm2518.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-ssm2602-spi-objs := ssm2602-spi.o @@ -180,6 +184,7 @@ snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-tscs42xx-objs := tscs42xx.o +snd-soc-tscs454-objs := tscs454.o snd-soc-ts3a227e-objs := ts3a227e.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o @@ -355,6 +360,7 @@ obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o +obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o @@ -379,6 +385,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o +obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o @@ -394,6 +401,7 @@ obj-$(CONFIG_SND_SOC_RT5659) += snd-soc-rt5659.o obj-$(CONFIG_SND_SOC_RT5660) += snd-soc-rt5660.o obj-$(CONFIG_SND_SOC_RT5663) += snd-soc-rt5663.o obj-$(CONFIG_SND_SOC_RT5665) += snd-soc-rt5665.o +obj-$(CONFIG_SND_SOC_RT5668) += snd-soc-rt5668.o obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o @@ -404,6 +412,7 @@ obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o obj-$(CONFIG_SND_SOC_SIRF_AUDIO_CODEC) += sirf-audio-codec.o +obj-$(CONFIG_SND_SOC_SSM2305) += snd-soc-ssm2305.o obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o @@ -432,6 +441,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC32X4_SPI) += snd-soc-tlv320aic32x4-spi.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o obj-$(CONFIG_SND_SOC_TSCS42XX) += snd-soc-tscs42xx.o +obj-$(CONFIG_SND_SOC_TSCS454) += snd-soc-tscs454.o 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 diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 12bf24c26818..ae41edd1c406 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -843,6 +843,15 @@ int adau17x1_setup_firmware(struct snd_soc_component *component, struct adau *adau = snd_soc_component_get_drvdata(component); struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + /* Check if sample rate is the same as before. If it is there is no + * point in performing the below steps as the call to + * sigmadsp_setup(...) will return directly when it finds the sample + * rate to be the same as before. By checking this we can prevent an + * audiable popping noise which occours when toggling DSP_RUN. + */ + if (adau->sigmadsp->current_samplerate == rate) + return 0; + snd_soc_dapm_mutex_lock(dapm); ret = regmap_read(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, &dspsr); diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index a4a2cb171bdf..bd6226bde45f 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1105,6 +1105,7 @@ static struct regmap_config cs35l35_regmap = { .readable_reg = cs35l35_readable_register, .precious_reg = cs35l35_precious_register, .cache_type = REGCACHE_RBTREE, + .use_single_rw = true, }; static irqreturn_t cs35l35_irq(int irq, void *data) diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index feca0a672976..80dc42197154 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -1733,10 +1733,10 @@ static ssize_t cs43130_show_ac_r(struct device *dev, return cs43130_show_ac(dev, buf, HP_RIGHT); } -static DEVICE_ATTR(hpload_dc_l, S_IRUGO, cs43130_show_dc_l, NULL); -static DEVICE_ATTR(hpload_dc_r, S_IRUGO, cs43130_show_dc_r, NULL); -static DEVICE_ATTR(hpload_ac_l, S_IRUGO, cs43130_show_ac_l, NULL); -static DEVICE_ATTR(hpload_ac_r, S_IRUGO, cs43130_show_ac_r, NULL); +static DEVICE_ATTR(hpload_dc_l, 0444, cs43130_show_dc_l, NULL); +static DEVICE_ATTR(hpload_dc_r, 0444, cs43130_show_dc_r, NULL); +static DEVICE_ATTR(hpload_ac_l, 0444, cs43130_show_ac_l, NULL); +static DEVICE_ATTR(hpload_ac_r, 0444, cs43130_show_ac_r, NULL); static struct reg_sequence hp_en_cal_seq[] = { {CS43130_INT_MASK_4, CS43130_INT_MASK_ALL}, diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 865f64c40b79..fb515aaa54fc 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1382,15 +1382,12 @@ static const char *eq_mode_name[] = {"EQ1 Mode", "EQ2 Mode"}; static int max98088_get_channel(struct snd_soc_component *component, const char *name) { - int i; + int ret; - for (i = 0; i < ARRAY_SIZE(eq_mode_name); i++) - if (strcmp(name, eq_mode_name[i]) == 0) - return i; - - /* Shouldn't happen */ - dev_err(component->dev, "Bad EQ channel name '%s'\n", name); - return -EINVAL; + ret = match_string(eq_mode_name, ARRAY_SIZE(eq_mode_name), name); + if (ret < 0) + dev_err(component->dev, "Bad EQ channel name '%s'\n", name); + return ret; } static void max98088_setup_eq1(struct snd_soc_component *component) diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 6bf2d0ba864f..3b3a10da7f40 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1634,15 +1634,12 @@ static const char *bq_mode_name[] = {"Biquad1 Mode", "Biquad2 Mode"}; static int max98095_get_bq_channel(struct snd_soc_component *component, const char *name) { - int i; - - for (i = 0; i < ARRAY_SIZE(bq_mode_name); i++) - if (strcmp(name, bq_mode_name[i]) == 0) - return i; + int ret; - /* Shouldn't happen */ - dev_err(component->dev, "Bad biquad channel name '%s'\n", name); - return -EINVAL; + ret = match_string(bq_mode_name, ARRAY_SIZE(bq_mode_name), name); + if (ret < 0) + dev_err(component->dev, "Bad biquad channel name '%s'\n", name); + return ret; } static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 5bbf889ad98e..de3d44e9199b 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -1,23 +1,14 @@ -/* - * Driver for the MAX9860 Mono Audio Voice Codec - * - * https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf - * - * The driver does not support sidetone since the DVST register field is - * backwards with the mute near the maximum level instead of the minimum. - * - * Author: Peter Rosin <peda@axentia.s> - * Copyright 2016 Axentia Technologies - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for the MAX9860 Mono Audio Voice Codec +// +// https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf +// +// The driver does not support sidetone since the DVST register field is +// backwards with the mute near the maximum level instead of the minimum. +// +// Author: Peter Rosin <peda@axentia.s> +// Copyright 2016 Axentia Technologies #include <linux/init.h> #include <linux/module.h> @@ -443,7 +434,8 @@ static int max9860_hw_params(struct snd_pcm_substream *substream, ret = regmap_update_bits(max9860->regmap, MAX9860_AUDIOCLKHIGH, MAX9860_PLL, MAX9860_PLL); if (ret) { - dev_err(component->dev, "Failed to enable PLL: %d\n", ret); + dev_err(component->dev, "Failed to enable PLL: %d\n", + ret); return ret; } } @@ -515,7 +507,8 @@ static int max9860_set_bias_level(struct snd_soc_component *component, ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN, MAX9860_SHDN, MAX9860_SHDN); if (ret) { - dev_err(component->dev, "Failed to remove SHDN: %d\n", ret); + dev_err(component->dev, "Failed to remove SHDN: %d\n", + ret); return ret; } break; @@ -598,8 +591,7 @@ static const struct dev_pm_ops max9860_pm_ops = { SET_RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL) }; -static int max9860_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int max9860_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct max9860_priv *max9860; @@ -698,7 +690,7 @@ static int max9860_probe(struct i2c_client *i2c, pm_runtime_idle(dev); ret = devm_snd_soc_register_component(dev, &max9860_component_driver, - &max9860_dai, 1); + &max9860_dai, 1); if (ret) { dev_err(dev, "Failed to register CODEC: %d\n", ret); goto err_pm; @@ -736,7 +728,7 @@ static const struct of_device_id max9860_of_match[] = { MODULE_DEVICE_TABLE(of, max9860_of_match); static struct i2c_driver max9860_i2c_driver = { - .probe = max9860_probe, + .probe_new = max9860_probe, .remove = max9860_remove, .id_table = max9860_i2c_id, .driver = { diff --git a/sound/soc/codecs/max9860.h b/sound/soc/codecs/max9860.h index 22041bd67a7d..e07b905eaf50 100644 --- a/sound/soc/codecs/max9860.h +++ b/sound/soc/codecs/max9860.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Driver for the MAX9860 Mono Audio Voice Codec * * Author: Peter Rosin <peda@axentia.s> * Copyright 2016 Axentia Technologies - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. */ #ifndef _SND_SOC_MAX9860 diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c new file mode 100644 index 000000000000..f73dcd753584 --- /dev/null +++ b/sound/soc/codecs/mt6351.c @@ -0,0 +1,1505 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt6351.c -- mt6351 ALSA SoC audio codec driver +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/delay.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "mt6351.h" + +/* MT6351_TOP_CLKSQ */ +#define RG_CLKSQ_EN_AUD_BIT (0) + +/* MT6351_TOP_CKPDN_CON0 */ +#define RG_AUDNCP_CK_PDN_BIT (12) +#define RG_AUDIF_CK_PDN_BIT (13) +#define RG_AUD_CK_PDN_BIT (14) +#define RG_ZCD13M_CK_PDN_BIT (15) + +/* MT6351_AUDDEC_ANA_CON0 */ +#define RG_AUDDACLPWRUP_VAUDP32_BIT (0) +#define RG_AUDDACRPWRUP_VAUDP32_BIT (1) +#define RG_AUD_DAC_PWR_UP_VA32_BIT (2) +#define RG_AUD_DAC_PWL_UP_VA32_BIT (3) + +#define RG_AUDHSPWRUP_VAUDP32_BIT (4) + +#define RG_AUDHPLPWRUP_VAUDP32_BIT (5) +#define RG_AUDHPRPWRUP_VAUDP32_BIT (6) + +#define RG_AUDHSMUXINPUTSEL_VAUDP32_SFT (7) +#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK (0x3) + +#define RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT (9) +#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK (0x3) + +#define RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT (11) +#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK (0x3) + +#define RG_AUDHSSCDISABLE_VAUDP32 (13) +#define RG_AUDHPLSCDISABLE_VAUDP32_BIT (14) +#define RG_AUDHPRSCDISABLE_VAUDP32_BIT (15) + +/* MT6351_AUDDEC_ANA_CON1 */ +#define RG_HSOUTPUTSTBENH_VAUDP32_BIT (8) + +/* MT6351_AUDDEC_ANA_CON3 */ +#define RG_AUDLOLPWRUP_VAUDP32_BIT (2) + +#define RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT (3) +#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK (0x3) + +#define RG_AUDLOLSCDISABLE_VAUDP32_BIT (5) +#define RG_LOOUTPUTSTBENH_VAUDP32_BIT (9) + +/* MT6351_AUDDEC_ANA_CON6 */ +#define RG_ABIDEC_RSVD0_VAUDP32_HPL_BIT (8) +#define RG_ABIDEC_RSVD0_VAUDP32_HPR_BIT (9) +#define RG_ABIDEC_RSVD0_VAUDP32_HS_BIT (10) +#define RG_ABIDEC_RSVD0_VAUDP32_LOL_BIT (11) + +/* MT6351_AUDDEC_ANA_CON9 */ +#define RG_AUDIBIASPWRDN_VAUDP32_BIT (8) +#define RG_RSTB_DECODER_VA32_BIT (9) +#define RG_AUDGLB_PWRDN_VA32_BIT (12) + +#define RG_LCLDO_DEC_EN_VA32_BIT (13) +#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_BIT (15) +/* MT6351_AUDDEC_ANA_CON10 */ +#define RG_NVREG_EN_VAUDP32_BIT (8) + +#define RG_AUDGLB_LP2_VOW_EN_VA32 10 + +/* MT6351_AFE_UL_DL_CON0 */ +#define RG_AFE_ON_BIT (0) + +/* MT6351_AFE_DL_SRC2_CON0_L */ +#define RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT (0) + +/* MT6351_AFE_UL_SRC_CON0_L */ +#define UL_SRC_ON_TMP_CTL (0) + +/* MT6351_AFE_TOP_CON0 */ +#define RG_DL_SINE_ON_SFT (0) +#define RG_DL_SINE_ON_MASK (0x1) + +#define RG_UL_SINE_ON_SFT (1) +#define RG_UL_SINE_ON_MASK (0x1) + +/* MT6351_AUDIO_TOP_CON0 */ +#define AUD_TOP_PDN_RESERVED_BIT 0 +#define AUD_TOP_PWR_CLK_DIS_CTL_BIT 2 +#define AUD_TOP_PDN_ADC_CTL_BIT 5 +#define AUD_TOP_PDN_DAC_CTL_BIT 6 +#define AUD_TOP_PDN_AFE_CTL_BIT 7 + +/* MT6351_AFE_SGEN_CFG0 */ +#define SGEN_C_MUTE_SW_CTL_BIT 6 +#define SGEN_C_DAC_EN_CTL_BIT 7 + +/* MT6351_AFE_NCP_CFG0 */ +#define RG_NCP_ON_BIT 0 + +/* MT6351_LDO_VUSB33_CON0 */ +#define RG_VUSB33_EN 1 +#define RG_VUSB33_ON_CTRL 3 + +/* MT6351_LDO_VA18_CON0 */ +#define RG_VA18_EN 1 +#define RG_VA18_ON_CTRL 3 + +/* MT6351_AUDENC_ANA_CON0 */ +#define RG_AUDPREAMPLON 0 +#define RG_AUDPREAMPLDCCEN 1 +#define RG_AUDPREAMPLDCPRECHARGE 2 + +#define RG_AUDPREAMPLINPUTSEL_SFT (4) +#define RG_AUDPREAMPLINPUTSEL_MASK (0x3) + +#define RG_AUDADCLPWRUP 12 + +#define RG_AUDADCLINPUTSEL_SFT (13) +#define RG_AUDADCLINPUTSEL_MASK (0x3) + +/* MT6351_AUDENC_ANA_CON1 */ +#define RG_AUDPREAMPRON 0 +#define RG_AUDPREAMPRDCCEN 1 +#define RG_AUDPREAMPRDCPRECHARGE 2 + +#define RG_AUDPREAMPRINPUTSEL_SFT (4) +#define RG_AUDPREAMPRINPUTSEL_MASK (0x3) + +#define RG_AUDADCRPWRUP 12 + +#define RG_AUDADCRINPUTSEL_SFT (13) +#define RG_AUDADCRINPUTSEL_MASK (0x3) + +/* MT6351_AUDENC_ANA_CON3 */ +#define RG_AUDADCCLKRSTB 6 + +/* MT6351_AUDENC_ANA_CON9 */ +#define RG_AUDPWDBMICBIAS0 0 +#define RG_AUDMICBIAS0VREF 4 +#define RG_AUDMICBIAS0LOWPEN 7 + +#define RG_AUDPWDBMICBIAS2 8 +#define RG_AUDMICBIAS2VREF 12 +#define RG_AUDMICBIAS2LOWPEN 15 + +/* MT6351_AUDENC_ANA_CON10 */ +#define RG_AUDPWDBMICBIAS1 0 +#define RG_AUDMICBIAS1DCSW1NEN 2 +#define RG_AUDMICBIAS1VREF 4 +#define RG_AUDMICBIAS1LOWPEN 7 + +enum { + AUDIO_ANALOG_VOLUME_HSOUTL, + AUDIO_ANALOG_VOLUME_HSOUTR, + AUDIO_ANALOG_VOLUME_HPOUTL, + AUDIO_ANALOG_VOLUME_HPOUTR, + AUDIO_ANALOG_VOLUME_LINEOUTL, + AUDIO_ANALOG_VOLUME_LINEOUTR, + AUDIO_ANALOG_VOLUME_MICAMP1, + AUDIO_ANALOG_VOLUME_MICAMP2, + AUDIO_ANALOG_VOLUME_TYPE_MAX +}; + +/* Supply subseq */ +enum { + SUPPLY_SUBSEQ_SETTING, + SUPPLY_SUBSEQ_ENABLE, + SUPPLY_SUBSEQ_MICBIAS, +}; + +#define REG_STRIDE 2 + +struct mt6351_priv { + struct device *dev; + struct regmap *regmap; + + unsigned int dl_rate; + unsigned int ul_rate; + + int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX]; + + int hp_en_counter; +}; + +static void set_hp_gain_zero(struct snd_soc_component *cmpnt) +{ + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON2, + 0x1f << 7, 0x8 << 7); + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON2, + 0x1f << 0, 0x8 << 0); +} + +static unsigned int get_cap_reg_val(struct snd_soc_component *cmpnt, + unsigned int rate) +{ + switch (rate) { + case 8000: + return 0; + case 16000: + return 1; + case 32000: + return 2; + case 48000: + return 3; + case 96000: + return 4; + case 192000: + return 5; + default: + dev_warn(cmpnt->dev, "%s(), error rate %d, return 3", + __func__, rate); + return 3; + } +} + +static unsigned int get_play_reg_val(struct snd_soc_component *cmpnt, + unsigned int rate) +{ + switch (rate) { + case 8000: + return 0; + case 11025: + return 1; + case 12000: + return 2; + case 16000: + return 3; + case 22050: + return 4; + case 24000: + return 5; + case 32000: + return 6; + case 44100: + return 7; + case 48000: + case 96000: + case 192000: + return 8; + default: + dev_warn(cmpnt->dev, "%s(), error rate %d, return 8", + __func__, rate); + return 8; + } +} + +static int mt6351_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *cmpnt = dai->component; + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int rate = params_rate(params); + + dev_dbg(priv->dev, "%s(), substream->stream %d, rate %d\n", + __func__, substream->stream, rate); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + priv->dl_rate = rate; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + priv->ul_rate = rate; + + return 0; +} + +static const struct snd_soc_dai_ops mt6351_codec_dai_ops = { + .hw_params = mt6351_codec_dai_hw_params, +}; + +#define MT6351_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ + SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\ + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE) + +static struct snd_soc_dai_driver mt6351_dai_driver[] = { + { + .name = "mt6351-snd-codec-aif1", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6351_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6351_FORMATS, + }, + .ops = &mt6351_codec_dai_ops, + }, +}; + +enum { + HP_GAIN_SET_ZERO, + HP_GAIN_RESTORE, +}; + +static void hp_gain_ramp_set(struct snd_soc_component *cmpnt, int hp_gain_ctl) +{ + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int idx, old_idx, offset, reg_idx; + + if (hp_gain_ctl == HP_GAIN_SET_ZERO) { + idx = 8; /* 0dB */ + old_idx = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + } else { + idx = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + old_idx = 8; /* 0dB */ + } + dev_dbg(priv->dev, "%s(), idx %d, old_idx %d\n", + __func__, idx, old_idx); + + if (idx > old_idx) + offset = idx - old_idx; + else + offset = old_idx - idx; + + reg_idx = old_idx; + + while (offset > 0) { + reg_idx = idx > old_idx ? reg_idx + 1 : reg_idx - 1; + + /* check valid range, and set value */ + if ((reg_idx >= 0 && reg_idx <= 0x12) || reg_idx == 0x1f) { + regmap_update_bits(cmpnt->regmap, + MT6351_ZCD_CON2, + 0xf9f, + (reg_idx << 7) | reg_idx); + usleep_range(100, 120); + } + offset--; + } +} + +static void hp_zcd_enable(struct snd_soc_component *cmpnt) +{ + /* Enable ZCD, for minimize pop noise */ + /* when adjust gain during HP buffer on */ + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x7 << 8, 0x1 << 8); + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 7, 0x0 << 7); + + /* timeout, 1=5ms, 0=30ms */ + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 6, 0x1 << 6); + + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x3 << 4, 0x0 << 4); + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x7 << 1, 0x5 << 1); + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 0, 0x1 << 0); +} + +static void hp_zcd_disable(struct snd_soc_component *cmpnt) +{ + regmap_write(cmpnt->regmap, MT6351_ZCD_CON0, 0x0000); +} + +static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); +static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new mt6351_snd_controls[] = { + /* dl pga gain */ + SOC_DOUBLE_TLV("Headphone Volume", + MT6351_ZCD_CON2, 0, 7, 0x12, 1, + playback_tlv), + SOC_DOUBLE_TLV("Lineout Volume", + MT6351_ZCD_CON1, 0, 7, 0x12, 1, + playback_tlv), + SOC_SINGLE_TLV("Handset Volume", + MT6351_ZCD_CON3, 0, 0x12, 1, + playback_tlv), + /* ul pga gain */ + SOC_DOUBLE_R_TLV("PGA Volume", + MT6351_AUDENC_ANA_CON0, MT6351_AUDENC_ANA_CON1, + 8, 4, 0, + pga_tlv), +}; + +/* MUX */ + +/* LOL MUX */ +static const char *const lo_in_mux_map[] = { + "Open", "Mute", "Playback", "Test Mode", +}; + +static int lo_in_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(lo_in_mux_map_enum, + MT6351_AUDDEC_ANA_CON3, + RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT, + RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK, + lo_in_mux_map, + lo_in_mux_map_value); + +static const struct snd_kcontrol_new lo_in_mux_control = + SOC_DAPM_ENUM("In Select", lo_in_mux_map_enum); + +/*HP MUX */ +static const char *const hp_in_mux_map[] = { + "Open", "LoudSPK Playback", "Audio Playback", "Test Mode", +}; + +static int hp_in_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(hpl_in_mux_map_enum, + MT6351_AUDDEC_ANA_CON0, + RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT, + RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK, + hp_in_mux_map, + hp_in_mux_map_value); + +static const struct snd_kcontrol_new hpl_in_mux_control = + SOC_DAPM_ENUM("HPL Select", hpl_in_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hpr_in_mux_map_enum, + MT6351_AUDDEC_ANA_CON0, + RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT, + RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK, + hp_in_mux_map, + hp_in_mux_map_value); + +static const struct snd_kcontrol_new hpr_in_mux_control = + SOC_DAPM_ENUM("HPR Select", hpr_in_mux_map_enum); + +/* RCV MUX */ +static const char *const rcv_in_mux_map[] = { + "Open", "Mute", "Voice Playback", "Test Mode", +}; + +static int rcv_in_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rcv_in_mux_map_enum, + MT6351_AUDDEC_ANA_CON0, + RG_AUDHSMUXINPUTSEL_VAUDP32_SFT, + RG_AUDHSMUXINPUTSEL_VAUDP32_MASK, + rcv_in_mux_map, + rcv_in_mux_map_value); + +static const struct snd_kcontrol_new rcv_in_mux_control = + SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum); + +/* DAC In MUX */ +static const char *const dac_in_mux_map[] = { + "Normal Path", "Sgen", +}; + +static int dac_in_mux_map_value[] = { + 0x0, 0x1, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum, + MT6351_AFE_TOP_CON0, + RG_DL_SINE_ON_SFT, + RG_DL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new dac_in_mux_control = + SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum); + +/* AIF Out MUX */ +static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum, + MT6351_AFE_TOP_CON0, + RG_UL_SINE_ON_SFT, + RG_UL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new aif_out_mux_control = + SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum); + +/* ADC L MUX */ +static const char *const adc_left_mux_map[] = { + "Idle", "AIN0", "Left Preamplifier", "Idle_1", +}; + +static int adc_left_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum, + MT6351_AUDENC_ANA_CON0, + RG_AUDADCLINPUTSEL_SFT, + RG_AUDADCLINPUTSEL_MASK, + adc_left_mux_map, + adc_left_mux_map_value); + +static const struct snd_kcontrol_new adc_left_mux_control = + SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum); + +/* ADC R MUX */ +static const char *const adc_right_mux_map[] = { + "Idle", "AIN0", "Right Preamplifier", "Idle_1", +}; + +static int adc_right_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum, + MT6351_AUDENC_ANA_CON1, + RG_AUDADCRINPUTSEL_SFT, + RG_AUDADCRINPUTSEL_MASK, + adc_right_mux_map, + adc_right_mux_map_value); + +static const struct snd_kcontrol_new adc_right_mux_control = + SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum); + +/* PGA L MUX */ +static const char *const pga_left_mux_map[] = { + "None", "AIN0", "AIN1", "AIN2", +}; + +static int pga_left_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum, + MT6351_AUDENC_ANA_CON0, + RG_AUDPREAMPLINPUTSEL_SFT, + RG_AUDPREAMPLINPUTSEL_MASK, + pga_left_mux_map, + pga_left_mux_map_value); + +static const struct snd_kcontrol_new pga_left_mux_control = + SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum); + +/* PGA R MUX */ +static const char *const pga_right_mux_map[] = { + "None", "AIN0", "AIN3", "AIN2", +}; + +static int pga_right_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum, + MT6351_AUDENC_ANA_CON1, + RG_AUDPREAMPRINPUTSEL_SFT, + RG_AUDPREAMPRINPUTSEL_MASK, + pga_right_mux_map, + pga_right_mux_map_value); + +static const struct snd_kcontrol_new pga_right_mux_control = + SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum); + +static int mt_reg_set_clr_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->on_val) { + /* SET REG */ + regmap_update_bits(cmpnt->regmap, + w->reg + REG_STRIDE, + 0x1 << w->shift, + 0x1 << w->shift); + } else { + /* CLR REG */ + regmap_update_bits(cmpnt->regmap, + w->reg + REG_STRIDE * 2, + 0x1 << w->shift, + 0x1 << w->shift); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (w->off_val) { + /* SET REG */ + regmap_update_bits(cmpnt->regmap, + w->reg + REG_STRIDE, + 0x1 << w->shift, + 0x1 << w->shift); + } else { + /* CLR REG */ + regmap_update_bits(cmpnt->regmap, + w->reg + REG_STRIDE * 2, + 0x1 << w->shift, + 0x1 << w->shift); + } + break; + default: + break; + } + + return 0; +} + +static int mt_ncp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(cmpnt->regmap, MT6351_AFE_NCP_CFG1, + 0xffff, 0x1515); + /* NCP: ck1 and ck2 clock frequecy adjust configure */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_NCP_CFG0, + 0xfffe, 0x8C00); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(250, 270); + break; + default: + break; + } + + return 0; +} + +static int mt_sgen_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(cmpnt->regmap, MT6351_AFE_SGEN_CFG0, + 0xffef, 0x0008); + regmap_update_bits(cmpnt->regmap, MT6351_AFE_SGEN_CFG1, + 0xffff, 0x0101); + break; + default: + break; + } + + return 0; +} + +static int mt_aif_in_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n", + __func__, event, priv->dl_rate); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* sdm audio fifo clock power on */ + regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2, + 0xffff, 0x0006); + /* scrambler clock on enable */ + regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON0, + 0xffff, 0xC3A1); + /* sdm power on */ + regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2, + 0xffff, 0x0003); + /* sdm fifo enable */ + regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2, + 0xffff, 0x000B); + /* set attenuation gain */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_DL_SDM_CON1, + 0xffff, 0x001E); + + regmap_write(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG0, + (get_play_reg_val(cmpnt, priv->dl_rate) << 12) | + 0x330); + regmap_write(cmpnt->regmap, MT6351_AFE_DL_SRC2_CON0_H, + (get_play_reg_val(cmpnt, priv->dl_rate) << 12) | + 0x300); + + regmap_update_bits(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG2, + 0x8000, 0x8000); + break; + default: + break; + } + + return 0; +} + +static int mt_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int reg; + + dev_dbg(priv->dev, "%s(), event 0x%x, hp_en_counter %d\n", + __func__, event, priv->hp_en_counter); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + priv->hp_en_counter++; + if (priv->hp_en_counter > 1) + break; /* already enabled, do nothing */ + else if (priv->hp_en_counter <= 0) + dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n", + __func__, + priv->hp_en_counter); + + hp_zcd_disable(cmpnt); + + /* from yoyo HQA script */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON6, + 0x0700, 0x0700); + + /* save target gain to restore after hardware open complete */ + regmap_read(cmpnt->regmap, MT6351_ZCD_CON2, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] = reg & 0x1f; + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = (reg >> 7) & 0x1f; + + /* Set HPR/HPL gain as minimum (~ -40dB) */ + regmap_update_bits(cmpnt->regmap, + MT6351_ZCD_CON2, 0xffff, 0x0F9F); + /* Set HS gain as minimum (~ -40dB) */ + regmap_update_bits(cmpnt->regmap, + MT6351_ZCD_CON3, 0xffff, 0x001F); + /* De_OSC of HP */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON2, + 0x0001, 0x0001); + /* enable output STBENH */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1, + 0xffff, 0x2000); + /* De_OSC of voice, enable output STBENH */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1, + 0xffff, 0x2100); + /* Enable voice driver */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0, + 0x0010, 0xE090); + /* Enable pre-charge buffer */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1, + 0xffff, 0x2140); + + usleep_range(50, 60); + + /* Apply digital DC compensation value to DAC */ + set_hp_gain_zero(cmpnt); + + /* Enable HPR/HPL */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1, + 0xffff, 0x2100); + /* Disable pre-charge buffer */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1, + 0xffff, 0x2000); + /* Disable De_OSC of voice */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0, + 0x0010, 0xF4EF); + /* Disable voice buffer */ + + /* from yoyo HQ */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON6, + 0x0700, 0x0300); + + /* Enable ZCD, for minimize pop noise */ + /* when adjust gain during HP buffer on */ + hp_zcd_enable(cmpnt); + + /* apply volume setting */ + hp_gain_ramp_set(cmpnt, HP_GAIN_RESTORE); + + break; + case SND_SOC_DAPM_PRE_PMD: + priv->hp_en_counter--; + if (priv->hp_en_counter > 0) + break; /* still being used, don't close */ + else if (priv->hp_en_counter < 0) + dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n", + __func__, + priv->hp_en_counter); + + /* Disable AUD_ZCD */ + hp_zcd_disable(cmpnt); + + /* Set HPR/HPL gain as -1dB, step by step */ + hp_gain_ramp_set(cmpnt, HP_GAIN_SET_ZERO); + + set_hp_gain_zero(cmpnt); + break; + case SND_SOC_DAPM_POST_PMD: + if (priv->hp_en_counter > 0) + break; /* still being used, don't close */ + else if (priv->hp_en_counter < 0) + dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n", + __func__, + priv->hp_en_counter); + + /* reset*/ + regmap_update_bits(cmpnt->regmap, + MT6351_AUDDEC_ANA_CON6, + 0x0700, + 0x0000); + /* De_OSC of HP */ + regmap_update_bits(cmpnt->regmap, + MT6351_AUDDEC_ANA_CON2, + 0x0001, + 0x0000); + + /* apply volume setting */ + hp_gain_ramp_set(cmpnt, HP_GAIN_RESTORE); + break; + default: + break; + } + + return 0; +} + +static int mt_aif_out_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n", + __func__, event, priv->ul_rate); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* dcclk_div=11'b00100000011, dcclk_ref_ck_sel=2'b00 */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0, + 0xffff, 0x2062); + /* dcclk_pdn=1'b0 */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0, + 0xffff, 0x2060); + /* dcclk_gen_on=1'b1 */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0, + 0xffff, 0x2061); + + /* UL sample rate and mode configure */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_UL_SRC_CON0_H, + 0x000E, + get_cap_reg_val(cmpnt, priv->ul_rate) << 1); + + /* fixed 260k path for 8/16/32/48 */ + if (priv->ul_rate <= 48000) { + /* anc ul path src on */ + regmap_update_bits(cmpnt->regmap, + MT6351_AFE_HPANC_CFG0, + 0x1 << 1, + 0x1 << 1); + /* ANC clk pdn release */ + regmap_update_bits(cmpnt->regmap, + MT6351_AFE_HPANC_CFG0, + 0x1 << 0, + 0x0 << 0); + } + break; + case SND_SOC_DAPM_PRE_PMD: + /* fixed 260k path for 8/16/32/48 */ + if (priv->ul_rate <= 48000) { + /* anc ul path src on */ + regmap_update_bits(cmpnt->regmap, + MT6351_AFE_HPANC_CFG0, + 0x1 << 1, + 0x0 << 1); + /* ANC clk pdn release */ + regmap_update_bits(cmpnt->regmap, + MT6351_AFE_HPANC_CFG0, + 0x1 << 0, + 0x1 << 0); + } + break; + default: + break; + } + + return 0; +} + +static int mt_adc_clkgen_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Audio ADC clock gen. mode: 00_divided by 2 (Normal) */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON3, + 0x3 << 4, 0x0); + break; + case SND_SOC_DAPM_POST_PMU: + /* ADC CLK from: 00_13MHz from CLKSQ (Default) */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON3, + 0x3 << 2, 0x0); + break; + default: + break; + } + return 0; +} + +static int mt_pga_left_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Audio L PGA precharge on */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0, + 0x3 << RG_AUDPREAMPLDCPRECHARGE, + 0x1 << RG_AUDPREAMPLDCPRECHARGE); + /* Audio L PGA mode: 1_DCC */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0, + 0x3 << RG_AUDPREAMPLDCCEN, + 0x1 << RG_AUDPREAMPLDCCEN); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(100, 120); + /* Audio L PGA precharge off */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0, + 0x3 << RG_AUDPREAMPLDCPRECHARGE, + 0x0 << RG_AUDPREAMPLDCPRECHARGE); + break; + default: + break; + } + return 0; +} + +static int mt_pga_right_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Audio R PGA precharge on */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1, + 0x3 << RG_AUDPREAMPRDCPRECHARGE, + 0x1 << RG_AUDPREAMPRDCPRECHARGE); + /* Audio R PGA mode: 1_DCC */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1, + 0x3 << RG_AUDPREAMPRDCCEN, + 0x1 << RG_AUDPREAMPRDCCEN); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(100, 120); + /* Audio R PGA precharge off */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1, + 0x3 << RG_AUDPREAMPRDCPRECHARGE, + 0x0 << RG_AUDPREAMPRDCPRECHARGE); + break; + default: + break; + } + return 0; +} + +static int mt_mic_bias_0_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* MIC Bias 0 LowPower: 0_Normal */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x3 << RG_AUDMICBIAS0LOWPEN, 0x0); + /* MISBIAS0 = 1P9V */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x7 << RG_AUDMICBIAS0VREF, + 0x2 << RG_AUDMICBIAS0VREF); + break; + case SND_SOC_DAPM_POST_PMD: + /* MISBIAS0 = 1P97 */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x7 << RG_AUDMICBIAS0VREF, + 0x0 << RG_AUDMICBIAS0VREF); + break; + default: + break; + } + return 0; +} + +static int mt_mic_bias_1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* MIC Bias 1 LowPower: 0_Normal */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10, + 0x3 << RG_AUDMICBIAS1LOWPEN, 0x0); + /* MISBIAS1 = 2P7V */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10, + 0x7 << RG_AUDMICBIAS1VREF, + 0x7 << RG_AUDMICBIAS1VREF); + break; + case SND_SOC_DAPM_POST_PMD: + /* MISBIAS1 = 1P7V */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10, + 0x7 << RG_AUDMICBIAS1VREF, + 0x0 << RG_AUDMICBIAS1VREF); + break; + default: + break; + } + return 0; +} + +static int mt_mic_bias_2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* MIC Bias 2 LowPower: 0_Normal */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x3 << RG_AUDMICBIAS2LOWPEN, 0x0); + /* MISBIAS2 = 1P9V */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x7 << RG_AUDMICBIAS2VREF, + 0x2 << RG_AUDMICBIAS2VREF); + break; + case SND_SOC_DAPM_POST_PMD: + /* MISBIAS2 = 1P97 */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x7 << RG_AUDMICBIAS2VREF, + 0x0 << RG_AUDMICBIAS2VREF); + break; + default: + break; + } + return 0; +} + +/* DAPM Kcontrols */ +static const struct snd_kcontrol_new mt_lineout_control = + SOC_DAPM_SINGLE("Switch", MT6351_AUDDEC_ANA_CON3, + RG_AUDLOLPWRUP_VAUDP32_BIT, 1, 0); + +/* DAPM Widgets */ +static const struct snd_soc_dapm_widget mt6351_dapm_widgets[] = { + /* Digital Clock */ + SND_SOC_DAPM_SUPPLY("AUDIO_TOP_AFE_CTL", MT6351_AUDIO_TOP_CON0, + AUD_TOP_PDN_AFE_CTL_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("AUDIO_TOP_DAC_CTL", MT6351_AUDIO_TOP_CON0, + AUD_TOP_PDN_DAC_CTL_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("AUDIO_TOP_ADC_CTL", MT6351_AUDIO_TOP_CON0, + AUD_TOP_PDN_ADC_CTL_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("AUDIO_TOP_PWR_CLK", MT6351_AUDIO_TOP_CON0, + AUD_TOP_PWR_CLK_DIS_CTL_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("AUDIO_TOP_PDN_RESERVED", MT6351_AUDIO_TOP_CON0, + AUD_TOP_PDN_RESERVED_BIT, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("NCP", MT6351_AFE_NCP_CFG0, + RG_NCP_ON_BIT, 0, + mt_ncp_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* Global Supply*/ + SND_SOC_DAPM_SUPPLY("AUDGLB", MT6351_AUDDEC_ANA_CON9, + RG_AUDGLB_PWRDN_VA32_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKSQ Audio", MT6351_TOP_CLKSQ, + RG_CLKSQ_EN_AUD_BIT, 0, + mt_reg_set_clr_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("ZCD13M_CK", MT6351_TOP_CKPDN_CON0, + RG_ZCD13M_CK_PDN_BIT, 1, + mt_reg_set_clr_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("AUD_CK", MT6351_TOP_CKPDN_CON0, + RG_AUD_CK_PDN_BIT, 1, + mt_reg_set_clr_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("AUDIF_CK", MT6351_TOP_CKPDN_CON0, + RG_AUDIF_CK_PDN_BIT, 1, + mt_reg_set_clr_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("AUDNCP_CK", MT6351_TOP_CKPDN_CON0, + RG_AUDNCP_CK_PDN_BIT, 1, + mt_reg_set_clr_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("AFE_ON", MT6351_AFE_UL_DL_CON0, RG_AFE_ON_BIT, 0, + NULL, 0), + + /* AIF Rx*/ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "AIF1 Playback", 0, + MT6351_AFE_DL_SRC2_CON0_L, + RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT, 0, + mt_aif_in_event, SND_SOC_DAPM_PRE_PMU), + + /* DL Supply */ + SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("NV Regulator", MT6351_AUDDEC_ANA_CON10, + RG_NVREG_EN_VAUDP32_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AUD_CLK", MT6351_AUDDEC_ANA_CON9, + RG_RSTB_DECODER_VA32_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("IBIST", MT6351_AUDDEC_ANA_CON9, + RG_AUDIBIASPWRDN_VAUDP32_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO", MT6351_AUDDEC_ANA_CON9, + RG_LCLDO_DEC_EN_VA32_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO_REMOTE_SENSE", MT6351_AUDDEC_ANA_CON9, + RG_LCLDO_DEC_REMOTE_SENSE_VA18_BIT, 0, NULL, 0), + + /* DAC */ + SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control), + + SND_SOC_DAPM_DAC("DACL", NULL, MT6351_AUDDEC_ANA_CON0, + RG_AUDDACLPWRUP_VAUDP32_BIT, 0), + SND_SOC_DAPM_SUPPLY("DACL_BIASGEN", MT6351_AUDDEC_ANA_CON0, + RG_AUD_DAC_PWL_UP_VA32_BIT, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DACR", NULL, MT6351_AUDDEC_ANA_CON0, + RG_AUDDACRPWRUP_VAUDP32_BIT, 0), + SND_SOC_DAPM_SUPPLY("DACR_BIASGEN", MT6351_AUDDEC_ANA_CON0, + RG_AUD_DAC_PWR_UP_VA32_BIT, 0, NULL, 0), + /* LOL */ + SND_SOC_DAPM_MUX("LOL Mux", SND_SOC_NOPM, 0, 0, &lo_in_mux_control), + + SND_SOC_DAPM_SUPPLY("LO Stability Enh", MT6351_AUDDEC_ANA_CON3, + RG_LOOUTPUTSTBENH_VAUDP32_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LOL Bias Gen", MT6351_AUDDEC_ANA_CON6, + RG_ABIDEC_RSVD0_VAUDP32_LOL_BIT, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("LOL Buffer", MT6351_AUDDEC_ANA_CON3, + RG_AUDLOLPWRUP_VAUDP32_BIT, 0, NULL, 0), + + /* Headphone */ + SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0, &hpl_in_mux_control), + SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0, &hpr_in_mux_control), + + SND_SOC_DAPM_OUT_DRV_E("HPL Power", MT6351_AUDDEC_ANA_CON0, + RG_AUDHPLPWRUP_VAUDP32_BIT, 0, NULL, 0, + mt_hp_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUT_DRV_E("HPR Power", MT6351_AUDDEC_ANA_CON0, + RG_AUDHPRPWRUP_VAUDP32_BIT, 0, NULL, 0, + mt_hp_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + + /* Receiver */ + SND_SOC_DAPM_MUX("RCV Mux", SND_SOC_NOPM, 0, 0, &rcv_in_mux_control), + + SND_SOC_DAPM_SUPPLY("RCV Stability Enh", MT6351_AUDDEC_ANA_CON1, + RG_HSOUTPUTSTBENH_VAUDP32_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RCV Bias Gen", MT6351_AUDDEC_ANA_CON6, + RG_ABIDEC_RSVD0_VAUDP32_HS_BIT, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("RCV Buffer", MT6351_AUDDEC_ANA_CON0, + RG_AUDHSPWRUP_VAUDP32_BIT, 0, NULL, 0), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("Receiver"), + SND_SOC_DAPM_OUTPUT("Headphone L"), + SND_SOC_DAPM_OUTPUT("Headphone R"), + SND_SOC_DAPM_OUTPUT("LINEOUT L"), + + /* SGEN */ + SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6351_AFE_SGEN_CFG0, + SGEN_C_DAC_EN_CTL_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6351_AFE_SGEN_CFG0, + SGEN_C_MUTE_SW_CTL_BIT, 1, + mt_sgen_event, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6351_AFE_DL_SRC2_CON0_L, + RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("SGEN DL"), + + /* Uplinks */ + SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "AIF1 Capture", 0, + MT6351_AFE_UL_SRC_CON0_L, + UL_SRC_ON_TMP_CTL, 0, + mt_aif_out_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY_S("VUSB33_LDO", SUPPLY_SUBSEQ_ENABLE, + MT6351_LDO_VUSB33_CON0, RG_VUSB33_EN, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("VUSB33_LDO_CTRL", SUPPLY_SUBSEQ_SETTING, + MT6351_LDO_VUSB33_CON0, RG_VUSB33_ON_CTRL, 1, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("VA18_LDO", SUPPLY_SUBSEQ_ENABLE, + MT6351_LDO_VA18_CON0, RG_VA18_EN, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("VA18_LDO_CTRL", SUPPLY_SUBSEQ_SETTING, + MT6351_LDO_VA18_CON0, RG_VA18_ON_CTRL, 1, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("ADC CLKGEN", SUPPLY_SUBSEQ_ENABLE, + MT6351_AUDENC_ANA_CON3, RG_AUDADCCLKRSTB, 0, + mt_adc_clkgen_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* Uplinks MUX */ + SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0, + &aif_out_mux_control), + + SND_SOC_DAPM_MUX("ADC L Mux", SND_SOC_NOPM, 0, 0, + &adc_left_mux_control), + SND_SOC_DAPM_MUX("ADC R Mux", SND_SOC_NOPM, 0, 0, + &adc_right_mux_control), + + SND_SOC_DAPM_ADC("ADC L", NULL, + MT6351_AUDENC_ANA_CON0, RG_AUDADCLPWRUP, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, + MT6351_AUDENC_ANA_CON1, RG_AUDADCRPWRUP, 0), + + SND_SOC_DAPM_MUX("PGA L Mux", SND_SOC_NOPM, 0, 0, + &pga_left_mux_control), + SND_SOC_DAPM_MUX("PGA R Mux", SND_SOC_NOPM, 0, 0, + &pga_right_mux_control), + + SND_SOC_DAPM_PGA_E("PGA L", MT6351_AUDENC_ANA_CON0, RG_AUDPREAMPLON, 0, + NULL, 0, + mt_pga_left_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PGA R", MT6351_AUDENC_ANA_CON1, RG_AUDPREAMPRON, 0, + NULL, 0, + mt_pga_right_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* main mic mic bias */ + SND_SOC_DAPM_SUPPLY_S("Mic Bias 0", SUPPLY_SUBSEQ_MICBIAS, + MT6351_AUDENC_ANA_CON9, RG_AUDPWDBMICBIAS0, 0, + mt_mic_bias_0_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /* ref mic mic bias */ + SND_SOC_DAPM_SUPPLY_S("Mic Bias 2", SUPPLY_SUBSEQ_MICBIAS, + MT6351_AUDENC_ANA_CON9, RG_AUDPWDBMICBIAS2, 0, + mt_mic_bias_2_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /* headset mic1/2 mic bias */ + SND_SOC_DAPM_SUPPLY_S("Mic Bias 1", SUPPLY_SUBSEQ_MICBIAS, + MT6351_AUDENC_ANA_CON10, RG_AUDPWDBMICBIAS1, 0, + mt_mic_bias_1_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("Mic Bias 1 DCC pull high", SUPPLY_SUBSEQ_MICBIAS, + MT6351_AUDENC_ANA_CON10, + RG_AUDMICBIAS1DCSW1NEN, 0, + NULL, 0), + + /* UL input */ + SND_SOC_DAPM_INPUT("AIN0"), + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), +}; + +static const struct snd_soc_dapm_route mt6351_dapm_routes[] = { + /* Capture */ + {"AIF1TX", NULL, "AIF Out Mux"}, + {"AIF1TX", NULL, "VUSB33_LDO"}, + {"VUSB33_LDO", NULL, "VUSB33_LDO_CTRL"}, + {"AIF1TX", NULL, "VA18_LDO"}, + {"VA18_LDO", NULL, "VA18_LDO_CTRL"}, + + {"AIF1TX", NULL, "AUDGLB"}, + {"AIF1TX", NULL, "CLKSQ Audio"}, + + {"AIF1TX", NULL, "AFE_ON"}, + + {"AIF1TX", NULL, "AUDIO_TOP_AFE_CTL"}, + {"AIF1TX", NULL, "AUDIO_TOP_ADC_CTL"}, + {"AIF1TX", NULL, "AUDIO_TOP_PWR_CLK"}, + {"AIF1TX", NULL, "AUDIO_TOP_PDN_RESERVED"}, + + {"AIF Out Mux", "Normal Path", "ADC L"}, + {"AIF Out Mux", "Normal Path", "ADC R"}, + + {"ADC L", NULL, "ADC L Mux"}, + {"ADC L", NULL, "AUD_CK"}, + {"ADC L", NULL, "AUDIF_CK"}, + {"ADC L", NULL, "ADC CLKGEN"}, + {"ADC R", NULL, "ADC R Mux"}, + {"ADC R", NULL, "AUD_CK"}, + {"ADC R", NULL, "AUDIF_CK"}, + {"ADC R", NULL, "ADC CLKGEN"}, + + {"ADC L Mux", "AIN0", "AIN0"}, + {"ADC L Mux", "Left Preamplifier", "PGA L"}, + + {"ADC R Mux", "AIN0", "AIN0"}, + {"ADC R Mux", "Right Preamplifier", "PGA R"}, + + {"PGA L", NULL, "PGA L Mux"}, + {"PGA R", NULL, "PGA R Mux"}, + + {"PGA L Mux", "AIN0", "AIN0"}, + {"PGA L Mux", "AIN1", "AIN1"}, + {"PGA L Mux", "AIN2", "AIN2"}, + + {"PGA R Mux", "AIN0", "AIN0"}, + {"PGA R Mux", "AIN3", "AIN3"}, + {"PGA R Mux", "AIN2", "AIN2"}, + + {"AIN0", NULL, "Mic Bias 0"}, + {"AIN2", NULL, "Mic Bias 2"}, + + {"AIN1", NULL, "Mic Bias 1"}, + {"AIN1", NULL, "Mic Bias 1 DCC pull high"}, + + /* DL Supply */ + {"DL Power Supply", NULL, "AUDGLB"}, + {"DL Power Supply", NULL, "CLKSQ Audio"}, + {"DL Power Supply", NULL, "ZCD13M_CK"}, + {"DL Power Supply", NULL, "AUD_CK"}, + {"DL Power Supply", NULL, "AUDIF_CK"}, + {"DL Power Supply", NULL, "AUDNCP_CK"}, + + {"DL Power Supply", NULL, "NV Regulator"}, + {"DL Power Supply", NULL, "AUD_CLK"}, + {"DL Power Supply", NULL, "IBIST"}, + {"DL Power Supply", NULL, "LDO"}, + {"LDO", NULL, "LDO_REMOTE_SENSE"}, + + /* DL Digital Supply */ + {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_PDN_RESERVED"}, + {"DL Digital Clock", NULL, "NCP"}, + {"DL Digital Clock", NULL, "AFE_ON"}, + + {"AIF_RX", NULL, "DL Digital Clock"}, + + /* DL Path */ + {"DAC In Mux", "Normal Path", "AIF_RX"}, + + {"DAC In Mux", "Sgen", "SGEN DL"}, + {"SGEN DL", NULL, "SGEN DL SRC"}, + {"SGEN DL", NULL, "SGEN MUTE"}, + {"SGEN DL", NULL, "SGEN DL Enable"}, + {"SGEN DL", NULL, "DL Digital Clock"}, + + {"DACL", NULL, "DAC In Mux"}, + {"DACL", NULL, "DL Power Supply"}, + {"DACL", NULL, "DACL_BIASGEN"}, + + {"DACR", NULL, "DAC In Mux"}, + {"DACR", NULL, "DL Power Supply"}, + {"DACR", NULL, "DACR_BIASGEN"}, + + {"LOL Mux", "Playback", "DACL"}, + + {"LOL Buffer", NULL, "LOL Mux"}, + {"LOL Buffer", NULL, "LO Stability Enh"}, + {"LOL Buffer", NULL, "LOL Bias Gen"}, + + {"LINEOUT L", NULL, "LOL Buffer"}, + + /* Headphone Path */ + {"HPL Mux", "Audio Playback", "DACL"}, + {"HPR Mux", "Audio Playback", "DACR"}, + + {"HPL Mux", "LoudSPK Playback", "DACL"}, + {"HPR Mux", "LoudSPK Playback", "DACR"}, + + {"HPL Power", NULL, "HPL Mux"}, + {"HPR Power", NULL, "HPR Mux"}, + + {"Headphone L", NULL, "HPL Power"}, + {"Headphone R", NULL, "HPR Power"}, + + /* Receiver Path */ + {"RCV Mux", "Voice Playback", "DACL"}, + + {"RCV Buffer", NULL, "RCV Mux"}, + {"RCV Buffer", NULL, "RCV Stability Enh"}, + {"RCV Buffer", NULL, "RCV Bias Gen"}, + + {"Receiver", NULL, "RCV Buffer"}, +}; + +static int mt6351_codec_init_reg(struct snd_soc_component *cmpnt) +{ + int ret = 0; + + /* Disable CLKSQ 26MHz */ + regmap_update_bits(cmpnt->regmap, MT6351_TOP_CLKSQ, 0x0001, 0x0); + /* disable AUDGLB */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON9, + 0x1000, 0x1000); + /* Turn off AUDNCP_CLKDIV engine clock,Turn off AUD 26M */ + regmap_update_bits(cmpnt->regmap, MT6351_TOP_CKPDN_CON0_SET, + 0x3800, 0x3800); + /* Disable HeadphoneL/HeadphoneR/voice short circuit protection */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0, + 0xe000, 0xe000); + /* [5] = 1, disable LO buffer left short circuit protection */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON3, + 0x20, 0x20); + /* Reverse the PMIC clock*/ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG2, + 0x8000, 0x8000); + return ret; +} + +static int mt6351_codec_probe(struct snd_soc_component *cmpnt) +{ + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + snd_soc_component_init_regmap(cmpnt, priv->regmap); + + mt6351_codec_init_reg(cmpnt); + return 0; +} + +static const struct snd_soc_component_driver mt6351_soc_component_driver = { + .probe = mt6351_codec_probe, + .controls = mt6351_snd_controls, + .num_controls = ARRAY_SIZE(mt6351_snd_controls), + .dapm_widgets = mt6351_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt6351_dapm_widgets), + .dapm_routes = mt6351_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mt6351_dapm_routes), +}; + +static int mt6351_codec_driver_probe(struct platform_device *pdev) +{ + struct mt6351_priv *priv; + + priv = devm_kzalloc(&pdev->dev, + sizeof(struct mt6351_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, priv); + + priv->dev = &pdev->dev; + + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->regmap) + return -ENODEV; + + dev_dbg(priv->dev, "%s(), dev name %s\n", + __func__, dev_name(&pdev->dev)); + + return devm_snd_soc_register_component(&pdev->dev, + &mt6351_soc_component_driver, + mt6351_dai_driver, + ARRAY_SIZE(mt6351_dai_driver)); +} + +static const struct of_device_id mt6351_of_match[] = { + {.compatible = "mediatek,mt6351-sound",}, + {} +}; + +static struct platform_driver mt6351_codec_driver = { + .driver = { + .name = "mt6351-sound", + .of_match_table = mt6351_of_match, + }, + .probe = mt6351_codec_driver_probe, +}; + +module_platform_driver(mt6351_codec_driver) + +/* Module information */ +MODULE_DESCRIPTION("MT6351 ALSA SoC codec driver"); +MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/mt6351.h b/sound/soc/codecs/mt6351.h new file mode 100644 index 000000000000..04b2ab694ec7 --- /dev/null +++ b/sound/soc/codecs/mt6351.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt6351.h -- mt6351 ALSA SoC audio codec driver + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + */ + +#ifndef __MT6351_H__ +#define __MT6351_H__ + +#define MT6351_AFE_UL_DL_CON0 (0x2000 + 0x0000) +#define MT6351_AFE_DL_SRC2_CON0_H (0x2000 + 0x0002) +#define MT6351_AFE_DL_SRC2_CON0_L (0x2000 + 0x0004) +#define MT6351_AFE_DL_SDM_CON0 (0x2000 + 0x0006) +#define MT6351_AFE_DL_SDM_CON1 (0x2000 + 0x0008) +#define MT6351_AFE_UL_SRC_CON0_H (0x2000 + 0x000a) +#define MT6351_AFE_UL_SRC_CON0_L (0x2000 + 0x000c) +#define MT6351_AFE_UL_SRC_CON1_H (0x2000 + 0x000e) +#define MT6351_AFE_UL_SRC_CON1_L (0x2000 + 0x0010) +#define MT6351_AFE_TOP_CON0 (0x2000 + 0x0012) +#define MT6351_AUDIO_TOP_CON0 (0x2000 + 0x0014) +#define MT6351_AFE_DL_SRC_MON0 (0x2000 + 0x0016) +#define MT6351_AFE_DL_SDM_TEST0 (0x2000 + 0x0018) +#define MT6351_AFE_MON_DEBUG0 (0x2000 + 0x001a) +#define MT6351_AFUNC_AUD_CON0 (0x2000 + 0x001c) +#define MT6351_AFUNC_AUD_CON1 (0x2000 + 0x001e) +#define MT6351_AFUNC_AUD_CON2 (0x2000 + 0x0020) +#define MT6351_AFUNC_AUD_CON3 (0x2000 + 0x0022) +#define MT6351_AFUNC_AUD_CON4 (0x2000 + 0x0024) +#define MT6351_AFUNC_AUD_MON0 (0x2000 + 0x0026) +#define MT6351_AFUNC_AUD_MON1 (0x2000 + 0x0028) +#define MT6351_AFE_UP8X_FIFO_CFG0 (0x2000 + 0x002c) +#define MT6351_AFE_UP8X_FIFO_LOG_MON0 (0x2000 + 0x002e) +#define MT6351_AFE_UP8X_FIFO_LOG_MON1 (0x2000 + 0x0030) +#define MT6351_AFE_DL_DC_COMP_CFG0 (0x2000 + 0x0032) +#define MT6351_AFE_DL_DC_COMP_CFG1 (0x2000 + 0x0034) +#define MT6351_AFE_DL_DC_COMP_CFG2 (0x2000 + 0x0036) +#define MT6351_AFE_PMIC_NEWIF_CFG0 (0x2000 + 0x0038) +#define MT6351_AFE_PMIC_NEWIF_CFG1 (0x2000 + 0x003a) +#define MT6351_AFE_PMIC_NEWIF_CFG2 (0x2000 + 0x003c) +#define MT6351_AFE_PMIC_NEWIF_CFG3 (0x2000 + 0x003e) +#define MT6351_AFE_SGEN_CFG0 (0x2000 + 0x0040) +#define MT6351_AFE_SGEN_CFG1 (0x2000 + 0x0042) +#define MT6351_AFE_ADDA2_UP8X_FIFO_LOG_MON0 (0x2000 + 0x004c) +#define MT6351_AFE_ADDA2_UP8X_FIFO_LOG_MON1 (0x2000 + 0x004e) +#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG0 (0x2000 + 0x0050) +#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG1 (0x2000 + 0x0052) +#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG2 (0x2000 + 0x0054) +#define MT6351_AFE_DCCLK_CFG0 (0x2000 + 0x0090) +#define MT6351_AFE_DCCLK_CFG1 (0x2000 + 0x0092) +#define MT6351_AFE_HPANC_CFG0 (0x2000 + 0x0094) +#define MT6351_AFE_NCP_CFG0 (0x2000 + 0x0096) +#define MT6351_AFE_NCP_CFG1 (0x2000 + 0x0098) + +#define MT6351_TOP_CKPDN_CON0 0x023A +#define MT6351_TOP_CKPDN_CON0_SET 0x023C +#define MT6351_TOP_CKPDN_CON0_CLR 0x023E + +#define MT6351_TOP_CLKSQ 0x029A +#define MT6351_TOP_CLKSQ_SET 0x029C +#define MT6351_TOP_CLKSQ_CLR 0x029E + +#define MT6351_ZCD_CON0 0x0800 +#define MT6351_ZCD_CON1 0x0802 +#define MT6351_ZCD_CON2 0x0804 +#define MT6351_ZCD_CON3 0x0806 +#define MT6351_ZCD_CON4 0x0808 +#define MT6351_ZCD_CON5 0x080A + +#define MT6351_LDO_VA18_CON0 0x0A00 +#define MT6351_LDO_VA18_CON1 0x0A02 +#define MT6351_LDO_VUSB33_CON0 0x0A16 +#define MT6351_LDO_VUSB33_CON1 0x0A18 + +#define MT6351_AUDDEC_ANA_CON0 0x0CF2 +#define MT6351_AUDDEC_ANA_CON1 0x0CF4 +#define MT6351_AUDDEC_ANA_CON2 0x0CF6 +#define MT6351_AUDDEC_ANA_CON3 0x0CF8 +#define MT6351_AUDDEC_ANA_CON4 0x0CFA +#define MT6351_AUDDEC_ANA_CON5 0x0CFC +#define MT6351_AUDDEC_ANA_CON6 0x0CFE +#define MT6351_AUDDEC_ANA_CON7 0x0D00 +#define MT6351_AUDDEC_ANA_CON8 0x0D02 +#define MT6351_AUDDEC_ANA_CON9 0x0D04 +#define MT6351_AUDDEC_ANA_CON10 0x0D06 + +#define MT6351_AUDENC_ANA_CON0 0x0D08 +#define MT6351_AUDENC_ANA_CON1 0x0D0A +#define MT6351_AUDENC_ANA_CON2 0x0D0C +#define MT6351_AUDENC_ANA_CON3 0x0D0E +#define MT6351_AUDENC_ANA_CON4 0x0D10 +#define MT6351_AUDENC_ANA_CON5 0x0D12 +#define MT6351_AUDENC_ANA_CON6 0x0D14 +#define MT6351_AUDENC_ANA_CON7 0x0D16 +#define MT6351_AUDENC_ANA_CON8 0x0D18 +#define MT6351_AUDENC_ANA_CON9 0x0D1A +#define MT6351_AUDENC_ANA_CON10 0x0D1C +#define MT6351_AUDENC_ANA_CON11 0x0D1E +#define MT6351_AUDENC_ANA_CON12 0x0D20 +#define MT6351_AUDENC_ANA_CON13 0x0D22 +#define MT6351_AUDENC_ANA_CON14 0x0D24 +#define MT6351_AUDENC_ANA_CON15 0x0D26 +#define MT6351_AUDENC_ANA_CON16 0x0D28 +#endif diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c index ca2ba1c7bb9a..bfd74b86c9d2 100644 --- a/sound/soc/codecs/nau8810.c +++ b/sound/soc/codecs/nau8810.c @@ -373,9 +373,11 @@ static const struct snd_kcontrol_new nau8810_mono_mixer_controls[] = { }; /* PGA Mute */ -static const struct snd_kcontrol_new nau8810_inpga_mute[] = { +static const struct snd_kcontrol_new nau8810_pgaboost_mixer_controls[] = { SOC_DAPM_SINGLE("PGA Mute Switch", NAU8810_REG_PGAGAIN, - NAU8810_PGAMT_SFT, 1, 0), + NAU8810_PGAMT_SFT, 1, 1), + SOC_DAPM_SINGLE("PMIC PGA Switch", NAU8810_REG_ADCBOOST, + NAU8810_PMICBSTGAIN_SFT, 0x7, 0), }; /* Input PGA */ @@ -386,11 +388,6 @@ static const struct snd_kcontrol_new nau8810_inpga[] = { NAU8810_PMICPGA_SFT, 1, 0), }; -/* Mic Input boost vol */ -static const struct snd_kcontrol_new nau8810_mic_boost_controls = - SOC_DAPM_SINGLE("Mic Volume", NAU8810_REG_ADCBOOST, - NAU8810_PMICBSTGAIN_SFT, 0x7, 0); - /* Loopback Switch */ static const struct snd_kcontrol_new nau8810_loopback = SOC_DAPM_SINGLE("Switch", NAU8810_REG_COMP, @@ -429,8 +426,8 @@ static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = { NAU8810_PGA_EN_SFT, 0, nau8810_inpga, ARRAY_SIZE(nau8810_inpga)), SND_SOC_DAPM_MIXER("Input Boost Stage", NAU8810_REG_POWER2, - NAU8810_BST_EN_SFT, 0, nau8810_inpga_mute, - ARRAY_SIZE(nau8810_inpga_mute)), + NAU8810_BST_EN_SFT, 0, nau8810_pgaboost_mixer_controls, + ARRAY_SIZE(nau8810_pgaboost_mixer_controls)), SND_SOC_DAPM_SUPPLY("Mic Bias", NAU8810_REG_POWER1, NAU8810_MICBIAS_EN_SFT, 0, NULL, 0), @@ -469,8 +466,8 @@ static const struct snd_soc_dapm_route nau8810_dapm_routes[] = { /* Input Boost Stage */ {"ADC", NULL, "Input Boost Stage"}, {"ADC", NULL, "PLL", check_mclk_select_pll}, - {"Input Boost Stage", NULL, "Input PGA"}, - {"Input Boost Stage", NULL, "MICP"}, + {"Input Boost Stage", "PGA Mute Switch", "Input PGA"}, + {"Input Boost Stage", "PMIC PGA Switch", "MICP"}, /* Input PGA */ {"Input PGA", NULL, "Mic Bias"}, diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 637e9527805f..6bd14453f06e 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -205,11 +205,11 @@ static int nau8824_sema_acquire(struct nau8824 *nau8824, long timeout) if (timeout) { ret = down_timeout(&nau8824->jd_sem, timeout); if (ret < 0) - dev_warn(nau8824->dev, "Acquire semaphone timeout\n"); + dev_warn(nau8824->dev, "Acquire semaphore timeout\n"); } else { ret = down_interruptible(&nau8824->jd_sem); if (ret < 0) - dev_warn(nau8824->dev, "Acquire semaphone fail\n"); + dev_warn(nau8824->dev, "Acquire semaphore fail\n"); } return ret; @@ -409,6 +409,15 @@ static const struct snd_kcontrol_new nau8824_snd_controls[] = { SOC_SINGLE("DACL LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 0, 1, 0), SOC_SINGLE("DACR LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 1, 1, 0), + + SOC_SINGLE("THD for key media", + NAU8824_REG_VDET_THRESHOLD_1, 8, 0xff, 0), + SOC_SINGLE("THD for key voice command", + NAU8824_REG_VDET_THRESHOLD_1, 0, 0xff, 0), + SOC_SINGLE("THD for key volume up", + NAU8824_REG_VDET_THRESHOLD_2, 8, 0xff, 0), + SOC_SINGLE("THD for key volume down", + NAU8824_REG_VDET_THRESHOLD_2, 0, 0xff, 0), }; static int nau8824_output_dac_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c index 507ac9412d6c..21f15219b3ad 100644 --- a/sound/soc/codecs/pcm1789.c +++ b/sound/soc/codecs/pcm1789.c @@ -3,7 +3,7 @@ // Copyright (C) 2018 Bootlin // Mylène Josserand <mylene.josserand@bootlin.com> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/workqueue.h> diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 5f9c069569d5..0fe5ced841a3 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/i2c.h> +#include <linux/acpi.h> #include "pcm512x.h" @@ -52,6 +53,7 @@ static const struct i2c_device_id pcm512x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); +#if defined(CONFIG_OF) static const struct of_device_id pcm512x_of_match[] = { { .compatible = "ti,pcm5121", }, { .compatible = "ti,pcm5122", }, @@ -60,6 +62,18 @@ static const struct of_device_id pcm512x_of_match[] = { { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id pcm512x_acpi_match[] = { + { "104C5121", 0 }, + { "104C5122", 0 }, + { "104C5141", 0 }, + { "104C5142", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match); +#endif static struct i2c_driver pcm512x_i2c_driver = { .probe = pcm512x_i2c_probe, @@ -67,7 +81,8 @@ static struct i2c_driver pcm512x_i2c_driver = { .id_table = pcm512x_i2c_id, .driver = { .name = "pcm512x", - .of_match_table = pcm512x_of_match, + .of_match_table = of_match_ptr(pcm512x_of_match), + .acpi_match_table = ACPI_PTR(pcm512x_acpi_match), .pm = &pcm512x_pm_ops, }, }; diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c new file mode 100644 index 000000000000..f4c8c45f4010 --- /dev/null +++ b/sound/soc/codecs/rt1305.c @@ -0,0 +1,1191 @@ +/* + * rt1305.c -- RT1305 ALSA SoC amplifier component driver + * + * Copyright 2018 Realtek Semiconductor Corp. + * Author: Shuming Fan <shumingf@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/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> +#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 "rl6231.h" +#include "rt1305.h" + + +#define RT1305_PR_RANGE_BASE (0xff + 1) +#define RT1305_PR_SPACING 0x100 + +#define RT1305_PR_BASE (RT1305_PR_RANGE_BASE + (0 * RT1305_PR_SPACING)) + + +static const struct regmap_range_cfg rt1305_ranges[] = { + { + .name = "PR", + .range_min = RT1305_PR_BASE, + .range_max = RT1305_PR_BASE + 0xff, + .selector_reg = RT1305_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT1305_PRIV_DATA, + .window_len = 0x1, + }, +}; + + +static const struct reg_sequence init_list[] = { + + { RT1305_PR_BASE + 0xcf, 0x5548 }, + { RT1305_PR_BASE + 0x5d, 0x0442 }, + { RT1305_PR_BASE + 0xc1, 0x0320 }, + + { RT1305_POWER_STATUS, 0x0000 }, + + { RT1305_SPK_TEMP_PROTECTION_1, 0xd6de }, + { RT1305_SPK_TEMP_PROTECTION_2, 0x0707 }, + { RT1305_SPK_TEMP_PROTECTION_3, 0x4090 }, + + { RT1305_DAC_SET_1, 0xdfdf }, /* 4 ohm 2W */ + { RT1305_ADC_SET_3, 0x0219 }, + { RT1305_ADC_SET_1, 0x170f }, /* 0.2 ohm RSense*/ + +}; +#define RT1305_INIT_REG_LEN ARRAY_SIZE(init_list) + +struct rt1305_priv { + struct snd_soc_component *component; + struct regmap *regmap; + + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int master; + + int pll_src; + int pll_in; + int pll_out; +}; + +static const struct reg_default rt1305_reg[] = { + + { 0x04, 0x0400 }, + { 0x05, 0x0880 }, + { 0x06, 0x0000 }, + { 0x07, 0x3100 }, + { 0x08, 0x8000 }, + { 0x09, 0x0000 }, + { 0x0a, 0x087e }, + { 0x0b, 0x0020 }, + { 0x0c, 0x0802 }, + { 0x0d, 0x0020 }, + { 0x10, 0x1d1d }, + { 0x11, 0x1d1d }, + { 0x12, 0xffff }, + { 0x14, 0x000c }, + { 0x16, 0x1717 }, + { 0x17, 0x4000 }, + { 0x18, 0x0019 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x24, 0x0000 }, + { 0x26, 0x0000 }, + { 0x28, 0x0000 }, + { 0x2a, 0x4000 }, + { 0x2b, 0x3000 }, + { 0x2d, 0x6000 }, + { 0x2e, 0x0000 }, + { 0x2f, 0x8000 }, + { 0x32, 0x0000 }, + { 0x39, 0x0001 }, + { 0x3a, 0x0000 }, + { 0x3b, 0x1020 }, + { 0x3c, 0x0000 }, + { 0x3d, 0x0000 }, + { 0x3e, 0x4c00 }, + { 0x3f, 0x3000 }, + { 0x40, 0x000c }, + { 0x42, 0x0400 }, + { 0x46, 0xc22c }, + { 0x47, 0x0000 }, + { 0x4b, 0x0000 }, + { 0x4c, 0x0300 }, + { 0x4f, 0xf000 }, + { 0x50, 0xc200 }, + { 0x51, 0x1f1f }, + { 0x52, 0x01f0 }, + { 0x53, 0x407f }, + { 0x54, 0xffff }, + { 0x58, 0x4005 }, + { 0x5e, 0x0000 }, + { 0x5f, 0x0000 }, + { 0x60, 0xee13 }, + { 0x62, 0x0000 }, + { 0x63, 0x5f5f }, + { 0x64, 0x0040 }, + { 0x65, 0x4000 }, + { 0x66, 0x4004 }, + { 0x67, 0x0306 }, + { 0x68, 0x8c04 }, + { 0x69, 0xe021 }, + { 0x6a, 0x0000 }, + { 0x6c, 0xaaaa }, + { 0x70, 0x0333 }, + { 0x71, 0x3330 }, + { 0x72, 0x3333 }, + { 0x73, 0x3300 }, + { 0x74, 0x0000 }, + { 0x75, 0x0000 }, + { 0x76, 0x0000 }, + { 0x7a, 0x0003 }, + { 0x7c, 0x10ec }, + { 0x7e, 0x6251 }, + { 0x80, 0x0800 }, + { 0x81, 0x4000 }, + { 0x82, 0x0000 }, + { 0x90, 0x7a01 }, + { 0x91, 0x8431 }, + { 0x92, 0x0180 }, + { 0x93, 0x0000 }, + { 0x94, 0x0000 }, + { 0x95, 0x0000 }, + { 0x96, 0x0000 }, + { 0x97, 0x0000 }, + { 0x98, 0x0000 }, + { 0x99, 0x0000 }, + { 0x9a, 0x0000 }, + { 0x9b, 0x0000 }, + { 0x9c, 0x0000 }, + { 0x9d, 0x0000 }, + { 0x9e, 0x0000 }, + { 0x9f, 0x0000 }, + { 0xa0, 0x0000 }, + { 0xb0, 0x8200 }, + { 0xb1, 0x00ff }, + { 0xb2, 0x0008 }, + { 0xc0, 0x0200 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xc3, 0x0000 }, + { 0xc4, 0x0000 }, + { 0xc5, 0x0000 }, + { 0xc6, 0x0000 }, + { 0xc7, 0x0000 }, + { 0xc8, 0x0000 }, + { 0xc9, 0x0000 }, + { 0xca, 0x0200 }, + { 0xcb, 0x0000 }, + { 0xcc, 0x0000 }, + { 0xcd, 0x0000 }, + { 0xce, 0x0000 }, + { 0xcf, 0x0000 }, + { 0xd0, 0x0000 }, + { 0xd1, 0x0000 }, + { 0xd2, 0x0000 }, + { 0xd3, 0x0000 }, + { 0xd4, 0x0200 }, + { 0xd5, 0x0000 }, + { 0xd6, 0x0000 }, + { 0xd7, 0x0000 }, + { 0xd8, 0x0000 }, + { 0xd9, 0x0000 }, + { 0xda, 0x0000 }, + { 0xdb, 0x0000 }, + { 0xdc, 0x0000 }, + { 0xdd, 0x0000 }, + { 0xde, 0x0200 }, + { 0xdf, 0x0000 }, + { 0xe0, 0x0000 }, + { 0xe1, 0x0000 }, + { 0xe2, 0x0000 }, + { 0xe3, 0x0000 }, + { 0xe4, 0x0000 }, + { 0xe5, 0x0000 }, + { 0xe6, 0x0000 }, + { 0xe7, 0x0000 }, + { 0xe8, 0x0200 }, + { 0xe9, 0x0000 }, + { 0xea, 0x0000 }, + { 0xeb, 0x0000 }, + { 0xec, 0x0000 }, + { 0xed, 0x0000 }, + { 0xee, 0x0000 }, + { 0xef, 0x0000 }, + { 0xf0, 0x0000 }, + { 0xf1, 0x0000 }, + { 0xf2, 0x0200 }, + { 0xf3, 0x0000 }, + { 0xf4, 0x0000 }, + { 0xf5, 0x0000 }, + { 0xf6, 0x0000 }, + { 0xf7, 0x0000 }, + { 0xf8, 0x0000 }, + { 0xf9, 0x0000 }, + { 0xfa, 0x0000 }, + { 0xfb, 0x0000 }, +}; + +static int rt1305_reg_init(struct snd_soc_component *component) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + regmap_multi_reg_write(rt1305->regmap, init_list, RT1305_INIT_REG_LEN); + return 0; +} + +static bool rt1305_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt1305_ranges); i++) { + if (reg >= rt1305_ranges[i].range_min && + reg <= rt1305_ranges[i].range_max) { + return true; + } + } + + switch (reg) { + case RT1305_RESET: + case RT1305_SPDIF_IN_SET_1: + case RT1305_SPDIF_IN_SET_2: + case RT1305_SPDIF_IN_SET_3: + case RT1305_POWER_CTRL_2: + case RT1305_CLOCK_DETECT: + case RT1305_BIQUAD_SET_1: + case RT1305_BIQUAD_SET_2: + case RT1305_EQ_SET_2: + case RT1305_SPK_TEMP_PROTECTION_0: + case RT1305_SPK_TEMP_PROTECTION_2: + case RT1305_SPK_DC_DETECT_1: + case RT1305_SILENCE_DETECT: + case RT1305_VERSION_ID: + case RT1305_VENDOR_ID: + case RT1305_DEVICE_ID: + case RT1305_EFUSE_1: + case RT1305_EFUSE_3: + case RT1305_DC_CALIB_1: + case RT1305_DC_CALIB_3: + case RT1305_DAC_OFFSET_1: + case RT1305_DAC_OFFSET_2: + case RT1305_DAC_OFFSET_3: + case RT1305_DAC_OFFSET_4: + case RT1305_DAC_OFFSET_5: + case RT1305_DAC_OFFSET_6: + case RT1305_DAC_OFFSET_7: + case RT1305_DAC_OFFSET_8: + case RT1305_DAC_OFFSET_9: + case RT1305_DAC_OFFSET_10: + case RT1305_DAC_OFFSET_11: + case RT1305_TRIM_1: + case RT1305_TRIM_2: + return true; + + default: + return false; + } +} + +static bool rt1305_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt1305_ranges); i++) { + if (reg >= rt1305_ranges[i].range_min && + reg <= rt1305_ranges[i].range_max) { + return true; + } + } + + switch (reg) { + case RT1305_RESET: + case RT1305_CLK_1 ... RT1305_CAL_EFUSE_CLOCK: + case RT1305_PLL0_1 ... RT1305_PLL1_2: + case RT1305_MIXER_CTRL_1: + case RT1305_MIXER_CTRL_2: + case RT1305_DAC_SET_1: + case RT1305_DAC_SET_2: + case RT1305_ADC_SET_1: + case RT1305_ADC_SET_2: + case RT1305_ADC_SET_3: + case RT1305_PATH_SET: + case RT1305_SPDIF_IN_SET_1: + case RT1305_SPDIF_IN_SET_2: + case RT1305_SPDIF_IN_SET_3: + case RT1305_SPDIF_OUT_SET_1: + case RT1305_SPDIF_OUT_SET_2: + case RT1305_SPDIF_OUT_SET_3: + case RT1305_I2S_SET_1: + case RT1305_I2S_SET_2: + case RT1305_PBTL_MONO_MODE_SRC: + case RT1305_MANUALLY_I2C_DEVICE: + case RT1305_POWER_STATUS: + case RT1305_POWER_CTRL_1: + case RT1305_POWER_CTRL_2: + case RT1305_POWER_CTRL_3: + case RT1305_POWER_CTRL_4: + case RT1305_POWER_CTRL_5: + case RT1305_CLOCK_DETECT: + case RT1305_BIQUAD_SET_1: + case RT1305_BIQUAD_SET_2: + case RT1305_ADJUSTED_HPF_1: + case RT1305_ADJUSTED_HPF_2: + case RT1305_EQ_SET_1: + case RT1305_EQ_SET_2: + case RT1305_SPK_TEMP_PROTECTION_0: + case RT1305_SPK_TEMP_PROTECTION_1: + case RT1305_SPK_TEMP_PROTECTION_2: + case RT1305_SPK_TEMP_PROTECTION_3: + case RT1305_SPK_DC_DETECT_1: + case RT1305_SPK_DC_DETECT_2: + case RT1305_LOUDNESS: + case RT1305_THERMAL_FOLD_BACK_1: + case RT1305_THERMAL_FOLD_BACK_2: + case RT1305_SILENCE_DETECT ... RT1305_SPK_EXCURSION_LIMITER_7: + case RT1305_VERSION_ID: + case RT1305_VENDOR_ID: + case RT1305_DEVICE_ID: + case RT1305_EFUSE_1: + case RT1305_EFUSE_2: + case RT1305_EFUSE_3: + case RT1305_DC_CALIB_1: + case RT1305_DC_CALIB_2: + case RT1305_DC_CALIB_3: + case RT1305_DAC_OFFSET_1 ... RT1305_DAC_OFFSET_14: + case RT1305_TRIM_1: + case RT1305_TRIM_2: + case RT1305_TUNE_INTERNAL_OSC: + case RT1305_BIQUAD1_H0_L_28_16 ... RT1305_BIQUAD3_A2_R_15_0: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9435, 37, 0); + +static const char * const rt1305_rx_data_ch_select[] = { + "LR", + "RL", + "Copy L", + "Copy R", +}; + +static SOC_ENUM_SINGLE_DECL(rt1305_rx_data_ch_enum, RT1305_I2S_SET_2, 2, + rt1305_rx_data_ch_select); + +static void rt1305_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT1305_RESET, 0); +} + +static const struct snd_kcontrol_new rt1305_snd_controls[] = { + SOC_DOUBLE_TLV("DAC Playback Volume", RT1305_DAC_SET_1, + 8, 0, 0xff, 0, dac_vol_tlv), + + /* I2S Data Channel Selection */ + SOC_ENUM("RX Channel Select", rt1305_rx_data_ch_enum), +}; + +static int rt1305_is_rc_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + unsigned int val; + + snd_soc_component_read(component, RT1305_CLK_1, &val); + + if (rt1305->sysclk_src == RT1305_FS_SYS_PRE_S_PLL1 && + (val & RT1305_SEL_PLL_SRC_2_RCCLK)) + return 1; + else + return 0; +} + +static int rt1305_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + if (rt1305->sysclk_src == RT1305_FS_SYS_PRE_S_PLL1) + return 1; + else + return 0; +} + +static int rt1305_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); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT1305_POWER_CTRL_1, + RT1305_POW_PDB_JD_MASK, RT1305_POW_PDB_JD); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT1305_POWER_CTRL_1, + RT1305_POW_PDB_JD_MASK, 0); + usleep_range(150000, 200000); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_kcontrol_new rt1305_sto_dac_l = + SOC_DAPM_SINGLE("Switch", RT1305_DAC_SET_2, + RT1305_DVOL_MUTE_L_EN_SFT, 1, 1); + +static const struct snd_kcontrol_new rt1305_sto_dac_r = + SOC_DAPM_SINGLE("Switch", RT1305_DAC_SET_2, + RT1305_DVOL_MUTE_R_EN_SFT, 1, 1); + +static const struct snd_soc_dapm_widget rt1305_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL0", RT1305_POWER_CTRL_1, + RT1305_POW_PLL0_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT1305_POWER_CTRL_1, + RT1305_POW_PLL1_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS", RT1305_POWER_CTRL_1, + RT1305_POW_MBIAS_LV_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG MBIAS", RT1305_POWER_CTRL_1, + RT1305_POW_BG_MBIAS_LV_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO2", RT1305_POWER_CTRL_1, + RT1305_POW_LDO2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG2", RT1305_POWER_CTRL_1, + RT1305_POW_BG2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO2 IB2", RT1305_POWER_CTRL_1, + RT1305_POW_LDO2_IB2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", RT1305_POWER_CTRL_1, + RT1305_POW_VREF_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF1", RT1305_POWER_CTRL_1, + RT1305_POW_VREF1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF2", RT1305_POWER_CTRL_1, + RT1305_POW_VREF2_BIT, 0, NULL, 0), + + + SND_SOC_DAPM_SUPPLY("DISC VREF", RT1305_POWER_CTRL_2, + RT1305_POW_DISC_VREF_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("FASTB VREF", RT1305_POWER_CTRL_2, + RT1305_POW_FASTB_VREF_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ULTRA FAST VREF", RT1305_POWER_CTRL_2, + RT1305_POW_ULTRA_FAST_VREF_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CHOP DAC", RT1305_POWER_CTRL_2, + RT1305_POW_CKXEN_DAC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CKGEN DAC", RT1305_POWER_CTRL_2, + RT1305_POW_EN_CKGEN_DAC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLAMP", RT1305_POWER_CTRL_2, + RT1305_POW_CLAMP_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BUFL", RT1305_POWER_CTRL_2, + RT1305_POW_BUFL_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BUFR", RT1305_POWER_CTRL_2, + RT1305_POW_BUFR_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CKGEN ADC", RT1305_POWER_CTRL_2, + RT1305_POW_EN_CKGEN_ADC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC3 L", RT1305_POWER_CTRL_2, + RT1305_POW_ADC3_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC3 R", RT1305_POWER_CTRL_2, + RT1305_POW_ADC3_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TRIOSC", RT1305_POWER_CTRL_2, + RT1305_POW_TRIOSC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AVDD1", RT1305_POWER_CTRL_2, + RT1305_POR_AVDD1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AVDD2", RT1305_POWER_CTRL_2, + RT1305_POR_AVDD2_BIT, 0, NULL, 0), + + + SND_SOC_DAPM_SUPPLY("VSENSE R", RT1305_POWER_CTRL_3, + RT1305_POW_VSENSE_RCH_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VSENSE L", RT1305_POWER_CTRL_3, + RT1305_POW_VSENSE_LCH_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ISENSE R", RT1305_POWER_CTRL_3, + RT1305_POW_ISENSE_RCH_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ISENSE L", RT1305_POWER_CTRL_3, + RT1305_POW_ISENSE_LCH_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("POR AVDD1", RT1305_POWER_CTRL_3, + RT1305_POW_POR_AVDD1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("POR AVDD2", RT1305_POWER_CTRL_3, + RT1305_POW_POR_AVDD2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VCM 6172", RT1305_POWER_CTRL_3, + RT1305_EN_VCM_6172_BIT, 0, NULL, 0), + + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("DAC L Power", RT1305_POWER_CTRL_2, + RT1305_POW_DAC1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R Power", RT1305_POWER_CTRL_2, + RT1305_POW_DAC1_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1305_sto_dac_l), + SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1305_sto_dac_r), + + /* Output Lines */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1305_classd_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), +}; + +static const struct snd_soc_dapm_route rt1305_dapm_routes[] = { + + { "DAC", NULL, "AIF1RX" }, + + { "DAC", NULL, "PLL0", rt1305_is_rc_clk_from_pll }, + { "DAC", NULL, "PLL1", rt1305_is_sys_clk_from_pll }, + + { "DAC", NULL, "MBIAS" }, + { "DAC", NULL, "BG MBIAS" }, + { "DAC", NULL, "LDO2" }, + { "DAC", NULL, "BG2" }, + { "DAC", NULL, "LDO2 IB2" }, + { "DAC", NULL, "VREF" }, + { "DAC", NULL, "VREF1" }, + { "DAC", NULL, "VREF2" }, + + { "DAC", NULL, "DISC VREF" }, + { "DAC", NULL, "FASTB VREF" }, + { "DAC", NULL, "ULTRA FAST VREF" }, + { "DAC", NULL, "CHOP DAC" }, + { "DAC", NULL, "CKGEN DAC" }, + { "DAC", NULL, "CLAMP" }, + { "DAC", NULL, "CKGEN ADC" }, + { "DAC", NULL, "TRIOSC" }, + { "DAC", NULL, "AVDD1" }, + { "DAC", NULL, "AVDD2" }, + + { "DAC", NULL, "POR AVDD1" }, + { "DAC", NULL, "POR AVDD2" }, + { "DAC", NULL, "VCM 6172" }, + + { "DAC L", "Switch", "DAC" }, + { "DAC R", "Switch", "DAC" }, + + { "DAC R", NULL, "VSENSE R" }, + { "DAC L", NULL, "VSENSE L" }, + { "DAC R", NULL, "ISENSE R" }, + { "DAC L", NULL, "ISENSE L" }, + { "DAC L", NULL, "ADC3 L" }, + { "DAC R", NULL, "ADC3 R" }, + { "DAC L", NULL, "BUFL" }, + { "DAC R", NULL, "BUFR" }, + { "DAC L", NULL, "DAC L Power" }, + { "DAC R", NULL, "DAC R Power" }, + + { "CLASS D", NULL, "DAC L" }, + { "CLASS D", NULL, "DAC R" }, + + { "SPOL", NULL, "CLASS D" }, + { "SPOR", NULL, "CLASS D" }, +}; + +static int rt1305_get_clk_info(int sclk, int rate) +{ + int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +static int rt1305_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 rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt1305->lrck = params_rate(params); + pre_div = rt1305_get_clk_info(rt1305->sysclk, rt1305->lrck); + if (pre_div < 0) { + dev_warn(component->dev, "Force using PLL "); + snd_soc_dai_set_pll(dai, 0, RT1305_PLL1_S_BCLK, + rt1305->lrck * 64, rt1305->lrck * 256); + snd_soc_dai_set_sysclk(dai, RT1305_FS_SYS_PRE_S_PLL1, + rt1305->lrck * 256, SND_SOC_CLOCK_IN); + pre_div = 0; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + bclk_ms = frame_size > 32; + rt1305->bclk = rt1305->lrck * (32 << bclk_ms); + + dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt1305->lrck, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + val_len |= RT1305_I2S_DL_SEL_16B; + break; + case 20: + val_len |= RT1305_I2S_DL_SEL_20B; + break; + case 24: + val_len |= RT1305_I2S_DL_SEL_24B; + break; + case 8: + val_len |= RT1305_I2S_DL_SEL_8B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT1305_AIF1: + mask_clk = RT1305_DIV_FS_SYS_MASK; + val_clk = pre_div << RT1305_DIV_FS_SYS_SFT; + snd_soc_component_update_bits(component, RT1305_I2S_SET_2, + RT1305_I2S_DL_SEL_MASK, + val_len); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1305_CLK_2, + mask_clk, val_clk); + + return 0; +} + +static int rt1305_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, reg1_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + reg_val |= RT1305_SEL_I2S_OUT_MODE_M; + rt1305->master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT1305_SEL_I2S_OUT_MODE_S; + rt1305->master = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg1_val |= RT1305_I2S_BCLK_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg1_val |= RT1305_I2S_DF_SEL_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg1_val |= RT1305_I2S_DF_SEL_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg1_val |= RT1305_I2S_DF_SEL_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT1305_AIF1: + snd_soc_component_update_bits(component, RT1305_I2S_SET_1, + RT1305_SEL_I2S_OUT_MODE_MASK, reg_val); + snd_soc_component_update_bits(component, RT1305_I2S_SET_2, + RT1305_I2S_DF_SEL_MASK | RT1305_I2S_BCLK_MASK, + reg1_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt1305_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt1305->sysclk && clk_id == rt1305->sysclk_src) + return 0; + + switch (clk_id) { + case RT1305_FS_SYS_PRE_S_MCLK: + reg_val |= RT1305_SEL_FS_SYS_PRE_MCLK; + snd_soc_component_update_bits(component, + RT1305_CLOCK_DETECT, RT1305_SEL_CLK_DET_SRC_MASK, + RT1305_SEL_CLK_DET_SRC_MCLK); + break; + case RT1305_FS_SYS_PRE_S_PLL1: + reg_val |= RT1305_SEL_FS_SYS_PRE_PLL; + break; + case RT1305_FS_SYS_PRE_S_RCCLK: + reg_val |= RT1305_SEL_FS_SYS_PRE_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT1305_CLK_1, + RT1305_SEL_FS_SYS_PRE_MASK, reg_val); + rt1305->sysclk = freq; + rt1305->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt1305_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt1305->pll_src && freq_in == rt1305->pll_in && + freq_out == rt1305->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt1305->pll_in = 0; + rt1305->pll_out = 0; + snd_soc_component_update_bits(component, RT1305_CLK_1, + RT1305_SEL_FS_SYS_PRE_MASK | RT1305_SEL_PLL_SRC_1_MASK, + RT1305_SEL_FS_SYS_PRE_PLL | RT1305_SEL_PLL_SRC_1_BCLK); + return 0; + } + + switch (source) { + case RT1305_PLL2_S_MCLK: + snd_soc_component_update_bits(component, RT1305_CLK_1, + RT1305_SEL_PLL_SRC_2_MASK | RT1305_SEL_PLL_SRC_1_MASK | + RT1305_DIV_PLL_SRC_2_MASK, + RT1305_SEL_PLL_SRC_2_MCLK | RT1305_SEL_PLL_SRC_1_PLL2); + snd_soc_component_update_bits(component, + RT1305_CLOCK_DETECT, RT1305_SEL_CLK_DET_SRC_MASK, + RT1305_SEL_CLK_DET_SRC_MCLK); + break; + case RT1305_PLL1_S_BCLK: + snd_soc_component_update_bits(component, + RT1305_CLK_1, RT1305_SEL_PLL_SRC_1_MASK, + RT1305_SEL_PLL_SRC_1_BCLK); + break; + case RT1305_PLL2_S_RCCLK: + snd_soc_component_update_bits(component, RT1305_CLK_1, + RT1305_SEL_PLL_SRC_2_MASK | RT1305_SEL_PLL_SRC_1_MASK | + RT1305_DIV_PLL_SRC_2_MASK, + RT1305_SEL_PLL_SRC_2_RCCLK | RT1305_SEL_PLL_SRC_1_PLL2); + freq_in = 98304000; + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT1305_PLL1_1, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT1305_PLL_1_M_SFT | + pll_code.m_bp << RT1305_PLL_1_M_BYPASS_SFT | + pll_code.n_code); + snd_soc_component_write(component, RT1305_PLL1_2, + pll_code.k_code); + + rt1305->pll_in = freq_in; + rt1305->pll_out = freq_out; + rt1305->pll_src = source; + + return 0; +} + +static int rt1305_probe(struct snd_soc_component *component) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + rt1305->component = component; + + /* initial settings */ + rt1305_reg_init(component); + + return 0; +} + +static void rt1305_remove(struct snd_soc_component *component) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + rt1305_reset(rt1305->regmap); +} + +#ifdef CONFIG_PM +static int rt1305_suspend(struct snd_soc_component *component) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1305->regmap, true); + regcache_mark_dirty(rt1305->regmap); + + return 0; +} + +static int rt1305_resume(struct snd_soc_component *component) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1305->regmap, false); + regcache_sync(rt1305->regmap); + + return 0; +} +#else +#define rt1305_suspend NULL +#define rt1305_resume NULL +#endif + +#define RT1305_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT1305_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt1305_aif_dai_ops = { + .hw_params = rt1305_hw_params, + .set_fmt = rt1305_set_dai_fmt, +}; + +static struct snd_soc_dai_driver rt1305_dai[] = { + { + .name = "rt1305-aif", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1305_STEREO_RATES, + .formats = RT1305_FORMATS, + }, + .ops = &rt1305_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt1305 = { + .probe = rt1305_probe, + .remove = rt1305_remove, + .suspend = rt1305_suspend, + .resume = rt1305_resume, + .controls = rt1305_snd_controls, + .num_controls = ARRAY_SIZE(rt1305_snd_controls), + .dapm_widgets = rt1305_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1305_dapm_widgets), + .dapm_routes = rt1305_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1305_dapm_routes), + .set_sysclk = rt1305_set_component_sysclk, + .set_pll = rt1305_set_component_pll, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config rt1305_regmap = { + .reg_bits = 8, + .val_bits = 16, + .max_register = RT1305_MAX_REG + 1 + (ARRAY_SIZE(rt1305_ranges) * + RT1305_PR_SPACING), + .volatile_reg = rt1305_volatile_register, + .readable_reg = rt1305_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt1305_reg, + .num_reg_defaults = ARRAY_SIZE(rt1305_reg), + .ranges = rt1305_ranges, + .num_ranges = ARRAY_SIZE(rt1305_ranges), + .use_single_rw = true, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id rt1305_of_match[] = { + { .compatible = "realtek,rt1305", }, + { .compatible = "realtek,rt1306", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1305_of_match); +#endif + +#ifdef CONFIG_ACPI +static struct acpi_device_id rt1305_acpi_match[] = { + {"10EC1305", 0,}, + {"10EC1306", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt1305_acpi_match); +#endif + +static const struct i2c_device_id rt1305_i2c_id[] = { + { "rt1305", 0 }, + { "rt1306", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1305_i2c_id); + +static void rt1305_calibrate(struct rt1305_priv *rt1305) +{ + unsigned int valmsb, vallsb, offsetl, offsetr; + unsigned int rh, rl, rhl, r0ohm; + u64 r0l, r0r; + + regcache_cache_bypass(rt1305->regmap, true); + + rt1305_reset(rt1305->regmap); + regmap_write(rt1305->regmap, RT1305_ADC_SET_3, 0x0219); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xcf, 0x5548); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc1, 0x0320); + regmap_write(rt1305->regmap, RT1305_CLOCK_DETECT, 0x1000); + regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0600); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xffd0); + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0080); + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0dfe); + + /* Sin Gen */ + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x0442); + + regmap_write(rt1305->regmap, RT1305_CAL_EFUSE_CLOCK, 0xb000); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc3, 0xd4a0); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xcc, 0x00cc); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc1, 0x0320); + regmap_write(rt1305->regmap, RT1305_POWER_STATUS, 0x0000); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0xffff); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfc20); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x06, 0x00c0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfca0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfce0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfcf0); + + /* EFUSE read */ + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0080); + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880); + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfce0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfca0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfc20); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x06, 0x0000); + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0000); + + regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_5, &valmsb); + regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_6, &vallsb); + offsetl = valmsb << 16 | vallsb; + regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_7, &valmsb); + regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_8, &vallsb); + offsetr = valmsb << 16 | vallsb; + pr_info("DC offsetl=0x%x, offsetr=0x%x\n", offsetl, offsetr); + + /* R0 calibration */ + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x9542); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfcf0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0xffff); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x1dfe); + regmap_write(rt1305->regmap, RT1305_SILENCE_DETECT, 0x0e13); + regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0650); + + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x50, 0x0064); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x51, 0x0770); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x52, 0xc30c); + regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0x8200); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xfb00); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xff80); + msleep(2000); + regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x55, &rh); + regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x56, &rl); + rhl = (rh << 16) | rl; + r0ohm = (rhl*10) / 33554432; + + pr_debug("Left_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl); + pr_info("Left channel %d.%dohm\n", (r0ohm/10), (r0ohm%10)); + + r0l = 562949953421312; + if (rhl != 0) + do_div(r0l, rhl); + pr_debug("Left_r0 = 0x%llx\n", r0l); + + regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0x9200); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xfb00); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xff80); + msleep(2000); + regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x55, &rh); + regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x56, &rl); + rhl = (rh << 16) | rl; + r0ohm = (rhl*10) / 33554432; + + pr_debug("Right_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl); + pr_info("Right channel %d.%dohm\n", (r0ohm/10), (r0ohm%10)); + + r0r = 562949953421312; + if (rhl != 0) + do_div(r0r, rhl); + pr_debug("Right_r0 = 0x%llx\n", r0r); + + regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0xc2ec); + + if ((r0l > R0_UPPER) && (r0l < R0_LOWER) && + (r0r > R0_UPPER) && (r0r < R0_LOWER)) { + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x4e, + (r0l >> 16) & 0xffff); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x4f, + r0l & 0xffff); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xfe, + ((r0r >> 16) & 0xffff) | 0xf800); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xfd, + r0r & 0xffff); + } else { + pr_err("R0 calibration failed\n"); + } + + /* restore some registers */ + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0dfe); + usleep_range(200000, 400000); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x0442); + regmap_write(rt1305->regmap, RT1305_CLOCK_DETECT, 0x3000); + regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0400); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0000); + regmap_write(rt1305->regmap, RT1305_CAL_EFUSE_CLOCK, 0x8000); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0x1020); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0x0000); + + regcache_cache_bypass(rt1305->regmap, false); +} + +static int rt1305_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt1305_priv *rt1305; + int ret; + unsigned int val; + + rt1305 = devm_kzalloc(&i2c->dev, sizeof(struct rt1305_priv), + GFP_KERNEL); + if (rt1305 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1305); + + rt1305->regmap = devm_regmap_init_i2c(i2c, &rt1305_regmap); + if (IS_ERR(rt1305->regmap)) { + ret = PTR_ERR(rt1305->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt1305->regmap, RT1305_DEVICE_ID, &val); + if (val != RT1305_DEVICE_ID_NUM) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt1305\n", val); + return -ENODEV; + } + + rt1305_reset(rt1305->regmap); + rt1305_calibrate(rt1305); + + return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt1305, + rt1305_dai, ARRAY_SIZE(rt1305_dai)); +} + +static int rt1305_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_component(&i2c->dev); + + return 0; +} + +static void rt1305_i2c_shutdown(struct i2c_client *client) +{ + struct rt1305_priv *rt1305 = i2c_get_clientdata(client); + + rt1305_reset(rt1305->regmap); +} + + +static struct i2c_driver rt1305_i2c_driver = { + .driver = { + .name = "rt1305", +#if defined(CONFIG_OF) + .of_match_table = rt1305_of_match, +#endif +#if defined(CONFIG_ACPI) + .acpi_match_table = ACPI_PTR(rt1305_acpi_match) +#endif + }, + .probe = rt1305_i2c_probe, + .remove = rt1305_i2c_remove, + .shutdown = rt1305_i2c_shutdown, + .id_table = rt1305_i2c_id, +}; +module_i2c_driver(rt1305_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1305 amplifier driver"); +MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1305.h b/sound/soc/codecs/rt1305.h new file mode 100644 index 000000000000..bde86f97729a --- /dev/null +++ b/sound/soc/codecs/rt1305.h @@ -0,0 +1,276 @@ +/* + * RT1305.h -- RT1305 ALSA SoC amplifier component driver + * + * Copyright 2018 Realtek Semiconductor Corp. + * Author: Shuming Fan <shumingf@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _RT1305_H_ +#define _RT1305_H_ + +#define RT1305_DEVICE_ID_NUM 0x6251 + +#define RT1305_RESET 0x00 +#define RT1305_CLK_1 0x04 +#define RT1305_CLK_2 0x05 +#define RT1305_CLK_3 0x06 +#define RT1305_DFLL_REG 0x07 +#define RT1305_CAL_EFUSE_CLOCK 0x08 +#define RT1305_PLL0_1 0x0a +#define RT1305_PLL0_2 0x0b +#define RT1305_PLL1_1 0x0c +#define RT1305_PLL1_2 0x0d +#define RT1305_MIXER_CTRL_1 0x10 +#define RT1305_MIXER_CTRL_2 0x11 +#define RT1305_DAC_SET_1 0x12 +#define RT1305_DAC_SET_2 0x14 +#define RT1305_ADC_SET_1 0x16 +#define RT1305_ADC_SET_2 0x17 +#define RT1305_ADC_SET_3 0x18 +#define RT1305_PATH_SET 0x20 +#define RT1305_SPDIF_IN_SET_1 0x22 +#define RT1305_SPDIF_IN_SET_2 0x24 +#define RT1305_SPDIF_IN_SET_3 0x26 +#define RT1305_SPDIF_OUT_SET_1 0x28 +#define RT1305_SPDIF_OUT_SET_2 0x2a +#define RT1305_SPDIF_OUT_SET_3 0x2b +#define RT1305_I2S_SET_1 0x2d +#define RT1305_I2S_SET_2 0x2e +#define RT1305_PBTL_MONO_MODE_SRC 0x2f +#define RT1305_MANUALLY_I2C_DEVICE 0x32 +#define RT1305_POWER_STATUS 0x39 +#define RT1305_POWER_CTRL_1 0x3a +#define RT1305_POWER_CTRL_2 0x3b +#define RT1305_POWER_CTRL_3 0x3c +#define RT1305_POWER_CTRL_4 0x3d +#define RT1305_POWER_CTRL_5 0x3e +#define RT1305_CLOCK_DETECT 0x3f +#define RT1305_BIQUAD_SET_1 0x40 +#define RT1305_BIQUAD_SET_2 0x42 +#define RT1305_ADJUSTED_HPF_1 0x46 +#define RT1305_ADJUSTED_HPF_2 0x47 +#define RT1305_EQ_SET_1 0x4b +#define RT1305_EQ_SET_2 0x4c +#define RT1305_SPK_TEMP_PROTECTION_0 0x4f +#define RT1305_SPK_TEMP_PROTECTION_1 0x50 +#define RT1305_SPK_TEMP_PROTECTION_2 0x51 +#define RT1305_SPK_TEMP_PROTECTION_3 0x52 +#define RT1305_SPK_DC_DETECT_1 0x53 +#define RT1305_SPK_DC_DETECT_2 0x54 +#define RT1305_LOUDNESS 0x58 +#define RT1305_THERMAL_FOLD_BACK_1 0x5e +#define RT1305_THERMAL_FOLD_BACK_2 0x5f +#define RT1305_SILENCE_DETECT 0x60 +#define RT1305_ALC_DRC_1 0x62 +#define RT1305_ALC_DRC_2 0x63 +#define RT1305_ALC_DRC_3 0x64 +#define RT1305_ALC_DRC_4 0x65 +#define RT1305_PRIV_INDEX 0x6a +#define RT1305_PRIV_DATA 0x6c +#define RT1305_SPK_EXCURSION_LIMITER_7 0x76 +#define RT1305_VERSION_ID 0x7a +#define RT1305_VENDOR_ID 0x7c +#define RT1305_DEVICE_ID 0x7e +#define RT1305_EFUSE_1 0x80 +#define RT1305_EFUSE_2 0x81 +#define RT1305_EFUSE_3 0x82 +#define RT1305_DC_CALIB_1 0x90 +#define RT1305_DC_CALIB_2 0x91 +#define RT1305_DC_CALIB_3 0x92 +#define RT1305_DAC_OFFSET_1 0x93 +#define RT1305_DAC_OFFSET_2 0x94 +#define RT1305_DAC_OFFSET_3 0x95 +#define RT1305_DAC_OFFSET_4 0x96 +#define RT1305_DAC_OFFSET_5 0x97 +#define RT1305_DAC_OFFSET_6 0x98 +#define RT1305_DAC_OFFSET_7 0x99 +#define RT1305_DAC_OFFSET_8 0x9a +#define RT1305_DAC_OFFSET_9 0x9b +#define RT1305_DAC_OFFSET_10 0x9c +#define RT1305_DAC_OFFSET_11 0x9d +#define RT1305_DAC_OFFSET_12 0x9e +#define RT1305_DAC_OFFSET_13 0x9f +#define RT1305_DAC_OFFSET_14 0xa0 +#define RT1305_TRIM_1 0xb0 +#define RT1305_TRIM_2 0xb1 +#define RT1305_TUNE_INTERNAL_OSC 0xb2 +#define RT1305_BIQUAD1_H0_L_28_16 0xc0 +#define RT1305_BIQUAD3_A2_R_15_0 0xfb +#define RT1305_MAX_REG 0xff + +/* CLOCK-1 (0x04) */ +#define RT1305_SEL_PLL_SRC_2_MASK (0x1 << 15) +#define RT1305_SEL_PLL_SRC_2_SFT 15 +#define RT1305_SEL_PLL_SRC_2_MCLK (0x0 << 15) +#define RT1305_SEL_PLL_SRC_2_RCCLK (0x1 << 15) +#define RT1305_DIV_PLL_SRC_2_MASK (0x3 << 13) +#define RT1305_DIV_PLL_SRC_2_SFT 13 +#define RT1305_SEL_PLL_SRC_1_MASK (0x3 << 10) +#define RT1305_SEL_PLL_SRC_1_SFT 10 +#define RT1305_SEL_PLL_SRC_1_PLL2 (0x0 << 10) +#define RT1305_SEL_PLL_SRC_1_BCLK (0x1 << 10) +#define RT1305_SEL_PLL_SRC_1_DFLL (0x2 << 10) +#define RT1305_SEL_FS_SYS_PRE_MASK (0x3 << 8) +#define RT1305_SEL_FS_SYS_PRE_SFT 8 +#define RT1305_SEL_FS_SYS_PRE_MCLK (0x0 << 8) +#define RT1305_SEL_FS_SYS_PRE_PLL (0x1 << 8) +#define RT1305_SEL_FS_SYS_PRE_RCCLK (0x2 << 8) +#define RT1305_DIV_FS_SYS_MASK (0x7 << 4) +#define RT1305_DIV_FS_SYS_SFT 4 + +/* PLL1M/N/K Code-1 (0x0c) */ +#define RT1305_PLL_1_M_SFT 12 +#define RT1305_PLL_1_M_BYPASS_MASK (0x1 << 11) +#define RT1305_PLL_1_M_BYPASS_SFT 11 +#define RT1305_PLL_1_M_BYPASS (0x1 << 11) +#define RT1305_PLL_1_N_MASK (0x1ff << 0) + +/* DAC Setting (0x14) */ +#define RT1305_DVOL_MUTE_L_EN_SFT 15 +#define RT1305_DVOL_MUTE_R_EN_SFT 14 + +/* I2S Setting-1 (0x2d) */ +#define RT1305_SEL_I2S_OUT_MODE_MASK (0x1 << 15) +#define RT1305_SEL_I2S_OUT_MODE_SFT 15 +#define RT1305_SEL_I2S_OUT_MODE_S (0x0 << 15) +#define RT1305_SEL_I2S_OUT_MODE_M (0x1 << 15) + +/* I2S Setting-2 (0x2e) */ +#define RT1305_I2S_DF_SEL_MASK (0x3 << 12) +#define RT1305_I2S_DF_SEL_SFT 12 +#define RT1305_I2S_DF_SEL_I2S (0x0 << 12) +#define RT1305_I2S_DF_SEL_LEFT (0x1 << 12) +#define RT1305_I2S_DF_SEL_PCM_A (0x2 << 12) +#define RT1305_I2S_DF_SEL_PCM_B (0x3 << 12) +#define RT1305_I2S_DL_SEL_MASK (0x3 << 10) +#define RT1305_I2S_DL_SEL_SFT 10 +#define RT1305_I2S_DL_SEL_16B (0x0 << 10) +#define RT1305_I2S_DL_SEL_20B (0x1 << 10) +#define RT1305_I2S_DL_SEL_24B (0x2 << 10) +#define RT1305_I2S_DL_SEL_8B (0x3 << 10) +#define RT1305_I2S_BCLK_MASK (0x1 << 9) +#define RT1305_I2S_BCLK_SFT 9 +#define RT1305_I2S_BCLK_NORMAL (0x0 << 9) +#define RT1305_I2S_BCLK_INV (0x1 << 9) + +/* Power Control-1 (0x3a) */ +#define RT1305_POW_PDB_JD_MASK (0x1 << 12) +#define RT1305_POW_PDB_JD (0x1 << 12) +#define RT1305_POW_PDB_JD_BIT 12 +#define RT1305_POW_PLL0_EN (0x1 << 11) +#define RT1305_POW_PLL0_EN_BIT 11 +#define RT1305_POW_PLL1_EN (0x1 << 10) +#define RT1305_POW_PLL1_EN_BIT 10 +#define RT1305_POW_PDB_JD_POLARITY (0x1 << 9) +#define RT1305_POW_PDB_JD_POLARITY_BIT 9 +#define RT1305_POW_MBIAS_LV (0x1 << 8) +#define RT1305_POW_MBIAS_LV_BIT 8 +#define RT1305_POW_BG_MBIAS_LV (0x1 << 7) +#define RT1305_POW_BG_MBIAS_LV_BIT 7 +#define RT1305_POW_LDO2 (0x1 << 6) +#define RT1305_POW_LDO2_BIT 6 +#define RT1305_POW_BG2 (0x1 << 5) +#define RT1305_POW_BG2_BIT 5 +#define RT1305_POW_LDO2_IB2 (0x1 << 4) +#define RT1305_POW_LDO2_IB2_BIT 4 +#define RT1305_POW_VREF (0x1 << 3) +#define RT1305_POW_VREF_BIT 3 +#define RT1305_POW_VREF1 (0x1 << 2) +#define RT1305_POW_VREF1_BIT 2 +#define RT1305_POW_VREF2 (0x1 << 1) +#define RT1305_POW_VREF2_BIT 1 + +/* Power Control-2 (0x3b) */ +#define RT1305_POW_DISC_VREF (1 << 15) +#define RT1305_POW_DISC_VREF_BIT 15 +#define RT1305_POW_FASTB_VREF (1 << 14) +#define RT1305_POW_FASTB_VREF_BIT 14 +#define RT1305_POW_ULTRA_FAST_VREF (1 << 13) +#define RT1305_POW_ULTRA_FAST_VREF_BIT 13 +#define RT1305_POW_CKXEN_DAC (1 << 12) +#define RT1305_POW_CKXEN_DAC_BIT 12 +#define RT1305_POW_EN_CKGEN_DAC (1 << 11) +#define RT1305_POW_EN_CKGEN_DAC_BIT 11 +#define RT1305_POW_DAC1_L (1 << 10) +#define RT1305_POW_DAC1_L_BIT 10 +#define RT1305_POW_DAC1_R (1 << 9) +#define RT1305_POW_DAC1_R_BIT 9 +#define RT1305_POW_CLAMP (1 << 8) +#define RT1305_POW_CLAMP_BIT 8 +#define RT1305_POW_BUFL (1 << 7) +#define RT1305_POW_BUFL_BIT 7 +#define RT1305_POW_BUFR (1 << 6) +#define RT1305_POW_BUFR_BIT 6 +#define RT1305_POW_EN_CKGEN_ADC (1 << 5) +#define RT1305_POW_EN_CKGEN_ADC_BIT 5 +#define RT1305_POW_ADC3_L (1 << 4) +#define RT1305_POW_ADC3_L_BIT 4 +#define RT1305_POW_ADC3_R (1 << 3) +#define RT1305_POW_ADC3_R_BIT 3 +#define RT1305_POW_TRIOSC (1 << 2) +#define RT1305_POW_TRIOSC_BIT 2 +#define RT1305_POR_AVDD1 (1 << 1) +#define RT1305_POR_AVDD1_BIT 1 +#define RT1305_POR_AVDD2 (1 << 0) +#define RT1305_POR_AVDD2_BIT 0 + +/* Power Control-3 (0x3c) */ +#define RT1305_POW_VSENSE_RCH (1 << 15) +#define RT1305_POW_VSENSE_RCH_BIT 15 +#define RT1305_POW_VSENSE_LCH (1 << 14) +#define RT1305_POW_VSENSE_LCH_BIT 14 +#define RT1305_POW_ISENSE_RCH (1 << 13) +#define RT1305_POW_ISENSE_RCH_BIT 13 +#define RT1305_POW_ISENSE_LCH (1 << 12) +#define RT1305_POW_ISENSE_LCH_BIT 12 +#define RT1305_POW_POR_AVDD1 (1 << 11) +#define RT1305_POW_POR_AVDD1_BIT 11 +#define RT1305_POW_POR_AVDD2 (1 << 10) +#define RT1305_POW_POR_AVDD2_BIT 10 +#define RT1305_EN_K_HV (1 << 9) +#define RT1305_EN_K_HV_BIT 9 +#define RT1305_EN_PRE_K_HV (1 << 8) +#define RT1305_EN_PRE_K_HV_BIT 8 +#define RT1305_EN_EFUSE_1P8V (1 << 7) +#define RT1305_EN_EFUSE_1P8V_BIT 7 +#define RT1305_EN_EFUSE_5V (1 << 6) +#define RT1305_EN_EFUSE_5V_BIT 6 +#define RT1305_EN_VCM_6172 (1 << 5) +#define RT1305_EN_VCM_6172_BIT 5 +#define RT1305_POR_EFUSE (1 << 4) +#define RT1305_POR_EFUSE_BIT 4 + +/* Clock Detect (0x3f) */ +#define RT1305_SEL_CLK_DET_SRC_MASK (0x1 << 12) +#define RT1305_SEL_CLK_DET_SRC_SFT 12 +#define RT1305_SEL_CLK_DET_SRC_MCLK (0x0 << 12) +#define RT1305_SEL_CLK_DET_SRC_BCLK (0x1 << 12) + + +/* System Clock Source */ +enum { + RT1305_FS_SYS_PRE_S_MCLK, + RT1305_FS_SYS_PRE_S_PLL1, + RT1305_FS_SYS_PRE_S_RCCLK, /* 98.304M Hz */ +}; + +/* PLL Source 1/2 */ +enum { + RT1305_PLL1_S_BCLK, + RT1305_PLL2_S_MCLK, + RT1305_PLL2_S_RCCLK, /* 98.304M Hz */ +}; + +enum { + RT1305_AIF1, + RT1305_AIFS +}; + +#define R0_UPPER 0x2E8BA2 //5.5 ohm +#define R0_LOWER 0x666666 //2.5 ohm + +#endif /* end of _RT1305_H_ */ diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 05567426f211..8bf8d360c25f 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -24,6 +24,7 @@ #include <linux/spi/spi.h> #include <linux/acpi.h> #include <sound/core.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -476,20 +477,6 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w, return idx; } -static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) -{ - struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); - unsigned int val; - - val = snd_soc_component_read32(component, RT5640_GLB_CLK); - val &= RT5640_SCLK_SRC_MASK; - if (val == RT5640_SCLK_SRC_PLL1) - return 1; - else - return 0; -} - static int is_using_asrc(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { @@ -1071,9 +1058,6 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w, } static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { - SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2, - RT5640_PWR_PLL_BIT, 0, NULL, 0), - /* ASRC */ SND_SOC_DAPM_SUPPLY_S("Stereo Filter ASRC", 1, RT5640_ASRC_1, 15, 0, NULL, 0), @@ -1427,22 +1411,18 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"Stereo ADC MIXL", "ADC1 Switch", "Stereo ADC L1 Mux"}, {"Stereo ADC MIXL", "ADC2 Switch", "Stereo ADC L2 Mux"}, {"Stereo ADC MIXL", NULL, "Stereo Filter"}, - {"Stereo Filter", NULL, "PLL1", is_sys_clk_from_pll}, {"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"}, {"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"}, {"Stereo ADC MIXR", NULL, "Stereo Filter"}, - {"Stereo Filter", NULL, "PLL1", is_sys_clk_from_pll}, {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"}, {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"}, {"Mono ADC MIXL", NULL, "Mono Left Filter"}, - {"Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll}, {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"}, {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"}, {"Mono ADC MIXR", NULL, "Mono Right Filter"}, - {"Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll}, {"IF2 ADC L", NULL, "Mono ADC MIXL"}, {"IF2 ADC R", NULL, "Mono ADC MIXR"}, @@ -1512,10 +1492,8 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"DIG MIXR", "DAC R1 Switch", "DAC MIXR"}, {"DAC L1", NULL, "Stereo DAC MIXL"}, - {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll}, {"DAC L1", NULL, "DAC L1 Power"}, {"DAC R1", NULL, "Stereo DAC MIXR"}, - {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll}, {"DAC R1", NULL, "DAC R1 Power"}, {"SPK MIXL", "REC MIXL Switch", "RECMIXL"}, @@ -1622,10 +1600,8 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = { {"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"}, {"DAC L2", NULL, "Mono DAC MIXL"}, - {"DAC L2", NULL, "PLL1", is_sys_clk_from_pll}, {"DAC L2", NULL, "DAC L2 Power"}, {"DAC R2", NULL, "Mono DAC MIXR"}, - {"DAC R2", NULL, "PLL1", is_sys_clk_from_pll}, {"DAC R2", NULL, "DAC R2 Power"}, {"SPK MIXL", "DAC L2 Switch", "DAC L2"}, @@ -1861,6 +1837,7 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, struct snd_soc_component *component = dai->component; struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); unsigned int reg_val = 0; + unsigned int pll_bit = 0; if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src) return 0; @@ -1871,6 +1848,7 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, break; case RT5640_SCLK_S_PLL1: reg_val |= RT5640_SCLK_SRC_PLL1; + pll_bit |= RT5640_PWR_PLL; break; case RT5640_SCLK_S_RCCLK: reg_val |= RT5640_SCLK_SRC_RCCLK; @@ -1879,6 +1857,8 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); return -EINVAL; } + snd_soc_component_update_bits(component, RT5640_PWR_ANLG2, + RT5640_PWR_PLL, pll_bit); snd_soc_component_update_bits(component, RT5640_GLB_CLK, RT5640_SCLK_SRC_MASK, reg_val); rt5640->sysclk = freq; @@ -2114,10 +2094,376 @@ int rt5640_sel_asrc_clk_src(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src); +static void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO2"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS1"); + /* OVCD is unreliable when used with RCCLK as sysclk-source */ + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Platform Clock"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +static void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_disable_pin_unlocked(dapm, "Platform Clock"); + snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS1"); + snd_soc_dapm_disable_pin_unlocked(dapm, "LDO2"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +static void rt5640_enable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_IRQ_MB1_OC_MASK, RT5640_IRQ_MB1_OC_NOR); + rt5640->ovcd_irq_enabled = true; +} + +static void rt5640_disable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_IRQ_MB1_OC_MASK, RT5640_IRQ_MB1_OC_BP); + rt5640->ovcd_irq_enabled = false; +} + +static void rt5640_clear_micbias1_ovcd(struct snd_soc_component *component) +{ + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_MB1_OC_STATUS, 0); +} + +static bool rt5640_micbias1_ovcd(struct snd_soc_component *component) +{ + int val; + + val = snd_soc_component_read32(component, RT5640_IRQ_CTRL2); + dev_dbg(component->dev, "irq ctrl2 %#04x\n", val); + + return (val & RT5640_MB1_OC_STATUS); +} + +static bool rt5640_jack_inserted(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + int val; + + val = snd_soc_component_read32(component, RT5640_INT_IRQ_ST); + dev_dbg(component->dev, "irq status %#04x\n", val); + + if (rt5640->jd_inverted) + return !(val & RT5640_JD_STATUS); + else + return (val & RT5640_JD_STATUS); +} + +/* Jack detect and button-press timings */ +#define JACK_SETTLE_TIME 100 /* milli seconds */ +#define JACK_DETECT_COUNT 5 +#define JACK_DETECT_MAXCOUNT 20 /* Aprox. 2 seconds worth of tries */ +#define JACK_UNPLUG_TIME 80 /* milli seconds */ +#define BP_POLL_TIME 10 /* milli seconds */ +#define BP_POLL_MAXCOUNT 200 /* assume something is wrong after this */ +#define BP_THRESHOLD 3 + +static void rt5640_start_button_press_work(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + rt5640->poll_count = 0; + rt5640->press_count = 0; + rt5640->release_count = 0; + rt5640->pressed = false; + rt5640->press_reported = false; + rt5640_clear_micbias1_ovcd(component); + schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} + +static void rt5640_button_press_work(struct work_struct *work) +{ + struct rt5640_priv *rt5640 = + container_of(work, struct rt5640_priv, bp_work.work); + struct snd_soc_component *component = rt5640->component; + + /* Check the jack was not removed underneath us */ + if (!rt5640_jack_inserted(component)) + return; + + if (rt5640_micbias1_ovcd(component)) { + rt5640->release_count = 0; + rt5640->press_count++; + /* Remember till after JACK_UNPLUG_TIME wait */ + if (rt5640->press_count >= BP_THRESHOLD) + rt5640->pressed = true; + rt5640_clear_micbias1_ovcd(component); + } else { + rt5640->press_count = 0; + rt5640->release_count++; + } + + /* + * The pins get temporarily shorted on jack unplug, so we poll for + * at least JACK_UNPLUG_TIME milli-seconds before reporting a press. + */ + rt5640->poll_count++; + if (rt5640->poll_count < (JACK_UNPLUG_TIME / BP_POLL_TIME)) { + schedule_delayed_work(&rt5640->bp_work, + msecs_to_jiffies(BP_POLL_TIME)); + return; + } + + if (rt5640->pressed && !rt5640->press_reported) { + dev_dbg(component->dev, "headset button press\n"); + snd_soc_jack_report(rt5640->jack, SND_JACK_BTN_0, + SND_JACK_BTN_0); + rt5640->press_reported = true; + } + + if (rt5640->release_count >= BP_THRESHOLD) { + if (rt5640->press_reported) { + dev_dbg(component->dev, "headset button release\n"); + snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); + } + /* Re-enable OVCD IRQ to detect next press */ + rt5640_enable_micbias1_ovcd_irq(component); + return; /* Stop polling */ + } + + schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} + +static int rt5640_detect_headset(struct snd_soc_component *component) +{ + int i, headset_count = 0, headphone_count = 0; + + /* + * We get the insertion event before the jack is fully inserted at which + * point the second ring on a TRRS connector may short the 2nd ring and + * sleeve contacts, also the overcurrent detection is not entirely + * reliable. So we try several times with a wait in between until we + * detect the same type JACK_DETECT_COUNT times in a row. + */ + for (i = 0; i < JACK_DETECT_MAXCOUNT; i++) { + /* Clear any previous over-current status flag */ + rt5640_clear_micbias1_ovcd(component); + + msleep(JACK_SETTLE_TIME); + + /* Check the jack is still connected before checking ovcd */ + if (!rt5640_jack_inserted(component)) + return 0; + + if (rt5640_micbias1_ovcd(component)) { + /* + * Over current detected, there is a short between the + * 2nd ring contact and the ground, so a TRS connector + * without a mic contact and thus plain headphones. + */ + dev_dbg(component->dev, "jack mic-gnd shorted\n"); + headset_count = 0; + headphone_count++; + if (headphone_count == JACK_DETECT_COUNT) + return SND_JACK_HEADPHONE; + } else { + dev_dbg(component->dev, "jack mic-gnd open\n"); + headphone_count = 0; + headset_count++; + if (headset_count == JACK_DETECT_COUNT) + return SND_JACK_HEADSET; + } + } + + dev_err(component->dev, "Error detecting headset vs headphones, bad contact?, assuming headphones\n"); + return SND_JACK_HEADPHONE; +} + +static void rt5640_jack_work(struct work_struct *work) +{ + struct rt5640_priv *rt5640 = + container_of(work, struct rt5640_priv, jack_work); + struct snd_soc_component *component = rt5640->component; + int status; + + if (!rt5640_jack_inserted(component)) { + /* Jack removed, or spurious IRQ? */ + if (rt5640->jack->status & SND_JACK_HEADPHONE) { + if (rt5640->jack->status & SND_JACK_MICROPHONE) { + cancel_delayed_work_sync(&rt5640->bp_work); + rt5640_disable_micbias1_ovcd_irq(component); + rt5640_disable_micbias1_for_ovcd(component); + } + snd_soc_jack_report(rt5640->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + dev_dbg(component->dev, "jack unplugged\n"); + } + } else if (!(rt5640->jack->status & SND_JACK_HEADPHONE)) { + /* Jack inserted */ + WARN_ON(rt5640->ovcd_irq_enabled); + rt5640_enable_micbias1_for_ovcd(component); + status = rt5640_detect_headset(component); + if (status == SND_JACK_HEADSET) { + /* Enable ovcd IRQ for button press detect. */ + rt5640_enable_micbias1_ovcd_irq(component); + } else { + /* No more need for overcurrent detect. */ + rt5640_disable_micbias1_for_ovcd(component); + } + dev_dbg(component->dev, "detect status %#02x\n", status); + snd_soc_jack_report(rt5640->jack, status, SND_JACK_HEADSET); + } else if (rt5640->ovcd_irq_enabled && rt5640_micbias1_ovcd(component)) { + dev_dbg(component->dev, "OVCD IRQ\n"); + + /* + * The ovcd IRQ keeps firing while the button is pressed, so + * we disable it and start polling the button until released. + * + * The disable will make the IRQ pin 0 again and since we get + * IRQs on both edges (so as to detect both jack plugin and + * unplug) this means we will immediately get another IRQ. + * The ovcd_irq_enabled check above makes the 2ND IRQ a NOP. + */ + rt5640_disable_micbias1_ovcd_irq(component); + rt5640_start_button_press_work(component); + + /* + * If the jack-detect IRQ flag goes high (unplug) after our + * above rt5640_jack_inserted() check and before we have + * disabled the OVCD IRQ, the IRQ pin will stay high and as + * we react to edges, we miss the unplug event -> recheck. + */ + queue_work(system_long_wq, &rt5640->jack_work); + } +} + +static irqreturn_t rt5640_irq(int irq, void *data) +{ + struct rt5640_priv *rt5640 = data; + + if (rt5640->jack) + queue_work(system_long_wq, &rt5640->jack_work); + + return IRQ_HANDLED; +} + +static void rt5640_cancel_work(void *data) +{ + struct rt5640_priv *rt5640 = data; + + cancel_work_sync(&rt5640->jack_work); + cancel_delayed_work_sync(&rt5640->bp_work); +} + +static void rt5640_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + /* Select JD-source */ + snd_soc_component_update_bits(component, RT5640_JD_CTRL, + RT5640_JD_MASK, rt5640->jd_src); + + /* Selecting GPIO01 as an interrupt */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1, + RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ); + + /* Set GPIO1 output */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, + RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); + + /* Enabling jd2 in general control 1 */ + snd_soc_component_write(component, RT5640_DUMMY1, 0x3f41); + + /* Enabling jd2 in general control 2 */ + snd_soc_component_write(component, RT5640_DUMMY2, 0x4001); + + snd_soc_component_write(component, RT5640_PR_BASE + RT5640_BIAS_CUR4, + 0xa800 | rt5640->ovcd_sf); + + snd_soc_component_update_bits(component, RT5640_MICBIAS, + RT5640_MIC1_OVTH_MASK | RT5640_MIC1_OVCD_MASK, + rt5640->ovcd_th | RT5640_MIC1_OVCD_EN); + + /* + * The over-current-detect is only reliable in detecting the absence + * of over-current, when the mic-contact in the jack is short-circuited, + * the hardware periodically retries if it can apply the bias-current + * leading to the ovcd status flip-flopping 1-0-1 with it being 0 about + * 10% of the time, as we poll the ovcd status bit we might hit that + * 10%, so we enable sticky mode and when checking OVCD we clear the + * status, msleep() a bit and then check to get a reliable reading. + */ + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_MB1_OC_STKY_MASK, RT5640_MB1_OC_STKY_EN); + + /* + * All IRQs get or-ed together, so we need the jack IRQ to report 0 + * when a jack is inserted so that the OVCD IRQ then toggles the IRQ + * pin 0/1 instead of it being stuck to 1. So we invert the JD polarity + * on systems where the hardware does not already do this. + */ + if (rt5640->jd_inverted) + snd_soc_component_write(component, RT5640_IRQ_CTRL1, + RT5640_IRQ_JD_NOR); + else + snd_soc_component_write(component, RT5640_IRQ_CTRL1, + RT5640_IRQ_JD_NOR | RT5640_JD_P_INV); + + rt5640->jack = jack; + if (rt5640->jack->status & SND_JACK_MICROPHONE) { + rt5640_enable_micbias1_for_ovcd(component); + rt5640_enable_micbias1_ovcd_irq(component); + } + + enable_irq(rt5640->irq); + /* sync initial jack state */ + queue_work(system_long_wq, &rt5640->jack_work); +} + +static void rt5640_disable_jack_detect(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + disable_irq(rt5640->irq); + rt5640_cancel_work(rt5640); + + if (rt5640->jack->status & SND_JACK_MICROPHONE) { + rt5640_disable_micbias1_ovcd_irq(component); + rt5640_disable_micbias1_for_ovcd(component); + snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); + } + + rt5640->jack = NULL; +} + +static int rt5640_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + if (jack) + rt5640_enable_jack_detect(component, jack); + else + rt5640_disable_jack_detect(component); + + return 0; +} + static int rt5640_probe(struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + u32 dmic1_data_pin = 0; + u32 dmic2_data_pin = 0; + bool dmic_en = false; + u32 val; /* Check if MCLK provided */ rt5640->mclk = devm_clk_get(component->dev, "mclk"); @@ -2159,9 +2505,86 @@ static int rt5640_probe(struct snd_soc_component *component) return -ENODEV; } - if (rt5640->pdata.dmic_en) - rt5640_dmic_enable(component, rt5640->pdata.dmic1_data_pin, - rt5640->pdata.dmic2_data_pin); + /* + * Note on some platforms the platform code may need to add device-props + * rather then relying only on properties set by the firmware. + * Therefor the property parsing MUST be done here, rather then from + * rt5640_i2c_probe(), so that the platform-code can attach extra + * properties before calling snd_soc_register_card(). + */ + if (device_property_read_bool(component->dev, "realtek,in1-differential")) + snd_soc_component_update_bits(component, RT5640_IN1_IN2, + RT5640_IN_DF1, RT5640_IN_DF1); + + if (device_property_read_bool(component->dev, "realtek,in2-differential")) + snd_soc_component_update_bits(component, RT5640_IN3_IN4, + RT5640_IN_DF2, RT5640_IN_DF2); + + if (device_property_read_bool(component->dev, "realtek,in3-differential")) + snd_soc_component_update_bits(component, RT5640_IN1_IN2, + RT5640_IN_DF2, RT5640_IN_DF2); + + if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin", + &val) == 0 && val) { + dmic1_data_pin = val - 1; + dmic_en = true; + } + + if (device_property_read_u32(component->dev, "realtek,dmic2-data-pin", + &val) == 0 && val) { + dmic2_data_pin = val - 1; + dmic_en = true; + } + + if (dmic_en) + rt5640_dmic_enable(component, dmic1_data_pin, dmic2_data_pin); + + if (device_property_read_u32(component->dev, + "realtek,jack-detect-source", &val) == 0) { + if (val <= RT5640_JD_SRC_GPIO4) + rt5640->jd_src = val << RT5640_JD_SFT; + else + dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n", + val); + } + + if (!device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted")) + rt5640->jd_inverted = true; + + /* + * Testing on various boards has shown that good defaults for the OVCD + * threshold and scale-factor are 2000µA and 0.75. For an effective + * limit of 1500µA, this seems to be more reliable then 1500µA and 1.0. + */ + rt5640->ovcd_th = RT5640_MIC1_OVTH_2000UA; + rt5640->ovcd_sf = RT5640_MIC_OVCD_SF_0P75; + + if (device_property_read_u32(component->dev, + "realtek,over-current-threshold-microamp", &val) == 0) { + switch (val) { + case 600: + rt5640->ovcd_th = RT5640_MIC1_OVTH_600UA; + break; + case 1500: + rt5640->ovcd_th = RT5640_MIC1_OVTH_1500UA; + break; + case 2000: + rt5640->ovcd_th = RT5640_MIC1_OVTH_2000UA; + break; + default: + dev_warn(component->dev, "Warning: Invalid over-current-threshold-microamp value: %d, defaulting to 2000uA\n", + val); + } + } + + if (device_property_read_u32(component->dev, + "realtek,over-current-scale-factor", &val) == 0) { + if (val <= RT5640_OVCD_SF_1P5) + rt5640->ovcd_sf = val << RT5640_MIC_OVCD_SF_SFT; + else + dev_warn(component->dev, "Warning: Invalid over-current-scale-factor value: %d, defaulting to 0.75\n", + val); + } return 0; } @@ -2180,8 +2603,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->pdata.ldo1_en)) - gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 0); + if (gpio_is_valid(rt5640->ldo1_en)) + gpio_set_value_cansleep(rt5640->ldo1_en, 0); return 0; } @@ -2190,8 +2613,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->pdata.ldo1_en)) { - gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 1); + if (gpio_is_valid(rt5640->ldo1_en)) { + gpio_set_value_cansleep(rt5640->ldo1_en, 1); msleep(400); } @@ -2263,6 +2686,7 @@ static const struct snd_soc_component_driver soc_component_dev_rt5640 = { .suspend = rt5640_suspend, .resume = rt5640_resume, .set_bias_level = rt5640_set_bias_level, + .set_jack = rt5640_set_jack, .controls = rt5640_snd_controls, .num_controls = ARRAY_SIZE(rt5640_snd_controls), .dapm_widgets = rt5640_dapm_widgets, @@ -2323,22 +2747,16 @@ MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match); static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np) { - rt5640->pdata.in1_diff = of_property_read_bool(np, - "realtek,in1-differential"); - rt5640->pdata.in2_diff = of_property_read_bool(np, - "realtek,in2-differential"); - - rt5640->pdata.ldo1_en = of_get_named_gpio(np, - "realtek,ldo1-en-gpios", 0); + 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->pdata.ldo1_en) && - (rt5640->pdata.ldo1_en != -ENOENT)) - return rt5640->pdata.ldo1_en; + if (!gpio_is_valid(rt5640->ldo1_en) && + (rt5640->ldo1_en != -ENOENT)) + return rt5640->ldo1_en; return 0; } @@ -2346,7 +2764,6 @@ static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np) static int rt5640_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { - struct rt5640_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5640_priv *rt5640; int ret; unsigned int val; @@ -2358,22 +2775,12 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, return -ENOMEM; i2c_set_clientdata(i2c, rt5640); - if (pdata) { - rt5640->pdata = *pdata; - /* - * Translate zero'd out (default) pdata value to an invalid - * GPIO ID. This makes the pdata and DT paths consistent in - * terms of the value left in this field when no GPIO is - * specified, but means we can't actually use GPIO 0. - */ - if (!rt5640->pdata.ldo1_en) - rt5640->pdata.ldo1_en = -EINVAL; - } else if (i2c->dev.of_node) { + if (i2c->dev.of_node) { ret = rt5640_parse_dt(rt5640, i2c->dev.of_node); if (ret) return ret; } else - rt5640->pdata.ldo1_en = -EINVAL; + rt5640->ldo1_en = -EINVAL; rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap); if (IS_ERR(rt5640->regmap)) { @@ -2383,13 +2790,13 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, return ret; } - if (gpio_is_valid(rt5640->pdata.ldo1_en)) { - ret = devm_gpio_request_one(&i2c->dev, rt5640->pdata.ldo1_en, + 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->pdata.ldo1_en, ret); + rt5640->ldo1_en, ret); return ret; } msleep(400); @@ -2412,19 +2819,27 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5640->regmap, RT5640_DUMMY1, RT5640_MCLK_DET, RT5640_MCLK_DET); - if (rt5640->pdata.in1_diff) - regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2, - RT5640_IN_DF1, RT5640_IN_DF1); - - if (rt5640->pdata.in2_diff) - regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4, - RT5640_IN_DF2, RT5640_IN_DF2); + rt5640->hp_mute = 1; + rt5640->irq = i2c->irq; + INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work); + INIT_WORK(&rt5640->jack_work, rt5640_jack_work); - if (rt5640->pdata.in3_diff) - regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2, - RT5640_IN_DF2, RT5640_IN_DF2); + /* Make sure work is stopped on probe-error / remove */ + ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640); + if (ret) + return ret; - rt5640->hp_mute = 1; + ret = devm_request_irq(&i2c->dev, rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5640", rt5640); + if (ret == 0) { + /* Gets re-enabled by rt5640_set_jack() */ + disable_irq(rt5640->irq); + } else { + dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n", + rt5640->irq, ret); + rt5640->irq = -ENXIO; + } return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5640, diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index c473e8ae2eda..e29e3e7d61b0 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -13,7 +13,8 @@ #define _RT5640_H #include <linux/clk.h> -#include <sound/rt5640.h> +#include <linux/workqueue.h> +#include <dt-bindings/sound/rt5640.h> /* Info */ #define RT5640_RESET 0x00 @@ -146,6 +147,7 @@ /* Index of Codec Private Register definition */ +#define RT5640_BIAS_CUR4 0x15 #define RT5640_CHPUMP_INT_REG1 0x24 #define RT5640_MAMP_INT_REG2 0x37 #define RT5640_3D_SPK 0x63 @@ -1607,10 +1609,17 @@ #define RT5640_MB2_OC_P_SFT 6 #define RT5640_MB2_OC_P_NOR (0x0 << 6) #define RT5640_MB2_OC_P_INV (0x1 << 6) -#define RT5640_MB1_OC_CLR (0x1 << 3) -#define RT5640_MB1_OC_CLR_SFT 3 -#define RT5640_MB2_OC_CLR (0x1 << 2) -#define RT5640_MB2_OC_CLR_SFT 2 +#define RT5640_MB1_OC_STATUS (0x1 << 3) +#define RT5640_MB1_OC_STATUS_SFT 3 +#define RT5640_MB2_OC_STATUS (0x1 << 2) +#define RT5640_MB2_OC_STATUS_SFT 2 + +/* GPIO and Internal Status (0xbf) */ +#define RT5640_GPIO1_STATUS (0x1 << 8) +#define RT5640_GPIO2_STATUS (0x1 << 7) +#define RT5640_JD_STATUS (0x1 << 4) +#define RT5640_OVT_STATUS (0x1 << 3) +#define RT5640_CLS_D_OVCD_STATUS (0x1 << 0) /* GPIO Control 1 (0xc0) */ #define RT5640_GP1_PIN_MASK (0x1 << 15) @@ -1978,6 +1987,15 @@ #define RT5640_MCLK_DET (0x1 << 11) /* Codec Private Register definition */ + +/* MIC Over current threshold scale factor (0x15) */ +#define RT5640_MIC_OVCD_SF_MASK (0x3 << 8) +#define RT5640_MIC_OVCD_SF_SFT 8 +#define RT5640_MIC_OVCD_SF_0P5 (0x0 << 8) +#define RT5640_MIC_OVCD_SF_0P75 (0x1 << 8) +#define RT5640_MIC_OVCD_SF_1P0 (0x2 << 8) +#define RT5640_MIC_OVCD_SF_1P5 (0x3 << 8) + /* 3D Speaker Control (0x63) */ #define RT5640_3D_SPK_MASK (0x1 << 15) #define RT5640_3D_SPK_SFT 15 @@ -2103,10 +2121,11 @@ enum { struct rt5640_priv { struct snd_soc_component *component; - struct rt5640_platform_data pdata; struct regmap *regmap; struct clk *mclk; + int ldo1_en; /* GPIO for LDO1_EN */ + int irq; int sysclk; int sysclk_src; int lrck[RT5640_AIFS]; @@ -2119,6 +2138,21 @@ struct rt5640_priv { bool hp_mute; bool asrc_en; + + /* Jack and button detect data */ + bool ovcd_irq_enabled; + bool pressed; + bool press_reported; + int press_count; + int release_count; + int poll_count; + struct delayed_work bp_work; + struct work_struct jack_work; + struct snd_soc_jack *jack; + unsigned int jd_src; + bool jd_inverted; + unsigned int ovcd_th; + unsigned int ovcd_sf; }; int rt5640_dmic_enable(struct snd_soc_component *component, diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index bc8d829ce45b..712384581ebf 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3652,6 +3652,11 @@ static const struct rt5645_platform_data asus_t100ha_platform_data = { .inv_jd1_1 = true, }; +static const struct rt5645_platform_data lenovo_ideapad_miix_310_pdata = { + .jd_mode = 3, + .in2_diff = true, +}; + static const struct rt5645_platform_data jd_mode3_platform_data = { .jd_mode = 3, }; @@ -3735,6 +3740,24 @@ static const struct dmi_system_id dmi_platform_data[] = { }, .driver_data = (void *)&jd_mode3_platform_data, }, + { + .ident = "Lenovo Ideapad Miix 310", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80SG"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"), + }, + .driver_data = (void *)&lenovo_ideapad_miix_310_pdata, + }, + { + .ident = "Lenovo Ideapad Miix 320", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"), + }, + .driver_data = (void *)&intel_braswell_platform_data, + }, { } }; diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 20c0aeea6ca3..9bd24ad42240 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -72,6 +72,8 @@ struct rt5663_priv { static const struct reg_sequence rt5663_patch_list[] = { { 0x002a, 0x8020 }, { 0x0086, 0x0028 }, + { 0x0117, 0x0f28 }, + { 0x02fb, 0x8089 }, }; static const struct reg_default rt5663_v2_reg[] = { @@ -593,7 +595,7 @@ static const struct reg_default rt5663_reg[] = { { 0x0113, 0x2000 }, { 0x0114, 0x0000 }, { 0x0116, 0x0000 }, - { 0x0117, 0x0f00 }, + { 0x0117, 0x0f28 }, { 0x0118, 0x0006 }, { 0x0125, 0x2424 }, { 0x0126, 0x5550 }, @@ -693,7 +695,7 @@ static const struct reg_default rt5663_reg[] = { { 0x0251, 0x0000 }, { 0x0252, 0x028a }, { 0x02fa, 0x0000 }, - { 0x02fb, 0x00a4 }, + { 0x02fb, 0x8089 }, { 0x02fc, 0x0300 }, { 0x0300, 0x0000 }, { 0x03d0, 0x0000 }, @@ -1556,6 +1558,14 @@ static int rt5663_jack_detect(struct snd_soc_component *component, int jack_inse RT5663_PWR_MB_MASK | RT5663_LDO1_DVO_MASK | RT5663_AMP_HP_MASK, RT5663_PWR_MB | RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_VREF1 | RT5663_PWR_VREF2); + msleep(20); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_FV1 | RT5663_PWR_FV2); snd_soc_component_update_bits(component, RT5663_AUTO_1MRC_CLK, RT5663_IRQ_POW_SAV_MASK, RT5663_IRQ_POW_SAV_EN); snd_soc_component_update_bits(component, RT5663_IRQ_1, @@ -1613,7 +1623,10 @@ static int rt5663_jack_detect(struct snd_soc_component *component, int jack_inse break; default: rt5663->jack_type = SND_JACK_HEADPHONE; - + snd_soc_component_update_bits(component, + RT5663_PWR_ANLG_1, + RT5663_PWR_MB_MASK | RT5663_PWR_VREF1_MASK | + RT5663_PWR_VREF2_MASK, 0); if (rt5663->pdata.impedance_sensing_num) break; @@ -1638,6 +1651,9 @@ static int rt5663_jack_detect(struct snd_soc_component *component, int jack_inse if (rt5663->jack_type == SND_JACK_HEADSET) rt5663_enable_push_button_irq(component, false); rt5663->jack_type = 0; + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_MB_MASK | RT5663_PWR_VREF1_MASK | + RT5663_PWR_VREF2_MASK, 0); } dev_dbg(component->dev, "jack_type = %d\n", rt5663->jack_type); @@ -1840,8 +1856,8 @@ static irqreturn_t rt5663_irq(int irq, void *data) return IRQ_HANDLED; } -int rt5663_set_jack_detect(struct snd_soc_component *component, - struct snd_soc_jack *hs_jack) +static int rt5663_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) { struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); @@ -1851,7 +1867,6 @@ int rt5663_set_jack_detect(struct snd_soc_component *component, return 0; } -EXPORT_SYMBOL_GPL(rt5663_set_jack_detect); static bool rt5663_check_jd_status(struct snd_soc_component *component) { @@ -2307,6 +2322,8 @@ static int rt5663_hp_event(struct snd_soc_dapm_widget *w, RT5663_HP_SIG_SRC1_MASK, RT5663_HP_SIG_SRC1_SILENCE); } else { + snd_soc_component_update_bits(component, + RT5663_DACREF_LDO, 0x3e0e, 0x3a0a); snd_soc_component_write(component, RT5663_DEPOP_2, 0x3003); snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1, RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_DIS); @@ -2332,6 +2349,8 @@ static int rt5663_hp_event(struct snd_soc_dapm_widget *w, snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0x0); snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1, RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_EN); + snd_soc_component_update_bits(component, + RT5663_DACREF_LDO, 0x3e0e, 0); } break; @@ -3086,9 +3105,17 @@ static int rt5663_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_OFF: - snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, - RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | - RT5663_PWR_FV1 | RT5663_PWR_FV2, 0x0); + if (rt5663->jack_type != SND_JACK_HEADSET) + snd_soc_component_update_bits(component, + RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1 | RT5663_PWR_FV2 | + RT5663_PWR_MB_MASK, 0); + else + snd_soc_component_update_bits(component, + RT5663_PWR_ANLG_1, + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_FV1 | RT5663_PWR_FV2); break; default: @@ -3216,10 +3243,10 @@ static const struct snd_soc_component_driver soc_component_dev_rt5663 = { .num_dapm_widgets = ARRAY_SIZE(rt5663_dapm_widgets), .dapm_routes = rt5663_dapm_routes, .num_dapm_routes = ARRAY_SIZE(rt5663_dapm_routes), + .set_jack = rt5663_set_jack_detect, .use_pmdown_time = 1, .endianness = 1, .non_legacy_dai_naming = 1, - }; static const struct regmap_config rt5663_v2_regmap = { @@ -3310,6 +3337,7 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663) regmap_write(rt5663->regmap, RT5663_HP_IMP_SEN_19, 0x000c); regmap_write(rt5663->regmap, RT5663_DUMMY_1, 0x0324); regmap_write(rt5663->regmap, RT5663_DIG_MISC, 0x8001); + regmap_write(rt5663->regmap, RT5663_VREFADJ_OP, 0x0f28); regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa23b); msleep(30); regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf23b); @@ -3344,6 +3372,7 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663) regmap_write(rt5663->regmap, RT5663_PWR_ANLG_2, 0x8003); regmap_write(rt5663->regmap, RT5663_PWR_ANLG_3, 0x018c); regmap_write(rt5663->regmap, RT5663_HP_CHARGE_PUMP_1, 0x1e32); + regmap_write(rt5663->regmap, RT5663_DUMMY_2, 0x8089); regmap_write(rt5663->regmap, RT5663_DACREF_LDO, 0x3b0b); msleep(40); regmap_write(rt5663->regmap, RT5663_STO_DAC_MIXER, 0x0000); @@ -3578,15 +3607,9 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5663->regmap, RT5663_GPIO_1, RT5663_GPIO1_TYPE_MASK, RT5663_GPIO1_TYPE_EN); regmap_write(rt5663->regmap, RT5663_VREF_RECMIX, 0x0032); - regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa2be); - msleep(20); - regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf2be); regmap_update_bits(rt5663->regmap, RT5663_GPIO_2, RT5663_GP1_PIN_CONF_MASK | RT5663_SEL_GPIO1_MASK, RT5663_GP1_PIN_CONF_OUTPUT | RT5663_SEL_GPIO1_EN); - /* DACREF LDO control */ - regmap_update_bits(rt5663->regmap, RT5663_DACREF_LDO, 0x3e0e, - 0x3a0a); regmap_update_bits(rt5663->regmap, RT5663_RECMIX, RT5663_RECMIX1_BST1_MASK, RT5663_RECMIX1_BST1_ON); regmap_update_bits(rt5663->regmap, RT5663_TDM_2, diff --git a/sound/soc/codecs/rt5663.h b/sound/soc/codecs/rt5663.h index 865203cc2034..794cf3fadf31 100644 --- a/sound/soc/codecs/rt5663.h +++ b/sound/soc/codecs/rt5663.h @@ -1125,8 +1125,6 @@ enum { RT5663_AD_STEREO_FILTER = 0x2, }; -int rt5663_set_jack_detect(struct snd_soc_component *component, - struct snd_soc_jack *hs_jack); int rt5663_sel_asrc_clk_src(struct snd_soc_component *component, unsigned int filter_mask, unsigned int clk_src); diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c new file mode 100644 index 000000000000..3c19d03f2446 --- /dev/null +++ b/sound/soc/codecs/rt5668.c @@ -0,0 +1,2639 @@ +/* + * rt5668.c -- RT5668B ALSA SoC audio component driver + * + * Copyright 2018 Realtek Semiconductor Corp. + * Author: Bard Liao <bardliao@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/acpi.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/mutex.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/rt5668.h> + +#include "rl6231.h" +#include "rt5668.h" + +#define RT5668_NUM_SUPPLIES 3 + +static const char *rt5668_supply_names[RT5668_NUM_SUPPLIES] = { + "AVDD", + "MICVDD", + "VBAT", +}; + +struct rt5668_priv { + struct snd_soc_component *component; + struct rt5668_platform_data pdata; + struct regmap *regmap; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5668_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work jd_check_work; + struct mutex calibrate_mutex; + + int sysclk; + int sysclk_src; + int lrck[RT5668_AIFS]; + int bclk[RT5668_AIFS]; + int master[RT5668_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; +}; + +static const struct reg_default rt5668_reg[] = { + {0x0002, 0x8080}, + {0x0003, 0x8000}, + {0x0005, 0x0000}, + {0x0006, 0x0000}, + {0x0008, 0x800f}, + {0x000b, 0x0000}, + {0x0010, 0x4040}, + {0x0011, 0x0000}, + {0x0012, 0x1404}, + {0x0013, 0x1000}, + {0x0014, 0xa00a}, + {0x0015, 0x0404}, + {0x0016, 0x0404}, + {0x0019, 0xafaf}, + {0x001c, 0x2f2f}, + {0x001f, 0x0000}, + {0x0022, 0x5757}, + {0x0023, 0x0039}, + {0x0024, 0x000b}, + {0x0026, 0xc0c4}, + {0x0029, 0x8080}, + {0x002a, 0xa0a0}, + {0x002b, 0x0300}, + {0x0030, 0x0000}, + {0x003c, 0x0080}, + {0x0044, 0x0c0c}, + {0x0049, 0x0000}, + {0x0061, 0x0000}, + {0x0062, 0x0000}, + {0x0063, 0x003f}, + {0x0064, 0x0000}, + {0x0065, 0x0000}, + {0x0066, 0x0030}, + {0x0067, 0x0000}, + {0x006b, 0x0000}, + {0x006c, 0x0000}, + {0x006d, 0x2200}, + {0x006e, 0x0a10}, + {0x0070, 0x8000}, + {0x0071, 0x8000}, + {0x0073, 0x0000}, + {0x0074, 0x0000}, + {0x0075, 0x0002}, + {0x0076, 0x0001}, + {0x0079, 0x0000}, + {0x007a, 0x0000}, + {0x007b, 0x0000}, + {0x007c, 0x0100}, + {0x007e, 0x0000}, + {0x0080, 0x0000}, + {0x0081, 0x0000}, + {0x0082, 0x0000}, + {0x0083, 0x0000}, + {0x0084, 0x0000}, + {0x0085, 0x0000}, + {0x0086, 0x0005}, + {0x0087, 0x0000}, + {0x0088, 0x0000}, + {0x008c, 0x0003}, + {0x008d, 0x0000}, + {0x008e, 0x0060}, + {0x008f, 0x1000}, + {0x0091, 0x0c26}, + {0x0092, 0x0073}, + {0x0093, 0x0000}, + {0x0094, 0x0080}, + {0x0098, 0x0000}, + {0x009a, 0x0000}, + {0x009b, 0x0000}, + {0x009c, 0x0000}, + {0x009d, 0x0000}, + {0x009e, 0x100c}, + {0x009f, 0x0000}, + {0x00a0, 0x0000}, + {0x00a3, 0x0002}, + {0x00a4, 0x0001}, + {0x00ae, 0x2040}, + {0x00af, 0x0000}, + {0x00b6, 0x0000}, + {0x00b7, 0x0000}, + {0x00b8, 0x0000}, + {0x00b9, 0x0002}, + {0x00be, 0x0000}, + {0x00c0, 0x0160}, + {0x00c1, 0x82a0}, + {0x00c2, 0x0000}, + {0x00d0, 0x0000}, + {0x00d1, 0x2244}, + {0x00d2, 0x3300}, + {0x00d3, 0x2200}, + {0x00d4, 0x0000}, + {0x00d9, 0x0009}, + {0x00da, 0x0000}, + {0x00db, 0x0000}, + {0x00dc, 0x00c0}, + {0x00dd, 0x2220}, + {0x00de, 0x3131}, + {0x00df, 0x3131}, + {0x00e0, 0x3131}, + {0x00e2, 0x0000}, + {0x00e3, 0x4000}, + {0x00e4, 0x0aa0}, + {0x00e5, 0x3131}, + {0x00e6, 0x3131}, + {0x00e7, 0x3131}, + {0x00e8, 0x3131}, + {0x00ea, 0xb320}, + {0x00eb, 0x0000}, + {0x00f0, 0x0000}, + {0x00f1, 0x00d0}, + {0x00f2, 0x00d0}, + {0x00f6, 0x0000}, + {0x00fa, 0x0000}, + {0x00fb, 0x0000}, + {0x00fc, 0x0000}, + {0x00fd, 0x0000}, + {0x00fe, 0x10ec}, + {0x00ff, 0x6530}, + {0x0100, 0xa0a0}, + {0x010b, 0x0000}, + {0x010c, 0xae00}, + {0x010d, 0xaaa0}, + {0x010e, 0x8aa2}, + {0x010f, 0x02a2}, + {0x0110, 0xc000}, + {0x0111, 0x04a2}, + {0x0112, 0x2800}, + {0x0113, 0x0000}, + {0x0117, 0x0100}, + {0x0125, 0x0410}, + {0x0132, 0x6026}, + {0x0136, 0x5555}, + {0x0138, 0x3700}, + {0x013a, 0x2000}, + {0x013b, 0x2000}, + {0x013c, 0x2005}, + {0x013f, 0x0000}, + {0x0142, 0x0000}, + {0x0145, 0x0002}, + {0x0146, 0x0000}, + {0x0147, 0x0000}, + {0x0148, 0x0000}, + {0x0149, 0x0000}, + {0x0150, 0x79a1}, + {0x0151, 0x0000}, + {0x0160, 0x4ec0}, + {0x0161, 0x0080}, + {0x0162, 0x0200}, + {0x0163, 0x0800}, + {0x0164, 0x0000}, + {0x0165, 0x0000}, + {0x0166, 0x0000}, + {0x0167, 0x000f}, + {0x0168, 0x000f}, + {0x0169, 0x0021}, + {0x0190, 0x413d}, + {0x0194, 0x0000}, + {0x0195, 0x0000}, + {0x0197, 0x0022}, + {0x0198, 0x0000}, + {0x0199, 0x0000}, + {0x01af, 0x0000}, + {0x01b0, 0x0400}, + {0x01b1, 0x0000}, + {0x01b2, 0x0000}, + {0x01b3, 0x0000}, + {0x01b4, 0x0000}, + {0x01b5, 0x0000}, + {0x01b6, 0x01c3}, + {0x01b7, 0x02a0}, + {0x01b8, 0x03e9}, + {0x01b9, 0x1389}, + {0x01ba, 0xc351}, + {0x01bb, 0x0009}, + {0x01bc, 0x0018}, + {0x01bd, 0x002a}, + {0x01be, 0x004c}, + {0x01bf, 0x0097}, + {0x01c0, 0x433d}, + {0x01c1, 0x2800}, + {0x01c2, 0x0000}, + {0x01c3, 0x0000}, + {0x01c4, 0x0000}, + {0x01c5, 0x0000}, + {0x01c6, 0x0000}, + {0x01c7, 0x0000}, + {0x01c8, 0x40af}, + {0x01c9, 0x0702}, + {0x01ca, 0x0000}, + {0x01cb, 0x0000}, + {0x01cc, 0x5757}, + {0x01cd, 0x5757}, + {0x01ce, 0x5757}, + {0x01cf, 0x5757}, + {0x01d0, 0x5757}, + {0x01d1, 0x5757}, + {0x01d2, 0x5757}, + {0x01d3, 0x5757}, + {0x01d4, 0x5757}, + {0x01d5, 0x5757}, + {0x01d6, 0x0000}, + {0x01d7, 0x0008}, + {0x01d8, 0x0029}, + {0x01d9, 0x3333}, + {0x01da, 0x0000}, + {0x01db, 0x0004}, + {0x01dc, 0x0000}, + {0x01de, 0x7c00}, + {0x01df, 0x0320}, + {0x01e0, 0x06a1}, + {0x01e1, 0x0000}, + {0x01e2, 0x0000}, + {0x01e3, 0x0000}, + {0x01e4, 0x0000}, + {0x01e6, 0x0001}, + {0x01e7, 0x0000}, + {0x01e8, 0x0000}, + {0x01ea, 0x0000}, + {0x01eb, 0x0000}, + {0x01ec, 0x0000}, + {0x01ed, 0x0000}, + {0x01ee, 0x0000}, + {0x01ef, 0x0000}, + {0x01f0, 0x0000}, + {0x01f1, 0x0000}, + {0x01f2, 0x0000}, + {0x01f3, 0x0000}, + {0x01f4, 0x0000}, + {0x0210, 0x6297}, + {0x0211, 0xa005}, + {0x0212, 0x824c}, + {0x0213, 0xf7ff}, + {0x0214, 0xf24c}, + {0x0215, 0x0102}, + {0x0216, 0x00a3}, + {0x0217, 0x0048}, + {0x0218, 0xa2c0}, + {0x0219, 0x0400}, + {0x021a, 0x00c8}, + {0x021b, 0x00c0}, + {0x021c, 0x0000}, + {0x0250, 0x4500}, + {0x0251, 0x40b3}, + {0x0252, 0x0000}, + {0x0253, 0x0000}, + {0x0254, 0x0000}, + {0x0255, 0x0000}, + {0x0256, 0x0000}, + {0x0257, 0x0000}, + {0x0258, 0x0000}, + {0x0259, 0x0000}, + {0x025a, 0x0005}, + {0x0270, 0x0000}, + {0x02ff, 0x0110}, + {0x0300, 0x001f}, + {0x0301, 0x032c}, + {0x0302, 0x5f21}, + {0x0303, 0x4000}, + {0x0304, 0x4000}, + {0x0305, 0x06d5}, + {0x0306, 0x8000}, + {0x0307, 0x0700}, + {0x0310, 0x4560}, + {0x0311, 0xa4a8}, + {0x0312, 0x7418}, + {0x0313, 0x0000}, + {0x0314, 0x0006}, + {0x0315, 0xffff}, + {0x0316, 0xc400}, + {0x0317, 0x0000}, + {0x03c0, 0x7e00}, + {0x03c1, 0x8000}, + {0x03c2, 0x8000}, + {0x03c3, 0x8000}, + {0x03c4, 0x8000}, + {0x03c5, 0x8000}, + {0x03c6, 0x8000}, + {0x03c7, 0x8000}, + {0x03c8, 0x8000}, + {0x03c9, 0x8000}, + {0x03ca, 0x8000}, + {0x03cb, 0x8000}, + {0x03cc, 0x8000}, + {0x03d0, 0x0000}, + {0x03d1, 0x0000}, + {0x03d2, 0x0000}, + {0x03d3, 0x0000}, + {0x03d4, 0x2000}, + {0x03d5, 0x2000}, + {0x03d6, 0x0000}, + {0x03d7, 0x0000}, + {0x03d8, 0x2000}, + {0x03d9, 0x2000}, + {0x03da, 0x2000}, + {0x03db, 0x2000}, + {0x03dc, 0x0000}, + {0x03dd, 0x0000}, + {0x03de, 0x0000}, + {0x03df, 0x2000}, + {0x03e0, 0x0000}, + {0x03e1, 0x0000}, + {0x03e2, 0x0000}, + {0x03e3, 0x0000}, + {0x03e4, 0x0000}, + {0x03e5, 0x0000}, + {0x03e6, 0x0000}, + {0x03e7, 0x0000}, + {0x03e8, 0x0000}, + {0x03e9, 0x0000}, + {0x03ea, 0x0000}, + {0x03eb, 0x0000}, + {0x03ec, 0x0000}, + {0x03ed, 0x0000}, + {0x03ee, 0x0000}, + {0x03ef, 0x0000}, + {0x03f0, 0x0800}, + {0x03f1, 0x0800}, + {0x03f2, 0x0800}, + {0x03f3, 0x0800}, +}; + +static bool rt5668_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5668_RESET: + case RT5668_CBJ_CTRL_2: + case RT5668_INT_ST_1: + case RT5668_4BTN_IL_CMD_1: + case RT5668_AJD1_CTRL: + case RT5668_HP_CALIB_CTRL_1: + case RT5668_DEVICE_ID: + case RT5668_I2C_MODE: + case RT5668_HP_CALIB_CTRL_10: + case RT5668_EFUSE_CTRL_2: + case RT5668_JD_TOP_VC_VTRL: + case RT5668_HP_IMP_SENS_CTRL_19: + case RT5668_IL_CMD_1: + case RT5668_SAR_IL_CMD_2: + case RT5668_SAR_IL_CMD_4: + case RT5668_SAR_IL_CMD_10: + case RT5668_SAR_IL_CMD_11: + case RT5668_EFUSE_CTRL_6...RT5668_EFUSE_CTRL_11: + case RT5668_HP_CALIB_STA_1...RT5668_HP_CALIB_STA_11: + return true; + default: + return false; + } +} + +static bool rt5668_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5668_RESET: + case RT5668_VERSION_ID: + case RT5668_VENDOR_ID: + case RT5668_DEVICE_ID: + case RT5668_HP_CTRL_1: + case RT5668_HP_CTRL_2: + case RT5668_HPL_GAIN: + case RT5668_HPR_GAIN: + case RT5668_I2C_CTRL: + case RT5668_CBJ_BST_CTRL: + case RT5668_CBJ_CTRL_1: + case RT5668_CBJ_CTRL_2: + case RT5668_CBJ_CTRL_3: + case RT5668_CBJ_CTRL_4: + case RT5668_CBJ_CTRL_5: + case RT5668_CBJ_CTRL_6: + case RT5668_CBJ_CTRL_7: + case RT5668_DAC1_DIG_VOL: + case RT5668_STO1_ADC_DIG_VOL: + case RT5668_STO1_ADC_BOOST: + case RT5668_HP_IMP_GAIN_1: + case RT5668_HP_IMP_GAIN_2: + case RT5668_SIDETONE_CTRL: + case RT5668_STO1_ADC_MIXER: + case RT5668_AD_DA_MIXER: + case RT5668_STO1_DAC_MIXER: + case RT5668_A_DAC1_MUX: + case RT5668_DIG_INF2_DATA: + case RT5668_REC_MIXER: + case RT5668_CAL_REC: + case RT5668_ALC_BACK_GAIN: + case RT5668_PWR_DIG_1: + case RT5668_PWR_DIG_2: + case RT5668_PWR_ANLG_1: + case RT5668_PWR_ANLG_2: + case RT5668_PWR_ANLG_3: + case RT5668_PWR_MIXER: + case RT5668_PWR_VOL: + case RT5668_CLK_DET: + case RT5668_RESET_LPF_CTRL: + case RT5668_RESET_HPF_CTRL: + case RT5668_DMIC_CTRL_1: + case RT5668_I2S1_SDP: + case RT5668_I2S2_SDP: + case RT5668_ADDA_CLK_1: + case RT5668_ADDA_CLK_2: + case RT5668_I2S1_F_DIV_CTRL_1: + case RT5668_I2S1_F_DIV_CTRL_2: + case RT5668_TDM_CTRL: + case RT5668_TDM_ADDA_CTRL_1: + case RT5668_TDM_ADDA_CTRL_2: + case RT5668_DATA_SEL_CTRL_1: + case RT5668_TDM_TCON_CTRL: + case RT5668_GLB_CLK: + case RT5668_PLL_CTRL_1: + case RT5668_PLL_CTRL_2: + case RT5668_PLL_TRACK_1: + case RT5668_PLL_TRACK_2: + case RT5668_PLL_TRACK_3: + case RT5668_PLL_TRACK_4: + case RT5668_PLL_TRACK_5: + case RT5668_PLL_TRACK_6: + case RT5668_PLL_TRACK_11: + case RT5668_SDW_REF_CLK: + case RT5668_DEPOP_1: + case RT5668_DEPOP_2: + case RT5668_HP_CHARGE_PUMP_1: + case RT5668_HP_CHARGE_PUMP_2: + case RT5668_MICBIAS_1: + case RT5668_MICBIAS_2: + case RT5668_PLL_TRACK_12: + case RT5668_PLL_TRACK_14: + case RT5668_PLL2_CTRL_1: + case RT5668_PLL2_CTRL_2: + case RT5668_PLL2_CTRL_3: + case RT5668_PLL2_CTRL_4: + case RT5668_RC_CLK_CTRL: + case RT5668_I2S_M_CLK_CTRL_1: + case RT5668_I2S2_F_DIV_CTRL_1: + case RT5668_I2S2_F_DIV_CTRL_2: + case RT5668_EQ_CTRL_1: + case RT5668_EQ_CTRL_2: + case RT5668_IRQ_CTRL_1: + case RT5668_IRQ_CTRL_2: + case RT5668_IRQ_CTRL_3: + case RT5668_IRQ_CTRL_4: + case RT5668_INT_ST_1: + case RT5668_GPIO_CTRL_1: + case RT5668_GPIO_CTRL_2: + case RT5668_GPIO_CTRL_3: + case RT5668_HP_AMP_DET_CTRL_1: + case RT5668_HP_AMP_DET_CTRL_2: + case RT5668_MID_HP_AMP_DET: + case RT5668_LOW_HP_AMP_DET: + case RT5668_DELAY_BUF_CTRL: + case RT5668_SV_ZCD_1: + case RT5668_SV_ZCD_2: + case RT5668_IL_CMD_1: + case RT5668_IL_CMD_2: + case RT5668_IL_CMD_3: + case RT5668_IL_CMD_4: + case RT5668_IL_CMD_5: + case RT5668_IL_CMD_6: + case RT5668_4BTN_IL_CMD_1: + case RT5668_4BTN_IL_CMD_2: + case RT5668_4BTN_IL_CMD_3: + case RT5668_4BTN_IL_CMD_4: + case RT5668_4BTN_IL_CMD_5: + case RT5668_4BTN_IL_CMD_6: + case RT5668_4BTN_IL_CMD_7: + case RT5668_ADC_STO1_HP_CTRL_1: + case RT5668_ADC_STO1_HP_CTRL_2: + case RT5668_AJD1_CTRL: + case RT5668_JD1_THD: + case RT5668_JD2_THD: + case RT5668_JD_CTRL_1: + case RT5668_DUMMY_1: + case RT5668_DUMMY_2: + case RT5668_DUMMY_3: + case RT5668_DAC_ADC_DIG_VOL1: + case RT5668_BIAS_CUR_CTRL_2: + case RT5668_BIAS_CUR_CTRL_3: + case RT5668_BIAS_CUR_CTRL_4: + case RT5668_BIAS_CUR_CTRL_5: + case RT5668_BIAS_CUR_CTRL_6: + case RT5668_BIAS_CUR_CTRL_7: + case RT5668_BIAS_CUR_CTRL_8: + case RT5668_BIAS_CUR_CTRL_9: + case RT5668_BIAS_CUR_CTRL_10: + case RT5668_VREF_REC_OP_FB_CAP_CTRL: + case RT5668_CHARGE_PUMP_1: + case RT5668_DIG_IN_CTRL_1: + case RT5668_PAD_DRIVING_CTRL: + case RT5668_SOFT_RAMP_DEPOP: + case RT5668_CHOP_DAC: + case RT5668_CHOP_ADC: + case RT5668_CALIB_ADC_CTRL: + case RT5668_VOL_TEST: + case RT5668_SPKVDD_DET_STA: + case RT5668_TEST_MODE_CTRL_1: + case RT5668_TEST_MODE_CTRL_2: + case RT5668_TEST_MODE_CTRL_3: + case RT5668_TEST_MODE_CTRL_4: + case RT5668_TEST_MODE_CTRL_5: + case RT5668_PLL1_INTERNAL: + case RT5668_PLL2_INTERNAL: + case RT5668_STO_NG2_CTRL_1: + case RT5668_STO_NG2_CTRL_2: + case RT5668_STO_NG2_CTRL_3: + case RT5668_STO_NG2_CTRL_4: + case RT5668_STO_NG2_CTRL_5: + case RT5668_STO_NG2_CTRL_6: + case RT5668_STO_NG2_CTRL_7: + case RT5668_STO_NG2_CTRL_8: + case RT5668_STO_NG2_CTRL_9: + case RT5668_STO_NG2_CTRL_10: + case RT5668_STO1_DAC_SIL_DET: + case RT5668_SIL_PSV_CTRL1: + case RT5668_SIL_PSV_CTRL2: + case RT5668_SIL_PSV_CTRL3: + case RT5668_SIL_PSV_CTRL4: + case RT5668_SIL_PSV_CTRL5: + case RT5668_HP_IMP_SENS_CTRL_01: + case RT5668_HP_IMP_SENS_CTRL_02: + case RT5668_HP_IMP_SENS_CTRL_03: + case RT5668_HP_IMP_SENS_CTRL_04: + case RT5668_HP_IMP_SENS_CTRL_05: + case RT5668_HP_IMP_SENS_CTRL_06: + case RT5668_HP_IMP_SENS_CTRL_07: + case RT5668_HP_IMP_SENS_CTRL_08: + case RT5668_HP_IMP_SENS_CTRL_09: + case RT5668_HP_IMP_SENS_CTRL_10: + case RT5668_HP_IMP_SENS_CTRL_11: + case RT5668_HP_IMP_SENS_CTRL_12: + case RT5668_HP_IMP_SENS_CTRL_13: + case RT5668_HP_IMP_SENS_CTRL_14: + case RT5668_HP_IMP_SENS_CTRL_15: + case RT5668_HP_IMP_SENS_CTRL_16: + case RT5668_HP_IMP_SENS_CTRL_17: + case RT5668_HP_IMP_SENS_CTRL_18: + case RT5668_HP_IMP_SENS_CTRL_19: + case RT5668_HP_IMP_SENS_CTRL_20: + case RT5668_HP_IMP_SENS_CTRL_21: + case RT5668_HP_IMP_SENS_CTRL_22: + case RT5668_HP_IMP_SENS_CTRL_23: + case RT5668_HP_IMP_SENS_CTRL_24: + case RT5668_HP_IMP_SENS_CTRL_25: + case RT5668_HP_IMP_SENS_CTRL_26: + case RT5668_HP_IMP_SENS_CTRL_27: + case RT5668_HP_IMP_SENS_CTRL_28: + case RT5668_HP_IMP_SENS_CTRL_29: + case RT5668_HP_IMP_SENS_CTRL_30: + case RT5668_HP_IMP_SENS_CTRL_31: + case RT5668_HP_IMP_SENS_CTRL_32: + case RT5668_HP_IMP_SENS_CTRL_33: + case RT5668_HP_IMP_SENS_CTRL_34: + case RT5668_HP_IMP_SENS_CTRL_35: + case RT5668_HP_IMP_SENS_CTRL_36: + case RT5668_HP_IMP_SENS_CTRL_37: + case RT5668_HP_IMP_SENS_CTRL_38: + case RT5668_HP_IMP_SENS_CTRL_39: + case RT5668_HP_IMP_SENS_CTRL_40: + case RT5668_HP_IMP_SENS_CTRL_41: + case RT5668_HP_IMP_SENS_CTRL_42: + case RT5668_HP_IMP_SENS_CTRL_43: + case RT5668_HP_LOGIC_CTRL_1: + case RT5668_HP_LOGIC_CTRL_2: + case RT5668_HP_LOGIC_CTRL_3: + case RT5668_HP_CALIB_CTRL_1: + case RT5668_HP_CALIB_CTRL_2: + case RT5668_HP_CALIB_CTRL_3: + case RT5668_HP_CALIB_CTRL_4: + case RT5668_HP_CALIB_CTRL_5: + case RT5668_HP_CALIB_CTRL_6: + case RT5668_HP_CALIB_CTRL_7: + case RT5668_HP_CALIB_CTRL_9: + case RT5668_HP_CALIB_CTRL_10: + case RT5668_HP_CALIB_CTRL_11: + case RT5668_HP_CALIB_STA_1: + case RT5668_HP_CALIB_STA_2: + case RT5668_HP_CALIB_STA_3: + case RT5668_HP_CALIB_STA_4: + case RT5668_HP_CALIB_STA_5: + case RT5668_HP_CALIB_STA_6: + case RT5668_HP_CALIB_STA_7: + case RT5668_HP_CALIB_STA_8: + case RT5668_HP_CALIB_STA_9: + case RT5668_HP_CALIB_STA_10: + case RT5668_HP_CALIB_STA_11: + case RT5668_SAR_IL_CMD_1: + case RT5668_SAR_IL_CMD_2: + case RT5668_SAR_IL_CMD_3: + case RT5668_SAR_IL_CMD_4: + case RT5668_SAR_IL_CMD_5: + case RT5668_SAR_IL_CMD_6: + case RT5668_SAR_IL_CMD_7: + case RT5668_SAR_IL_CMD_8: + case RT5668_SAR_IL_CMD_9: + case RT5668_SAR_IL_CMD_10: + case RT5668_SAR_IL_CMD_11: + case RT5668_SAR_IL_CMD_12: + case RT5668_SAR_IL_CMD_13: + case RT5668_EFUSE_CTRL_1: + case RT5668_EFUSE_CTRL_2: + case RT5668_EFUSE_CTRL_3: + case RT5668_EFUSE_CTRL_4: + case RT5668_EFUSE_CTRL_5: + case RT5668_EFUSE_CTRL_6: + case RT5668_EFUSE_CTRL_7: + case RT5668_EFUSE_CTRL_8: + case RT5668_EFUSE_CTRL_9: + case RT5668_EFUSE_CTRL_10: + case RT5668_EFUSE_CTRL_11: + case RT5668_JD_TOP_VC_VTRL: + case RT5668_DRC1_CTRL_0: + case RT5668_DRC1_CTRL_1: + case RT5668_DRC1_CTRL_2: + case RT5668_DRC1_CTRL_3: + case RT5668_DRC1_CTRL_4: + case RT5668_DRC1_CTRL_5: + case RT5668_DRC1_CTRL_6: + case RT5668_DRC1_HARD_LMT_CTRL_1: + case RT5668_DRC1_HARD_LMT_CTRL_2: + case RT5668_DRC1_PRIV_1: + case RT5668_DRC1_PRIV_2: + case RT5668_DRC1_PRIV_3: + case RT5668_DRC1_PRIV_4: + case RT5668_DRC1_PRIV_5: + case RT5668_DRC1_PRIV_6: + case RT5668_DRC1_PRIV_7: + case RT5668_DRC1_PRIV_8: + case RT5668_EQ_AUTO_RCV_CTRL1: + case RT5668_EQ_AUTO_RCV_CTRL2: + case RT5668_EQ_AUTO_RCV_CTRL3: + case RT5668_EQ_AUTO_RCV_CTRL4: + case RT5668_EQ_AUTO_RCV_CTRL5: + case RT5668_EQ_AUTO_RCV_CTRL6: + case RT5668_EQ_AUTO_RCV_CTRL7: + case RT5668_EQ_AUTO_RCV_CTRL8: + case RT5668_EQ_AUTO_RCV_CTRL9: + case RT5668_EQ_AUTO_RCV_CTRL10: + case RT5668_EQ_AUTO_RCV_CTRL11: + case RT5668_EQ_AUTO_RCV_CTRL12: + case RT5668_EQ_AUTO_RCV_CTRL13: + case RT5668_ADC_L_EQ_LPF1_A1: + case RT5668_R_EQ_LPF1_A1: + case RT5668_L_EQ_LPF1_H0: + case RT5668_R_EQ_LPF1_H0: + case RT5668_L_EQ_BPF1_A1: + case RT5668_R_EQ_BPF1_A1: + case RT5668_L_EQ_BPF1_A2: + case RT5668_R_EQ_BPF1_A2: + case RT5668_L_EQ_BPF1_H0: + case RT5668_R_EQ_BPF1_H0: + case RT5668_L_EQ_BPF2_A1: + case RT5668_R_EQ_BPF2_A1: + case RT5668_L_EQ_BPF2_A2: + case RT5668_R_EQ_BPF2_A2: + case RT5668_L_EQ_BPF2_H0: + case RT5668_R_EQ_BPF2_H0: + case RT5668_L_EQ_BPF3_A1: + case RT5668_R_EQ_BPF3_A1: + case RT5668_L_EQ_BPF3_A2: + case RT5668_R_EQ_BPF3_A2: + case RT5668_L_EQ_BPF3_H0: + case RT5668_R_EQ_BPF3_H0: + case RT5668_L_EQ_BPF4_A1: + case RT5668_R_EQ_BPF4_A1: + case RT5668_L_EQ_BPF4_A2: + case RT5668_R_EQ_BPF4_A2: + case RT5668_L_EQ_BPF4_H0: + case RT5668_R_EQ_BPF4_H0: + case RT5668_L_EQ_HPF1_A1: + case RT5668_R_EQ_HPF1_A1: + case RT5668_L_EQ_HPF1_H0: + case RT5668_R_EQ_HPF1_H0: + case RT5668_L_EQ_PRE_VOL: + case RT5668_R_EQ_PRE_VOL: + case RT5668_L_EQ_POST_VOL: + case RT5668_R_EQ_POST_VOL: + case RT5668_I2C_MODE: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5668_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5668_if2_adc_enum, + RT5668_DIG_INF2_DATA, RT5668_IF2_ADC_SEL_SFT, rt5668_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5668_if1_01_adc_enum, + RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC1_SEL_SFT, rt5668_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5668_if1_23_adc_enum, + RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC2_SEL_SFT, rt5668_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5668_if1_45_adc_enum, + RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC3_SEL_SFT, rt5668_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5668_if1_67_adc_enum, + RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC4_SEL_SFT, rt5668_data_select); + +static const struct snd_kcontrol_new rt5668_if2_adc_swap_mux = + SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5668_if2_adc_enum); + +static const struct snd_kcontrol_new rt5668_if1_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5668_if1_01_adc_enum); + +static const struct snd_kcontrol_new rt5668_if1_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5668_if1_23_adc_enum); + +static const struct snd_kcontrol_new rt5668_if1_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5668_if1_45_adc_enum); + +static const struct snd_kcontrol_new rt5668_if1_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5668_if1_67_adc_enum); + +static void rt5668_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT5668_RESET, 0); + regmap_write(regmap, RT5668_I2C_MODE, 1); +} +/** + * rt5668_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5668 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the component driver will turn on + * ASRC for these filters if ASRC is selected as their clock source. + */ +int rt5668_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + + switch (clk_src) { + case RT5668_CLK_SEL_SYS: + case RT5668_CLK_SEL_I2S1_ASRC: + case RT5668_CLK_SEL_I2S2_ASRC: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5668_DA_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5668_PLL_TRACK_2, + RT5668_FILTER_CLK_SEL_MASK, + clk_src << RT5668_FILTER_CLK_SEL_SFT); + } + + if (filter_mask & RT5668_AD_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5668_PLL_TRACK_3, + RT5668_FILTER_CLK_SEL_MASK, + clk_src << RT5668_FILTER_CLK_SEL_SFT); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5668_sel_asrc_clk_src); + +static int rt5668_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read32(component, RT5668_4BTN_IL_CMD_1); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5668_4BTN_IL_CMD_1, val); + pr_debug("%s btn_type=%x\n", __func__, btn_type); + + return btn_type; +} + +static void rt5668_enable_push_button_irq(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1, + RT5668_SAR_BUTT_DET_MASK, RT5668_SAR_BUTT_DET_EN); + snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_13, + RT5668_SAR_SOUR_MASK, RT5668_SAR_SOUR_BTN); + snd_soc_component_write(component, RT5668_IL_CMD_1, 0x0040); + snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2, + RT5668_4BTN_IL_MASK | RT5668_4BTN_IL_RST_MASK, + RT5668_4BTN_IL_EN | RT5668_4BTN_IL_NOR); + snd_soc_component_update_bits(component, RT5668_IRQ_CTRL_3, + RT5668_IL_IRQ_MASK, RT5668_IL_IRQ_EN); + } else { + snd_soc_component_update_bits(component, RT5668_IRQ_CTRL_3, + RT5668_IL_IRQ_MASK, RT5668_IL_IRQ_DIS); + snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1, + RT5668_SAR_BUTT_DET_MASK, RT5668_SAR_BUTT_DET_DIS); + snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2, + RT5668_4BTN_IL_MASK, RT5668_4BTN_IL_DIS); + snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2, + RT5668_4BTN_IL_RST_MASK, RT5668_4BTN_IL_RST); + snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_13, + RT5668_SAR_SOUR_MASK, RT5668_SAR_SOUR_TYPE); + } +} + +/** + * rt5668_headset_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5668_headset_detect(struct snd_soc_component *component, + int jack_insert) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + unsigned int val, count; + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_1, + RT5668_TRIG_JD_MASK, RT5668_TRIG_JD_HIGH); + + count = 0; + val = snd_soc_component_read32(component, RT5668_CBJ_CTRL_2) + & RT5668_JACK_TYPE_MASK; + while (val == 0 && count < 50) { + usleep_range(10000, 15000); + val = snd_soc_component_read32(component, + RT5668_CBJ_CTRL_2) & RT5668_JACK_TYPE_MASK; + count++; + } + + switch (val) { + case 0x1: + case 0x2: + rt5668->jack_type = SND_JACK_HEADSET; + rt5668_enable_push_button_irq(component, true); + break; + default: + rt5668->jack_type = SND_JACK_HEADPHONE; + } + + } else { + rt5668_enable_push_button_irq(component, false); + snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_1, + RT5668_TRIG_JD_MASK, RT5668_TRIG_JD_LOW); + snd_soc_dapm_disable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + + rt5668->jack_type = 0; + } + + dev_dbg(component->dev, "jack_type = %d\n", rt5668->jack_type); + return rt5668->jack_type; +} + +static irqreturn_t rt5668_irq(int irq, void *data) +{ + struct rt5668_priv *rt5668 = data; + + mod_delayed_work(system_power_efficient_wq, + &rt5668->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static void rt5668_jd_check_handler(struct work_struct *work) +{ + struct rt5668_priv *rt5668 = container_of(work, struct rt5668_priv, + jd_check_work.work); + + if (snd_soc_component_read32(rt5668->component, RT5668_AJD1_CTRL) + & RT5668_JDH_RS_MASK) { + /* jack out */ + rt5668->jack_type = rt5668_headset_detect(rt5668->component, 0); + + snd_soc_jack_report(rt5668->hs_jack, rt5668->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else { + schedule_delayed_work(&rt5668->jd_check_work, 500); + } +} + +static int rt5668_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + switch (rt5668->pdata.jd_src) { + case RT5668_JD1: + snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_2, + RT5668_EXT_JD_SRC, RT5668_EXT_JD_SRC_MANUAL); + snd_soc_component_write(component, RT5668_CBJ_CTRL_1, 0xd002); + snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_3, + RT5668_CBJ_IN_BUF_EN, RT5668_CBJ_IN_BUF_EN); + snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1, + RT5668_SAR_POW_MASK, RT5668_SAR_POW_EN); + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP1_PIN_MASK, RT5668_GP1_PIN_IRQ); + regmap_update_bits(rt5668->regmap, RT5668_RC_CLK_CTRL, + RT5668_POW_IRQ | RT5668_POW_JDH | + RT5668_POW_ANA, RT5668_POW_IRQ | + RT5668_POW_JDH | RT5668_POW_ANA); + regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_2, + RT5668_PWR_JDH | RT5668_PWR_JDL, + RT5668_PWR_JDH | RT5668_PWR_JDL); + regmap_update_bits(rt5668->regmap, RT5668_IRQ_CTRL_2, + RT5668_JD1_EN_MASK | RT5668_JD1_POL_MASK, + RT5668_JD1_EN | RT5668_JD1_POL_NOR); + mod_delayed_work(system_power_efficient_wq, + &rt5668->jack_detect_work, msecs_to_jiffies(250)); + break; + + case RT5668_JD_NULL: + regmap_update_bits(rt5668->regmap, RT5668_IRQ_CTRL_2, + RT5668_JD1_EN_MASK, RT5668_JD1_DIS); + regmap_update_bits(rt5668->regmap, RT5668_RC_CLK_CTRL, + RT5668_POW_JDH | RT5668_POW_JDL, 0); + break; + + default: + dev_warn(component->dev, "Wrong JD source\n"); + break; + } + + rt5668->hs_jack = hs_jack; + + return 0; +} + +static void rt5668_jack_detect_handler(struct work_struct *work) +{ + struct rt5668_priv *rt5668 = + container_of(work, struct rt5668_priv, jack_detect_work.work); + int val, btn_type; + + while (!rt5668->component) + usleep_range(10000, 15000); + + while (!rt5668->component->card->instantiated) + usleep_range(10000, 15000); + + mutex_lock(&rt5668->calibrate_mutex); + + val = snd_soc_component_read32(rt5668->component, RT5668_AJD1_CTRL) + & RT5668_JDH_RS_MASK; + if (!val) { + /* jack in */ + if (rt5668->jack_type == 0) { + /* jack was out, report jack type */ + rt5668->jack_type = + rt5668_headset_detect(rt5668->component, 1); + } else { + /* jack is already in, report button event */ + rt5668->jack_type = SND_JACK_HEADSET; + btn_type = rt5668_button_detect(rt5668->component); + /** + * rt5668 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + rt5668->jack_type |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + rt5668->jack_type |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + rt5668->jack_type |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + rt5668->jack_type |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + btn_type = 0; + dev_err(rt5668->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + } else { + /* jack out */ + rt5668->jack_type = rt5668_headset_detect(rt5668->component, 0); + } + + snd_soc_jack_report(rt5668->hs_jack, rt5668->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (rt5668->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5668->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5668->jd_check_work); + + mutex_unlock(&rt5668->calibrate_mutex); +} + +static const struct snd_kcontrol_new rt5668_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5668_HPL_GAIN, + RT5668_HPR_GAIN, RT5668_G_HP_SFT, 15, 1, hp_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5668_DAC1_DIG_VOL, + RT5668_L_VOL_SFT, RT5668_R_VOL_SFT, 175, 0, dac_vol_tlv), + + /* IN Boost Volume */ + SOC_SINGLE_TLV("CBJ Boost Volume", RT5668_CBJ_BST_CTRL, + RT5668_BST_CBJ_SFT, 8, 0, bst_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("STO1 ADC Capture Switch", RT5668_STO1_ADC_DIG_VOL, + RT5668_L_MUTE_SFT, RT5668_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5668_STO1_ADC_DIG_VOL, + RT5668_L_VOL_SFT, RT5668_R_VOL_SFT, 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5668_STO1_ADC_BOOST, + RT5668_STO1_ADC_L_BST_SFT, RT5668_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), +}; + + +static int rt5668_div_sel(struct rt5668_priv *rt5668, + int target, const int div[], int size) +{ + int i; + + if (rt5668->sysclk < target) { + pr_err("sysclk rate %d is too low\n", + rt5668->sysclk); + return 0; + } + + for (i = 0; i < size - 1; i++) { + pr_info("div[%d]=%d\n", i, div[i]); + if (target * div[i] == rt5668->sysclk) + return i; + if (target * div[i + 1] > rt5668->sysclk) { + pr_err("can't find div for sysclk %d\n", + rt5668->sysclk); + return i; + } + } + + if (target * div[i] < rt5668->sysclk) + pr_err("sysclk rate %d is too high\n", + rt5668->sysclk); + + return size - 1; + +} + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(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 rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + int idx = -EINVAL; + static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128}; + + idx = rt5668_div_sel(rt5668, 1500000, div, ARRAY_SIZE(div)); + + snd_soc_component_update_bits(component, RT5668_DMIC_CTRL_1, + RT5668_DMIC_CLK_MASK, idx << RT5668_DMIC_CLK_SFT); + + return 0; +} + +static int set_filter_clk(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 rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + int ref, val, reg, idx = -EINVAL; + static const int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48}; + + val = snd_soc_component_read32(component, RT5668_GPIO_CTRL_1) & + RT5668_GP4_PIN_MASK; + if (w->shift == RT5668_PWR_ADC_S1F_BIT && + val == RT5668_GP4_PIN_ADCDAT2) + ref = 256 * rt5668->lrck[RT5668_AIF2]; + else + ref = 256 * rt5668->lrck[RT5668_AIF1]; + + idx = rt5668_div_sel(rt5668, ref, div, ARRAY_SIZE(div)); + + if (w->shift == RT5668_PWR_ADC_S1F_BIT) + reg = RT5668_PLL_TRACK_3; + else + reg = RT5668_PLL_TRACK_2; + + snd_soc_component_update_bits(component, reg, + RT5668_FILTER_CLK_SEL_MASK, idx << RT5668_FILTER_CLK_SEL_SFT); + + return 0; +} + +static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read32(component, RT5668_GLB_CLK); + val &= RT5668_SCLK_SRC_MASK; + if (val == RT5668_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case RT5668_ADC_STO1_ASRC_SFT: + reg = RT5668_PLL_TRACK_3; + shift = RT5668_FILTER_CLK_SEL_SFT; + break; + case RT5668_DAC_STO1_ASRC_SFT: + reg = RT5668_PLL_TRACK_2; + shift = RT5668_FILTER_CLK_SEL_SFT; + break; + default: + return 0; + } + + val = (snd_soc_component_read32(component, reg) >> shift) & 0xf; + switch (val) { + case RT5668_CLK_SEL_I2S1_ASRC: + case RT5668_CLK_SEL_I2S2_ASRC: + return 1; + default: + return 0; + } + +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5668_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5668_STO1_ADC_MIXER, + RT5668_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5668_STO1_ADC_MIXER, + RT5668_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5668_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5668_STO1_ADC_MIXER, + RT5668_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5668_STO1_ADC_MIXER, + RT5668_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5668_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5668_AD_DA_MIXER, + RT5668_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5668_AD_DA_MIXER, + RT5668_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5668_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5668_AD_DA_MIXER, + RT5668_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5668_AD_DA_MIXER, + RT5668_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5668_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5668_STO1_DAC_MIXER, + RT5668_M_DAC_L1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5668_STO1_DAC_MIXER, + RT5668_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5668_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5668_STO1_DAC_MIXER, + RT5668_M_DAC_L1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5668_STO1_DAC_MIXER, + RT5668_M_DAC_R1_STO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5668_rec1_l_mix[] = { + SOC_DAPM_SINGLE("CBJ Switch", RT5668_REC_MIXER, + RT5668_M_CBJ_RM1_L_SFT, 1, 1), +}; + +/* STO1 ADC1 Source */ +/* MX-26 [13] [5] */ +static const char * const rt5668_sto1_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adc1l_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADC1L_SRC_SFT, rt5668_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5668_sto1_adc1l_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5668_sto1_adc1l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adc1r_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADC1R_SRC_SFT, rt5668_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5668_sto1_adc1r_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5668_sto1_adc1r_enum); + +/* STO1 ADC Source */ +/* MX-26 [11:10] [3:2] */ +static const char * const rt5668_sto1_adc_src[] = { + "ADC1 L", "ADC1 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adcl_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADCL_SRC_SFT, rt5668_sto1_adc_src); + +static const struct snd_kcontrol_new rt5668_sto1_adcl_mux = + SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5668_sto1_adcl_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adcr_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADCR_SRC_SFT, rt5668_sto1_adc_src); + +static const struct snd_kcontrol_new rt5668_sto1_adcr_mux = + SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5668_sto1_adcr_enum); + +/* STO1 ADC2 Source */ +/* MX-26 [12] [4] */ +static const char * const rt5668_sto1_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adc2l_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADC2L_SRC_SFT, rt5668_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5668_sto1_adc2l_mux = + SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5668_sto1_adc2l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adc2r_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADC2R_SRC_SFT, rt5668_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5668_sto1_adc2r_mux = + SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5668_sto1_adc2r_enum); + +/* MX-79 [6:4] I2S1 ADC data location */ +static const unsigned int rt5668_if1_adc_slot_values[] = { + 0, + 2, + 4, + 6, +}; + +static const char * const rt5668_if1_adc_slot_src[] = { + "Slot 0", "Slot 2", "Slot 4", "Slot 6" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5668_if1_adc_slot_enum, + RT5668_TDM_CTRL, RT5668_TDM_ADC_LCA_SFT, RT5668_TDM_ADC_LCA_MASK, + rt5668_if1_adc_slot_src, rt5668_if1_adc_slot_values); + +static const struct snd_kcontrol_new rt5668_if1_adc_slot_mux = + SOC_DAPM_ENUM("IF1 ADC Slot location", rt5668_if1_adc_slot_enum); + +/* Analog DAC L1 Source, Analog DAC R1 Source*/ +/* MX-2B [4], MX-2B [0]*/ +static const char * const rt5668_alg_dac1_src[] = { + "Stereo1 DAC Mixer", "DAC1" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5668_alg_dac_l1_enum, RT5668_A_DAC1_MUX, + RT5668_A_DACL1_SFT, rt5668_alg_dac1_src); + +static const struct snd_kcontrol_new rt5668_alg_dac_l1_mux = + SOC_DAPM_ENUM("Analog DAC L1 Source", rt5668_alg_dac_l1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5668_alg_dac_r1_enum, RT5668_A_DAC1_MUX, + RT5668_A_DACR1_SFT, rt5668_alg_dac1_src); + +static const struct snd_kcontrol_new rt5668_alg_dac_r1_mux = + SOC_DAPM_ENUM("Analog DAC R1 Source", rt5668_alg_dac_r1_enum); + +/* Out Switch */ +static const struct snd_kcontrol_new hpol_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5668_HP_CTRL_1, + RT5668_L_MUTE_SFT, 1, 1); +static const struct snd_kcontrol_new hpor_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5668_HP_CTRL_1, + RT5668_R_MUTE_SFT, 1, 1); + +static int rt5668_hp_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); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write(component, + RT5668_HP_LOGIC_CTRL_2, 0x0012); + snd_soc_component_write(component, + RT5668_HP_CTRL_2, 0x6000); + snd_soc_component_update_bits(component, RT5668_STO_NG2_CTRL_1, + RT5668_NG2_EN_MASK, RT5668_NG2_EN); + snd_soc_component_update_bits(component, + RT5668_DEPOP_1, 0x60, 0x60); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + RT5668_DEPOP_1, 0x60, 0x0); + snd_soc_component_write(component, + RT5668_HP_CTRL_2, 0x0000); + break; + + default: + return 0; + } + + return 0; + +} + +static int set_dmic_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /*Add delay to avoid pop noise*/ + msleep(150); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5655_set_verf(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case RT5668_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5668_PWR_ANLG_1, RT5668_PWR_FV1, 0); + break; + + case RT5668_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5668_PWR_ANLG_1, RT5668_PWR_FV2, 0); + break; + + default: + break; + } + break; + + case SND_SOC_DAPM_POST_PMU: + usleep_range(15000, 20000); + switch (w->shift) { + case RT5668_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5668_PWR_ANLG_1, RT5668_PWR_FV1, + RT5668_PWR_FV1); + break; + + case RT5668_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5668_PWR_ANLG_1, RT5668_PWR_FV2, + RT5668_PWR_FV2); + break; + + default: + break; + } + break; + + default: + return 0; + } + + return 0; +} + +static const unsigned int rt5668_adcdat_pin_values[] = { + 1, + 3, +}; + +static const char * const rt5668_adcdat_pin_select[] = { + "ADCDAT1", + "ADCDAT2", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5668_adcdat_pin_enum, + RT5668_GPIO_CTRL_1, RT5668_GP4_PIN_SFT, RT5668_GP4_PIN_MASK, + rt5668_adcdat_pin_select, rt5668_adcdat_pin_values); + +static const struct snd_kcontrol_new rt5668_adcdat_pin_ctrl = + SOC_DAPM_ENUM("ADCDAT", rt5668_adcdat_pin_enum); + +static const struct snd_soc_dapm_widget rt5668_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5668_PWR_ANLG_3, RT5668_PWR_LDO2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5668_PWR_ANLG_3, RT5668_PWR_PLL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B", RT5668_PWR_ANLG_3, RT5668_PWR_PLL2B_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F", RT5668_PWR_ANLG_3, RT5668_PWR_PLL2F_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Vref1", RT5668_PWR_ANLG_1, RT5668_PWR_VREF1_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref2", RT5668_PWR_ANLG_1, RT5668_PWR_VREF2_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5668_PLL_TRACK_1, + RT5668_DAC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5668_PLL_TRACK_1, + RT5668_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5668_PLL_TRACK_1, + RT5668_AD_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5668_PLL_TRACK_1, + RT5668_DA_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5668_PLL_TRACK_1, + RT5668_DMIC_ASRC_SFT, 0, NULL, 0), + + /* Input Side */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5668_PWR_ANLG_2, RT5668_PWR_MB1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5668_PWR_ANLG_2, RT5668_PWR_MB2_BIT, + 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + + SND_SOC_DAPM_INPUT("IN1P"), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5668_DMIC_CTRL_1, + RT5668_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, + 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("CBJ Power", RT5668_PWR_ANLG_3, + RT5668_PWR_CBJ_BIT, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5668_rec1_l_mix, + ARRAY_SIZE(rt5668_rec1_l_mix)), + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5668_PWR_ANLG_2, + RT5668_PWR_RM1_L_BIT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5668_PWR_DIG_1, + RT5668_PWR_ADC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5668_PWR_DIG_1, + RT5668_PWR_ADC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5668_CHOP_ADC, + RT5668_CKGEN_ADC1_SFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adcl_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adcr_mux), + SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if1_adc_slot_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5668_PWR_DIG_2, + RT5668_PWR_ADC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5668_STO1_ADC_DIG_VOL, + RT5668_L_MUTE_SFT, 1, rt5668_sto1_adc_l_mix, + ARRAY_SIZE(rt5668_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5668_STO1_ADC_DIG_VOL, + RT5668_R_MUTE_SFT, 1, rt5668_sto1_adc_r_mix, + ARRAY_SIZE(rt5668_sto1_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5668_PWR_DIG_1, RT5668_PWR_I2S1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5668_PWR_DIG_1, RT5668_PWR_I2S2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if2_adc_swap_mux), + + SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, + &rt5668_adcdat_pin_ctrl), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, + RT5668_I2S1_SDP, RT5668_SEL_ADCDAT_SFT, 1), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, + RT5668_I2S2_SDP, RT5668_I2S2_PIN_CFG_SFT, 1), + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5668_dac_l_mix, ARRAY_SIZE(rt5668_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5668_dac_r_mix, ARRAY_SIZE(rt5668_dac_r_mix)), + + /* DAC channel Mux */ + SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, + &rt5668_alg_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, + &rt5668_alg_dac_r1_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5668_PWR_DIG_2, + RT5668_PWR_DAC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5668_sto1_dac_l_mix, ARRAY_SIZE(rt5668_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5668_sto1_dac_r_mix, ARRAY_SIZE(rt5668_sto1_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5668_PWR_DIG_1, + RT5668_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5668_PWR_DIG_1, + RT5668_PWR_DAC_R1_BIT, 0), + SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 3, RT5668_CHOP_DAC, + RT5668_CKGEN_DAC1_SFT, 0, NULL, 0), + + /* HPO */ + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5668_hp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("HP Amp L", RT5668_PWR_ANLG_1, + RT5668_PWR_HA_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP Amp R", RT5668_PWR_ANLG_1, + RT5668_PWR_HA_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5668_DEPOP_1, + RT5668_PUMP_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5668_DEPOP_1, + RT5668_CAPLESS_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("HPOL Playback", SND_SOC_NOPM, 0, 0, + &hpol_switch), + SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0, + &hpor_switch), + + /* CLK DET */ + SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5668_CLK_DET, + RT5668_SYS_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5668_CLK_DET, + RT5668_PLL1_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL2", RT5668_CLK_DET, + RT5668_PLL2_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET", RT5668_CLK_DET, + RT5668_POW_CLK_DET_SFT, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + +}; + +static const struct snd_soc_dapm_route rt5668_dapm_routes[] = { + /*PLL*/ + {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + + /*ASRC*/ + {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, + {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, + {"ADC STO1 ASRC", NULL, "AD ASRC"}, + {"DAC STO1 ASRC", NULL, "DA ASRC"}, + + /*Vref*/ + {"MICBIAS1", NULL, "Vref1"}, + {"MICBIAS1", NULL, "Vref2"}, + {"MICBIAS2", NULL, "Vref1"}, + {"MICBIAS2", NULL, "Vref2"}, + + {"CLKDET SYS", NULL, "CLKDET"}, + + {"IN1P", NULL, "LDO2"}, + + {"BST1 CBJ", NULL, "IN1P"}, + {"BST1 CBJ", NULL, "CBJ Power"}, + {"CBJ Power", NULL, "Vref2"}, + + {"RECMIX1L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX1L", NULL, "RECMIX1L Power"}, + + {"ADC1 L", NULL, "RECMIX1L"}, + {"ADC1 L", NULL, "ADC1 L Power"}, + {"ADC1 L", NULL, "ADC1 clock"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC CLK", NULL, "DMIC ASRC"}, + + {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"}, + + {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"}, + {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"}, + {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"}, + {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"}, + {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, + + {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + + {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"}, + {"IF1_ADC Mux", NULL, "I2S1"}, + {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"}, + {"AIF1TX", NULL, "ADCDAT Mux"}, + {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"}, + {"AIF2TX", NULL, "ADCDAT Mux"}, + + {"IF1 DAC1 L", NULL, "AIF1RX"}, + {"IF1 DAC1 L", NULL, "I2S1"}, + {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"}, + {"IF1 DAC1 R", NULL, "AIF1RX"}, + {"IF1 DAC1 R", NULL, "I2S1"}, + {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"}, + + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"}, + {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"}, + + {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + + {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + + {"DAC L1 Source", "DAC1", "DAC1 MIXL"}, + {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC R1 Source", "DAC1", "DAC1 MIXR"}, + {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + + {"DAC L1", NULL, "DAC L1 Source"}, + {"DAC R1", NULL, "DAC R1 Source"}, + + {"DAC L1", NULL, "DAC 1 Clock"}, + {"DAC R1", NULL, "DAC 1 Clock"}, + + {"HP Amp", NULL, "DAC L1"}, + {"HP Amp", NULL, "DAC R1"}, + {"HP Amp", NULL, "HP Amp L"}, + {"HP Amp", NULL, "HP Amp R"}, + {"HP Amp", NULL, "Capless"}, + {"HP Amp", NULL, "Charge Pump"}, + {"HP Amp", NULL, "CLKDET SYS"}, + {"HP Amp", NULL, "CBJ Power"}, + {"HP Amp", NULL, "Vref2"}, + {"HPOL Playback", "Switch", "HP Amp"}, + {"HPOR Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HPOL Playback"}, + {"HPOR", NULL, "HPOR Playback"}, +}; + +static int rt5668_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; + unsigned int val = 0; + + switch (slots) { + case 4: + val |= RT5668_TDM_TX_CH_4; + val |= RT5668_TDM_RX_CH_4; + break; + case 6: + val |= RT5668_TDM_TX_CH_6; + val |= RT5668_TDM_RX_CH_6; + break; + case 8: + val |= RT5668_TDM_TX_CH_8; + val |= RT5668_TDM_RX_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5668_TDM_CTRL, + RT5668_TDM_TX_CH_MASK | RT5668_TDM_RX_CH_MASK, val); + + switch (slot_width) { + case 16: + val = RT5668_TDM_CL_16; + break; + case 20: + val = RT5668_TDM_CL_20; + break; + case 24: + val = RT5668_TDM_CL_24; + break; + case 32: + val = RT5668_TDM_CL_32; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5668_TDM_TCON_CTRL, + RT5668_TDM_CL_MASK, val); + + return 0; +} + + +static int rt5668_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 rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + unsigned int len_1 = 0, len_2 = 0; + int pre_div, frame_size; + + rt5668->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5668->sysclk, rt5668->lrck[dai->id]); + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt5668->lrck[dai->id], pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + len_1 |= RT5668_I2S1_DL_20; + len_2 |= RT5668_I2S2_DL_20; + break; + case 24: + len_1 |= RT5668_I2S1_DL_24; + len_2 |= RT5668_I2S2_DL_24; + break; + case 32: + len_1 |= RT5668_I2S1_DL_32; + len_2 |= RT5668_I2S2_DL_24; + break; + case 8: + len_1 |= RT5668_I2S2_DL_8; + len_2 |= RT5668_I2S2_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5668_AIF1: + snd_soc_component_update_bits(component, RT5668_I2S1_SDP, + RT5668_I2S1_DL_MASK, len_1); + if (rt5668->master[RT5668_AIF1]) { + snd_soc_component_update_bits(component, + RT5668_ADDA_CLK_1, RT5668_I2S_M_DIV_MASK, + pre_div << RT5668_I2S_M_DIV_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5668_I2S1_SDP, RT5668_I2S1_MONO_MASK, + RT5668_I2S1_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5668_I2S1_SDP, RT5668_I2S1_MONO_MASK, + RT5668_I2S1_MONO_DIS); + break; + case RT5668_AIF2: + snd_soc_component_update_bits(component, RT5668_I2S2_SDP, + RT5668_I2S2_DL_MASK, len_2); + if (rt5668->master[RT5668_AIF2]) { + snd_soc_component_update_bits(component, + RT5668_I2S_M_CLK_CTRL_1, RT5668_I2S2_M_PD_MASK, + pre_div << RT5668_I2S2_M_PD_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5668_I2S2_SDP, RT5668_I2S2_MONO_MASK, + RT5668_I2S2_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5668_I2S2_SDP, RT5668_I2S2_MONO_MASK, + RT5668_I2S2_MONO_DIS); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5668_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, tdm_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5668->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rt5668->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5668_I2S_BP_INV; + tdm_ctrl |= RT5668_TDM_S_BP_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + if (dai->id == RT5668_AIF1) + tdm_ctrl |= RT5668_TDM_S_LP_INV | RT5668_TDM_M_BP_INV; + else + return -EINVAL; + break; + case SND_SOC_DAIFMT_IB_IF: + if (dai->id == RT5668_AIF1) + tdm_ctrl |= RT5668_TDM_S_BP_INV | RT5668_TDM_S_LP_INV | + RT5668_TDM_M_BP_INV | RT5668_TDM_M_LP_INV; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5668_I2S_DF_LEFT; + tdm_ctrl |= RT5668_TDM_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5668_I2S_DF_PCM_A; + tdm_ctrl |= RT5668_TDM_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5668_I2S_DF_PCM_B; + tdm_ctrl |= RT5668_TDM_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5668_AIF1: + snd_soc_component_update_bits(component, RT5668_I2S1_SDP, + RT5668_I2S_DF_MASK, reg_val); + snd_soc_component_update_bits(component, RT5668_TDM_TCON_CTRL, + RT5668_TDM_MS_MASK | RT5668_TDM_S_BP_MASK | + RT5668_TDM_DF_MASK | RT5668_TDM_M_BP_MASK | + RT5668_TDM_M_LP_MASK | RT5668_TDM_S_LP_MASK, + tdm_ctrl | rt5668->master[dai->id]); + break; + case RT5668_AIF2: + if (rt5668->master[dai->id] == 0) + reg_val |= RT5668_I2S2_MS_S; + snd_soc_component_update_bits(component, RT5668_I2S2_SDP, + RT5668_I2S2_MS_MASK | RT5668_I2S_BP_MASK | + RT5668_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5668_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, src = 0; + + if (freq == rt5668->sysclk && clk_id == rt5668->sysclk_src) + return 0; + + switch (clk_id) { + case RT5668_SCLK_S_MCLK: + reg_val |= RT5668_SCLK_SRC_MCLK; + src = RT5668_CLK_SRC_MCLK; + break; + case RT5668_SCLK_S_PLL1: + reg_val |= RT5668_SCLK_SRC_PLL1; + src = RT5668_CLK_SRC_PLL1; + break; + case RT5668_SCLK_S_PLL2: + reg_val |= RT5668_SCLK_SRC_PLL2; + src = RT5668_CLK_SRC_PLL2; + break; + case RT5668_SCLK_S_RCCLK: + reg_val |= RT5668_SCLK_SRC_RCCLK; + src = RT5668_CLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5668_GLB_CLK, + RT5668_SCLK_SRC_MASK, reg_val); + + if (rt5668->master[RT5668_AIF2]) { + snd_soc_component_update_bits(component, + RT5668_I2S_M_CLK_CTRL_1, RT5668_I2S2_SRC_MASK, + src << RT5668_I2S2_SRC_SFT); + } + + rt5668->sysclk = freq; + rt5668->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt5668_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5668->pll_src && freq_in == rt5668->pll_in && + freq_out == rt5668->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5668->pll_in = 0; + rt5668->pll_out = 0; + snd_soc_component_update_bits(component, RT5668_GLB_CLK, + RT5668_SCLK_SRC_MASK, RT5668_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5668_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5668_GLB_CLK, + RT5668_PLL1_SRC_MASK, RT5668_PLL1_SRC_MCLK); + break; + case RT5668_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, RT5668_GLB_CLK, + RT5668_PLL1_SRC_MASK, RT5668_PLL1_SRC_BCLK1); + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5668_PLL_CTRL_1, + pll_code.n_code << RT5668_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5668_PLL_CTRL_2, + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5668_PLL_M_SFT | + pll_code.m_bp << RT5668_PLL_M_BP_SFT); + + rt5668->pll_in = freq_in; + rt5668->pll_out = freq_out; + rt5668->pll_src = source; + + return 0; +} + +static int rt5668_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + rt5668->bclk[dai->id] = ratio; + + switch (ratio) { + case 64: + snd_soc_component_update_bits(component, RT5668_ADDA_CLK_2, + RT5668_I2S2_BCLK_MS2_MASK, + RT5668_I2S2_BCLK_MS2_64); + break; + case 32: + snd_soc_component_update_bits(component, RT5668_ADDA_CLK_2, + RT5668_I2S2_BCLK_MS2_MASK, + RT5668_I2S2_BCLK_MS2_32); + break; + default: + dev_err(dai->dev, "Invalid bclk ratio %d\n", ratio); + return -EINVAL; + } + + return 0; +} + +static int rt5668_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1, + RT5668_PWR_MB | RT5668_PWR_BG, + RT5668_PWR_MB | RT5668_PWR_BG); + regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1, + RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO, + RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO); + break; + + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1, + RT5668_PWR_MB, RT5668_PWR_MB); + regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1, + RT5668_DIG_GATE_CTRL, RT5668_DIG_GATE_CTRL); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1, + RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO, 0); + regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1, + RT5668_PWR_MB | RT5668_PWR_BG, 0); + break; + + default: + break; + } + + return 0; +} + +static int rt5668_probe(struct snd_soc_component *component) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + rt5668->component = component; + + return 0; +} + +static void rt5668_remove(struct snd_soc_component *component) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + rt5668_reset(rt5668->regmap); +} + +#ifdef CONFIG_PM +static int rt5668_suspend(struct snd_soc_component *component) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5668->regmap, true); + regcache_mark_dirty(rt5668->regmap); + return 0; +} + +static int rt5668_resume(struct snd_soc_component *component) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5668->regmap, false); + regcache_sync(rt5668->regmap); + + return 0; +} +#else +#define rt5668_suspend NULL +#define rt5668_resume NULL +#endif + +#define RT5668_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5668_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5668_aif1_dai_ops = { + .hw_params = rt5668_hw_params, + .set_fmt = rt5668_set_dai_fmt, + .set_tdm_slot = rt5668_set_tdm_slot, +}; + +static const struct snd_soc_dai_ops rt5668_aif2_dai_ops = { + .hw_params = rt5668_hw_params, + .set_fmt = rt5668_set_dai_fmt, + .set_bclk_ratio = rt5668_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5668_dai[] = { + { + .name = "rt5668-aif1", + .id = RT5668_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5668_STEREO_RATES, + .formats = RT5668_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5668_STEREO_RATES, + .formats = RT5668_FORMATS, + }, + .ops = &rt5668_aif1_dai_ops, + }, + { + .name = "rt5668-aif2", + .id = RT5668_AIF2, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5668_STEREO_RATES, + .formats = RT5668_FORMATS, + }, + .ops = &rt5668_aif2_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5668 = { + .probe = rt5668_probe, + .remove = rt5668_remove, + .suspend = rt5668_suspend, + .resume = rt5668_resume, + .set_bias_level = rt5668_set_bias_level, + .controls = rt5668_snd_controls, + .num_controls = ARRAY_SIZE(rt5668_snd_controls), + .dapm_widgets = rt5668_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5668_dapm_widgets), + .dapm_routes = rt5668_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5668_dapm_routes), + .set_sysclk = rt5668_set_component_sysclk, + .set_pll = rt5668_set_component_pll, + .set_jack = rt5668_set_jack_detect, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config rt5668_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT5668_I2C_MODE, + .volatile_reg = rt5668_volatile_register, + .readable_reg = rt5668_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5668_reg, + .num_reg_defaults = ARRAY_SIZE(rt5668_reg), + .use_single_rw = true, +}; + +static const struct i2c_device_id rt5668_i2c_id[] = { + {"rt5668b", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5668_i2c_id); + +static int rt5668_parse_dt(struct rt5668_priv *rt5668, struct device *dev) +{ + + of_property_read_u32(dev->of_node, "realtek,dmic1-data-pin", + &rt5668->pdata.dmic1_data_pin); + of_property_read_u32(dev->of_node, "realtek,dmic1-clk-pin", + &rt5668->pdata.dmic1_clk_pin); + 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; +} + +static void rt5668_calibrate(struct rt5668_priv *rt5668) +{ + int value, count; + + mutex_lock(&rt5668->calibrate_mutex); + + rt5668_reset(rt5668->regmap); + regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xa2bf); + usleep_range(15000, 20000); + regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xf2bf); + regmap_write(rt5668->regmap, RT5668_MICBIAS_2, 0x0380); + regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x8001); + regmap_write(rt5668->regmap, RT5668_TEST_MODE_CTRL_1, 0x0000); + regmap_write(rt5668->regmap, RT5668_STO1_DAC_MIXER, 0x2080); + regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0x4040); + regmap_write(rt5668->regmap, RT5668_DEPOP_1, 0x0069); + regmap_write(rt5668->regmap, RT5668_CHOP_DAC, 0x3000); + regmap_write(rt5668->regmap, RT5668_HP_CTRL_2, 0x6000); + regmap_write(rt5668->regmap, RT5668_HP_CHARGE_PUMP_1, 0x0f26); + regmap_write(rt5668->regmap, RT5668_CALIB_ADC_CTRL, 0x7f05); + regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0x686c); + regmap_write(rt5668->regmap, RT5668_CAL_REC, 0x0d0d); + regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_9, 0x000f); + regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x8d01); + regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_2, 0x0321); + regmap_write(rt5668->regmap, RT5668_HP_LOGIC_CTRL_2, 0x0004); + regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_1, 0x7c00); + regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_3, 0x06a1); + regmap_write(rt5668->regmap, RT5668_A_DAC1_MUX, 0x0311); + regmap_write(rt5668->regmap, RT5668_RESET_HPF_CTRL, 0x0000); + regmap_write(rt5668->regmap, RT5668_ADC_STO1_HP_CTRL_1, 0x3320); + + regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_1, 0xfc00); + + for (count = 0; count < 60; count++) { + regmap_read(rt5668->regmap, RT5668_HP_CALIB_STA_1, &value); + if (!(value & 0x8000)) + break; + + usleep_range(10000, 10005); + } + + if (count >= 60) + pr_err("HP Calibration Failure\n"); + + /* restore settings */ + regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x0000); + + mutex_unlock(&rt5668->calibrate_mutex); + +} + +static int rt5668_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5668_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5668_priv *rt5668; + int i, ret; + unsigned int val; + + rt5668 = devm_kzalloc(&i2c->dev, sizeof(struct rt5668_priv), + GFP_KERNEL); + + if (rt5668 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5668); + + if (pdata) + rt5668->pdata = *pdata; + else + rt5668_parse_dt(rt5668, &i2c->dev); + + rt5668->regmap = devm_regmap_init_i2c(i2c, &rt5668_regmap); + if (IS_ERR(rt5668->regmap)) { + ret = PTR_ERR(rt5668->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(rt5668->supplies); i++) + rt5668->supplies[i].supply = rt5668_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5668->supplies), + rt5668->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5668->supplies), + rt5668->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + 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"); + } + + /* Sleep for 300 ms miniumum */ + usleep_range(300000, 350000); + + regmap_write(rt5668->regmap, RT5668_I2C_MODE, 0x1); + usleep_range(10000, 15000); + + regmap_read(rt5668->regmap, RT5668_DEVICE_ID, &val); + if (val != DEVICE_ID) { + pr_err("Device with ID register %x is not rt5668\n", val); + return -ENODEV; + } + + rt5668_reset(rt5668->regmap); + + rt5668_calibrate(rt5668); + + regmap_write(rt5668->regmap, RT5668_DEPOP_1, 0x0000); + + /* DMIC pin*/ + if (rt5668->pdata.dmic1_data_pin != RT5668_DMIC1_NULL) { + switch (rt5668->pdata.dmic1_data_pin) { + case RT5668_DMIC1_DATA_GPIO2: /* share with LRCK2 */ + regmap_update_bits(rt5668->regmap, RT5668_DMIC_CTRL_1, + RT5668_DMIC_1_DP_MASK, RT5668_DMIC_1_DP_GPIO2); + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP2_PIN_MASK, RT5668_GP2_PIN_DMIC_SDA); + break; + + case RT5668_DMIC1_DATA_GPIO5: /* share with DACDAT1 */ + regmap_update_bits(rt5668->regmap, RT5668_DMIC_CTRL_1, + RT5668_DMIC_1_DP_MASK, RT5668_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP5_PIN_MASK, RT5668_GP5_PIN_DMIC_SDA); + break; + + default: + dev_dbg(&i2c->dev, "invalid DMIC_DAT pin\n"); + break; + } + + switch (rt5668->pdata.dmic1_clk_pin) { + case RT5668_DMIC1_CLK_GPIO1: /* share with IRQ */ + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP1_PIN_MASK, RT5668_GP1_PIN_DMIC_CLK); + break; + + case RT5668_DMIC1_CLK_GPIO3: /* share with BCLK2 */ + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP3_PIN_MASK, RT5668_GP3_PIN_DMIC_CLK); + break; + + default: + dev_dbg(&i2c->dev, "invalid DMIC_CLK pin\n"); + break; + } + } + + regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1, + RT5668_LDO1_DVO_MASK | RT5668_HP_DRIVER_MASK, + RT5668_LDO1_DVO_14 | RT5668_HP_DRIVER_5X); + regmap_write(rt5668->regmap, RT5668_MICBIAS_2, 0x0380); + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP4_PIN_MASK | RT5668_GP5_PIN_MASK, + RT5668_GP4_PIN_ADCDAT1 | RT5668_GP5_PIN_DACDAT1); + regmap_write(rt5668->regmap, RT5668_TEST_MODE_CTRL_1, 0x0000); + + INIT_DELAYED_WORK(&rt5668->jack_detect_work, + rt5668_jack_detect_handler); + INIT_DELAYED_WORK(&rt5668->jd_check_work, + rt5668_jd_check_handler); + + mutex_init(&rt5668->calibrate_mutex); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5668_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5668", rt5668); + if (ret) + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + + } + + return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668, + rt5668_dai, ARRAY_SIZE(rt5668_dai)); +} + +static int rt5668_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_component(&i2c->dev); + + return 0; +} + +static void rt5668_i2c_shutdown(struct i2c_client *client) +{ + struct rt5668_priv *rt5668 = i2c_get_clientdata(client); + + rt5668_reset(rt5668->regmap); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt5668_of_match[] = { + {.compatible = "realtek,rt5668b"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5668_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5668_acpi_match[] = { + {"10EC5668", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5668_acpi_match); +#endif + +static struct i2c_driver rt5668_i2c_driver = { + .driver = { + .name = "rt5668b", + .of_match_table = of_match_ptr(rt5668_of_match), + .acpi_match_table = ACPI_PTR(rt5668_acpi_match), + }, + .probe = rt5668_i2c_probe, + .remove = rt5668_i2c_remove, + .shutdown = rt5668_i2c_shutdown, + .id_table = rt5668_i2c_id, +}; +module_i2c_driver(rt5668_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5668B driver"); +MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5668.h b/sound/soc/codecs/rt5668.h new file mode 100644 index 000000000000..3e7bcfd569ec --- /dev/null +++ b/sound/soc/codecs/rt5668.h @@ -0,0 +1,1318 @@ +/* + * rt5668.h -- RT5668/RT5658 ALSA SoC audio driver + * + * Copyright 2018 Realtek Microelectronics + * Author: Bard Liao <bardliao@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __RT5668_H__ +#define __RT5668_H__ + +#include <sound/rt5668.h> + +#define DEVICE_ID 0x6530 + +/* Info */ +#define RT5668_RESET 0x0000 +#define RT5668_VERSION_ID 0x00fd +#define RT5668_VENDOR_ID 0x00fe +#define RT5668_DEVICE_ID 0x00ff +/* I/O - Output */ +#define RT5668_HP_CTRL_1 0x0002 +#define RT5668_HP_CTRL_2 0x0003 +#define RT5668_HPL_GAIN 0x0005 +#define RT5668_HPR_GAIN 0x0006 + +#define RT5668_I2C_CTRL 0x0008 + +/* I/O - Input */ +#define RT5668_CBJ_BST_CTRL 0x000b +#define RT5668_CBJ_CTRL_1 0x0010 +#define RT5668_CBJ_CTRL_2 0x0011 +#define RT5668_CBJ_CTRL_3 0x0012 +#define RT5668_CBJ_CTRL_4 0x0013 +#define RT5668_CBJ_CTRL_5 0x0014 +#define RT5668_CBJ_CTRL_6 0x0015 +#define RT5668_CBJ_CTRL_7 0x0016 +/* I/O - ADC/DAC/DMIC */ +#define RT5668_DAC1_DIG_VOL 0x0019 +#define RT5668_STO1_ADC_DIG_VOL 0x001c +#define RT5668_STO1_ADC_BOOST 0x001f +#define RT5668_HP_IMP_GAIN_1 0x0022 +#define RT5668_HP_IMP_GAIN_2 0x0023 +/* Mixer - D-D */ +#define RT5668_SIDETONE_CTRL 0x0024 +#define RT5668_STO1_ADC_MIXER 0x0026 +#define RT5668_AD_DA_MIXER 0x0029 +#define RT5668_STO1_DAC_MIXER 0x002a +#define RT5668_A_DAC1_MUX 0x002b +#define RT5668_DIG_INF2_DATA 0x0030 +/* Mixer - ADC */ +#define RT5668_REC_MIXER 0x003c +#define RT5668_CAL_REC 0x0044 +#define RT5668_ALC_BACK_GAIN 0x0049 +/* Power */ +#define RT5668_PWR_DIG_1 0x0061 +#define RT5668_PWR_DIG_2 0x0062 +#define RT5668_PWR_ANLG_1 0x0063 +#define RT5668_PWR_ANLG_2 0x0064 +#define RT5668_PWR_ANLG_3 0x0065 +#define RT5668_PWR_MIXER 0x0066 +#define RT5668_PWR_VOL 0x0067 +/* Clock Detect */ +#define RT5668_CLK_DET 0x006b +/* Filter Auto Reset */ +#define RT5668_RESET_LPF_CTRL 0x006c +#define RT5668_RESET_HPF_CTRL 0x006d +/* DMIC */ +#define RT5668_DMIC_CTRL_1 0x006e +/* Format - ADC/DAC */ +#define RT5668_I2S1_SDP 0x0070 +#define RT5668_I2S2_SDP 0x0071 +#define RT5668_ADDA_CLK_1 0x0073 +#define RT5668_ADDA_CLK_2 0x0074 +#define RT5668_I2S1_F_DIV_CTRL_1 0x0075 +#define RT5668_I2S1_F_DIV_CTRL_2 0x0076 +/* Format - TDM Control */ +#define RT5668_TDM_CTRL 0x0079 +#define RT5668_TDM_ADDA_CTRL_1 0x007a +#define RT5668_TDM_ADDA_CTRL_2 0x007b +#define RT5668_DATA_SEL_CTRL_1 0x007c +#define RT5668_TDM_TCON_CTRL 0x007e +/* Function - Analog */ +#define RT5668_GLB_CLK 0x0080 +#define RT5668_PLL_CTRL_1 0x0081 +#define RT5668_PLL_CTRL_2 0x0082 +#define RT5668_PLL_TRACK_1 0x0083 +#define RT5668_PLL_TRACK_2 0x0084 +#define RT5668_PLL_TRACK_3 0x0085 +#define RT5668_PLL_TRACK_4 0x0086 +#define RT5668_PLL_TRACK_5 0x0087 +#define RT5668_PLL_TRACK_6 0x0088 +#define RT5668_PLL_TRACK_11 0x008c +#define RT5668_SDW_REF_CLK 0x008d +#define RT5668_DEPOP_1 0x008e +#define RT5668_DEPOP_2 0x008f +#define RT5668_HP_CHARGE_PUMP_1 0x0091 +#define RT5668_HP_CHARGE_PUMP_2 0x0092 +#define RT5668_MICBIAS_1 0x0093 +#define RT5668_MICBIAS_2 0x0094 +#define RT5668_PLL_TRACK_12 0x0098 +#define RT5668_PLL_TRACK_14 0x009a +#define RT5668_PLL2_CTRL_1 0x009b +#define RT5668_PLL2_CTRL_2 0x009c +#define RT5668_PLL2_CTRL_3 0x009d +#define RT5668_PLL2_CTRL_4 0x009e +#define RT5668_RC_CLK_CTRL 0x009f +#define RT5668_I2S_M_CLK_CTRL_1 0x00a0 +#define RT5668_I2S2_F_DIV_CTRL_1 0x00a3 +#define RT5668_I2S2_F_DIV_CTRL_2 0x00a4 +/* Function - Digital */ +#define RT5668_EQ_CTRL_1 0x00ae +#define RT5668_EQ_CTRL_2 0x00af +#define RT5668_IRQ_CTRL_1 0x00b6 +#define RT5668_IRQ_CTRL_2 0x00b7 +#define RT5668_IRQ_CTRL_3 0x00b8 +#define RT5668_IRQ_CTRL_4 0x00b9 +#define RT5668_INT_ST_1 0x00be +#define RT5668_GPIO_CTRL_1 0x00c0 +#define RT5668_GPIO_CTRL_2 0x00c1 +#define RT5668_GPIO_CTRL_3 0x00c2 +#define RT5668_HP_AMP_DET_CTRL_1 0x00d0 +#define RT5668_HP_AMP_DET_CTRL_2 0x00d1 +#define RT5668_MID_HP_AMP_DET 0x00d2 +#define RT5668_LOW_HP_AMP_DET 0x00d3 +#define RT5668_DELAY_BUF_CTRL 0x00d4 +#define RT5668_SV_ZCD_1 0x00d9 +#define RT5668_SV_ZCD_2 0x00da +#define RT5668_IL_CMD_1 0x00db +#define RT5668_IL_CMD_2 0x00dc +#define RT5668_IL_CMD_3 0x00dd +#define RT5668_IL_CMD_4 0x00de +#define RT5668_IL_CMD_5 0x00df +#define RT5668_IL_CMD_6 0x00e0 +#define RT5668_4BTN_IL_CMD_1 0x00e2 +#define RT5668_4BTN_IL_CMD_2 0x00e3 +#define RT5668_4BTN_IL_CMD_3 0x00e4 +#define RT5668_4BTN_IL_CMD_4 0x00e5 +#define RT5668_4BTN_IL_CMD_5 0x00e6 +#define RT5668_4BTN_IL_CMD_6 0x00e7 +#define RT5668_4BTN_IL_CMD_7 0x00e8 + +#define RT5668_ADC_STO1_HP_CTRL_1 0x00ea +#define RT5668_ADC_STO1_HP_CTRL_2 0x00eb +#define RT5668_AJD1_CTRL 0x00f0 +#define RT5668_JD1_THD 0x00f1 +#define RT5668_JD2_THD 0x00f2 +#define RT5668_JD_CTRL_1 0x00f6 +/* General Control */ +#define RT5668_DUMMY_1 0x00fa +#define RT5668_DUMMY_2 0x00fb +#define RT5668_DUMMY_3 0x00fc + +#define RT5668_DAC_ADC_DIG_VOL1 0x0100 +#define RT5668_BIAS_CUR_CTRL_2 0x010b +#define RT5668_BIAS_CUR_CTRL_3 0x010c +#define RT5668_BIAS_CUR_CTRL_4 0x010d +#define RT5668_BIAS_CUR_CTRL_5 0x010e +#define RT5668_BIAS_CUR_CTRL_6 0x010f +#define RT5668_BIAS_CUR_CTRL_7 0x0110 +#define RT5668_BIAS_CUR_CTRL_8 0x0111 +#define RT5668_BIAS_CUR_CTRL_9 0x0112 +#define RT5668_BIAS_CUR_CTRL_10 0x0113 +#define RT5668_VREF_REC_OP_FB_CAP_CTRL 0x0117 +#define RT5668_CHARGE_PUMP_1 0x0125 +#define RT5668_DIG_IN_CTRL_1 0x0132 +#define RT5668_PAD_DRIVING_CTRL 0x0136 +#define RT5668_SOFT_RAMP_DEPOP 0x0138 +#define RT5668_CHOP_DAC 0x013a +#define RT5668_CHOP_ADC 0x013b +#define RT5668_CALIB_ADC_CTRL 0x013c +#define RT5668_VOL_TEST 0x013f +#define RT5668_SPKVDD_DET_STA 0x0142 +#define RT5668_TEST_MODE_CTRL_1 0x0145 +#define RT5668_TEST_MODE_CTRL_2 0x0146 +#define RT5668_TEST_MODE_CTRL_3 0x0147 +#define RT5668_TEST_MODE_CTRL_4 0x0148 +#define RT5668_TEST_MODE_CTRL_5 0x0149 +#define RT5668_PLL1_INTERNAL 0x0150 +#define RT5668_PLL2_INTERNAL 0x0151 +#define RT5668_STO_NG2_CTRL_1 0x0160 +#define RT5668_STO_NG2_CTRL_2 0x0161 +#define RT5668_STO_NG2_CTRL_3 0x0162 +#define RT5668_STO_NG2_CTRL_4 0x0163 +#define RT5668_STO_NG2_CTRL_5 0x0164 +#define RT5668_STO_NG2_CTRL_6 0x0165 +#define RT5668_STO_NG2_CTRL_7 0x0166 +#define RT5668_STO_NG2_CTRL_8 0x0167 +#define RT5668_STO_NG2_CTRL_9 0x0168 +#define RT5668_STO_NG2_CTRL_10 0x0169 +#define RT5668_STO1_DAC_SIL_DET 0x0190 +#define RT5668_SIL_PSV_CTRL1 0x0194 +#define RT5668_SIL_PSV_CTRL2 0x0195 +#define RT5668_SIL_PSV_CTRL3 0x0197 +#define RT5668_SIL_PSV_CTRL4 0x0198 +#define RT5668_SIL_PSV_CTRL5 0x0199 +#define RT5668_HP_IMP_SENS_CTRL_01 0x01af +#define RT5668_HP_IMP_SENS_CTRL_02 0x01b0 +#define RT5668_HP_IMP_SENS_CTRL_03 0x01b1 +#define RT5668_HP_IMP_SENS_CTRL_04 0x01b2 +#define RT5668_HP_IMP_SENS_CTRL_05 0x01b3 +#define RT5668_HP_IMP_SENS_CTRL_06 0x01b4 +#define RT5668_HP_IMP_SENS_CTRL_07 0x01b5 +#define RT5668_HP_IMP_SENS_CTRL_08 0x01b6 +#define RT5668_HP_IMP_SENS_CTRL_09 0x01b7 +#define RT5668_HP_IMP_SENS_CTRL_10 0x01b8 +#define RT5668_HP_IMP_SENS_CTRL_11 0x01b9 +#define RT5668_HP_IMP_SENS_CTRL_12 0x01ba +#define RT5668_HP_IMP_SENS_CTRL_13 0x01bb +#define RT5668_HP_IMP_SENS_CTRL_14 0x01bc +#define RT5668_HP_IMP_SENS_CTRL_15 0x01bd +#define RT5668_HP_IMP_SENS_CTRL_16 0x01be +#define RT5668_HP_IMP_SENS_CTRL_17 0x01bf +#define RT5668_HP_IMP_SENS_CTRL_18 0x01c0 +#define RT5668_HP_IMP_SENS_CTRL_19 0x01c1 +#define RT5668_HP_IMP_SENS_CTRL_20 0x01c2 +#define RT5668_HP_IMP_SENS_CTRL_21 0x01c3 +#define RT5668_HP_IMP_SENS_CTRL_22 0x01c4 +#define RT5668_HP_IMP_SENS_CTRL_23 0x01c5 +#define RT5668_HP_IMP_SENS_CTRL_24 0x01c6 +#define RT5668_HP_IMP_SENS_CTRL_25 0x01c7 +#define RT5668_HP_IMP_SENS_CTRL_26 0x01c8 +#define RT5668_HP_IMP_SENS_CTRL_27 0x01c9 +#define RT5668_HP_IMP_SENS_CTRL_28 0x01ca +#define RT5668_HP_IMP_SENS_CTRL_29 0x01cb +#define RT5668_HP_IMP_SENS_CTRL_30 0x01cc +#define RT5668_HP_IMP_SENS_CTRL_31 0x01cd +#define RT5668_HP_IMP_SENS_CTRL_32 0x01ce +#define RT5668_HP_IMP_SENS_CTRL_33 0x01cf +#define RT5668_HP_IMP_SENS_CTRL_34 0x01d0 +#define RT5668_HP_IMP_SENS_CTRL_35 0x01d1 +#define RT5668_HP_IMP_SENS_CTRL_36 0x01d2 +#define RT5668_HP_IMP_SENS_CTRL_37 0x01d3 +#define RT5668_HP_IMP_SENS_CTRL_38 0x01d4 +#define RT5668_HP_IMP_SENS_CTRL_39 0x01d5 +#define RT5668_HP_IMP_SENS_CTRL_40 0x01d6 +#define RT5668_HP_IMP_SENS_CTRL_41 0x01d7 +#define RT5668_HP_IMP_SENS_CTRL_42 0x01d8 +#define RT5668_HP_IMP_SENS_CTRL_43 0x01d9 +#define RT5668_HP_LOGIC_CTRL_1 0x01da +#define RT5668_HP_LOGIC_CTRL_2 0x01db +#define RT5668_HP_LOGIC_CTRL_3 0x01dc +#define RT5668_HP_CALIB_CTRL_1 0x01de +#define RT5668_HP_CALIB_CTRL_2 0x01df +#define RT5668_HP_CALIB_CTRL_3 0x01e0 +#define RT5668_HP_CALIB_CTRL_4 0x01e1 +#define RT5668_HP_CALIB_CTRL_5 0x01e2 +#define RT5668_HP_CALIB_CTRL_6 0x01e3 +#define RT5668_HP_CALIB_CTRL_7 0x01e4 +#define RT5668_HP_CALIB_CTRL_9 0x01e6 +#define RT5668_HP_CALIB_CTRL_10 0x01e7 +#define RT5668_HP_CALIB_CTRL_11 0x01e8 +#define RT5668_HP_CALIB_STA_1 0x01ea +#define RT5668_HP_CALIB_STA_2 0x01eb +#define RT5668_HP_CALIB_STA_3 0x01ec +#define RT5668_HP_CALIB_STA_4 0x01ed +#define RT5668_HP_CALIB_STA_5 0x01ee +#define RT5668_HP_CALIB_STA_6 0x01ef +#define RT5668_HP_CALIB_STA_7 0x01f0 +#define RT5668_HP_CALIB_STA_8 0x01f1 +#define RT5668_HP_CALIB_STA_9 0x01f2 +#define RT5668_HP_CALIB_STA_10 0x01f3 +#define RT5668_HP_CALIB_STA_11 0x01f4 +#define RT5668_SAR_IL_CMD_1 0x0210 +#define RT5668_SAR_IL_CMD_2 0x0211 +#define RT5668_SAR_IL_CMD_3 0x0212 +#define RT5668_SAR_IL_CMD_4 0x0213 +#define RT5668_SAR_IL_CMD_5 0x0214 +#define RT5668_SAR_IL_CMD_6 0x0215 +#define RT5668_SAR_IL_CMD_7 0x0216 +#define RT5668_SAR_IL_CMD_8 0x0217 +#define RT5668_SAR_IL_CMD_9 0x0218 +#define RT5668_SAR_IL_CMD_10 0x0219 +#define RT5668_SAR_IL_CMD_11 0x021a +#define RT5668_SAR_IL_CMD_12 0x021b +#define RT5668_SAR_IL_CMD_13 0x021c +#define RT5668_EFUSE_CTRL_1 0x0250 +#define RT5668_EFUSE_CTRL_2 0x0251 +#define RT5668_EFUSE_CTRL_3 0x0252 +#define RT5668_EFUSE_CTRL_4 0x0253 +#define RT5668_EFUSE_CTRL_5 0x0254 +#define RT5668_EFUSE_CTRL_6 0x0255 +#define RT5668_EFUSE_CTRL_7 0x0256 +#define RT5668_EFUSE_CTRL_8 0x0257 +#define RT5668_EFUSE_CTRL_9 0x0258 +#define RT5668_EFUSE_CTRL_10 0x0259 +#define RT5668_EFUSE_CTRL_11 0x025a +#define RT5668_JD_TOP_VC_VTRL 0x0270 +#define RT5668_DRC1_CTRL_0 0x02ff +#define RT5668_DRC1_CTRL_1 0x0300 +#define RT5668_DRC1_CTRL_2 0x0301 +#define RT5668_DRC1_CTRL_3 0x0302 +#define RT5668_DRC1_CTRL_4 0x0303 +#define RT5668_DRC1_CTRL_5 0x0304 +#define RT5668_DRC1_CTRL_6 0x0305 +#define RT5668_DRC1_HARD_LMT_CTRL_1 0x0306 +#define RT5668_DRC1_HARD_LMT_CTRL_2 0x0307 +#define RT5668_DRC1_PRIV_1 0x0310 +#define RT5668_DRC1_PRIV_2 0x0311 +#define RT5668_DRC1_PRIV_3 0x0312 +#define RT5668_DRC1_PRIV_4 0x0313 +#define RT5668_DRC1_PRIV_5 0x0314 +#define RT5668_DRC1_PRIV_6 0x0315 +#define RT5668_DRC1_PRIV_7 0x0316 +#define RT5668_DRC1_PRIV_8 0x0317 +#define RT5668_EQ_AUTO_RCV_CTRL1 0x03c0 +#define RT5668_EQ_AUTO_RCV_CTRL2 0x03c1 +#define RT5668_EQ_AUTO_RCV_CTRL3 0x03c2 +#define RT5668_EQ_AUTO_RCV_CTRL4 0x03c3 +#define RT5668_EQ_AUTO_RCV_CTRL5 0x03c4 +#define RT5668_EQ_AUTO_RCV_CTRL6 0x03c5 +#define RT5668_EQ_AUTO_RCV_CTRL7 0x03c6 +#define RT5668_EQ_AUTO_RCV_CTRL8 0x03c7 +#define RT5668_EQ_AUTO_RCV_CTRL9 0x03c8 +#define RT5668_EQ_AUTO_RCV_CTRL10 0x03c9 +#define RT5668_EQ_AUTO_RCV_CTRL11 0x03ca +#define RT5668_EQ_AUTO_RCV_CTRL12 0x03cb +#define RT5668_EQ_AUTO_RCV_CTRL13 0x03cc +#define RT5668_ADC_L_EQ_LPF1_A1 0x03d0 +#define RT5668_R_EQ_LPF1_A1 0x03d1 +#define RT5668_L_EQ_LPF1_H0 0x03d2 +#define RT5668_R_EQ_LPF1_H0 0x03d3 +#define RT5668_L_EQ_BPF1_A1 0x03d4 +#define RT5668_R_EQ_BPF1_A1 0x03d5 +#define RT5668_L_EQ_BPF1_A2 0x03d6 +#define RT5668_R_EQ_BPF1_A2 0x03d7 +#define RT5668_L_EQ_BPF1_H0 0x03d8 +#define RT5668_R_EQ_BPF1_H0 0x03d9 +#define RT5668_L_EQ_BPF2_A1 0x03da +#define RT5668_R_EQ_BPF2_A1 0x03db +#define RT5668_L_EQ_BPF2_A2 0x03dc +#define RT5668_R_EQ_BPF2_A2 0x03dd +#define RT5668_L_EQ_BPF2_H0 0x03de +#define RT5668_R_EQ_BPF2_H0 0x03df +#define RT5668_L_EQ_BPF3_A1 0x03e0 +#define RT5668_R_EQ_BPF3_A1 0x03e1 +#define RT5668_L_EQ_BPF3_A2 0x03e2 +#define RT5668_R_EQ_BPF3_A2 0x03e3 +#define RT5668_L_EQ_BPF3_H0 0x03e4 +#define RT5668_R_EQ_BPF3_H0 0x03e5 +#define RT5668_L_EQ_BPF4_A1 0x03e6 +#define RT5668_R_EQ_BPF4_A1 0x03e7 +#define RT5668_L_EQ_BPF4_A2 0x03e8 +#define RT5668_R_EQ_BPF4_A2 0x03e9 +#define RT5668_L_EQ_BPF4_H0 0x03ea +#define RT5668_R_EQ_BPF4_H0 0x03eb +#define RT5668_L_EQ_HPF1_A1 0x03ec +#define RT5668_R_EQ_HPF1_A1 0x03ed +#define RT5668_L_EQ_HPF1_H0 0x03ee +#define RT5668_R_EQ_HPF1_H0 0x03ef +#define RT5668_L_EQ_PRE_VOL 0x03f0 +#define RT5668_R_EQ_PRE_VOL 0x03f1 +#define RT5668_L_EQ_POST_VOL 0x03f2 +#define RT5668_R_EQ_POST_VOL 0x03f3 +#define RT5668_I2C_MODE 0xffff + + +/* global definition */ +#define RT5668_L_MUTE (0x1 << 15) +#define RT5668_L_MUTE_SFT 15 +#define RT5668_VOL_L_MUTE (0x1 << 14) +#define RT5668_VOL_L_SFT 14 +#define RT5668_R_MUTE (0x1 << 7) +#define RT5668_R_MUTE_SFT 7 +#define RT5668_VOL_R_MUTE (0x1 << 6) +#define RT5668_VOL_R_SFT 6 +#define RT5668_L_VOL_MASK (0x3f << 8) +#define RT5668_L_VOL_SFT 8 +#define RT5668_R_VOL_MASK (0x3f) +#define RT5668_R_VOL_SFT 0 + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5668_G_HP (0xf << 8) +#define RT5668_G_HP_SFT 8 +#define RT5668_G_STO_DA_DMIX (0xf) +#define RT5668_G_STO_DA_SFT 0 + +/* CBJ Control (0x000b) */ +#define RT5668_BST_CBJ_MASK (0xf << 8) +#define RT5668_BST_CBJ_SFT 8 + +/* Embeeded Jack and Type Detection Control 1 (0x0010) */ +#define RT5668_EMB_JD_EN (0x1 << 15) +#define RT5668_EMB_JD_EN_SFT 15 +#define RT5668_EMB_JD_RST (0x1 << 14) +#define RT5668_JD_MODE (0x1 << 13) +#define RT5668_JD_MODE_SFT 13 +#define RT5668_DET_TYPE (0x1 << 12) +#define RT5668_DET_TYPE_SFT 12 +#define RT5668_POLA_EXT_JD_MASK (0x1 << 11) +#define RT5668_POLA_EXT_JD_LOW (0x1 << 11) +#define RT5668_POLA_EXT_JD_HIGH (0x0 << 11) +#define RT5668_EXT_JD_DIG (0x1 << 9) +#define RT5668_POL_FAST_OFF_MASK (0x1 << 8) +#define RT5668_POL_FAST_OFF_HIGH (0x1 << 8) +#define RT5668_POL_FAST_OFF_LOW (0x0 << 8) +#define RT5668_FAST_OFF_MASK (0x1 << 7) +#define RT5668_FAST_OFF_EN (0x1 << 7) +#define RT5668_FAST_OFF_DIS (0x0 << 7) +#define RT5668_VREF_POW_MASK (0x1 << 6) +#define RT5668_VREF_POW_FSM (0x0 << 6) +#define RT5668_VREF_POW_REG (0x1 << 6) +#define RT5668_MB1_PATH_MASK (0x1 << 5) +#define RT5668_CTRL_MB1_REG (0x1 << 5) +#define RT5668_CTRL_MB1_FSM (0x0 << 5) +#define RT5668_MB2_PATH_MASK (0x1 << 4) +#define RT5668_CTRL_MB2_REG (0x1 << 4) +#define RT5668_CTRL_MB2_FSM (0x0 << 4) +#define RT5668_TRIG_JD_MASK (0x1 << 3) +#define RT5668_TRIG_JD_HIGH (0x1 << 3) +#define RT5668_TRIG_JD_LOW (0x0 << 3) +#define RT5668_MIC_CAP_MASK (0x1 << 1) +#define RT5668_MIC_CAP_HS (0x1 << 1) +#define RT5668_MIC_CAP_HP (0x0 << 1) +#define RT5668_MIC_CAP_SRC_MASK (0x1) +#define RT5668_MIC_CAP_SRC_REG (0x1) +#define RT5668_MIC_CAP_SRC_ANA (0x0) + +/* Embeeded Jack and Type Detection Control 2 (0x0011) */ +#define RT5668_EXT_JD_SRC (0x7 << 4) +#define RT5668_EXT_JD_SRC_SFT 4 +#define RT5668_EXT_JD_SRC_GPIO_JD1 (0x0 << 4) +#define RT5668_EXT_JD_SRC_GPIO_JD2 (0x1 << 4) +#define RT5668_EXT_JD_SRC_JDH (0x2 << 4) +#define RT5668_EXT_JD_SRC_JDL (0x3 << 4) +#define RT5668_EXT_JD_SRC_MANUAL (0x4 << 4) +#define RT5668_JACK_TYPE_MASK (0x3) + +/* Combo Jack and Type Detection Control 3 (0x0012) */ +#define RT5668_CBJ_IN_BUF_EN (0x1 << 7) + +/* Combo Jack and Type Detection Control 4 (0x0013) */ +#define RT5668_SEL_SHT_MID_TON_MASK (0x3 << 12) +#define RT5668_SEL_SHT_MID_TON_2 (0x0 << 12) +#define RT5668_SEL_SHT_MID_TON_3 (0x1 << 12) +#define RT5668_CBJ_JD_TEST_MASK (0x1 << 6) +#define RT5668_CBJ_JD_TEST_NORM (0x0 << 6) +#define RT5668_CBJ_JD_TEST_MODE (0x1 << 6) + +/* DAC1 Digital Volume (0x0019) */ +#define RT5668_DAC_L1_VOL_MASK (0xff << 8) +#define RT5668_DAC_L1_VOL_SFT 8 +#define RT5668_DAC_R1_VOL_MASK (0xff) +#define RT5668_DAC_R1_VOL_SFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5668_ADC_L_VOL_MASK (0x7f << 8) +#define RT5668_ADC_L_VOL_SFT 8 +#define RT5668_ADC_R_VOL_MASK (0x7f) +#define RT5668_ADC_R_VOL_SFT 0 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5668_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5668_STO1_ADC_L_BST_SFT 14 +#define RT5668_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5668_STO1_ADC_R_BST_SFT 12 + +/* Sidetone Control (0x0024) */ +#define RT5668_ST_SRC_SEL (0x1 << 8) +#define RT5668_ST_SRC_SFT 8 +#define RT5668_ST_EN_MASK (0x1 << 6) +#define RT5668_ST_DIS (0x0 << 6) +#define RT5668_ST_EN (0x1 << 6) +#define RT5668_ST_EN_SFT 6 + +/* Stereo1 ADC Mixer Control (0x0026) */ +#define RT5668_M_STO1_ADC_L1 (0x1 << 15) +#define RT5668_M_STO1_ADC_L1_SFT 15 +#define RT5668_M_STO1_ADC_L2 (0x1 << 14) +#define RT5668_M_STO1_ADC_L2_SFT 14 +#define RT5668_STO1_ADC1L_SRC_MASK (0x1 << 13) +#define RT5668_STO1_ADC1L_SRC_SFT 13 +#define RT5668_STO1_ADC1_SRC_ADC (0x1 << 13) +#define RT5668_STO1_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5668_STO1_ADC2L_SRC_MASK (0x1 << 12) +#define RT5668_STO1_ADC2L_SRC_SFT 12 +#define RT5668_STO1_ADCL_SRC_MASK (0x3 << 10) +#define RT5668_STO1_ADCL_SRC_SFT 10 +#define RT5668_STO1_DD_L_SRC_MASK (0x1 << 9) +#define RT5668_STO1_DD_L_SRC_SFT 9 +#define RT5668_STO1_DMIC_SRC_MASK (0x1 << 8) +#define RT5668_STO1_DMIC_SRC_SFT 8 +#define RT5668_STO1_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5668_STO1_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5668_M_STO1_ADC_R1 (0x1 << 7) +#define RT5668_M_STO1_ADC_R1_SFT 7 +#define RT5668_M_STO1_ADC_R2 (0x1 << 6) +#define RT5668_M_STO1_ADC_R2_SFT 6 +#define RT5668_STO1_ADC1R_SRC_MASK (0x1 << 5) +#define RT5668_STO1_ADC1R_SRC_SFT 5 +#define RT5668_STO1_ADC2R_SRC_MASK (0x1 << 4) +#define RT5668_STO1_ADC2R_SRC_SFT 4 +#define RT5668_STO1_ADCR_SRC_MASK (0x3 << 2) +#define RT5668_STO1_ADCR_SRC_SFT 2 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5668_M_ADCMIX_L (0x1 << 15) +#define RT5668_M_ADCMIX_L_SFT 15 +#define RT5668_M_DAC1_L (0x1 << 14) +#define RT5668_M_DAC1_L_SFT 14 +#define RT5668_DAC1_R_SEL_MASK (0x1 << 10) +#define RT5668_DAC1_R_SEL_SFT 10 +#define RT5668_DAC1_L_SEL_MASK (0x1 << 8) +#define RT5668_DAC1_L_SEL_SFT 8 +#define RT5668_M_ADCMIX_R (0x1 << 7) +#define RT5668_M_ADCMIX_R_SFT 7 +#define RT5668_M_DAC1_R (0x1 << 6) +#define RT5668_M_DAC1_R_SFT 6 + +/* Stereo1 DAC Mixer Control (0x002a) */ +#define RT5668_M_DAC_L1_STO_L (0x1 << 15) +#define RT5668_M_DAC_L1_STO_L_SFT 15 +#define RT5668_G_DAC_L1_STO_L_MASK (0x1 << 14) +#define RT5668_G_DAC_L1_STO_L_SFT 14 +#define RT5668_M_DAC_R1_STO_L (0x1 << 13) +#define RT5668_M_DAC_R1_STO_L_SFT 13 +#define RT5668_G_DAC_R1_STO_L_MASK (0x1 << 12) +#define RT5668_G_DAC_R1_STO_L_SFT 12 +#define RT5668_M_DAC_L1_STO_R (0x1 << 7) +#define RT5668_M_DAC_L1_STO_R_SFT 7 +#define RT5668_G_DAC_L1_STO_R_MASK (0x1 << 6) +#define RT5668_G_DAC_L1_STO_R_SFT 6 +#define RT5668_M_DAC_R1_STO_R (0x1 << 5) +#define RT5668_M_DAC_R1_STO_R_SFT 5 +#define RT5668_G_DAC_R1_STO_R_MASK (0x1 << 4) +#define RT5668_G_DAC_R1_STO_R_SFT 4 + +/* Analog DAC1 Input Source Control (0x002b) */ +#define RT5668_M_ST_STO_L (0x1 << 9) +#define RT5668_M_ST_STO_L_SFT 9 +#define RT5668_M_ST_STO_R (0x1 << 8) +#define RT5668_M_ST_STO_R_SFT 8 +#define RT5668_DAC_L1_SRC_MASK (0x3 << 4) +#define RT5668_A_DACL1_SFT 4 +#define RT5668_DAC_R1_SRC_MASK (0x3) +#define RT5668_A_DACR1_SFT 0 + +/* Digital Interface Data Control (0x0030) */ +#define RT5668_IF2_ADC_SEL_MASK (0x3 << 0) +#define RT5668_IF2_ADC_SEL_SFT 0 + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5668_G_CBJ_RM1_L (0x7 << 10) +#define RT5668_G_CBJ_RM1_L_SFT 10 +#define RT5668_M_CBJ_RM1_L (0x1 << 7) +#define RT5668_M_CBJ_RM1_L_SFT 7 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5668_PWR_I2S1 (0x1 << 15) +#define RT5668_PWR_I2S1_BIT 15 +#define RT5668_PWR_I2S2 (0x1 << 14) +#define RT5668_PWR_I2S2_BIT 14 +#define RT5668_PWR_DAC_L1 (0x1 << 11) +#define RT5668_PWR_DAC_L1_BIT 11 +#define RT5668_PWR_DAC_R1 (0x1 << 10) +#define RT5668_PWR_DAC_R1_BIT 10 +#define RT5668_PWR_LDO (0x1 << 8) +#define RT5668_PWR_LDO_BIT 8 +#define RT5668_PWR_ADC_L1 (0x1 << 4) +#define RT5668_PWR_ADC_L1_BIT 4 +#define RT5668_PWR_ADC_R1 (0x1 << 3) +#define RT5668_PWR_ADC_R1_BIT 3 +#define RT5668_DIG_GATE_CTRL (0x1 << 0) +#define RT5668_DIG_GATE_CTRL_SFT 0 + + +/* Power Management for Digital 2 (0x0062) */ +#define RT5668_PWR_ADC_S1F (0x1 << 15) +#define RT5668_PWR_ADC_S1F_BIT 15 +#define RT5668_PWR_DAC_S1F (0x1 << 10) +#define RT5668_PWR_DAC_S1F_BIT 10 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5668_PWR_VREF1 (0x1 << 15) +#define RT5668_PWR_VREF1_BIT 15 +#define RT5668_PWR_FV1 (0x1 << 14) +#define RT5668_PWR_FV1_BIT 14 +#define RT5668_PWR_VREF2 (0x1 << 13) +#define RT5668_PWR_VREF2_BIT 13 +#define RT5668_PWR_FV2 (0x1 << 12) +#define RT5668_PWR_FV2_BIT 12 +#define RT5668_LDO1_DBG_MASK (0x3 << 10) +#define RT5668_PWR_MB (0x1 << 9) +#define RT5668_PWR_MB_BIT 9 +#define RT5668_PWR_BG (0x1 << 7) +#define RT5668_PWR_BG_BIT 7 +#define RT5668_LDO1_BYPASS_MASK (0x1 << 6) +#define RT5668_LDO1_BYPASS (0x1 << 6) +#define RT5668_LDO1_NOT_BYPASS (0x0 << 6) +#define RT5668_PWR_MA_BIT 6 +#define RT5668_LDO1_DVO_MASK (0x3 << 4) +#define RT5668_LDO1_DVO_09 (0x0 << 4) +#define RT5668_LDO1_DVO_10 (0x1 << 4) +#define RT5668_LDO1_DVO_12 (0x2 << 4) +#define RT5668_LDO1_DVO_14 (0x3 << 4) +#define RT5668_HP_DRIVER_MASK (0x3 << 2) +#define RT5668_HP_DRIVER_1X (0x0 << 2) +#define RT5668_HP_DRIVER_3X (0x1 << 2) +#define RT5668_HP_DRIVER_5X (0x3 << 2) +#define RT5668_PWR_HA_L (0x1 << 1) +#define RT5668_PWR_HA_L_BIT 1 +#define RT5668_PWR_HA_R (0x1 << 0) +#define RT5668_PWR_HA_R_BIT 0 + +/* Power Management for Analog 2 (0x0064) */ +#define RT5668_PWR_MB1 (0x1 << 11) +#define RT5668_PWR_MB1_PWR_DOWN (0x0 << 11) +#define RT5668_PWR_MB1_BIT 11 +#define RT5668_PWR_MB2 (0x1 << 10) +#define RT5668_PWR_MB2_PWR_DOWN (0x0 << 10) +#define RT5668_PWR_MB2_BIT 10 +#define RT5668_PWR_JDH (0x1 << 3) +#define RT5668_PWR_JDH_BIT 3 +#define RT5668_PWR_JDL (0x1 << 2) +#define RT5668_PWR_JDL_BIT 2 +#define RT5668_PWR_RM1_L (0x1 << 1) +#define RT5668_PWR_RM1_L_BIT 1 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5668_PWR_CBJ (0x1 << 9) +#define RT5668_PWR_CBJ_BIT 9 +#define RT5668_PWR_PLL (0x1 << 6) +#define RT5668_PWR_PLL_BIT 6 +#define RT5668_PWR_PLL2B (0x1 << 5) +#define RT5668_PWR_PLL2B_BIT 5 +#define RT5668_PWR_PLL2F (0x1 << 4) +#define RT5668_PWR_PLL2F_BIT 4 +#define RT5668_PWR_LDO2 (0x1 << 2) +#define RT5668_PWR_LDO2_BIT 2 +#define RT5668_PWR_DET_SPKVDD (0x1 << 1) +#define RT5668_PWR_DET_SPKVDD_BIT 1 + +/* Power Management for Mixer (0x0066) */ +#define RT5668_PWR_STO1_DAC_L (0x1 << 5) +#define RT5668_PWR_STO1_DAC_L_BIT 5 +#define RT5668_PWR_STO1_DAC_R (0x1 << 4) +#define RT5668_PWR_STO1_DAC_R_BIT 4 + +/* MCLK and System Clock Detection Control (0x006b) */ +#define RT5668_SYS_CLK_DET (0x1 << 15) +#define RT5668_SYS_CLK_DET_SFT 15 +#define RT5668_PLL1_CLK_DET (0x1 << 14) +#define RT5668_PLL1_CLK_DET_SFT 14 +#define RT5668_PLL2_CLK_DET (0x1 << 13) +#define RT5668_PLL2_CLK_DET_SFT 13 +#define RT5668_POW_CLK_DET2_SFT 8 +#define RT5668_POW_CLK_DET_SFT 0 + +/* Digital Microphone Control 1 (0x006e) */ +#define RT5668_DMIC_1_EN_MASK (0x1 << 15) +#define RT5668_DMIC_1_EN_SFT 15 +#define RT5668_DMIC_1_DIS (0x0 << 15) +#define RT5668_DMIC_1_EN (0x1 << 15) +#define RT5668_DMIC_1_DP_MASK (0x3 << 4) +#define RT5668_DMIC_1_DP_SFT 4 +#define RT5668_DMIC_1_DP_GPIO2 (0x0 << 4) +#define RT5668_DMIC_1_DP_GPIO5 (0x1 << 4) +#define RT5668_DMIC_CLK_MASK (0xf << 0) +#define RT5668_DMIC_CLK_SFT 0 + +/* I2S1 Audio Serial Data Port Control (0x0070) */ +#define RT5668_SEL_ADCDAT_MASK (0x1 << 15) +#define RT5668_SEL_ADCDAT_OUT (0x0 << 15) +#define RT5668_SEL_ADCDAT_IN (0x1 << 15) +#define RT5668_SEL_ADCDAT_SFT 15 +#define RT5668_I2S1_TX_CHL_MASK (0x7 << 12) +#define RT5668_I2S1_TX_CHL_SFT 12 +#define RT5668_I2S1_TX_CHL_16 (0x0 << 12) +#define RT5668_I2S1_TX_CHL_20 (0x1 << 12) +#define RT5668_I2S1_TX_CHL_24 (0x2 << 12) +#define RT5668_I2S1_TX_CHL_32 (0x3 << 12) +#define RT5668_I2S1_TX_CHL_8 (0x4 << 12) +#define RT5668_I2S1_RX_CHL_MASK (0x7 << 8) +#define RT5668_I2S1_RX_CHL_SFT 8 +#define RT5668_I2S1_RX_CHL_16 (0x0 << 8) +#define RT5668_I2S1_RX_CHL_20 (0x1 << 8) +#define RT5668_I2S1_RX_CHL_24 (0x2 << 8) +#define RT5668_I2S1_RX_CHL_32 (0x3 << 8) +#define RT5668_I2S1_RX_CHL_8 (0x4 << 8) +#define RT5668_I2S1_MONO_MASK (0x1 << 7) +#define RT5668_I2S1_MONO_EN (0x1 << 7) +#define RT5668_I2S1_MONO_DIS (0x0 << 7) +#define RT5668_I2S2_MONO_MASK (0x1 << 6) +#define RT5668_I2S2_MONO_EN (0x1 << 6) +#define RT5668_I2S2_MONO_DIS (0x0 << 6) +#define RT5668_I2S1_DL_MASK (0x7 << 4) +#define RT5668_I2S1_DL_SFT 4 +#define RT5668_I2S1_DL_16 (0x0 << 4) +#define RT5668_I2S1_DL_20 (0x1 << 4) +#define RT5668_I2S1_DL_24 (0x2 << 4) +#define RT5668_I2S1_DL_32 (0x3 << 4) +#define RT5668_I2S1_DL_8 (0x4 << 4) + +/* I2S1/2 Audio Serial Data Port Control (0x0070)(0x0071) */ +#define RT5668_I2S2_MS_MASK (0x1 << 15) +#define RT5668_I2S2_MS_SFT 15 +#define RT5668_I2S2_MS_M (0x0 << 15) +#define RT5668_I2S2_MS_S (0x1 << 15) +#define RT5668_I2S2_PIN_CFG_MASK (0x1 << 14) +#define RT5668_I2S2_PIN_CFG_SFT 14 +#define RT5668_I2S2_CLK_SEL_MASK (0x1 << 11) +#define RT5668_I2S2_CLK_SEL_SFT 11 +#define RT5668_I2S2_OUT_MASK (0x1 << 9) +#define RT5668_I2S2_OUT_SFT 9 +#define RT5668_I2S2_OUT_UM (0x0 << 9) +#define RT5668_I2S2_OUT_M (0x1 << 9) +#define RT5668_I2S_BP_MASK (0x1 << 8) +#define RT5668_I2S_BP_SFT 8 +#define RT5668_I2S_BP_NOR (0x0 << 8) +#define RT5668_I2S_BP_INV (0x1 << 8) +#define RT5668_I2S2_MONO_EN (0x1 << 6) +#define RT5668_I2S2_MONO_DIS (0x0 << 6) +#define RT5668_I2S2_DL_MASK (0x3 << 4) +#define RT5668_I2S2_DL_SFT 4 +#define RT5668_I2S2_DL_16 (0x0 << 4) +#define RT5668_I2S2_DL_20 (0x1 << 4) +#define RT5668_I2S2_DL_24 (0x2 << 4) +#define RT5668_I2S2_DL_8 (0x3 << 4) +#define RT5668_I2S_DF_MASK (0x7) +#define RT5668_I2S_DF_SFT 0 +#define RT5668_I2S_DF_I2S (0x0) +#define RT5668_I2S_DF_LEFT (0x1) +#define RT5668_I2S_DF_PCM_A (0x2) +#define RT5668_I2S_DF_PCM_B (0x3) +#define RT5668_I2S_DF_PCM_A_N (0x6) +#define RT5668_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5668_ADC_OSR_MASK (0xf << 12) +#define RT5668_ADC_OSR_SFT 12 +#define RT5668_ADC_OSR_D_1 (0x0 << 12) +#define RT5668_ADC_OSR_D_2 (0x1 << 12) +#define RT5668_ADC_OSR_D_4 (0x2 << 12) +#define RT5668_ADC_OSR_D_6 (0x3 << 12) +#define RT5668_ADC_OSR_D_8 (0x4 << 12) +#define RT5668_ADC_OSR_D_12 (0x5 << 12) +#define RT5668_ADC_OSR_D_16 (0x6 << 12) +#define RT5668_ADC_OSR_D_24 (0x7 << 12) +#define RT5668_ADC_OSR_D_32 (0x8 << 12) +#define RT5668_ADC_OSR_D_48 (0x9 << 12) +#define RT5668_I2S_M_DIV_MASK (0xf << 12) +#define RT5668_I2S_M_DIV_SFT 8 +#define RT5668_I2S_M_D_1 (0x0 << 8) +#define RT5668_I2S_M_D_2 (0x1 << 8) +#define RT5668_I2S_M_D_3 (0x2 << 8) +#define RT5668_I2S_M_D_4 (0x3 << 8) +#define RT5668_I2S_M_D_6 (0x4 << 8) +#define RT5668_I2S_M_D_8 (0x5 << 8) +#define RT5668_I2S_M_D_12 (0x6 << 8) +#define RT5668_I2S_M_D_16 (0x7 << 8) +#define RT5668_I2S_M_D_24 (0x8 << 8) +#define RT5668_I2S_M_D_32 (0x9 << 8) +#define RT5668_I2S_M_D_48 (0x10 << 8) +#define RT5668_I2S_CLK_SRC_MASK (0x7 << 4) +#define RT5668_I2S_CLK_SRC_SFT 4 +#define RT5668_I2S_CLK_SRC_MCLK (0x0 << 4) +#define RT5668_I2S_CLK_SRC_PLL1 (0x1 << 4) +#define RT5668_I2S_CLK_SRC_PLL2 (0x2 << 4) +#define RT5668_I2S_CLK_SRC_SDW (0x3 << 4) +#define RT5668_I2S_CLK_SRC_RCCLK (0x4 << 4) /* 25M */ +#define RT5668_DAC_OSR_MASK (0xf << 0) +#define RT5668_DAC_OSR_SFT 0 +#define RT5668_DAC_OSR_D_1 (0x0 << 0) +#define RT5668_DAC_OSR_D_2 (0x1 << 0) +#define RT5668_DAC_OSR_D_4 (0x2 << 0) +#define RT5668_DAC_OSR_D_6 (0x3 << 0) +#define RT5668_DAC_OSR_D_8 (0x4 << 0) +#define RT5668_DAC_OSR_D_12 (0x5 << 0) +#define RT5668_DAC_OSR_D_16 (0x6 << 0) +#define RT5668_DAC_OSR_D_24 (0x7 << 0) +#define RT5668_DAC_OSR_D_32 (0x8 << 0) +#define RT5668_DAC_OSR_D_48 (0x9 << 0) + +/* ADC/DAC Clock Control 2 (0x0074) */ +#define RT5668_I2S2_BCLK_MS2_MASK (0x1 << 11) +#define RT5668_I2S2_BCLK_MS2_SFT 11 +#define RT5668_I2S2_BCLK_MS2_32 (0x0 << 11) +#define RT5668_I2S2_BCLK_MS2_64 (0x1 << 11) + + +/* TDM control 1 (0x0079) */ +#define RT5668_TDM_TX_CH_MASK (0x3 << 12) +#define RT5668_TDM_TX_CH_2 (0x0 << 12) +#define RT5668_TDM_TX_CH_4 (0x1 << 12) +#define RT5668_TDM_TX_CH_6 (0x2 << 12) +#define RT5668_TDM_TX_CH_8 (0x3 << 12) +#define RT5668_TDM_RX_CH_MASK (0x3 << 8) +#define RT5668_TDM_RX_CH_2 (0x0 << 8) +#define RT5668_TDM_RX_CH_4 (0x1 << 8) +#define RT5668_TDM_RX_CH_6 (0x2 << 8) +#define RT5668_TDM_RX_CH_8 (0x3 << 8) +#define RT5668_TDM_ADC_LCA_MASK (0xf << 4) +#define RT5668_TDM_ADC_LCA_SFT 4 +#define RT5668_TDM_ADC_DL_SFT 0 + +/* TDM control 3 (0x007a) */ +#define RT5668_IF1_ADC1_SEL_SFT 14 +#define RT5668_IF1_ADC2_SEL_SFT 12 +#define RT5668_IF1_ADC3_SEL_SFT 10 +#define RT5668_IF1_ADC4_SEL_SFT 8 +#define RT5668_TDM_ADC_SEL_SFT 4 + +/* TDM/I2S control (0x007e) */ +#define RT5668_TDM_S_BP_MASK (0x1 << 15) +#define RT5668_TDM_S_BP_SFT 15 +#define RT5668_TDM_S_BP_NOR (0x0 << 15) +#define RT5668_TDM_S_BP_INV (0x1 << 15) +#define RT5668_TDM_S_LP_MASK (0x1 << 14) +#define RT5668_TDM_S_LP_SFT 14 +#define RT5668_TDM_S_LP_NOR (0x0 << 14) +#define RT5668_TDM_S_LP_INV (0x1 << 14) +#define RT5668_TDM_DF_MASK (0x7 << 11) +#define RT5668_TDM_DF_SFT 11 +#define RT5668_TDM_DF_I2S (0x0 << 11) +#define RT5668_TDM_DF_LEFT (0x1 << 11) +#define RT5668_TDM_DF_PCM_A (0x2 << 11) +#define RT5668_TDM_DF_PCM_B (0x3 << 11) +#define RT5668_TDM_DF_PCM_A_N (0x6 << 11) +#define RT5668_TDM_DF_PCM_B_N (0x7 << 11) +#define RT5668_TDM_CL_MASK (0x3 << 4) +#define RT5668_TDM_CL_16 (0x0 << 4) +#define RT5668_TDM_CL_20 (0x1 << 4) +#define RT5668_TDM_CL_24 (0x2 << 4) +#define RT5668_TDM_CL_32 (0x3 << 4) +#define RT5668_TDM_M_BP_MASK (0x1 << 2) +#define RT5668_TDM_M_BP_SFT 2 +#define RT5668_TDM_M_BP_NOR (0x0 << 2) +#define RT5668_TDM_M_BP_INV (0x1 << 2) +#define RT5668_TDM_M_LP_MASK (0x1 << 1) +#define RT5668_TDM_M_LP_SFT 1 +#define RT5668_TDM_M_LP_NOR (0x0 << 1) +#define RT5668_TDM_M_LP_INV (0x1 << 1) +#define RT5668_TDM_MS_MASK (0x1 << 0) +#define RT5668_TDM_MS_SFT 0 +#define RT5668_TDM_MS_M (0x0 << 0) +#define RT5668_TDM_MS_S (0x1 << 0) + +/* Global Clock Control (0x0080) */ +#define RT5668_SCLK_SRC_MASK (0x7 << 13) +#define RT5668_SCLK_SRC_SFT 13 +#define RT5668_SCLK_SRC_MCLK (0x0 << 13) +#define RT5668_SCLK_SRC_PLL1 (0x1 << 13) +#define RT5668_SCLK_SRC_PLL2 (0x2 << 13) +#define RT5668_SCLK_SRC_SDW (0x3 << 13) +#define RT5668_SCLK_SRC_RCCLK (0x4 << 13) +#define RT5668_PLL1_SRC_MASK (0x3 << 10) +#define RT5668_PLL1_SRC_SFT 10 +#define RT5668_PLL1_SRC_MCLK (0x0 << 10) +#define RT5668_PLL1_SRC_BCLK1 (0x1 << 10) +#define RT5668_PLL1_SRC_SDW (0x2 << 10) +#define RT5668_PLL1_SRC_RC (0x3 << 10) +#define RT5668_PLL2_SRC_MASK (0x3 << 8) +#define RT5668_PLL2_SRC_SFT 8 +#define RT5668_PLL2_SRC_MCLK (0x0 << 8) +#define RT5668_PLL2_SRC_BCLK1 (0x1 << 8) +#define RT5668_PLL2_SRC_SDW (0x2 << 8) +#define RT5668_PLL2_SRC_RC (0x3 << 8) + + + +#define RT5668_PLL_INP_MAX 40000000 +#define RT5668_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5668_PLL_N_MAX 0x001ff +#define RT5668_PLL_N_MASK (RT5668_PLL_N_MAX << 7) +#define RT5668_PLL_N_SFT 7 +#define RT5668_PLL_K_MAX 0x001f +#define RT5668_PLL_K_MASK (RT5668_PLL_K_MAX) +#define RT5668_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5668_PLL_M_MAX 0x00f +#define RT5668_PLL_M_MASK (RT5668_PLL_M_MAX << 12) +#define RT5668_PLL_M_SFT 12 +#define RT5668_PLL_M_BP (0x1 << 11) +#define RT5668_PLL_M_BP_SFT 11 +#define RT5668_PLL_K_BP (0x1 << 10) +#define RT5668_PLL_K_BP_SFT 10 + +/* PLL tracking mode 1 (0x0083) */ +#define RT5668_DA_ASRC_MASK (0x1 << 13) +#define RT5668_DA_ASRC_SFT 13 +#define RT5668_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5668_DAC_STO1_ASRC_SFT 12 +#define RT5668_AD_ASRC_MASK (0x1 << 8) +#define RT5668_AD_ASRC_SFT 8 +#define RT5668_AD_ASRC_SEL_MASK (0x1 << 4) +#define RT5668_AD_ASRC_SEL_SFT 4 +#define RT5668_DMIC_ASRC_MASK (0x1 << 3) +#define RT5668_DMIC_ASRC_SFT 3 +#define RT5668_ADC_STO1_ASRC_MASK (0x1 << 2) +#define RT5668_ADC_STO1_ASRC_SFT 2 +#define RT5668_DA_ASRC_SEL_MASK (0x1 << 0) +#define RT5668_DA_ASRC_SEL_SFT 0 + +/* PLL tracking mode 2 3 (0x0084)(0x0085)*/ +#define RT5668_FILTER_CLK_SEL_MASK (0x7 << 12) +#define RT5668_FILTER_CLK_SEL_SFT 12 + +/* ASRC Control 4 (0x0086) */ +#define RT5668_ASRCIN_FTK_N1_MASK (0x3 << 14) +#define RT5668_ASRCIN_FTK_N1_SFT 14 +#define RT5668_ASRCIN_FTK_N2_MASK (0x3 << 12) +#define RT5668_ASRCIN_FTK_N2_SFT 12 +#define RT5668_ASRCIN_FTK_M1_MASK (0x7 << 8) +#define RT5668_ASRCIN_FTK_M1_SFT 8 +#define RT5668_ASRCIN_FTK_M2_MASK (0x7 << 4) +#define RT5668_ASRCIN_FTK_M2_SFT 4 + +/* SoundWire reference clk (0x008d) */ +#define RT5668_PLL2_OUT_MASK (0x1 << 8) +#define RT5668_PLL2_OUT_98M (0x0 << 8) +#define RT5668_PLL2_OUT_49M (0x1 << 8) +#define RT5668_SDW_REF_2_MASK (0xf << 4) +#define RT5668_SDW_REF_2_SFT 4 +#define RT5668_SDW_REF_2_48K (0x0 << 4) +#define RT5668_SDW_REF_2_96K (0x1 << 4) +#define RT5668_SDW_REF_2_192K (0x2 << 4) +#define RT5668_SDW_REF_2_32K (0x3 << 4) +#define RT5668_SDW_REF_2_24K (0x4 << 4) +#define RT5668_SDW_REF_2_16K (0x5 << 4) +#define RT5668_SDW_REF_2_12K (0x6 << 4) +#define RT5668_SDW_REF_2_8K (0x7 << 4) +#define RT5668_SDW_REF_2_44K (0x8 << 4) +#define RT5668_SDW_REF_2_88K (0x9 << 4) +#define RT5668_SDW_REF_2_176K (0xa << 4) +#define RT5668_SDW_REF_2_353K (0xb << 4) +#define RT5668_SDW_REF_2_22K (0xc << 4) +#define RT5668_SDW_REF_2_384K (0xd << 4) +#define RT5668_SDW_REF_2_11K (0xe << 4) +#define RT5668_SDW_REF_1_MASK (0xf << 0) +#define RT5668_SDW_REF_1_SFT 0 +#define RT5668_SDW_REF_1_48K (0x0 << 0) +#define RT5668_SDW_REF_1_96K (0x1 << 0) +#define RT5668_SDW_REF_1_192K (0x2 << 0) +#define RT5668_SDW_REF_1_32K (0x3 << 0) +#define RT5668_SDW_REF_1_24K (0x4 << 0) +#define RT5668_SDW_REF_1_16K (0x5 << 0) +#define RT5668_SDW_REF_1_12K (0x6 << 0) +#define RT5668_SDW_REF_1_8K (0x7 << 0) +#define RT5668_SDW_REF_1_44K (0x8 << 0) +#define RT5668_SDW_REF_1_88K (0x9 << 0) +#define RT5668_SDW_REF_1_176K (0xa << 0) +#define RT5668_SDW_REF_1_353K (0xb << 0) +#define RT5668_SDW_REF_1_22K (0xc << 0) +#define RT5668_SDW_REF_1_384K (0xd << 0) +#define RT5668_SDW_REF_1_11K (0xe << 0) + +/* Depop Mode Control 1 (0x008e) */ +#define RT5668_PUMP_EN (0x1 << 3) +#define RT5668_PUMP_EN_SFT 3 +#define RT5668_CAPLESS_EN (0x1 << 0) +#define RT5668_CAPLESS_EN_SFT 0 + +/* Depop Mode Control 2 (0x8f) */ +#define RT5668_RAMP_MASK (0x1 << 12) +#define RT5668_RAMP_SFT 12 +#define RT5668_RAMP_DIS (0x0 << 12) +#define RT5668_RAMP_EN (0x1 << 12) +#define RT5668_BPS_MASK (0x1 << 11) +#define RT5668_BPS_SFT 11 +#define RT5668_BPS_DIS (0x0 << 11) +#define RT5668_BPS_EN (0x1 << 11) +#define RT5668_FAST_UPDN_MASK (0x1 << 10) +#define RT5668_FAST_UPDN_SFT 10 +#define RT5668_FAST_UPDN_DIS (0x0 << 10) +#define RT5668_FAST_UPDN_EN (0x1 << 10) +#define RT5668_VLO_MASK (0x1 << 7) +#define RT5668_VLO_SFT 7 +#define RT5668_VLO_3V (0x0 << 7) +#define RT5668_VLO_33V (0x1 << 7) + +/* HPOUT charge pump 1 (0x0091) */ +#define RT5668_OSW_L_MASK (0x1 << 11) +#define RT5668_OSW_L_SFT 11 +#define RT5668_OSW_L_DIS (0x0 << 11) +#define RT5668_OSW_L_EN (0x1 << 11) +#define RT5668_OSW_R_MASK (0x1 << 10) +#define RT5668_OSW_R_SFT 10 +#define RT5668_OSW_R_DIS (0x0 << 10) +#define RT5668_OSW_R_EN (0x1 << 10) +#define RT5668_PM_HP_MASK (0x3 << 8) +#define RT5668_PM_HP_SFT 8 +#define RT5668_PM_HP_LV (0x0 << 8) +#define RT5668_PM_HP_MV (0x1 << 8) +#define RT5668_PM_HP_HV (0x2 << 8) +#define RT5668_IB_HP_MASK (0x3 << 6) +#define RT5668_IB_HP_SFT 6 +#define RT5668_IB_HP_125IL (0x0 << 6) +#define RT5668_IB_HP_25IL (0x1 << 6) +#define RT5668_IB_HP_5IL (0x2 << 6) +#define RT5668_IB_HP_1IL (0x3 << 6) + +/* Micbias Control1 (0x93) */ +#define RT5668_MIC1_OV_MASK (0x3 << 14) +#define RT5668_MIC1_OV_SFT 14 +#define RT5668_MIC1_OV_2V7 (0x0 << 14) +#define RT5668_MIC1_OV_2V4 (0x1 << 14) +#define RT5668_MIC1_OV_2V25 (0x3 << 14) +#define RT5668_MIC1_OV_1V8 (0x4 << 14) +#define RT5668_MIC1_CLK_MASK (0x1 << 13) +#define RT5668_MIC1_CLK_SFT 13 +#define RT5668_MIC1_CLK_DIS (0x0 << 13) +#define RT5668_MIC1_CLK_EN (0x1 << 13) +#define RT5668_MIC1_OVCD_MASK (0x1 << 12) +#define RT5668_MIC1_OVCD_SFT 12 +#define RT5668_MIC1_OVCD_DIS (0x0 << 12) +#define RT5668_MIC1_OVCD_EN (0x1 << 12) +#define RT5668_MIC1_OVTH_MASK (0x3 << 10) +#define RT5668_MIC1_OVTH_SFT 10 +#define RT5668_MIC1_OVTH_768UA (0x0 << 10) +#define RT5668_MIC1_OVTH_960UA (0x1 << 10) +#define RT5668_MIC1_OVTH_1152UA (0x2 << 10) +#define RT5668_MIC1_OVTH_1960UA (0x3 << 10) +#define RT5668_MIC2_OV_MASK (0x3 << 8) +#define RT5668_MIC2_OV_SFT 8 +#define RT5668_MIC2_OV_2V7 (0x0 << 8) +#define RT5668_MIC2_OV_2V4 (0x1 << 8) +#define RT5668_MIC2_OV_2V25 (0x3 << 8) +#define RT5668_MIC2_OV_1V8 (0x4 << 8) +#define RT5668_MIC2_CLK_MASK (0x1 << 7) +#define RT5668_MIC2_CLK_SFT 7 +#define RT5668_MIC2_CLK_DIS (0x0 << 7) +#define RT5668_MIC2_CLK_EN (0x1 << 7) +#define RT5668_MIC2_OVTH_MASK (0x3 << 4) +#define RT5668_MIC2_OVTH_SFT 4 +#define RT5668_MIC2_OVTH_768UA (0x0 << 4) +#define RT5668_MIC2_OVTH_960UA (0x1 << 4) +#define RT5668_MIC2_OVTH_1152UA (0x2 << 4) +#define RT5668_MIC2_OVTH_1960UA (0x3 << 4) +#define RT5668_PWR_MB_MASK (0x1 << 3) +#define RT5668_PWR_MB_SFT 3 +#define RT5668_PWR_MB_PD (0x0 << 3) +#define RT5668_PWR_MB_PU (0x1 << 3) + +/* Micbias Control2 (0x0094) */ +#define RT5668_PWR_CLK25M_MASK (0x1 << 9) +#define RT5668_PWR_CLK25M_SFT 9 +#define RT5668_PWR_CLK25M_PD (0x0 << 9) +#define RT5668_PWR_CLK25M_PU (0x1 << 9) +#define RT5668_PWR_CLK1M_MASK (0x1 << 8) +#define RT5668_PWR_CLK1M_SFT 8 +#define RT5668_PWR_CLK1M_PD (0x0 << 8) +#define RT5668_PWR_CLK1M_PU (0x1 << 8) + +/* RC Clock Control (0x009f) */ +#define RT5668_POW_IRQ (0x1 << 15) +#define RT5668_POW_JDH (0x1 << 14) +#define RT5668_POW_JDL (0x1 << 13) +#define RT5668_POW_ANA (0x1 << 12) + +/* I2S Master Mode Clock Control 1 (0x00a0) */ +#define RT5668_CLK_SRC_MCLK (0x0) +#define RT5668_CLK_SRC_PLL1 (0x1) +#define RT5668_CLK_SRC_PLL2 (0x2) +#define RT5668_CLK_SRC_SDW (0x3) +#define RT5668_CLK_SRC_RCCLK (0x4) +#define RT5668_I2S_PD_1 (0x0) +#define RT5668_I2S_PD_2 (0x1) +#define RT5668_I2S_PD_3 (0x2) +#define RT5668_I2S_PD_4 (0x3) +#define RT5668_I2S_PD_6 (0x4) +#define RT5668_I2S_PD_8 (0x5) +#define RT5668_I2S_PD_12 (0x6) +#define RT5668_I2S_PD_16 (0x7) +#define RT5668_I2S_PD_24 (0x8) +#define RT5668_I2S_PD_32 (0x9) +#define RT5668_I2S_PD_48 (0xa) +#define RT5668_I2S2_SRC_MASK (0x3 << 4) +#define RT5668_I2S2_SRC_SFT 4 +#define RT5668_I2S2_M_PD_MASK (0xf << 0) +#define RT5668_I2S2_M_PD_SFT 0 + +/* IRQ Control 1 (0x00b6) */ +#define RT5668_JD1_PULSE_EN_MASK (0x1 << 10) +#define RT5668_JD1_PULSE_EN_SFT 10 +#define RT5668_JD1_PULSE_DIS (0x0 << 10) +#define RT5668_JD1_PULSE_EN (0x1 << 10) + +/* IRQ Control 2 (0x00b7) */ +#define RT5668_JD1_EN_MASK (0x1 << 15) +#define RT5668_JD1_EN_SFT 15 +#define RT5668_JD1_DIS (0x0 << 15) +#define RT5668_JD1_EN (0x1 << 15) +#define RT5668_JD1_POL_MASK (0x1 << 13) +#define RT5668_JD1_POL_NOR (0x0 << 13) +#define RT5668_JD1_POL_INV (0x1 << 13) + +/* IRQ Control 3 (0x00b8) */ +#define RT5668_IL_IRQ_MASK (0x1 << 7) +#define RT5668_IL_IRQ_DIS (0x0 << 7) +#define RT5668_IL_IRQ_EN (0x1 << 7) + +/* GPIO Control 1 (0x00c0) */ +#define RT5668_GP1_PIN_MASK (0x3 << 14) +#define RT5668_GP1_PIN_SFT 14 +#define RT5668_GP1_PIN_GPIO1 (0x0 << 14) +#define RT5668_GP1_PIN_IRQ (0x1 << 14) +#define RT5668_GP1_PIN_DMIC_CLK (0x2 << 14) +#define RT5668_GP2_PIN_MASK (0x3 << 12) +#define RT5668_GP2_PIN_SFT 12 +#define RT5668_GP2_PIN_GPIO2 (0x0 << 12) +#define RT5668_GP2_PIN_LRCK2 (0x1 << 12) +#define RT5668_GP2_PIN_DMIC_SDA (0x2 << 12) +#define RT5668_GP3_PIN_MASK (0x3 << 10) +#define RT5668_GP3_PIN_SFT 10 +#define RT5668_GP3_PIN_GPIO3 (0x0 << 10) +#define RT5668_GP3_PIN_BCLK2 (0x1 << 10) +#define RT5668_GP3_PIN_DMIC_CLK (0x2 << 10) +#define RT5668_GP4_PIN_MASK (0x3 << 8) +#define RT5668_GP4_PIN_SFT 8 +#define RT5668_GP4_PIN_GPIO4 (0x0 << 8) +#define RT5668_GP4_PIN_ADCDAT1 (0x1 << 8) +#define RT5668_GP4_PIN_DMIC_CLK (0x2 << 8) +#define RT5668_GP4_PIN_ADCDAT2 (0x3 << 8) +#define RT5668_GP5_PIN_MASK (0x3 << 6) +#define RT5668_GP5_PIN_SFT 6 +#define RT5668_GP5_PIN_GPIO5 (0x0 << 6) +#define RT5668_GP5_PIN_DACDAT1 (0x1 << 6) +#define RT5668_GP5_PIN_DMIC_SDA (0x2 << 6) +#define RT5668_GP6_PIN_MASK (0x1 << 5) +#define RT5668_GP6_PIN_SFT 5 +#define RT5668_GP6_PIN_GPIO6 (0x0 << 5) +#define RT5668_GP6_PIN_LRCK1 (0x1 << 5) + +/* GPIO Control 2 (0x00c1)*/ +#define RT5668_GP1_PF_MASK (0x1 << 15) +#define RT5668_GP1_PF_IN (0x0 << 15) +#define RT5668_GP1_PF_OUT (0x1 << 15) +#define RT5668_GP1_OUT_MASK (0x1 << 14) +#define RT5668_GP1_OUT_L (0x0 << 14) +#define RT5668_GP1_OUT_H (0x1 << 14) +#define RT5668_GP2_PF_MASK (0x1 << 13) +#define RT5668_GP2_PF_IN (0x0 << 13) +#define RT5668_GP2_PF_OUT (0x1 << 13) +#define RT5668_GP2_OUT_MASK (0x1 << 12) +#define RT5668_GP2_OUT_L (0x0 << 12) +#define RT5668_GP2_OUT_H (0x1 << 12) +#define RT5668_GP3_PF_MASK (0x1 << 11) +#define RT5668_GP3_PF_IN (0x0 << 11) +#define RT5668_GP3_PF_OUT (0x1 << 11) +#define RT5668_GP3_OUT_MASK (0x1 << 10) +#define RT5668_GP3_OUT_L (0x0 << 10) +#define RT5668_GP3_OUT_H (0x1 << 10) +#define RT5668_GP4_PF_MASK (0x1 << 9) +#define RT5668_GP4_PF_IN (0x0 << 9) +#define RT5668_GP4_PF_OUT (0x1 << 9) +#define RT5668_GP4_OUT_MASK (0x1 << 8) +#define RT5668_GP4_OUT_L (0x0 << 8) +#define RT5668_GP4_OUT_H (0x1 << 8) +#define RT5668_GP5_PF_MASK (0x1 << 7) +#define RT5668_GP5_PF_IN (0x0 << 7) +#define RT5668_GP5_PF_OUT (0x1 << 7) +#define RT5668_GP5_OUT_MASK (0x1 << 6) +#define RT5668_GP5_OUT_L (0x0 << 6) +#define RT5668_GP5_OUT_H (0x1 << 6) +#define RT5668_GP6_PF_MASK (0x1 << 5) +#define RT5668_GP6_PF_IN (0x0 << 5) +#define RT5668_GP6_PF_OUT (0x1 << 5) +#define RT5668_GP6_OUT_MASK (0x1 << 4) +#define RT5668_GP6_OUT_L (0x0 << 4) +#define RT5668_GP6_OUT_H (0x1 << 4) + + +/* GPIO Status (0x00c2) */ +#define RT5668_GP6_STA (0x1 << 6) +#define RT5668_GP5_STA (0x1 << 5) +#define RT5668_GP4_STA (0x1 << 4) +#define RT5668_GP3_STA (0x1 << 3) +#define RT5668_GP2_STA (0x1 << 2) +#define RT5668_GP1_STA (0x1 << 1) + +/* Soft volume and zero cross control 1 (0x00d9) */ +#define RT5668_SV_MASK (0x1 << 15) +#define RT5668_SV_SFT 15 +#define RT5668_SV_DIS (0x0 << 15) +#define RT5668_SV_EN (0x1 << 15) +#define RT5668_ZCD_MASK (0x1 << 10) +#define RT5668_ZCD_SFT 10 +#define RT5668_ZCD_PD (0x0 << 10) +#define RT5668_ZCD_PU (0x1 << 10) +#define RT5668_SV_DLY_MASK (0xf) +#define RT5668_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0x00da) */ +#define RT5668_ZCD_BST1_CBJ_MASK (0x1 << 7) +#define RT5668_ZCD_BST1_CBJ_SFT 7 +#define RT5668_ZCD_BST1_CBJ_DIS (0x0 << 7) +#define RT5668_ZCD_BST1_CBJ_EN (0x1 << 7) +#define RT5668_ZCD_RECMIX_MASK (0x1) +#define RT5668_ZCD_RECMIX_SFT 0 +#define RT5668_ZCD_RECMIX_DIS (0x0) +#define RT5668_ZCD_RECMIX_EN (0x1) + +/* 4 Button Inline Command Control 2 (0x00e3) */ +#define RT5668_4BTN_IL_MASK (0x1 << 15) +#define RT5668_4BTN_IL_EN (0x1 << 15) +#define RT5668_4BTN_IL_DIS (0x0 << 15) +#define RT5668_4BTN_IL_RST_MASK (0x1 << 14) +#define RT5668_4BTN_IL_NOR (0x1 << 14) +#define RT5668_4BTN_IL_RST (0x0 << 14) + +/* Analog JD Control (0x00f0) */ +#define RT5668_JDH_RS_MASK (0x1 << 4) +#define RT5668_JDH_NO_PLUG (0x1 << 4) +#define RT5668_JDH_PLUG (0x0 << 4) + +/* Chopper and Clock control for DAC (0x013a)*/ +#define RT5668_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5668_CKXEN_DAC1_SFT 13 +#define RT5668_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5668_CKGEN_DAC1_SFT 12 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5668_CKXEN_ADC1_MASK (0x1 << 13) +#define RT5668_CKXEN_ADC1_SFT 13 +#define RT5668_CKGEN_ADC1_MASK (0x1 << 12) +#define RT5668_CKGEN_ADC1_SFT 12 + +/* Volume test (0x013f)*/ +#define RT5668_SEL_CLK_VOL_MASK (0x1 << 15) +#define RT5668_SEL_CLK_VOL_EN (0x1 << 15) +#define RT5668_SEL_CLK_VOL_DIS (0x0 << 15) + +/* Test Mode Control 1 (0x0145) */ +#define RT5668_AD2DA_LB_MASK (0x1 << 10) +#define RT5668_AD2DA_LB_SFT 10 + +/* Stereo Noise Gate Control 1 (0x0160) */ +#define RT5668_NG2_EN_MASK (0x1 << 15) +#define RT5668_NG2_EN (0x1 << 15) +#define RT5668_NG2_DIS (0x0 << 15) + +/* Stereo1 DAC Silence Detection Control (0x0190) */ +#define RT5668_DEB_STO_DAC_MASK (0x7 << 4) +#define RT5668_DEB_80_MS (0x0 << 4) + +/* SAR ADC Inline Command Control 1 (0x0210) */ +#define RT5668_SAR_BUTT_DET_MASK (0x1 << 15) +#define RT5668_SAR_BUTT_DET_EN (0x1 << 15) +#define RT5668_SAR_BUTT_DET_DIS (0x0 << 15) +#define RT5668_SAR_BUTDET_MODE_MASK (0x1 << 14) +#define RT5668_SAR_BUTDET_POW_SAV (0x1 << 14) +#define RT5668_SAR_BUTDET_POW_NORM (0x0 << 14) +#define RT5668_SAR_BUTDET_RST_MASK (0x1 << 13) +#define RT5668_SAR_BUTDET_RST_NORMAL (0x1 << 13) +#define RT5668_SAR_BUTDET_RST (0x0 << 13) +#define RT5668_SAR_POW_MASK (0x1 << 12) +#define RT5668_SAR_POW_EN (0x1 << 12) +#define RT5668_SAR_POW_DIS (0x0 << 12) +#define RT5668_SAR_RST_MASK (0x1 << 11) +#define RT5668_SAR_RST_NORMAL (0x1 << 11) +#define RT5668_SAR_RST (0x0 << 11) +#define RT5668_SAR_BYPASS_MASK (0x1 << 10) +#define RT5668_SAR_BYPASS_EN (0x1 << 10) +#define RT5668_SAR_BYPASS_DIS (0x0 << 10) +#define RT5668_SAR_SEL_MB1_MASK (0x1 << 9) +#define RT5668_SAR_SEL_MB1_SEL (0x1 << 9) +#define RT5668_SAR_SEL_MB1_NOSEL (0x0 << 9) +#define RT5668_SAR_SEL_MB2_MASK (0x1 << 8) +#define RT5668_SAR_SEL_MB2_SEL (0x1 << 8) +#define RT5668_SAR_SEL_MB2_NOSEL (0x0 << 8) +#define RT5668_SAR_SEL_MODE_MASK (0x1 << 7) +#define RT5668_SAR_SEL_MODE_CMP (0x1 << 7) +#define RT5668_SAR_SEL_MODE_ADC (0x0 << 7) +#define RT5668_SAR_SEL_MB1_MB2_MASK (0x1 << 5) +#define RT5668_SAR_SEL_MB1_MB2_AUTO (0x1 << 5) +#define RT5668_SAR_SEL_MB1_MB2_MANU (0x0 << 5) +#define RT5668_SAR_SEL_SIGNAL_MASK (0x1 << 4) +#define RT5668_SAR_SEL_SIGNAL_AUTO (0x1 << 4) +#define RT5668_SAR_SEL_SIGNAL_MANU (0x0 << 4) + +/* SAR ADC Inline Command Control 13 (0x021c) */ +#define RT5668_SAR_SOUR_MASK (0x3f) +#define RT5668_SAR_SOUR_BTN (0x3f) +#define RT5668_SAR_SOUR_TYPE (0x0) + + +/* System Clock Source */ +enum { + RT5668_SCLK_S_MCLK, + RT5668_SCLK_S_PLL1, + RT5668_SCLK_S_PLL2, + RT5668_SCLK_S_RCCLK, +}; + +/* PLL Source */ +enum { + RT5668_PLL1_S_MCLK, + RT5668_PLL1_S_BCLK1, + RT5668_PLL1_S_RCCLK, +}; + +enum { + RT5668_AIF1, + RT5668_AIF2, + RT5668_AIFS +}; + +/* filter mask */ +enum { + RT5668_DA_STEREO1_FILTER = 0x1, + RT5668_AD_STEREO1_FILTER = (0x1 << 1), +}; + +enum { + RT5668_CLK_SEL_SYS, + RT5668_CLK_SEL_I2S1_ASRC, + RT5668_CLK_SEL_I2S2_ASRC, +}; + +int rt5668_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +#endif /* __RT5668_H__ */ diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index dc7df337d5f8..732ef928b25d 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -71,7 +71,7 @@ static const struct regmap_range_cfg rt5670_ranges[] = { static const struct reg_sequence init_list[] = { { RT5670_PR_BASE + 0x14, 0x9a8a }, - { RT5670_PR_BASE + 0x38, 0x3ba1 }, + { RT5670_PR_BASE + 0x38, 0x1fe1 }, { RT5670_PR_BASE + 0x3d, 0x3640 }, { 0x8a, 0x0123 }, }; diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index bc1a23dd7c2d..8a0181a2db08 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -5006,13 +5006,6 @@ static const struct regmap_config rt5677_regmap = { .num_ranges = ARRAY_SIZE(rt5677_ranges), }; -static const struct i2c_device_id rt5677_i2c_id[] = { - { "rt5677", RT5677 }, - { "rt5676", RT5676 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id); - static const struct of_device_id rt5677_of_match[] = { { .compatible = "realtek,rt5677", RT5677 }, { } @@ -5130,8 +5123,7 @@ static void rt5677_free_irq(struct i2c_client *i2c) regmap_del_irq_chip(i2c->irq, rt5677->irq_data); } -static int rt5677_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int rt5677_i2c_probe(struct i2c_client *i2c) { struct rt5677_priv *rt5677; int ret; @@ -5278,9 +5270,8 @@ static struct i2c_driver rt5677_i2c_driver = { .of_match_table = rt5677_of_match, .acpi_match_table = ACPI_PTR(rt5677_acpi_match), }, - .probe = rt5677_i2c_probe, + .probe_new = rt5677_i2c_probe, .remove = rt5677_i2c_remove, - .id_table = rt5677_i2c_id, }; module_i2c_driver(rt5677_i2c_driver); diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 7c1d65830c05..60764f6201b1 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1,12 +1,8 @@ -/* - * sgtl5000.c -- SGTL5000 ALSA SoC Audio driver - * - * Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// sgtl5000.c -- SGTL5000 ALSA SoC Audio driver +// +// Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. #include <linux/module.h> #include <linux/moduleparam.h> @@ -457,7 +453,7 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol, * avc_put_threshold function: register_value = 10^(dB/20) * 0.636 * 2^15 ==> * dB = ( fls(register_value) - 14.347 ) * 6.02 * - * As this calculation is expensive and the threshold dB values may not exeed + * As this calculation is expensive and the threshold dB values may not exceed * 0 to 96 we use pre-calculated values. */ static int avc_get_threshold(struct snd_kcontrol *kcontrol, @@ -490,7 +486,7 @@ static int avc_get_threshold(struct snd_kcontrol *kcontrol, * * The register value is calculated by following formula: * register_value = 10^(dB/20) * 0.636 * 2^15 - * As this calculation is expensive and the threshold dB values may not exeed + * As this calculation is expensive and the threshold dB values may not exceed * 0 to 96 we use pre-calculated values. */ static int avc_put_threshold(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h index 28cf637155bb..18cae08bbd3a 100644 --- a/sound/soc/codecs/sgtl5000.h +++ b/sound/soc/codecs/sgtl5000.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * sgtl5000.h - SGTL5000 audio codec interface * * Copyright 2010-2011 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef _SGTL5000_H diff --git a/sound/soc/codecs/ssm2305.c b/sound/soc/codecs/ssm2305.c new file mode 100644 index 000000000000..2968959c4b75 --- /dev/null +++ b/sound/soc/codecs/ssm2305.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Analog Devices SSM2305 Amplifier Driver +// +// Copyright (C) 2018 Pengutronix, Marco Felsch <kernel@pengutronix.de> +// + +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <sound/soc.h> + +#define DRV_NAME "ssm2305" + +struct ssm2305 { + /* shutdown gpio */ + struct gpio_desc *gpiod_shutdown; +}; + +static int ssm2305_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kctrl, int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct ssm2305 *data = snd_soc_component_get_drvdata(c); + + gpiod_set_value_cansleep(data->gpiod_shutdown, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget ssm2305_dapm_widgets[] = { + /* Stereo input/output */ + SND_SOC_DAPM_INPUT("L_IN"), + SND_SOC_DAPM_INPUT("R_IN"), + SND_SOC_DAPM_OUTPUT("L_OUT"), + SND_SOC_DAPM_OUTPUT("R_OUT"), + + SND_SOC_DAPM_SUPPLY("Power", SND_SOC_NOPM, 0, 0, ssm2305_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route ssm2305_dapm_routes[] = { + { "L_OUT", NULL, "L_IN" }, + { "R_OUT", NULL, "R_IN" }, + { "L_IN", NULL, "Power" }, + { "R_IN", NULL, "Power" }, +}; + +static const struct snd_soc_component_driver ssm2305_component_driver = { + .dapm_widgets = ssm2305_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm2305_dapm_widgets), + .dapm_routes = ssm2305_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ssm2305_dapm_routes), +}; + +static int ssm2305_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ssm2305 *priv; + int err; + + /* Allocate the private data */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + /* Get shutdown gpio */ + priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", + GPIOD_OUT_LOW); + if (IS_ERR(priv->gpiod_shutdown)) { + err = PTR_ERR(priv->gpiod_shutdown); + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get 'shutdown' gpio: %d\n", + err); + return err; + } + + return devm_snd_soc_register_component(dev, &ssm2305_component_driver, + NULL, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id ssm2305_of_match[] = { + { .compatible = "adi,ssm2305", }, + { } +}; +MODULE_DEVICE_TABLE(of, ssm2305_of_match); +#endif + +static struct platform_driver ssm2305_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(ssm2305_of_match), + }, + .probe = ssm2305_probe, +}; + +module_platform_driver(ssm2305_driver); + +MODULE_DESCRIPTION("ASoC SSM2305 amplifier driver"); +MODULE_AUTHOR("Marco Felsch <m.felsch@pengutronix.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c index 4f3a16c520a2..14999b999fd3 100644 --- a/sound/soc/codecs/tas6424.c +++ b/sound/soc/codecs/tas6424.c @@ -16,6 +16,7 @@ #include <linux/slab.h> #include <linux/regulator/consumer.h> #include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -43,6 +44,8 @@ struct tas6424_data { unsigned int last_fault1; unsigned int last_fault2; unsigned int last_warn; + struct gpio_desc *standby_gpio; + struct gpio_desc *mute_gpio; }; /* @@ -61,6 +64,8 @@ static const struct snd_kcontrol_new tas6424_snd_controls[] = { TAS6424_CH3_VOL_CTRL, 0, 0xff, 0, dac_tlv), SOC_SINGLE_TLV("Speaker Driver CH4 Playback Volume", TAS6424_CH4_VOL_CTRL, 0, 0xff, 0, dac_tlv), + SOC_SINGLE_STROBE("Auto Diagnostics Switch", TAS6424_DC_DIAG_CTRL1, + TAS6424_LDGBYPASS_SHIFT, 1), }; static int tas6424_dac_event(struct snd_soc_dapm_widget *w, @@ -249,10 +254,16 @@ static int tas6424_set_dai_tdm_slot(struct snd_soc_dai *dai, static int tas6424_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_component *component = dai->component; + struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); unsigned int val; dev_dbg(component->dev, "%s() mute=%d\n", __func__, mute); + if (tas6424->mute_gpio) { + gpiod_set_value_cansleep(tas6424->mute_gpio, mute); + return 0; + } + if (mute) val = TAS6424_ALL_STATE_MUTE; else @@ -287,6 +298,12 @@ static int tas6424_power_on(struct snd_soc_component *component) { struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); int ret; + u8 chan_states; + int no_auto_diags = 0; + unsigned int reg_val; + + if (!regmap_read(tas6424->regmap, TAS6424_DC_DIAG_CTRL1, ®_val)) + no_auto_diags = reg_val & TAS6424_LDGBYPASS_MASK; ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies), tas6424->supplies); @@ -303,12 +320,25 @@ static int tas6424_power_on(struct snd_soc_component *component) return ret; } - snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, TAS6424_ALL_STATE_MUTE); + if (tas6424->mute_gpio) { + gpiod_set_value_cansleep(tas6424->mute_gpio, 0); + /* + * channels are muted via the mute pin. Don't also mute + * them via the registers so that subsequent register + * access is not necessary to un-mute the channels + */ + chan_states = TAS6424_ALL_STATE_PLAY; + } else { + chan_states = TAS6424_ALL_STATE_MUTE; + } + snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, chan_states); /* any time we come out of HIZ, the output channels automatically run DC - * load diagnostics, wait here until this completes + * load diagnostics if autodiagnotics are enabled. wait here until this + * completes. */ - msleep(230); + if (!no_auto_diags) + msleep(230); return 0; } @@ -627,6 +657,38 @@ static int tas6424_i2c_probe(struct i2c_client *client, return ret; } + /* + * Get control of the standby pin and set it LOW to take the codec + * out of the stand-by mode. + * Note: The actual pin polarity is taken care of in the GPIO lib + * according the polarity specified in the DTS. + */ + tas6424->standby_gpio = devm_gpiod_get_optional(dev, "standby", + GPIOD_OUT_LOW); + if (IS_ERR(tas6424->standby_gpio)) { + if (PTR_ERR(tas6424->standby_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "failed to get standby GPIO: %ld\n", + PTR_ERR(tas6424->standby_gpio)); + tas6424->standby_gpio = NULL; + } + + /* + * Get control of the mute pin and set it HIGH in order to start with + * all the output muted. + * Note: The actual pin polarity is taken care of in the GPIO lib + * according the polarity specified in the DTS. + */ + tas6424->mute_gpio = devm_gpiod_get_optional(dev, "mute", + GPIOD_OUT_HIGH); + if (IS_ERR(tas6424->mute_gpio)) { + if (PTR_ERR(tas6424->mute_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "failed to get nmute GPIO: %ld\n", + PTR_ERR(tas6424->mute_gpio)); + tas6424->mute_gpio = NULL; + } + for (i = 0; i < ARRAY_SIZE(tas6424->supplies); i++) tas6424->supplies[i].supply = tas6424_supply_names[i]; ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(tas6424->supplies), @@ -671,6 +733,10 @@ static int tas6424_i2c_remove(struct i2c_client *client) cancel_delayed_work_sync(&tas6424->fault_check_work); + /* put the codec in stand-by */ + if (tas6424->standby_gpio) + gpiod_set_value_cansleep(tas6424->standby_gpio, 1); + ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), tas6424->supplies); if (ret < 0) { diff --git a/sound/soc/codecs/tas6424.h b/sound/soc/codecs/tas6424.h index 430588328a06..b5958c45ed0e 100644 --- a/sound/soc/codecs/tas6424.h +++ b/sound/soc/codecs/tas6424.h @@ -111,6 +111,10 @@ TAS6424_CH3_STATE_DIAG | \ TAS6424_CH4_STATE_DIAG) +/* TAS6424_DC_DIAG_CTRL1 */ +#define TAS6424_LDGBYPASS_SHIFT 0 +#define TAS6424_LDGBYPASS_MASK BIT(TAS6424_LDGBYPASS_SHIFT) + /* TAS6424_GLOB_FAULT1_REG */ #define TAS6424_FAULT_CLOCK BIT(4) #define TAS6424_FAULT_PVDD_OV BIT(3) diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index 6d213c6d3920..abc114a3ae2b 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -1,15 +1,9 @@ -/* - * tfa9879.c -- driver for NXP Semiconductors TFA9879 - * - * Copyright (C) 2014 Axentia Technologies AB - * Author: Peter Rosin <peda@axentia.se> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// tfa9879.c -- driver for NXP Semiconductors TFA9879 +// +// Copyright (C) 2014 Axentia Technologies AB +// Author: Peter Rosin <peda@axentia.se> #include <linux/module.h> #include <linux/init.h> @@ -88,13 +82,14 @@ static int tfa9879_hw_params(struct snd_pcm_substream *substream, } if (tfa9879->lsb_justified) - snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1, - TFA9879_I2S_SET_MASK, - i2s_set << TFA9879_I2S_SET_SHIFT); + snd_soc_component_update_bits(component, + TFA9879_SERIAL_INTERFACE_1, + TFA9879_I2S_SET_MASK, + i2s_set << TFA9879_I2S_SET_SHIFT); snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1, - TFA9879_I2S_FS_MASK, - fs << TFA9879_I2S_FS_SHIFT); + TFA9879_I2S_FS_MASK, + fs << TFA9879_I2S_FS_SHIFT); return 0; } @@ -103,8 +98,8 @@ static int tfa9879_digital_mute(struct snd_soc_dai *dai, int mute) struct snd_soc_component *component = dai->component; snd_soc_component_update_bits(component, TFA9879_MISC_CONTROL, - TFA9879_S_MUTE_MASK, - !!mute << TFA9879_S_MUTE_SHIFT); + TFA9879_S_MUTE_MASK, + !!mute << TFA9879_S_MUTE_SHIFT); return 0; } @@ -152,11 +147,11 @@ static int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1, - TFA9879_SCK_POL_MASK, - sck_pol << TFA9879_SCK_POL_SHIFT); + TFA9879_SCK_POL_MASK, + sck_pol << TFA9879_SCK_POL_SHIFT); snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1, - TFA9879_I2S_SET_MASK, - i2s_set << TFA9879_I2S_SET_SHIFT); + TFA9879_I2S_SET_MASK, + i2s_set << TFA9879_I2S_SET_SHIFT); return 0; } @@ -276,8 +271,7 @@ static struct snd_soc_dai_driver tfa9879_dai = { .ops = &tfa9879_dai_ops, }; -static int tfa9879_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int tfa9879_i2c_probe(struct i2c_client *i2c) { struct tfa9879_priv *tfa9879; int i; @@ -298,7 +292,7 @@ static int tfa9879_i2c_probe(struct i2c_client *i2c, tfa9879_regs[i].reg, tfa9879_regs[i].def); return devm_snd_soc_register_component(&i2c->dev, &tfa9879_component, - &tfa9879_dai, 1); + &tfa9879_dai, 1); } static const struct i2c_device_id tfa9879_i2c_id[] = { @@ -318,7 +312,7 @@ static struct i2c_driver tfa9879_i2c_driver = { .name = "tfa9879", .of_match_table = tfa9879_of_match, }, - .probe = tfa9879_i2c_probe, + .probe_new = tfa9879_i2c_probe, .id_table = tfa9879_i2c_id, }; diff --git a/sound/soc/codecs/tfa9879.h b/sound/soc/codecs/tfa9879.h index 3408c90c4628..66c88d0396fe 100644 --- a/sound/soc/codecs/tfa9879.h +++ b/sound/soc/codecs/tfa9879.h @@ -1,14 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * tfa9879.h -- driver for NXP Semiconductors TFA9879 * * Copyright (C) 2014 Axentia Technologies AB * Author: Peter Rosin <peda@axentia.se> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * */ #ifndef _TFA9879_H diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index bbfc73a79b18..d18ff17719cc 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/mutex.h> +#include <linux/clk.h> #include <sound/tlv.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -31,7 +32,6 @@ struct tscs42xx { int bclk_ratio; int samplerate; - unsigned int blrcm; struct mutex audio_params_lock; u8 coeff_ram[COEFF_RAM_SIZE]; @@ -42,7 +42,8 @@ struct tscs42xx { struct regmap *regmap; - struct device *dev; + struct clk *sysclk; + int sysclk_src_id; }; struct coeff_ram_ctl { @@ -204,7 +205,8 @@ static int power_up_audio_plls(struct snd_soc_component *component) break; default: ret = -EINVAL; - dev_err(component->dev, "Unrecognized PLL output freq (%d)\n", ret); + dev_err(component->dev, + "Unrecognized PLL output freq (%d)\n", ret); return ret; } @@ -261,7 +263,8 @@ exit: static int coeff_ram_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); struct coeff_ram_ctl *ctl = (struct coeff_ram_ctl *)kcontrol->private_value; @@ -280,7 +283,8 @@ static int coeff_ram_get(struct snd_kcontrol *kcontrol, static int coeff_ram_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); struct coeff_ram_ctl *ctl = (struct coeff_ram_ctl *)kcontrol->private_value; @@ -363,7 +367,8 @@ static int dapm_micb_event(struct snd_soc_dapm_widget *w, static int pll_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 snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); int ret; if (SND_SOC_DAPM_EVENT_ON(event)) @@ -377,7 +382,8 @@ static int pll_event(struct snd_soc_dapm_widget *w, static int dac_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 snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); int ret; @@ -819,16 +825,19 @@ static int setup_sample_format(struct snd_soc_component *component, dev_err(component->dev, "Unsupported format width (%d)\n", ret); return ret; } - ret = snd_soc_component_update_bits(component, R_AIC1, RM_AIC1_WL, width); + ret = snd_soc_component_update_bits(component, + R_AIC1, RM_AIC1_WL, width); if (ret < 0) { - dev_err(component->dev, "Failed to set sample width (%d)\n", ret); + dev_err(component->dev, + "Failed to set sample width (%d)\n", ret); return ret; } return 0; } -static int setup_sample_rate(struct snd_soc_component *component, unsigned int rate) +static int setup_sample_rate(struct snd_soc_component *component, + unsigned int rate) { struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); unsigned int br, bm; @@ -881,24 +890,32 @@ static int setup_sample_rate(struct snd_soc_component *component, unsigned int r } /* DAC and ADC share bit and frame clock */ - ret = snd_soc_component_update_bits(component, R_DACSR, RM_DACSR_DBR, br); + ret = snd_soc_component_update_bits(component, + R_DACSR, RM_DACSR_DBR, br); if (ret < 0) { - dev_err(component->dev, "Failed to update register (%d)\n", ret); + dev_err(component->dev, + "Failed to update register (%d)\n", ret); return ret; } - ret = snd_soc_component_update_bits(component, R_DACSR, RM_DACSR_DBM, bm); + ret = snd_soc_component_update_bits(component, + R_DACSR, RM_DACSR_DBM, bm); if (ret < 0) { - dev_err(component->dev, "Failed to update register (%d)\n", ret); + dev_err(component->dev, + "Failed to update register (%d)\n", ret); return ret; } - ret = snd_soc_component_update_bits(component, R_ADCSR, RM_DACSR_DBR, br); + ret = snd_soc_component_update_bits(component, + R_ADCSR, RM_DACSR_DBR, br); if (ret < 0) { - dev_err(component->dev, "Failed to update register (%d)\n", ret); + dev_err(component->dev, + "Failed to update register (%d)\n", ret); return ret; } - ret = snd_soc_component_update_bits(component, R_ADCSR, RM_DACSR_DBM, bm); + ret = snd_soc_component_update_bits(component, + R_ADCSR, RM_DACSR_DBM, bm); if (ret < 0) { - dev_err(component->dev, "Failed to update register (%d)\n", ret); + dev_err(component->dev, + "Failed to update register (%d)\n", ret); return ret; } @@ -1076,7 +1093,8 @@ static int tscs42xx_hw_params(struct snd_pcm_substream *substream, ret = setup_sample_rate(component, params_rate(params)); if (ret < 0) { - dev_err(component->dev, "Failed to setup sample rate (%d)\n", ret); + dev_err(component->dev, + "Failed to setup sample rate (%d)\n", ret); return ret; } @@ -1087,7 +1105,8 @@ static inline int dac_mute(struct snd_soc_component *component) { int ret; - ret = snd_soc_component_update_bits(component, R_CNVRTR1, RM_CNVRTR1_DACMU, + ret = snd_soc_component_update_bits(component, + R_CNVRTR1, RM_CNVRTR1_DACMU, RV_CNVRTR1_DACMU_ENABLE); if (ret < 0) { dev_err(component->dev, "Failed to mute DAC (%d)\n", @@ -1102,7 +1121,8 @@ static inline int dac_unmute(struct snd_soc_component *component) { int ret; - ret = snd_soc_component_update_bits(component, R_CNVRTR1, RM_CNVRTR1_DACMU, + ret = snd_soc_component_update_bits(component, + R_CNVRTR1, RM_CNVRTR1_DACMU, RV_CNVRTR1_DACMU_DISABLE); if (ret < 0) { dev_err(component->dev, "Failed to unmute DAC (%d)\n", @@ -1117,8 +1137,8 @@ static inline int adc_mute(struct snd_soc_component *component) { int ret; - ret = snd_soc_component_update_bits(component, R_CNVRTR0, RM_CNVRTR0_ADCMU, - RV_CNVRTR0_ADCMU_ENABLE); + ret = snd_soc_component_update_bits(component, + R_CNVRTR0, RM_CNVRTR0_ADCMU, RV_CNVRTR0_ADCMU_ENABLE); if (ret < 0) { dev_err(component->dev, "Failed to mute ADC (%d)\n", ret); @@ -1132,8 +1152,8 @@ static inline int adc_unmute(struct snd_soc_component *component) { int ret; - ret = snd_soc_component_update_bits(component, R_CNVRTR0, RM_CNVRTR0_ADCMU, - RV_CNVRTR0_ADCMU_DISABLE); + ret = snd_soc_component_update_bits(component, + R_CNVRTR0, RM_CNVRTR0_ADCMU, RV_CNVRTR0_ADCMU_DISABLE); if (ret < 0) { dev_err(component->dev, "Failed to unmute ADC (%d)\n", ret); @@ -1171,8 +1191,8 @@ static int tscs42xx_set_dai_fmt(struct snd_soc_dai *codec_dai, /* Slave mode not supported since it needs always-on frame clock */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - ret = snd_soc_component_update_bits(component, R_AIC1, RM_AIC1_MS, - RV_AIC1_MS_MASTER); + ret = snd_soc_component_update_bits(component, + R_AIC1, RM_AIC1_MS, RV_AIC1_MS_MASTER); if (ret < 0) { dev_err(component->dev, "Failed to set codec DAI master (%d)\n", ret); @@ -1211,14 +1231,18 @@ static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai, return -EINVAL; } - ret = snd_soc_component_update_bits(component, R_DACSR, RM_DACSR_DBCM, value); + ret = snd_soc_component_update_bits(component, + R_DACSR, RM_DACSR_DBCM, value); if (ret < 0) { - dev_err(component->dev, "Failed to set DAC BCLK ratio (%d)\n", ret); + dev_err(component->dev, + "Failed to set DAC BCLK ratio (%d)\n", ret); return ret; } - ret = snd_soc_component_update_bits(component, R_ADCSR, RM_ADCSR_ABCM, value); + ret = snd_soc_component_update_bits(component, + R_ADCSR, RM_ADCSR_ABCM, value); if (ret < 0) { - dev_err(component->dev, "Failed to set ADC BCLK ratio (%d)\n", ret); + dev_err(component->dev, + "Failed to set ADC BCLK ratio (%d)\n", ret); return ret; } @@ -1231,13 +1255,46 @@ static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai, return 0; } -static int tscs42xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir) +static const struct snd_soc_dai_ops tscs42xx_dai_ops = { + .hw_params = tscs42xx_hw_params, + .mute_stream = tscs42xx_mute_stream, + .set_fmt = tscs42xx_set_dai_fmt, + .set_bclk_ratio = tscs42xx_set_dai_bclk_ratio, +}; + +static int part_is_valid(struct tscs42xx *tscs42xx) { - struct snd_soc_component *component = codec_dai->component; + int val; int ret; + unsigned int reg; + + ret = regmap_read(tscs42xx->regmap, R_DEVIDH, ®); + if (ret < 0) + return ret; + + val = reg << 8; + ret = regmap_read(tscs42xx->regmap, R_DEVIDL, ®); + if (ret < 0) + return ret; + + val |= reg; + + switch (val) { + case 0x4A74: + case 0x4A73: + return true; + default: + return false; + }; +} - switch (clk_id) { +static int set_sysclk(struct snd_soc_component *component) +{ + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + unsigned long freq; + int ret; + + switch (tscs42xx->sysclk_src_id) { case TSCS42XX_PLL_SRC_XTAL: case TSCS42XX_PLL_SRC_MCLK1: ret = snd_soc_component_write(component, R_PLLREFSEL, @@ -1265,6 +1322,7 @@ static int tscs42xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, return -EINVAL; } + freq = clk_get_rate(tscs42xx->sysclk); ret = set_pll_ctl_from_input_freq(component, freq); if (ret < 0) { dev_err(component->dev, @@ -1275,41 +1333,13 @@ static int tscs42xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, return 0; } -static const struct snd_soc_dai_ops tscs42xx_dai_ops = { - .hw_params = tscs42xx_hw_params, - .mute_stream = tscs42xx_mute_stream, - .set_fmt = tscs42xx_set_dai_fmt, - .set_bclk_ratio = tscs42xx_set_dai_bclk_ratio, - .set_sysclk = tscs42xx_set_dai_sysclk, -}; - -static int part_is_valid(struct tscs42xx *tscs42xx) +static int tscs42xx_probe(struct snd_soc_component *component) { - int val; - int ret; - unsigned int reg; - - ret = regmap_read(tscs42xx->regmap, R_DEVIDH, ®); - if (ret < 0) - return ret; - - val = reg << 8; - ret = regmap_read(tscs42xx->regmap, R_DEVIDL, ®); - if (ret < 0) - return ret; - - val |= reg; - - switch (val) { - case 0x4A74: - case 0x4A73: - return true; - default: - return false; - }; + return set_sysclk(component); } -static struct snd_soc_component_driver soc_codec_dev_tscs42xx = { +static const struct snd_soc_component_driver soc_codec_dev_tscs42xx = { + .probe = tscs42xx_probe, .dapm_widgets = tscs42xx_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tscs42xx_dapm_widgets), .dapm_routes = tscs42xx_intercon, @@ -1367,11 +1397,15 @@ static const struct reg_sequence tscs42xx_patch[] = { { R_AIC2, RV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED }, }; +static char const * const src_names[TSCS42XX_PLL_SRC_CNT] = { + "xtal", "mclk1", "mclk2"}; + static int tscs42xx_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct tscs42xx *tscs42xx; - int ret = 0; + int src; + int ret; tscs42xx = devm_kzalloc(&i2c->dev, sizeof(*tscs42xx), GFP_KERNEL); if (!tscs42xx) { @@ -1381,12 +1415,29 @@ static int tscs42xx_i2c_probe(struct i2c_client *i2c, return ret; } i2c_set_clientdata(i2c, tscs42xx); - tscs42xx->dev = &i2c->dev; + + for (src = TSCS42XX_PLL_SRC_XTAL; src < TSCS42XX_PLL_SRC_CNT; src++) { + tscs42xx->sysclk = devm_clk_get(&i2c->dev, src_names[src]); + if (!IS_ERR(tscs42xx->sysclk)) { + break; + } else if (PTR_ERR(tscs42xx->sysclk) != -ENOENT) { + ret = PTR_ERR(tscs42xx->sysclk); + dev_err(&i2c->dev, "Failed to get sysclk (%d)\n", ret); + return ret; + } + } + if (src == TSCS42XX_PLL_SRC_CNT) { + ret = -EINVAL; + dev_err(&i2c->dev, "Failed to get a valid clock name (%d)\n", + ret); + return ret; + } + tscs42xx->sysclk_src_id = src; tscs42xx->regmap = devm_regmap_init_i2c(i2c, &tscs42xx_regmap); if (IS_ERR(tscs42xx->regmap)) { ret = PTR_ERR(tscs42xx->regmap); - dev_err(tscs42xx->dev, "Failed to allocate regmap (%d)\n", ret); + dev_err(&i2c->dev, "Failed to allocate regmap (%d)\n", ret); return ret; } @@ -1394,21 +1445,21 @@ static int tscs42xx_i2c_probe(struct i2c_client *i2c, ret = part_is_valid(tscs42xx); if (ret <= 0) { - dev_err(tscs42xx->dev, "No valid part (%d)\n", ret); + dev_err(&i2c->dev, "No valid part (%d)\n", ret); ret = -ENODEV; return ret; } ret = regmap_write(tscs42xx->regmap, R_RESET, RV_RESET_ENABLE); if (ret < 0) { - dev_err(tscs42xx->dev, "Failed to reset device (%d)\n", ret); + dev_err(&i2c->dev, "Failed to reset device (%d)\n", ret); return ret; } ret = regmap_register_patch(tscs42xx->regmap, tscs42xx_patch, ARRAY_SIZE(tscs42xx_patch)); if (ret < 0) { - dev_err(tscs42xx->dev, "Failed to apply patch (%d)\n", ret); + dev_err(&i2c->dev, "Failed to apply patch (%d)\n", ret); return ret; } @@ -1416,10 +1467,10 @@ static int tscs42xx_i2c_probe(struct i2c_client *i2c, mutex_init(&tscs42xx->coeff_ram_lock); mutex_init(&tscs42xx->pll_lock); - ret = devm_snd_soc_register_component(tscs42xx->dev, &soc_codec_dev_tscs42xx, - &tscs42xx_dai, 1); + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_tscs42xx, &tscs42xx_dai, 1); if (ret) { - dev_err(tscs42xx->dev, "Failed to register codec (%d)\n", ret); + dev_err(&i2c->dev, "Failed to register codec (%d)\n", ret); return ret; } diff --git a/sound/soc/codecs/tscs42xx.h b/sound/soc/codecs/tscs42xx.h index d4a30bcbf64b..814c8f3c4a68 100644 --- a/sound/soc/codecs/tscs42xx.h +++ b/sound/soc/codecs/tscs42xx.h @@ -7,10 +7,10 @@ #define __WOOKIE_H__ enum { - TSCS42XX_PLL_SRC_NONE, TSCS42XX_PLL_SRC_XTAL, TSCS42XX_PLL_SRC_MCLK1, TSCS42XX_PLL_SRC_MCLK2, + TSCS42XX_PLL_SRC_CNT, }; #define R_HPVOLL 0x0 diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c new file mode 100644 index 000000000000..ff85a0bf6170 --- /dev/null +++ b/sound/soc/codecs/tscs454.c @@ -0,0 +1,3497 @@ +// SPDX-License-Identifier: GPL-2.0 +// tscs454.c -- TSCS454 ALSA SoC Audio driver +// Copyright 2018 Tempo Semiconductor, Inc. +// Author: Steven Eckhoff <steven.eckhoff.opensource@gmail.com> + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/mutex.h> + +#include <sound/tlv.h> +#include <sound/pcm_params.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include "tscs454.h" + +static const unsigned int PLL_48K_RATE = (48000 * 256); +static const unsigned int PLL_44_1K_RATE = (44100 * 256); + +#define COEFF_SIZE 3 +#define BIQUAD_COEFF_COUNT 5 +#define BIQUAD_SIZE (COEFF_SIZE * BIQUAD_COEFF_COUNT) + +#define COEFF_RAM_MAX_ADDR 0xcd +#define COEFF_RAM_COEFF_COUNT (COEFF_RAM_MAX_ADDR + 1) +#define COEFF_RAM_SIZE (COEFF_SIZE * COEFF_RAM_COEFF_COUNT) + +enum { + TSCS454_DAI1_ID, + TSCS454_DAI2_ID, + TSCS454_DAI3_ID, + TSCS454_DAI_COUNT, +}; + +struct pll { + int id; + unsigned int users; + struct mutex lock; +}; + +static inline void pll_init(struct pll *pll, int id) +{ + pll->id = id; + mutex_init(&pll->lock); +} + +struct internal_rate { + struct pll *pll; +}; + +struct aif { + unsigned int id; + bool master; + struct pll *pll; +}; + +static inline void aif_init(struct aif *aif, unsigned int id) +{ + aif->id = id; +} + +struct coeff_ram { + u8 cache[COEFF_RAM_SIZE]; + bool synced; + struct mutex lock; +}; + +static inline void init_coeff_ram_cache(u8 *cache) +{ + static const u8 norm_addrs[] = { 0x00, 0x05, 0x0a, 0x0f, 0x14, 0x19, + 0x1f, 0x20, 0x25, 0x2a, 0x2f, 0x34, 0x39, 0x3f, 0x40, 0x45, + 0x4a, 0x4f, 0x54, 0x59, 0x5f, 0x60, 0x65, 0x6a, 0x6f, 0x74, + 0x79, 0x7f, 0x80, 0x85, 0x8c, 0x91, 0x96, 0x97, 0x9c, 0xa3, + 0xa8, 0xad, 0xaf, 0xb0, 0xb5, 0xba, 0xbf, 0xc4, 0xc9}; + int i; + + for (i = 0; i < ARRAY_SIZE(norm_addrs); i++) + cache[((norm_addrs[i] + 1) * COEFF_SIZE) - 1] = 0x40; +} + +static inline void coeff_ram_init(struct coeff_ram *ram) +{ + init_coeff_ram_cache(ram->cache); + mutex_init(&ram->lock); +} + +struct aifs_status { + u8 streams; +}; + +static inline void set_aif_status_active(struct aifs_status *status, + int aif_id, bool playback) +{ + u8 mask = 0x01 << (aif_id * 2 + !playback); + + status->streams |= mask; +} + +static inline void set_aif_status_inactive(struct aifs_status *status, + int aif_id, bool playback) +{ + u8 mask = ~(0x01 << (aif_id * 2 + !playback)); + + status->streams &= mask; +} + +static bool aifs_active(struct aifs_status *status) +{ + return status->streams; +} + +static bool aif_active(struct aifs_status *status, int aif_id) +{ + return (0x03 << aif_id * 2) & status->streams; +} + +struct tscs454 { + struct regmap *regmap; + struct aif aifs[TSCS454_DAI_COUNT]; + + struct aifs_status aifs_status; + struct mutex aifs_status_lock; + + struct pll pll1; + struct pll pll2; + struct internal_rate internal_rate; + + struct coeff_ram dac_ram; + struct coeff_ram spk_ram; + struct coeff_ram sub_ram; + + struct clk *sysclk; + int sysclk_src_id; + unsigned int bclk_freq; +}; + +struct coeff_ram_ctl { + unsigned int addr; + struct soc_bytes_ext bytes_ext; +}; + +static const struct reg_sequence tscs454_patch[] = { + /* Assign ASRC out of the box so DAI 1 just works */ + { R_AUDIOMUX1, FV_ASRCIMUX_I2S1 | FV_I2S2MUX_I2S2 }, + { R_AUDIOMUX2, FV_ASRCOMUX_I2S1 | FV_DACMUX_I2S1 | FV_I2S3MUX_I2S3 }, + { R_AUDIOMUX3, FV_CLSSDMUX_I2S1 | FV_SUBMUX_I2S1_LR }, + { R_TDMCTL0, FV_TDMMD_256 }, + { VIRT_ADDR(0x0A, 0x13), 1 << 3 }, +}; + +static bool tscs454_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_PLLSTAT: + + case R_SPKCRRDL: + case R_SPKCRRDM: + case R_SPKCRRDH: + case R_SPKCRS: + + case R_DACCRRDL: + case R_DACCRRDM: + case R_DACCRRDH: + case R_DACCRS: + + case R_SUBCRRDL: + case R_SUBCRRDM: + case R_SUBCRRDH: + case R_SUBCRS: + return true; + default: + return false; + }; +} + +static bool tscs454_writable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_SPKCRRDL: + case R_SPKCRRDM: + case R_SPKCRRDH: + + case R_DACCRRDL: + case R_DACCRRDM: + case R_DACCRRDH: + + case R_SUBCRRDL: + case R_SUBCRRDM: + case R_SUBCRRDH: + return false; + default: + return true; + }; +} + +static bool tscs454_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_SPKCRWDL: + case R_SPKCRWDM: + case R_SPKCRWDH: + + case R_DACCRWDL: + case R_DACCRWDM: + case R_DACCRWDH: + + case R_SUBCRWDL: + case R_SUBCRWDM: + case R_SUBCRWDH: + return false; + default: + return true; + }; +} + +static bool tscs454_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_SPKCRWDL: + case R_SPKCRWDM: + case R_SPKCRWDH: + case R_SPKCRRDL: + case R_SPKCRRDM: + case R_SPKCRRDH: + + case R_DACCRWDL: + case R_DACCRWDM: + case R_DACCRWDH: + case R_DACCRRDL: + case R_DACCRRDM: + case R_DACCRRDH: + + case R_SUBCRWDL: + case R_SUBCRWDM: + case R_SUBCRWDH: + case R_SUBCRRDL: + case R_SUBCRRDM: + case R_SUBCRRDH: + return true; + default: + return false; + }; +} + +static const struct regmap_range_cfg tscs454_regmap_range_cfg = { + .name = "Pages", + .range_min = VIRT_BASE, + .range_max = VIRT_ADDR(0xFE, 0x02), + .selector_reg = R_PAGESEL, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, +}; + +static struct regmap_config const tscs454_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = tscs454_writable, + .readable_reg = tscs454_readable, + .volatile_reg = tscs454_volatile, + .precious_reg = tscs454_precious, + .ranges = &tscs454_regmap_range_cfg, + .num_ranges = 1, + .max_register = VIRT_ADDR(0xFE, 0x02), + .cache_type = REGCACHE_RBTREE, +}; + +static inline int tscs454_data_init(struct tscs454 *tscs454, + struct i2c_client *i2c) +{ + int i; + int ret; + + tscs454->regmap = devm_regmap_init_i2c(i2c, &tscs454_regmap_cfg); + if (IS_ERR(tscs454->regmap)) { + ret = PTR_ERR(tscs454->regmap); + return ret; + } + + for (i = 0; i < TSCS454_DAI_COUNT; i++) + aif_init(&tscs454->aifs[i], i); + + mutex_init(&tscs454->aifs_status_lock); + pll_init(&tscs454->pll1, 1); + pll_init(&tscs454->pll2, 2); + + coeff_ram_init(&tscs454->dac_ram); + coeff_ram_init(&tscs454->spk_ram); + coeff_ram_init(&tscs454->sub_ram); + + return 0; +} + +struct reg_setting { + unsigned int addr; + unsigned int val; +}; + +static int coeff_ram_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + u8 *coeff_ram; + struct mutex *coeff_ram_lock; + + if (strstr(kcontrol->id.name, "DAC")) { + coeff_ram = tscs454->dac_ram.cache; + coeff_ram_lock = &tscs454->dac_ram.lock; + } else if (strstr(kcontrol->id.name, "Speaker")) { + coeff_ram = tscs454->spk_ram.cache; + coeff_ram_lock = &tscs454->spk_ram.lock; + } else if (strstr(kcontrol->id.name, "Sub")) { + coeff_ram = tscs454->sub_ram.cache; + coeff_ram_lock = &tscs454->sub_ram.lock; + } else { + return -EINVAL; + } + + mutex_lock(coeff_ram_lock); + + memcpy(ucontrol->value.bytes.data, + &coeff_ram[ctl->addr * COEFF_SIZE], params->max); + + mutex_unlock(coeff_ram_lock); + + return 0; +} + +#define DACCRSTAT_MAX_TRYS 10 +static int write_coeff_ram(struct snd_soc_component *component, u8 *coeff_ram, + unsigned int r_stat, unsigned int r_addr, unsigned int r_wr, + unsigned int coeff_addr, unsigned int coeff_cnt) +{ + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + unsigned int val; + int cnt; + int trys; + int ret; + + for (cnt = 0; cnt < coeff_cnt; cnt++, coeff_addr++) { + + for (trys = 0; trys < DACCRSTAT_MAX_TRYS; trys++) { + ret = snd_soc_component_read(component, r_stat, &val); + if (ret < 0) { + dev_err(component->dev, + "Failed to read stat (%d)\n", ret); + return ret; + } + if (!val) + break; + } + + if (trys == DACCRSTAT_MAX_TRYS) { + ret = -EIO; + dev_err(component->dev, + "Coefficient write error (%d)\n", ret); + return ret; + } + + ret = regmap_write(tscs454->regmap, r_addr, coeff_addr); + if (ret < 0) { + dev_err(component->dev, + "Failed to write dac ram address (%d)\n", ret); + return ret; + } + + ret = regmap_bulk_write(tscs454->regmap, r_wr, + &coeff_ram[coeff_addr * COEFF_SIZE], + COEFF_SIZE); + if (ret < 0) { + dev_err(component->dev, + "Failed to write dac ram (%d)\n", ret); + return ret; + } + } + + return 0; +} + +static int coeff_ram_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + unsigned int coeff_cnt = params->max / COEFF_SIZE; + u8 *coeff_ram; + struct mutex *coeff_ram_lock; + bool *coeff_ram_synced; + unsigned int r_stat; + unsigned int r_addr; + unsigned int r_wr; + unsigned int val; + int ret; + + if (strstr(kcontrol->id.name, "DAC")) { + coeff_ram = tscs454->dac_ram.cache; + coeff_ram_lock = &tscs454->dac_ram.lock; + coeff_ram_synced = &tscs454->dac_ram.synced; + r_stat = R_DACCRS; + r_addr = R_DACCRADD; + r_wr = R_DACCRWDL; + } else if (strstr(kcontrol->id.name, "Speaker")) { + coeff_ram = tscs454->spk_ram.cache; + coeff_ram_lock = &tscs454->spk_ram.lock; + coeff_ram_synced = &tscs454->spk_ram.synced; + r_stat = R_SPKCRS; + r_addr = R_SPKCRADD; + r_wr = R_SPKCRWDL; + } else if (strstr(kcontrol->id.name, "Sub")) { + coeff_ram = tscs454->sub_ram.cache; + coeff_ram_lock = &tscs454->sub_ram.lock; + coeff_ram_synced = &tscs454->sub_ram.synced; + r_stat = R_SUBCRS; + r_addr = R_SUBCRADD; + r_wr = R_SUBCRWDL; + } else { + return -EINVAL; + } + + mutex_lock(coeff_ram_lock); + + *coeff_ram_synced = false; + + memcpy(&coeff_ram[ctl->addr * COEFF_SIZE], + ucontrol->value.bytes.data, params->max); + + mutex_lock(&tscs454->pll1.lock); + mutex_lock(&tscs454->pll2.lock); + + ret = snd_soc_component_read(component, R_PLLSTAT, &val); + if (ret < 0) { + dev_err(component->dev, "Failed to read PLL status (%d)\n", + ret); + goto exit; + } + if (val) { /* PLLs locked */ + ret = write_coeff_ram(component, coeff_ram, + r_stat, r_addr, r_wr, + ctl->addr, coeff_cnt); + if (ret < 0) { + dev_err(component->dev, + "Failed to flush coeff ram cache (%d)\n", ret); + goto exit; + } + *coeff_ram_synced = true; + } + + ret = 0; +exit: + mutex_unlock(&tscs454->pll2.lock); + mutex_unlock(&tscs454->pll1.lock); + mutex_unlock(coeff_ram_lock); + + return ret; +} + +static inline int coeff_ram_sync(struct snd_soc_component *component, + struct tscs454 *tscs454) +{ + int ret; + + mutex_lock(&tscs454->dac_ram.lock); + if (!tscs454->dac_ram.synced) { + ret = write_coeff_ram(component, tscs454->dac_ram.cache, + R_DACCRS, R_DACCRADD, R_DACCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) { + mutex_unlock(&tscs454->dac_ram.lock); + return ret; + } + } + mutex_unlock(&tscs454->dac_ram.lock); + + mutex_lock(&tscs454->spk_ram.lock); + if (!tscs454->spk_ram.synced) { + ret = write_coeff_ram(component, tscs454->spk_ram.cache, + R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) { + mutex_unlock(&tscs454->spk_ram.lock); + return ret; + } + } + mutex_unlock(&tscs454->spk_ram.lock); + + mutex_lock(&tscs454->sub_ram.lock); + if (!tscs454->sub_ram.synced) { + ret = write_coeff_ram(component, tscs454->sub_ram.cache, + R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) { + mutex_unlock(&tscs454->sub_ram.lock); + return ret; + } + } + mutex_unlock(&tscs454->sub_ram.lock); + + return 0; +} + +#define PLL_REG_SETTINGS_COUNT 11 +struct pll_ctl { + int freq_in; + struct reg_setting settings[PLL_REG_SETTINGS_COUNT]; +}; + +#define PLL_CTL(f, t, c1, r1, o1, f1l, f1h, c2, r2, o2, f2l, f2h) \ + { \ + .freq_in = f, \ + .settings = { \ + {R_PLL1CTL, c1}, \ + {R_PLL1RDIV, r1}, \ + {R_PLL1ODIV, o1}, \ + {R_PLL1FDIVL, f1l}, \ + {R_PLL1FDIVH, f1h}, \ + {R_PLL2CTL, c2}, \ + {R_PLL2RDIV, r2}, \ + {R_PLL2ODIV, o2}, \ + {R_PLL2FDIVL, f2l}, \ + {R_PLL2FDIVH, f2h}, \ + {R_TIMEBASE, t}, \ + }, \ + } + +static const struct pll_ctl pll_ctls[] = { + PLL_CTL(1411200, 0x05, + 0xB9, 0x07, 0x02, 0xC3, 0x04, + 0x5A, 0x02, 0x03, 0xE0, 0x01), + PLL_CTL(1536000, 0x05, + 0x5A, 0x02, 0x03, 0xE0, 0x01, + 0x5A, 0x02, 0x03, 0xB9, 0x01), + PLL_CTL(2822400, 0x0A, + 0x63, 0x07, 0x04, 0xC3, 0x04, + 0x62, 0x07, 0x03, 0x48, 0x03), + PLL_CTL(3072000, 0x0B, + 0x62, 0x07, 0x03, 0x48, 0x03, + 0x5A, 0x04, 0x03, 0xB9, 0x01), + PLL_CTL(5644800, 0x15, + 0x63, 0x0E, 0x04, 0xC3, 0x04, + 0x5A, 0x08, 0x03, 0xE0, 0x01), + PLL_CTL(6144000, 0x17, + 0x5A, 0x08, 0x03, 0xE0, 0x01, + 0x5A, 0x08, 0x03, 0xB9, 0x01), + PLL_CTL(12000000, 0x2E, + 0x5B, 0x19, 0x03, 0x00, 0x03, + 0x6A, 0x19, 0x05, 0x98, 0x04), + PLL_CTL(19200000, 0x4A, + 0x53, 0x14, 0x03, 0x80, 0x01, + 0x5A, 0x19, 0x03, 0xB9, 0x01), + PLL_CTL(22000000, 0x55, + 0x6A, 0x37, 0x05, 0x00, 0x06, + 0x62, 0x26, 0x03, 0x49, 0x02), + PLL_CTL(22579200, 0x57, + 0x62, 0x31, 0x03, 0x20, 0x03, + 0x53, 0x1D, 0x03, 0xB3, 0x01), + PLL_CTL(24000000, 0x5D, + 0x53, 0x19, 0x03, 0x80, 0x01, + 0x5B, 0x19, 0x05, 0x4C, 0x02), + PLL_CTL(24576000, 0x5F, + 0x53, 0x1D, 0x03, 0xB3, 0x01, + 0x62, 0x40, 0x03, 0x72, 0x03), + PLL_CTL(27000000, 0x68, + 0x62, 0x4B, 0x03, 0x00, 0x04, + 0x6A, 0x7D, 0x03, 0x20, 0x06), + PLL_CTL(36000000, 0x8C, + 0x5B, 0x4B, 0x03, 0x00, 0x03, + 0x6A, 0x7D, 0x03, 0x98, 0x04), + PLL_CTL(11289600, 0x2B, + 0x6A, 0x31, 0x03, 0x40, 0x06, + 0x5A, 0x12, 0x03, 0x1C, 0x02), + PLL_CTL(26000000, 0x65, + 0x63, 0x41, 0x05, 0x00, 0x06, + 0x5A, 0x26, 0x03, 0xEF, 0x01), + PLL_CTL(12288000, 0x2F, + 0x5A, 0x12, 0x03, 0x1C, 0x02, + 0x62, 0x20, 0x03, 0x72, 0x03), + PLL_CTL(40000000, 0x9B, + 0xA2, 0x7D, 0x03, 0x80, 0x04, + 0x63, 0x7D, 0x05, 0xE4, 0x06), + PLL_CTL(512000, 0x01, + 0x62, 0x01, 0x03, 0xD0, 0x02, + 0x5B, 0x01, 0x04, 0x72, 0x03), + PLL_CTL(705600, 0x02, + 0x62, 0x02, 0x03, 0x15, 0x04, + 0x62, 0x01, 0x04, 0x80, 0x02), + PLL_CTL(1024000, 0x03, + 0x62, 0x02, 0x03, 0xD0, 0x02, + 0x5B, 0x02, 0x04, 0x72, 0x03), + PLL_CTL(2048000, 0x07, + 0x62, 0x04, 0x03, 0xD0, 0x02, + 0x5B, 0x04, 0x04, 0x72, 0x03), + PLL_CTL(2400000, 0x08, + 0x62, 0x05, 0x03, 0x00, 0x03, + 0x63, 0x05, 0x05, 0x98, 0x04), +}; + +static inline const struct pll_ctl *get_pll_ctl(unsigned long freq_in) +{ + int i; + struct pll_ctl const *pll_ctl = NULL; + + for (i = 0; i < ARRAY_SIZE(pll_ctls); ++i) + if (pll_ctls[i].freq_in == freq_in) { + pll_ctl = &pll_ctls[i]; + break; + } + + return pll_ctl; +} + +enum { + PLL_INPUT_XTAL = 0, + PLL_INPUT_MCLK1, + PLL_INPUT_MCLK2, + PLL_INPUT_BCLK, +}; + +static int set_sysclk(struct snd_soc_component *component) +{ + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct pll_ctl const *pll_ctl; + unsigned long freq; + int i; + int ret; + + if (tscs454->sysclk_src_id < PLL_INPUT_BCLK) + freq = clk_get_rate(tscs454->sysclk); + else + freq = tscs454->bclk_freq; + pll_ctl = get_pll_ctl(freq); + if (!pll_ctl) { + ret = -EINVAL; + dev_err(component->dev, + "Invalid PLL input %lu (%d)\n", freq, ret); + return ret; + } + + for (i = 0; i < PLL_REG_SETTINGS_COUNT; ++i) { + ret = snd_soc_component_write(component, + pll_ctl->settings[i].addr, + pll_ctl->settings[i].val); + if (ret < 0) { + dev_err(component->dev, + "Failed to set pll setting (%d)\n", + ret); + return ret; + } + } + + return 0; +} + +static inline void reserve_pll(struct pll *pll) +{ + mutex_lock(&pll->lock); + pll->users++; + mutex_unlock(&pll->lock); +} + +static inline void free_pll(struct pll *pll) +{ + mutex_lock(&pll->lock); + pll->users--; + mutex_unlock(&pll->lock); +} + +static int pll_connected(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + int users; + + if (strstr(source->name, "PLL 1")) { + mutex_lock(&tscs454->pll1.lock); + users = tscs454->pll1.users; + mutex_unlock(&tscs454->pll1.lock); + dev_dbg(component->dev, "%s(): PLL 1 users = %d\n", __func__, + users); + } else { + mutex_lock(&tscs454->pll2.lock); + users = tscs454->pll2.users; + mutex_unlock(&tscs454->pll2.lock); + dev_dbg(component->dev, "%s(): PLL 2 users = %d\n", __func__, + users); + } + + return users; +} + +/* + * PLL must be enabled after power up and must be disabled before power down + * for proper clock switching. + */ +static int pll_power_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 tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + bool enable; + bool pll1; + unsigned int msk; + unsigned int val; + int ret; + + if (strstr(w->name, "PLL 1")) + pll1 = true; + else + pll1 = false; + + msk = pll1 ? FM_PLLCTL_PLL1CLKEN : FM_PLLCTL_PLL2CLKEN; + + if (event == SND_SOC_DAPM_POST_PMU) + enable = true; + else + enable = false; + + if (enable) + val = pll1 ? FV_PLL1CLKEN_ENABLE : FV_PLL2CLKEN_ENABLE; + else + val = pll1 ? FV_PLL1CLKEN_DISABLE : FV_PLL2CLKEN_DISABLE; + + ret = snd_soc_component_update_bits(component, R_PLLCTL, msk, val); + if (ret < 0) { + dev_err(component->dev, "Failed to %s PLL %d (%d)\n", + enable ? "enable" : "disable", + pll1 ? 1 : 2, + ret); + return ret; + } + + if (enable) { + msleep(20); // Wait for lock + ret = coeff_ram_sync(component, tscs454); + if (ret < 0) { + dev_err(component->dev, + "Failed to sync coeff ram (%d)\n", ret); + return ret; + } + } + + return 0; +} + +static inline int aif_set_master(struct snd_soc_component *component, + unsigned int aif_id, bool master) +{ + unsigned int reg; + unsigned int mask; + unsigned int val; + int ret; + + switch (aif_id) { + case TSCS454_DAI1_ID: + reg = R_I2SP1CTL; + break; + case TSCS454_DAI2_ID: + reg = R_I2SP2CTL; + break; + case TSCS454_DAI3_ID: + reg = R_I2SP3CTL; + break; + default: + ret = -ENODEV; + dev_err(component->dev, "Unknown DAI %d (%d)\n", aif_id, ret); + return ret; + } + mask = FM_I2SPCTL_PORTMS; + val = master ? FV_PORTMS_MASTER : FV_PORTMS_SLAVE; + + ret = snd_soc_component_update_bits(component, reg, mask, val); + if (ret < 0) { + dev_err(component->dev, "Failed to set DAI %d to %s (%d)\n", + aif_id, master ? "master" : "slave", ret); + return ret; + } + + return 0; +} + +static inline +int aif_prepare(struct snd_soc_component *component, struct aif *aif) +{ + int ret; + + ret = aif_set_master(component, aif->id, aif->master); + if (ret < 0) + return ret; + + return 0; +} + +static inline int aif_free(struct snd_soc_component *component, + struct aif *aif, bool playback) +{ + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + + mutex_lock(&tscs454->aifs_status_lock); + + dev_dbg(component->dev, "%s(): aif %d\n", __func__, aif->id); + + set_aif_status_inactive(&tscs454->aifs_status, aif->id, playback); + + dev_dbg(component->dev, "Set aif %d inactive. Streams status is 0x%x\n", + aif->id, tscs454->aifs_status.streams); + + if (!aif_active(&tscs454->aifs_status, aif->id)) { + /* Do config in slave mode */ + aif_set_master(component, aif->id, false); + dev_dbg(component->dev, "Freeing pll %d from aif %d\n", + aif->pll->id, aif->id); + free_pll(aif->pll); + } + + if (!aifs_active(&tscs454->aifs_status)) { + dev_dbg(component->dev, "Freeing pll %d from ir\n", + tscs454->internal_rate.pll->id); + free_pll(tscs454->internal_rate.pll); + } + + mutex_unlock(&tscs454->aifs_status_lock); + + return 0; +} + +/* R_PLLCTL PG 0 ADDR 0x15 */ +static char const * const bclk_sel_txt[] = { + "BCLK 1", "BCLK 2", "BCLK 3"}; + +static struct soc_enum const bclk_sel_enum = + SOC_ENUM_SINGLE(R_PLLCTL, FB_PLLCTL_BCLKSEL, + ARRAY_SIZE(bclk_sel_txt), bclk_sel_txt); + +/* R_ISRC PG 0 ADDR 0x16 */ +static char const * const isrc_br_txt[] = { + "44.1kHz", "48kHz"}; + +static struct soc_enum const isrc_br_enum = + SOC_ENUM_SINGLE(R_ISRC, FB_ISRC_IBR, + ARRAY_SIZE(isrc_br_txt), isrc_br_txt); + +static char const * const isrc_bm_txt[] = { + "0.25x", "0.5x", "1.0x", "2.0x"}; + +static struct soc_enum const isrc_bm_enum = + SOC_ENUM_SINGLE(R_ISRC, FB_ISRC_IBM, + ARRAY_SIZE(isrc_bm_txt), isrc_bm_txt); + +/* R_SCLKCTL PG 0 ADDR 0x18 */ +static char const * const modular_rate_txt[] = { + "Reserved", "Half", "Full", "Auto",}; + +static struct soc_enum const adc_modular_rate_enum = + SOC_ENUM_SINGLE(R_SCLKCTL, FB_SCLKCTL_ASDM, + ARRAY_SIZE(modular_rate_txt), modular_rate_txt); + +static struct soc_enum const dac_modular_rate_enum = + SOC_ENUM_SINGLE(R_SCLKCTL, FB_SCLKCTL_DSDM, + ARRAY_SIZE(modular_rate_txt), modular_rate_txt); + +/* R_I2SIDCTL PG 0 ADDR 0x38 */ +static char const * const data_ctrl_txt[] = { + "L/R", "L/L", "R/R", "R/L"}; + +static struct soc_enum const data_in_ctrl_enums[] = { + SOC_ENUM_SINGLE(R_I2SIDCTL, FB_I2SIDCTL_I2SI1DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), + SOC_ENUM_SINGLE(R_I2SIDCTL, FB_I2SIDCTL_I2SI2DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), + SOC_ENUM_SINGLE(R_I2SIDCTL, FB_I2SIDCTL_I2SI3DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), +}; + +/* R_I2SODCTL PG 0 ADDR 0x39 */ +static struct soc_enum const data_out_ctrl_enums[] = { + SOC_ENUM_SINGLE(R_I2SODCTL, FB_I2SODCTL_I2SO1DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), + SOC_ENUM_SINGLE(R_I2SODCTL, FB_I2SODCTL_I2SO2DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), + SOC_ENUM_SINGLE(R_I2SODCTL, FB_I2SODCTL_I2SO3DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), +}; + +/* R_AUDIOMUX1 PG 0 ADDR 0x3A */ +static char const * const asrc_mux_txt[] = { + "None", "DAI 1", "DAI 2", "DAI 3"}; + +static struct soc_enum const asrc_in_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX1, FB_AUDIOMUX1_ASRCIMUX, + ARRAY_SIZE(asrc_mux_txt), asrc_mux_txt); + +static char const * const dai_mux_txt[] = { + "CH 0_1", "CH 2_3", "CH 4_5", "ADC/DMic 1", + "DMic 2", "ClassD", "DAC", "Sub"}; + +static struct soc_enum const dai2_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX1, FB_AUDIOMUX1_I2S2MUX, + ARRAY_SIZE(dai_mux_txt), dai_mux_txt); + +static struct snd_kcontrol_new const dai2_mux_dapm_enum = + SOC_DAPM_ENUM("DAI 2 Mux", dai2_mux_enum); + +static struct soc_enum const dai1_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX1, FB_AUDIOMUX1_I2S1MUX, + ARRAY_SIZE(dai_mux_txt), dai_mux_txt); + +static struct snd_kcontrol_new const dai1_mux_dapm_enum = + SOC_DAPM_ENUM("DAI 1 Mux", dai1_mux_enum); + +/* R_AUDIOMUX2 PG 0 ADDR 0x3B */ +static struct soc_enum const asrc_out_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX2, FB_AUDIOMUX2_ASRCOMUX, + ARRAY_SIZE(asrc_mux_txt), asrc_mux_txt); + +static struct soc_enum const dac_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX2, FB_AUDIOMUX2_DACMUX, + ARRAY_SIZE(dai_mux_txt), dai_mux_txt); + +static struct snd_kcontrol_new const dac_mux_dapm_enum = + SOC_DAPM_ENUM("DAC Mux", dac_mux_enum); + +static struct soc_enum const dai3_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX2, FB_AUDIOMUX2_I2S3MUX, + ARRAY_SIZE(dai_mux_txt), dai_mux_txt); + +static struct snd_kcontrol_new const dai3_mux_dapm_enum = + SOC_DAPM_ENUM("DAI 3 Mux", dai3_mux_enum); + +/* R_AUDIOMUX3 PG 0 ADDR 0x3C */ +static char const * const sub_mux_txt[] = { + "CH 0", "CH 1", "CH 0 + 1", + "CH 2", "CH 3", "CH 2 + 3", + "CH 4", "CH 5", "CH 4 + 5", + "ADC/DMic 1 Left", "ADC/DMic 1 Right", + "ADC/DMic 1 Left Plus Right", + "DMic 2 Left", "DMic 2 Right", "DMic 2 Left Plus Right", + "ClassD Left", "ClassD Right", "ClassD Left Plus Right"}; + +static struct soc_enum const sub_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX3, FB_AUDIOMUX3_SUBMUX, + ARRAY_SIZE(sub_mux_txt), sub_mux_txt); + +static struct snd_kcontrol_new const sub_mux_dapm_enum = + SOC_DAPM_ENUM("Sub Mux", sub_mux_enum); + +static struct soc_enum const classd_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX3, FB_AUDIOMUX3_CLSSDMUX, + ARRAY_SIZE(dai_mux_txt), dai_mux_txt); + +static struct snd_kcontrol_new const classd_mux_dapm_enum = + SOC_DAPM_ENUM("ClassD Mux", classd_mux_enum); + +/* R_HSDCTL1 PG 1 ADDR 0x01 */ +static char const * const jack_type_txt[] = { + "3 Terminal", "4 Terminal"}; + +static struct soc_enum const hp_jack_type_enum = + SOC_ENUM_SINGLE(R_HSDCTL1, FB_HSDCTL1_HPJKTYPE, + ARRAY_SIZE(jack_type_txt), jack_type_txt); + +static char const * const hs_det_pol_txt[] = { + "Rising", "Falling"}; + +static struct soc_enum const hs_det_pol_enum = + SOC_ENUM_SINGLE(R_HSDCTL1, FB_HSDCTL1_HSDETPOL, + ARRAY_SIZE(hs_det_pol_txt), hs_det_pol_txt); + +/* R_HSDCTL1 PG 1 ADDR 0x02 */ +static char const * const hs_mic_bias_force_txt[] = { + "Off", "Ring", "Sleeve"}; + +static struct soc_enum const hs_mic_bias_force_enum = + SOC_ENUM_SINGLE(R_HSDCTL2, FB_HSDCTL2_FMICBIAS1, + ARRAY_SIZE(hs_mic_bias_force_txt), + hs_mic_bias_force_txt); + +static char const * const plug_type_txt[] = { + "OMTP", "CTIA", "Reserved", "Headphone"}; + +static struct soc_enum const plug_type_force_enum = + SOC_ENUM_SINGLE(R_HSDCTL2, FB_HSDCTL2_FPLUGTYPE, + ARRAY_SIZE(plug_type_txt), plug_type_txt); + + +/* R_CH0AIC PG 1 ADDR 0x06 */ +static char const * const in_bst_mux_txt[] = { + "Input 1", "Input 2", "Input 3", "D2S"}; + +static struct soc_enum const in_bst_mux_ch0_enum = + SOC_ENUM_SINGLE(R_CH0AIC, FB_CH0AIC_INSELL, + ARRAY_SIZE(in_bst_mux_txt), + in_bst_mux_txt); +static struct snd_kcontrol_new const in_bst_mux_ch0_dapm_enum = + SOC_DAPM_ENUM("Input Boost Channel 0 Enum", + in_bst_mux_ch0_enum); + +static DECLARE_TLV_DB_SCALE(in_bst_vol_tlv_arr, 0, 1000, 0); + +static char const * const adc_mux_txt[] = { + "Input 1 Boost Bypass", "Input 2 Boost Bypass", + "Input 3 Boost Bypass", "Input Boost"}; + +static struct soc_enum const adc_mux_ch0_enum = + SOC_ENUM_SINGLE(R_CH0AIC, FB_CH0AIC_LADCIN, + ARRAY_SIZE(adc_mux_txt), adc_mux_txt); +static struct snd_kcontrol_new const adc_mux_ch0_dapm_enum = + SOC_DAPM_ENUM("ADC Channel 0 Enum", adc_mux_ch0_enum); + +static char const * const in_proc_mux_txt[] = { + "ADC", "DMic"}; + +static struct soc_enum const in_proc_ch0_enum = + SOC_ENUM_SINGLE(R_CH0AIC, FB_CH0AIC_IPCH0S, + ARRAY_SIZE(in_proc_mux_txt), in_proc_mux_txt); +static struct snd_kcontrol_new const in_proc_mux_ch0_dapm_enum = + SOC_DAPM_ENUM("Input Processor Channel 0 Enum", + in_proc_ch0_enum); + +/* R_CH1AIC PG 1 ADDR 0x07 */ +static struct soc_enum const in_bst_mux_ch1_enum = + SOC_ENUM_SINGLE(R_CH1AIC, FB_CH1AIC_INSELR, + ARRAY_SIZE(in_bst_mux_txt), + in_bst_mux_txt); +static struct snd_kcontrol_new const in_bst_mux_ch1_dapm_enum = + SOC_DAPM_ENUM("Input Boost Channel 1 Enum", + in_bst_mux_ch1_enum); + +static struct soc_enum const adc_mux_ch1_enum = + SOC_ENUM_SINGLE(R_CH1AIC, FB_CH1AIC_RADCIN, + ARRAY_SIZE(adc_mux_txt), adc_mux_txt); +static struct snd_kcontrol_new const adc_mux_ch1_dapm_enum = + SOC_DAPM_ENUM("ADC Channel 1 Enum", adc_mux_ch1_enum); + +static struct soc_enum const in_proc_ch1_enum = + SOC_ENUM_SINGLE(R_CH1AIC, FB_CH1AIC_IPCH1S, + ARRAY_SIZE(in_proc_mux_txt), in_proc_mux_txt); +static struct snd_kcontrol_new const in_proc_mux_ch1_dapm_enum = + SOC_DAPM_ENUM("Input Processor Channel 1 Enum", + in_proc_ch1_enum); + +/* R_ICTL0 PG 1 ADDR 0x0A */ +static char const * const pol_txt[] = { + "Normal", "Invert"}; + +static struct soc_enum const in_pol_ch1_enum = + SOC_ENUM_SINGLE(R_ICTL0, FB_ICTL0_IN0POL, + ARRAY_SIZE(pol_txt), pol_txt); + +static struct soc_enum const in_pol_ch0_enum = + SOC_ENUM_SINGLE(R_ICTL0, FB_ICTL0_IN1POL, + ARRAY_SIZE(pol_txt), pol_txt); + +static char const * const in_proc_ch_sel_txt[] = { + "Normal", "Mono Mix to Channel 0", + "Mono Mix to Channel 1", "Add"}; + +static struct soc_enum const in_proc_ch01_sel_enum = + SOC_ENUM_SINGLE(R_ICTL0, FB_ICTL0_INPCH10SEL, + ARRAY_SIZE(in_proc_ch_sel_txt), + in_proc_ch_sel_txt); + +/* R_ICTL1 PG 1 ADDR 0x0B */ +static struct soc_enum const in_pol_ch3_enum = + SOC_ENUM_SINGLE(R_ICTL1, FB_ICTL1_IN2POL, + ARRAY_SIZE(pol_txt), pol_txt); + +static struct soc_enum const in_pol_ch2_enum = + SOC_ENUM_SINGLE(R_ICTL1, FB_ICTL1_IN3POL, + ARRAY_SIZE(pol_txt), pol_txt); + +static struct soc_enum const in_proc_ch23_sel_enum = + SOC_ENUM_SINGLE(R_ICTL1, FB_ICTL1_INPCH32SEL, + ARRAY_SIZE(in_proc_ch_sel_txt), + in_proc_ch_sel_txt); + +/* R_MICBIAS PG 1 ADDR 0x0C */ +static char const * const mic_bias_txt[] = { + "2.5V", "2.1V", "1.8V", "Vdd"}; + +static struct soc_enum const mic_bias_2_enum = + SOC_ENUM_SINGLE(R_MICBIAS, FB_MICBIAS_MICBOV2, + ARRAY_SIZE(mic_bias_txt), mic_bias_txt); + +static struct soc_enum const mic_bias_1_enum = + SOC_ENUM_SINGLE(R_MICBIAS, FB_MICBIAS_MICBOV1, + ARRAY_SIZE(mic_bias_txt), mic_bias_txt); + +/* R_PGACTL0 PG 1 ADDR 0x0D */ +/* R_PGACTL1 PG 1 ADDR 0x0E */ +/* R_PGACTL2 PG 1 ADDR 0x0F */ +/* R_PGACTL3 PG 1 ADDR 0x10 */ +static DECLARE_TLV_DB_SCALE(in_pga_vol_tlv_arr, -1725, 75, 0); + +/* R_ICH0VOL PG1 ADDR 0x12 */ +/* R_ICH1VOL PG1 ADDR 0x13 */ +/* R_ICH2VOL PG1 ADDR 0x14 */ +/* R_ICH3VOL PG1 ADDR 0x15 */ +static DECLARE_TLV_DB_MINMAX(in_vol_tlv_arr, -7125, 2400); + +/* R_ASRCILVOL PG1 ADDR 0x16 */ +/* R_ASRCIRVOL PG1 ADDR 0x17 */ +/* R_ASRCOLVOL PG1 ADDR 0x18 */ +/* R_ASRCORVOL PG1 ADDR 0x19 */ +static DECLARE_TLV_DB_MINMAX(asrc_vol_tlv_arr, -9562, 600); + +/* R_ALCCTL0 PG1 ADDR 0x1D */ +static char const * const alc_mode_txt[] = { + "ALC", "Limiter"}; + +static struct soc_enum const alc_mode_enum = + SOC_ENUM_SINGLE(R_ALCCTL0, FB_ALCCTL0_ALCMODE, + ARRAY_SIZE(alc_mode_txt), alc_mode_txt); + +static char const * const alc_ref_text[] = { + "Channel 0", "Channel 1", "Channel 2", "Channel 3", "Peak"}; + +static struct soc_enum const alc_ref_enum = + SOC_ENUM_SINGLE(R_ALCCTL0, FB_ALCCTL0_ALCREF, + ARRAY_SIZE(alc_ref_text), alc_ref_text); + +/* R_ALCCTL1 PG 1 ADDR 0x1E */ +static DECLARE_TLV_DB_SCALE(alc_max_gain_tlv_arr, -1200, 600, 0); +static DECLARE_TLV_DB_SCALE(alc_target_tlv_arr, -2850, 150, 0); + +/* R_ALCCTL2 PG 1 ADDR 0x1F */ +static DECLARE_TLV_DB_SCALE(alc_min_gain_tlv_arr, -1725, 600, 0); + +/* R_NGATE PG 1 ADDR 0x21 */ +static DECLARE_TLV_DB_SCALE(ngth_tlv_arr, -7650, 150, 0); + +static char const * const ngate_type_txt[] = { + "PGA Constant", "ADC Mute"}; + +static struct soc_enum const ngate_type_enum = + SOC_ENUM_SINGLE(R_NGATE, FB_NGATE_NGG, + ARRAY_SIZE(ngate_type_txt), ngate_type_txt); + +/* R_DMICCTL PG 1 ADDR 0x22 */ +static char const * const dmic_mono_sel_txt[] = { + "Stereo", "Mono"}; + +static struct soc_enum const dmic_mono_sel_enum = + SOC_ENUM_SINGLE(R_DMICCTL, FB_DMICCTL_DMONO, + ARRAY_SIZE(dmic_mono_sel_txt), dmic_mono_sel_txt); + +/* R_DACCTL PG 2 ADDR 0x01 */ +static struct soc_enum const dac_pol_r_enum = + SOC_ENUM_SINGLE(R_DACCTL, FB_DACCTL_DACPOLR, + ARRAY_SIZE(pol_txt), pol_txt); + +static struct soc_enum const dac_pol_l_enum = + SOC_ENUM_SINGLE(R_DACCTL, FB_DACCTL_DACPOLL, + ARRAY_SIZE(pol_txt), pol_txt); + +static char const * const dac_dith_txt[] = { + "Half", "Full", "Disabled", "Static"}; + +static struct soc_enum const dac_dith_enum = + SOC_ENUM_SINGLE(R_DACCTL, FB_DACCTL_DACDITH, + ARRAY_SIZE(dac_dith_txt), dac_dith_txt); + +/* R_SPKCTL PG 2 ADDR 0x02 */ +static struct soc_enum const spk_pol_r_enum = + SOC_ENUM_SINGLE(R_SPKCTL, FB_SPKCTL_SPKPOLR, + ARRAY_SIZE(pol_txt), pol_txt); + +static struct soc_enum const spk_pol_l_enum = + SOC_ENUM_SINGLE(R_SPKCTL, FB_SPKCTL_SPKPOLL, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SUBCTL PG 2 ADDR 0x03 */ +static struct soc_enum const sub_pol_enum = + SOC_ENUM_SINGLE(R_SUBCTL, FB_SUBCTL_SUBPOL, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_MVOLL PG 2 ADDR 0x08 */ +/* R_MVOLR PG 2 ADDR 0x09 */ +static DECLARE_TLV_DB_MINMAX(mvol_tlv_arr, -9562, 0); + +/* R_HPVOLL PG 2 ADDR 0x0A */ +/* R_HPVOLR PG 2 ADDR 0x0B */ +static DECLARE_TLV_DB_SCALE(hp_vol_tlv_arr, -8850, 75, 0); + +/* R_SPKVOLL PG 2 ADDR 0x0C */ +/* R_SPKVOLR PG 2 ADDR 0x0D */ +static DECLARE_TLV_DB_SCALE(spk_vol_tlv_arr, -7725, 75, 0); + +/* R_SPKEQFILT PG 3 ADDR 0x01 */ +static char const * const eq_txt[] = { + "Pre Scale", + "Pre Scale + EQ Band 0", + "Pre Scale + EQ Band 0 - 1", + "Pre Scale + EQ Band 0 - 2", + "Pre Scale + EQ Band 0 - 3", + "Pre Scale + EQ Band 0 - 4", + "Pre Scale + EQ Band 0 - 5", +}; + +static struct soc_enum const spk_eq_enums[] = { + SOC_ENUM_SINGLE(R_SPKEQFILT, FB_SPKEQFILT_EQ2BE, + ARRAY_SIZE(eq_txt), eq_txt), + SOC_ENUM_SINGLE(R_SPKEQFILT, FB_SPKEQFILT_EQ1BE, + ARRAY_SIZE(eq_txt), eq_txt), +}; + +/* R_SPKMBCCTL PG 3 ADDR 0x0B */ +static char const * const lvl_mode_txt[] = { + "Average", "Peak"}; + +static struct soc_enum const spk_mbc3_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_LVLMODE3, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static char const * const win_sel_txt[] = { + "512", "64"}; + +static struct soc_enum const spk_mbc3_win_sel_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_WINSEL3, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const spk_mbc2_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_LVLMODE2, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const spk_mbc2_win_sel_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_WINSEL2, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const spk_mbc1_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_LVLMODE1, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const spk_mbc1_win_sel_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_WINSEL1, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_SPKMBCMUG1 PG 3 ADDR 0x0C */ +static struct soc_enum const spk_mbc1_phase_pol_enum = + SOC_ENUM_SINGLE(R_SPKMBCMUG1, FB_SPKMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +static DECLARE_TLV_DB_MINMAX(mbc_mug_tlv_arr, -4650, 0); + +/* R_SPKMBCTHR1 PG 3 ADDR 0x0D */ +static DECLARE_TLV_DB_MINMAX(thr_tlv_arr, -9562, 0); + +/* R_SPKMBCRAT1 PG 3 ADDR 0x0E */ +static char const * const comp_rat_txt[] = { + "Reserved", "1.5:1", "2:1", "3:1", "4:1", "5:1", "6:1", + "7:1", "8:1", "9:1", "10:1", "11:1", "12:1", "13:1", "14:1", + "15:1", "16:1", "17:1", "18:1", "19:1", "20:1"}; + +static struct soc_enum const spk_mbc1_comp_rat_enum = + SOC_ENUM_SINGLE(R_SPKMBCRAT1, FB_SPKMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SPKMBCMUG2 PG 3 ADDR 0x13 */ +static struct soc_enum const spk_mbc2_phase_pol_enum = + SOC_ENUM_SINGLE(R_SPKMBCMUG2, FB_SPKMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SPKMBCRAT2 PG 3 ADDR 0x15 */ +static struct soc_enum const spk_mbc2_comp_rat_enum = + SOC_ENUM_SINGLE(R_SPKMBCRAT2, FB_SPKMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SPKMBCMUG3 PG 3 ADDR 0x1A */ +static struct soc_enum const spk_mbc3_phase_pol_enum = + SOC_ENUM_SINGLE(R_SPKMBCMUG3, FB_SPKMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SPKMBCRAT3 PG 3 ADDR 0x1C */ +static struct soc_enum const spk_mbc3_comp_rat_enum = + SOC_ENUM_SINGLE(R_SPKMBCRAT3, FB_SPKMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SPKCLECTL PG 3 ADDR 0x21 */ +static struct soc_enum const spk_cle_lvl_mode_enum = + SOC_ENUM_SINGLE(R_SPKCLECTL, FB_SPKCLECTL_LVLMODE, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const spk_cle_win_sel_enum = + SOC_ENUM_SINGLE(R_SPKCLECTL, FB_SPKCLECTL_WINSEL, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_SPKCLEMUG PG 3 ADDR 0x22 */ +static DECLARE_TLV_DB_MINMAX(cle_mug_tlv_arr, 0, 4650); + +/* R_SPKCOMPRAT PG 3 ADDR 0x24 */ +static struct soc_enum const spk_comp_rat_enum = + SOC_ENUM_SINGLE(R_SPKCOMPRAT, FB_SPKCOMPRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SPKEXPTHR PG 3 ADDR 0x2F */ +static char const * const exp_rat_txt[] = { + "Reserved", "Reserved", "1:2", "1:3", + "1:4", "1:5", "1:6", "1:7"}; + +static struct soc_enum const spk_exp_rat_enum = + SOC_ENUM_SINGLE(R_SPKEXPRAT, FB_SPKEXPRAT_RATIO, + ARRAY_SIZE(exp_rat_txt), exp_rat_txt); + +/* R_DACEQFILT PG 4 ADDR 0x01 */ +static struct soc_enum const dac_eq_enums[] = { + SOC_ENUM_SINGLE(R_DACEQFILT, FB_DACEQFILT_EQ2BE, + ARRAY_SIZE(eq_txt), eq_txt), + SOC_ENUM_SINGLE(R_DACEQFILT, FB_DACEQFILT_EQ1BE, + ARRAY_SIZE(eq_txt), eq_txt), +}; + +/* R_DACMBCCTL PG 4 ADDR 0x0B */ +static struct soc_enum const dac_mbc3_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE3, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const dac_mbc3_win_sel_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL3, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const dac_mbc2_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE2, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const dac_mbc2_win_sel_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL2, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const dac_mbc1_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE1, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const dac_mbc1_win_sel_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL1, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_DACMBCMUG1 PG 4 ADDR 0x0C */ +static struct soc_enum const dac_mbc1_phase_pol_enum = + SOC_ENUM_SINGLE(R_DACMBCMUG1, FB_DACMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_DACMBCRAT1 PG 4 ADDR 0x0E */ +static struct soc_enum const dac_mbc1_comp_rat_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT1, FB_DACMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_DACMBCMUG2 PG 4 ADDR 0x13 */ +static struct soc_enum const dac_mbc2_phase_pol_enum = + SOC_ENUM_SINGLE(R_DACMBCMUG2, FB_DACMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_DACMBCRAT2 PG 4 ADDR 0x15 */ +static struct soc_enum const dac_mbc2_comp_rat_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT2, FB_DACMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_DACMBCMUG3 PG 4 ADDR 0x1A */ +static struct soc_enum const dac_mbc3_phase_pol_enum = + SOC_ENUM_SINGLE(R_DACMBCMUG3, FB_DACMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_DACMBCRAT3 PG 4 ADDR 0x1C */ +static struct soc_enum const dac_mbc3_comp_rat_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT3, FB_DACMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_DACCLECTL PG 4 ADDR 0x21 */ +static struct soc_enum const dac_cle_lvl_mode_enum = + SOC_ENUM_SINGLE(R_DACCLECTL, FB_DACCLECTL_LVLMODE, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const dac_cle_win_sel_enum = + SOC_ENUM_SINGLE(R_DACCLECTL, FB_DACCLECTL_WINSEL, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_DACCOMPRAT PG 4 ADDR 0x24 */ +static struct soc_enum const dac_comp_rat_enum = + SOC_ENUM_SINGLE(R_DACCOMPRAT, FB_DACCOMPRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_DACEXPRAT PG 4 ADDR 0x30 */ +static struct soc_enum const dac_exp_rat_enum = + SOC_ENUM_SINGLE(R_DACEXPRAT, FB_DACEXPRAT_RATIO, + ARRAY_SIZE(exp_rat_txt), exp_rat_txt); + +/* R_SUBEQFILT PG 5 ADDR 0x01 */ +static struct soc_enum const sub_eq_enums[] = { + SOC_ENUM_SINGLE(R_SUBEQFILT, FB_SUBEQFILT_EQ2BE, + ARRAY_SIZE(eq_txt), eq_txt), + SOC_ENUM_SINGLE(R_SUBEQFILT, FB_SUBEQFILT_EQ1BE, + ARRAY_SIZE(eq_txt), eq_txt), +}; + +/* R_SUBMBCCTL PG 5 ADDR 0x0B */ +static struct soc_enum const sub_mbc3_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_LVLMODE3, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const sub_mbc3_win_sel_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_WINSEL3, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const sub_mbc2_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_LVLMODE2, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const sub_mbc2_win_sel_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_WINSEL2, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const sub_mbc1_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_LVLMODE1, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const sub_mbc1_win_sel_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_WINSEL1, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_SUBMBCMUG1 PG 5 ADDR 0x0C */ +static struct soc_enum const sub_mbc1_phase_pol_enum = + SOC_ENUM_SINGLE(R_SUBMBCMUG1, FB_SUBMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SUBMBCRAT1 PG 5 ADDR 0x0E */ +static struct soc_enum const sub_mbc1_comp_rat_enum = + SOC_ENUM_SINGLE(R_SUBMBCRAT1, FB_SUBMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SUBMBCMUG2 PG 5 ADDR 0x13 */ +static struct soc_enum const sub_mbc2_phase_pol_enum = + SOC_ENUM_SINGLE(R_SUBMBCMUG2, FB_SUBMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SUBMBCRAT2 PG 5 ADDR 0x15 */ +static struct soc_enum const sub_mbc2_comp_rat_enum = + SOC_ENUM_SINGLE(R_SUBMBCRAT2, FB_SUBMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SUBMBCMUG3 PG 5 ADDR 0x1A */ +static struct soc_enum const sub_mbc3_phase_pol_enum = + SOC_ENUM_SINGLE(R_SUBMBCMUG3, FB_SUBMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SUBMBCRAT3 PG 5 ADDR 0x1C */ +static struct soc_enum const sub_mbc3_comp_rat_enum = + SOC_ENUM_SINGLE(R_SUBMBCRAT3, FB_SUBMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SUBCLECTL PG 5 ADDR 0x21 */ +static struct soc_enum const sub_cle_lvl_mode_enum = + SOC_ENUM_SINGLE(R_SUBCLECTL, FB_SUBCLECTL_LVLMODE, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); +static struct soc_enum const sub_cle_win_sel_enum = + SOC_ENUM_SINGLE(R_SUBCLECTL, FB_SUBCLECTL_WINSEL, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_SUBCOMPRAT PG 5 ADDR 0x24 */ +static struct soc_enum const sub_comp_rat_enum = + SOC_ENUM_SINGLE(R_SUBCOMPRAT, FB_SUBCOMPRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SUBEXPRAT PG 5 ADDR 0x30 */ +static struct soc_enum const sub_exp_rat_enum = + SOC_ENUM_SINGLE(R_SUBEXPRAT, FB_SUBEXPRAT_RATIO, + ARRAY_SIZE(exp_rat_txt), exp_rat_txt); + +static int bytes_info_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} + +/* CH 0_1 Input Mux */ +static char const * const ch_0_1_mux_txt[] = {"DAI 1", "TDM 0_1"}; + +static struct soc_enum const ch_0_1_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(ch_0_1_mux_txt), ch_0_1_mux_txt); + +static struct snd_kcontrol_new const ch_0_1_mux_dapm_enum = + SOC_DAPM_ENUM("CH 0_1 Input Mux", ch_0_1_mux_enum); + +/* CH 2_3 Input Mux */ +static char const * const ch_2_3_mux_txt[] = {"DAI 2", "TDM 2_3"}; + +static struct soc_enum const ch_2_3_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(ch_2_3_mux_txt), ch_2_3_mux_txt); + +static struct snd_kcontrol_new const ch_2_3_mux_dapm_enum = + SOC_DAPM_ENUM("CH 2_3 Input Mux", ch_2_3_mux_enum); + +/* CH 4_5 Input Mux */ +static char const * const ch_4_5_mux_txt[] = {"DAI 3", "TDM 4_5"}; + +static struct soc_enum const ch_4_5_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(ch_4_5_mux_txt), ch_4_5_mux_txt); + +static struct snd_kcontrol_new const ch_4_5_mux_dapm_enum = + SOC_DAPM_ENUM("CH 4_5 Input Mux", ch_4_5_mux_enum); + +#define COEFF_RAM_CTL(xname, xcount, xaddr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = bytes_info_ext, \ + .get = coeff_ram_get, .put = coeff_ram_put, \ + .private_value = (unsigned long)&(struct coeff_ram_ctl) { \ + .addr = xaddr, \ + .bytes_ext = {.max = xcount, }, \ + } \ +} + +static struct snd_kcontrol_new const tscs454_snd_controls[] = { + /* R_PLLCTL PG 0 ADDR 0x15 */ + SOC_ENUM("PLL BCLK Input", bclk_sel_enum), + /* R_ISRC PG 0 ADDR 0x16 */ + SOC_ENUM("Internal Rate", isrc_br_enum), + SOC_ENUM("Internal Rate Multiple", isrc_bm_enum), + /* R_SCLKCTL PG 0 ADDR 0x18 */ + SOC_ENUM("ADC Modular Rate", adc_modular_rate_enum), + SOC_ENUM("DAC Modular Rate", dac_modular_rate_enum), + /* R_ASRC PG 0 ADDR 0x28 */ + SOC_SINGLE("ASRC Out High Bandwidth Switch", + R_ASRC, FB_ASRC_ASRCOBW, 1, 0), + SOC_SINGLE("ASRC In High Bandwidth Switch", + R_ASRC, FB_ASRC_ASRCIBW, 1, 0), + /* R_I2SIDCTL PG 0 ADDR 0x38 */ + SOC_ENUM("I2S 1 Data In Control", data_in_ctrl_enums[0]), + SOC_ENUM("I2S 2 Data In Control", data_in_ctrl_enums[1]), + SOC_ENUM("I2S 3 Data In Control", data_in_ctrl_enums[2]), + /* R_I2SODCTL PG 0 ADDR 0x39 */ + SOC_ENUM("I2S 1 Data Out Control", data_out_ctrl_enums[0]), + SOC_ENUM("I2S 2 Data Out Control", data_out_ctrl_enums[1]), + SOC_ENUM("I2S 3 Data Out Control", data_out_ctrl_enums[2]), + /* R_AUDIOMUX1 PG 0 ADDR 0x3A */ + SOC_ENUM("ASRC In", asrc_in_mux_enum), + /* R_AUDIOMUX2 PG 0 ADDR 0x3B */ + SOC_ENUM("ASRC Out", asrc_out_mux_enum), + /* R_HSDCTL1 PG 1 ADDR 0x01 */ + SOC_ENUM("Headphone Jack Type", hp_jack_type_enum), + SOC_ENUM("Headset Detection Polarity", hs_det_pol_enum), + SOC_SINGLE("Headphone Detection Switch", + R_HSDCTL1, FB_HSDCTL1_HPID_EN, 1, 0), + SOC_SINGLE("Headset OMTP/CTIA Switch", + R_HSDCTL1, FB_HSDCTL1_GBLHS_EN, 1, 0), + /* R_HSDCTL1 PG 1 ADDR 0x02 */ + SOC_ENUM("Headset Mic Bias Force", hs_mic_bias_force_enum), + SOC_SINGLE("Manual Mic Bias Switch", + R_HSDCTL2, FB_HSDCTL2_MB1MODE, 1, 0), + SOC_SINGLE("Ring/Sleeve Auto Switch", + R_HSDCTL2, FB_HSDCTL2_SWMODE, 1, 0), + SOC_ENUM("Manual Mode Plug Type", plug_type_force_enum), + /* R_CH0AIC PG 1 ADDR 0x06 */ + SOC_SINGLE_TLV("Input Boost Channel 0 Volume", R_CH0AIC, + FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr), + /* R_CH1AIC PG 1 ADDR 0x07 */ + SOC_SINGLE_TLV("Input Boost Channel 1 Volume", R_CH1AIC, + FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr), + /* R_CH2AIC PG 1 ADDR 0x08 */ + SOC_SINGLE_TLV("Input Boost Channel 2 Volume", R_CH2AIC, + FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr), + /* R_CH3AIC PG 1 ADDR 0x09 */ + SOC_SINGLE_TLV("Input Boost Channel 3 Volume", R_CH3AIC, + FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr), + /* R_ICTL0 PG 1 ADDR 0x0A */ + SOC_ENUM("Input Channel 1 Polarity", in_pol_ch1_enum), + SOC_ENUM("Input Channel 0 Polarity", in_pol_ch0_enum), + SOC_ENUM("Input Processor Channel 0/1 Operation", + in_proc_ch01_sel_enum), + SOC_SINGLE("Input Channel 1 Mute Switch", + R_ICTL0, FB_ICTL0_IN1MUTE, 1, 0), + SOC_SINGLE("Input Channel 0 Mute Switch", + R_ICTL0, FB_ICTL0_IN0MUTE, 1, 0), + SOC_SINGLE("Input Channel 1 HPF Disable Switch", + R_ICTL0, FB_ICTL0_IN1HP, 1, 0), + SOC_SINGLE("Input Channel 0 HPF Disable Switch", + R_ICTL0, FB_ICTL0_IN0HP, 1, 0), + /* R_ICTL1 PG 1 ADDR 0x0B */ + SOC_ENUM("Input Channel 3 Polarity", in_pol_ch3_enum), + SOC_ENUM("Input Channel 2 Polarity", in_pol_ch2_enum), + SOC_ENUM("Input Processor Channel 2/3 Operation", + in_proc_ch23_sel_enum), + SOC_SINGLE("Input Channel 3 Mute Switch", + R_ICTL1, FB_ICTL1_IN3MUTE, 1, 0), + SOC_SINGLE("Input Channel 2 Mute Switch", + R_ICTL1, FB_ICTL1_IN2MUTE, 1, 0), + SOC_SINGLE("Input Channel 3 HPF Disable Switch", + R_ICTL1, FB_ICTL1_IN3HP, 1, 0), + SOC_SINGLE("Input Channel 2 HPF Disable Switch", + R_ICTL1, FB_ICTL1_IN2HP, 1, 0), + /* R_MICBIAS PG 1 ADDR 0x0C */ + SOC_ENUM("Mic Bias 2 Voltage", mic_bias_2_enum), + SOC_ENUM("Mic Bias 1 Voltage", mic_bias_1_enum), + /* R_PGACTL0 PG 1 ADDR 0x0D */ + SOC_SINGLE("Input Channel 0 PGA Mute Switch", + R_PGACTL0, FB_PGACTL_PGAMUTE, 1, 0), + SOC_SINGLE_TLV("Input Channel 0 PGA Volume", R_PGACTL0, + FB_PGACTL_PGAVOL, + FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr), + /* R_PGACTL1 PG 1 ADDR 0x0E */ + SOC_SINGLE("Input Channel 1 PGA Mute Switch", + R_PGACTL1, FB_PGACTL_PGAMUTE, 1, 0), + SOC_SINGLE_TLV("Input Channel 1 PGA Volume", R_PGACTL1, + FB_PGACTL_PGAVOL, + FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr), + /* R_PGACTL2 PG 1 ADDR 0x0F */ + SOC_SINGLE("Input Channel 2 PGA Mute Switch", + R_PGACTL2, FB_PGACTL_PGAMUTE, 1, 0), + SOC_SINGLE_TLV("Input Channel 2 PGA Volume", R_PGACTL2, + FB_PGACTL_PGAVOL, + FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr), + /* R_PGACTL3 PG 1 ADDR 0x10 */ + SOC_SINGLE("Input Channel 3 PGA Mute Switch", + R_PGACTL3, FB_PGACTL_PGAMUTE, 1, 0), + SOC_SINGLE_TLV("Input Channel 3 PGA Volume", R_PGACTL3, + FB_PGACTL_PGAVOL, + FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr), + /* R_ICH0VOL PG 1 ADDR 0x12 */ + SOC_SINGLE_TLV("Input Channel 0 Volume", R_ICH0VOL, + FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr), + /* R_ICH1VOL PG 1 ADDR 0x13 */ + SOC_SINGLE_TLV("Input Channel 1 Volume", R_ICH1VOL, + FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr), + /* R_ICH2VOL PG 1 ADDR 0x14 */ + SOC_SINGLE_TLV("Input Channel 2 Volume", R_ICH2VOL, + FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr), + /* R_ICH3VOL PG 1 ADDR 0x15 */ + SOC_SINGLE_TLV("Input Channel 3 Volume", R_ICH3VOL, + FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr), + /* R_ASRCILVOL PG 1 ADDR 0x16 */ + SOC_SINGLE_TLV("ASRC Input Left Volume", R_ASRCILVOL, + FB_ASRCILVOL_ASRCILVOL, FM_ASRCILVOL_ASRCILVOL, + 0, asrc_vol_tlv_arr), + /* R_ASRCIRVOL PG 1 ADDR 0x17 */ + SOC_SINGLE_TLV("ASRC Input Right Volume", R_ASRCIRVOL, + FB_ASRCIRVOL_ASRCIRVOL, FM_ASRCIRVOL_ASRCIRVOL, + 0, asrc_vol_tlv_arr), + /* R_ASRCOLVOL PG 1 ADDR 0x18 */ + SOC_SINGLE_TLV("ASRC Output Left Volume", R_ASRCOLVOL, + FB_ASRCOLVOL_ASRCOLVOL, FM_ASRCOLVOL_ASRCOLVOL, + 0, asrc_vol_tlv_arr), + /* R_ASRCORVOL PG 1 ADDR 0x19 */ + SOC_SINGLE_TLV("ASRC Output Right Volume", R_ASRCORVOL, + FB_ASRCORVOL_ASRCOLVOL, FM_ASRCORVOL_ASRCOLVOL, + 0, asrc_vol_tlv_arr), + /* R_IVOLCTLU PG 1 ADDR 0x1C */ + /* R_ALCCTL0 PG 1 ADDR 0x1D */ + SOC_ENUM("ALC Mode", alc_mode_enum), + SOC_ENUM("ALC Reference", alc_ref_enum), + SOC_SINGLE("Input Channel 3 ALC Switch", + R_ALCCTL0, FB_ALCCTL0_ALCEN3, 1, 0), + SOC_SINGLE("Input Channel 2 ALC Switch", + R_ALCCTL0, FB_ALCCTL0_ALCEN2, 1, 0), + SOC_SINGLE("Input Channel 1 ALC Switch", + R_ALCCTL0, FB_ALCCTL0_ALCEN1, 1, 0), + SOC_SINGLE("Input Channel 0 ALC Switch", + R_ALCCTL0, FB_ALCCTL0_ALCEN0, 1, 0), + /* R_ALCCTL1 PG 1 ADDR 0x1E */ + SOC_SINGLE_TLV("ALC Max Gain Volume", R_ALCCTL1, + FB_ALCCTL1_MAXGAIN, FM_ALCCTL1_MAXGAIN, + 0, alc_max_gain_tlv_arr), + SOC_SINGLE_TLV("ALC Target Volume", R_ALCCTL1, + FB_ALCCTL1_ALCL, FM_ALCCTL1_ALCL, + 0, alc_target_tlv_arr), + /* R_ALCCTL2 PG 1 ADDR 0x1F */ + SOC_SINGLE("ALC Zero Cross Switch", + R_ALCCTL2, FB_ALCCTL2_ALCZC, 1, 0), + SOC_SINGLE_TLV("ALC Min Gain Volume", R_ALCCTL2, + FB_ALCCTL2_MINGAIN, FM_ALCCTL2_MINGAIN, + 0, alc_min_gain_tlv_arr), + SOC_SINGLE_RANGE("ALC Hold", R_ALCCTL2, + FB_ALCCTL2_HLD, 0, FM_ALCCTL2_HLD, 0), + /* R_ALCCTL3 PG 1 ADDR 0x20 */ + SOC_SINGLE_RANGE("ALC Decay", R_ALCCTL3, + FB_ALCCTL3_DCY, 0, FM_ALCCTL3_DCY, 0), + SOC_SINGLE_RANGE("ALC Attack", R_ALCCTL3, + FB_ALCCTL3_ATK, 0, FM_ALCCTL3_ATK, 0), + /* R_NGATE PG 1 ADDR 0x21 */ + SOC_SINGLE_TLV("Noise Gate Threshold Volume", R_NGATE, + FB_NGATE_NGTH, FM_NGATE_NGTH, 0, ngth_tlv_arr), + SOC_ENUM("Noise Gate Type", ngate_type_enum), + SOC_SINGLE("Noise Gate Switch", R_NGATE, FB_NGATE_NGAT, 1, 0), + /* R_DMICCTL PG 1 ADDR 0x22 */ + SOC_SINGLE("Digital Mic 2 Switch", R_DMICCTL, FB_DMICCTL_DMIC2EN, 1, 0), + SOC_SINGLE("Digital Mic 1 Switch", R_DMICCTL, FB_DMICCTL_DMIC1EN, 1, 0), + SOC_ENUM("Digital Mic Mono Select", dmic_mono_sel_enum), + /* R_DACCTL PG 2 ADDR 0x01 */ + SOC_ENUM("DAC Polarity Left", dac_pol_r_enum), + SOC_ENUM("DAC Polarity Right", dac_pol_l_enum), + SOC_ENUM("DAC Dither", dac_dith_enum), + SOC_SINGLE("DAC Mute Switch", R_DACCTL, FB_DACCTL_DACMUTE, 1, 0), + SOC_SINGLE("DAC De-Emphasis Switch", R_DACCTL, FB_DACCTL_DACDEM, 1, 0), + /* R_SPKCTL PG 2 ADDR 0x02 */ + SOC_ENUM("Speaker Polarity Right", spk_pol_r_enum), + SOC_ENUM("Speaker Polarity Left", spk_pol_l_enum), + SOC_SINGLE("Speaker Mute Switch", R_SPKCTL, FB_SPKCTL_SPKMUTE, 1, 0), + SOC_SINGLE("Speaker De-Emphasis Switch", + R_SPKCTL, FB_SPKCTL_SPKDEM, 1, 0), + /* R_SUBCTL PG 2 ADDR 0x03 */ + SOC_ENUM("Sub Polarity", sub_pol_enum), + SOC_SINGLE("SUB Mute Switch", R_SUBCTL, FB_SUBCTL_SUBMUTE, 1, 0), + SOC_SINGLE("Sub De-Emphasis Switch", R_SUBCTL, FB_SUBCTL_SUBDEM, 1, 0), + /* R_DCCTL PG 2 ADDR 0x04 */ + SOC_SINGLE("Sub DC Removal Switch", R_DCCTL, FB_DCCTL_SUBDCBYP, 1, 1), + SOC_SINGLE("DAC DC Removal Switch", R_DCCTL, FB_DCCTL_DACDCBYP, 1, 1), + SOC_SINGLE("Speaker DC Removal Switch", + R_DCCTL, FB_DCCTL_SPKDCBYP, 1, 1), + SOC_SINGLE("DC Removal Coefficient Switch", R_DCCTL, FB_DCCTL_DCCOEFSEL, + FM_DCCTL_DCCOEFSEL, 0), + /* R_OVOLCTLU PG 2 ADDR 0x06 */ + SOC_SINGLE("Output Fade Switch", R_OVOLCTLU, FB_OVOLCTLU_OFADE, 1, 0), + /* R_MVOLL PG 2 ADDR 0x08 */ + /* R_MVOLR PG 2 ADDR 0x09 */ + SOC_DOUBLE_R_TLV("Master Volume", R_MVOLL, R_MVOLR, + FB_MVOLL_MVOL_L, FM_MVOLL_MVOL_L, 0, mvol_tlv_arr), + /* R_HPVOLL PG 2 ADDR 0x0A */ + /* R_HPVOLR PG 2 ADDR 0x0B */ + SOC_DOUBLE_R_TLV("Headphone Volume", R_HPVOLL, R_HPVOLR, + FB_HPVOLL_HPVOL_L, FM_HPVOLL_HPVOL_L, 0, + hp_vol_tlv_arr), + /* R_SPKVOLL PG 2 ADDR 0x0C */ + /* R_SPKVOLR PG 2 ADDR 0x0D */ + SOC_DOUBLE_R_TLV("Speaker Volume", R_SPKVOLL, R_SPKVOLR, + FB_SPKVOLL_SPKVOL_L, FM_SPKVOLL_SPKVOL_L, 0, + spk_vol_tlv_arr), + /* R_SUBVOL PG 2 ADDR 0x10 */ + SOC_SINGLE_TLV("Sub Volume", R_SUBVOL, + FB_SUBVOL_SUBVOL, FM_SUBVOL_SUBVOL, 0, spk_vol_tlv_arr), + /* R_SPKEQFILT PG 3 ADDR 0x01 */ + SOC_SINGLE("Speaker EQ 2 Switch", + R_SPKEQFILT, FB_SPKEQFILT_EQ2EN, 1, 0), + SOC_ENUM("Speaker EQ 2 Band", spk_eq_enums[0]), + SOC_SINGLE("Speaker EQ 1 Switch", + R_SPKEQFILT, FB_SPKEQFILT_EQ1EN, 1, 0), + SOC_ENUM("Speaker EQ 1 Band", spk_eq_enums[1]), + /* R_SPKMBCEN PG 3 ADDR 0x0A */ + SOC_SINGLE("Speaker MBC 3 Switch", + R_SPKMBCEN, FB_SPKMBCEN_MBCEN3, 1, 0), + SOC_SINGLE("Speaker MBC 2 Switch", + R_SPKMBCEN, FB_SPKMBCEN_MBCEN2, 1, 0), + SOC_SINGLE("Speaker MBC 1 Switch", + R_SPKMBCEN, FB_SPKMBCEN_MBCEN1, 1, 0), + /* R_SPKMBCCTL PG 3 ADDR 0x0B */ + SOC_ENUM("Speaker MBC 3 Mode", spk_mbc3_lvl_det_mode_enum), + SOC_ENUM("Speaker MBC 3 Window", spk_mbc3_win_sel_enum), + SOC_ENUM("Speaker MBC 2 Mode", spk_mbc2_lvl_det_mode_enum), + SOC_ENUM("Speaker MBC 2 Window", spk_mbc2_win_sel_enum), + SOC_ENUM("Speaker MBC 1 Mode", spk_mbc1_lvl_det_mode_enum), + SOC_ENUM("Speaker MBC 1 Window", spk_mbc1_win_sel_enum), + /* R_SPKMBCMUG1 PG 3 ADDR 0x0C */ + SOC_ENUM("Speaker MBC 1 Phase Polarity", spk_mbc1_phase_pol_enum), + SOC_SINGLE_TLV("Speaker MBC1 Make-Up Gain Volume", R_SPKMBCMUG1, + FB_SPKMBCMUG_MUGAIN, FM_SPKMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SPKMBCTHR1 PG 3 ADDR 0x0D */ + SOC_SINGLE_TLV("Speaker MBC 1 Compressor Threshold Volume", + R_SPKMBCTHR1, FB_SPKMBCTHR_THRESH, FM_SPKMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKMBCRAT1 PG 3 ADDR 0x0E */ + SOC_ENUM("Speaker MBC 1 Compressor Ratio", spk_mbc1_comp_rat_enum), + /* R_SPKMBCATK1L PG 3 ADDR 0x0F */ + /* R_SPKMBCATK1H PG 3 ADDR 0x10 */ + SND_SOC_BYTES("Speaker MBC 1 Attack", R_SPKMBCATK1L, 2), + /* R_SPKMBCREL1L PG 3 ADDR 0x11 */ + /* R_SPKMBCREL1H PG 3 ADDR 0x12 */ + SND_SOC_BYTES("Speaker MBC 1 Release", R_SPKMBCREL1L, 2), + /* R_SPKMBCMUG2 PG 3 ADDR 0x13 */ + SOC_ENUM("Speaker MBC 2 Phase Polarity", spk_mbc2_phase_pol_enum), + SOC_SINGLE_TLV("Speaker MBC2 Make-Up Gain Volume", R_SPKMBCMUG2, + FB_SPKMBCMUG_MUGAIN, FM_SPKMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SPKMBCTHR2 PG 3 ADDR 0x14 */ + SOC_SINGLE_TLV("Speaker MBC 2 Compressor Threshold Volume", + R_SPKMBCTHR2, FB_SPKMBCTHR_THRESH, FM_SPKMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKMBCRAT2 PG 3 ADDR 0x15 */ + SOC_ENUM("Speaker MBC 2 Compressor Ratio", spk_mbc2_comp_rat_enum), + /* R_SPKMBCATK2L PG 3 ADDR 0x16 */ + /* R_SPKMBCATK2H PG 3 ADDR 0x17 */ + SND_SOC_BYTES("Speaker MBC 2 Attack", R_SPKMBCATK2L, 2), + /* R_SPKMBCREL2L PG 3 ADDR 0x18 */ + /* R_SPKMBCREL2H PG 3 ADDR 0x19 */ + SND_SOC_BYTES("Speaker MBC 2 Release", R_SPKMBCREL2L, 2), + /* R_SPKMBCMUG3 PG 3 ADDR 0x1A */ + SOC_ENUM("Speaker MBC 3 Phase Polarity", spk_mbc3_phase_pol_enum), + SOC_SINGLE_TLV("Speaker MBC 3 Make-Up Gain Volume", R_SPKMBCMUG3, + FB_SPKMBCMUG_MUGAIN, FM_SPKMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SPKMBCTHR3 PG 3 ADDR 0x1B */ + SOC_SINGLE_TLV("Speaker MBC 3 Threshold Volume", R_SPKMBCTHR3, + FB_SPKMBCTHR_THRESH, FM_SPKMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKMBCRAT3 PG 3 ADDR 0x1C */ + SOC_ENUM("Speaker MBC 3 Compressor Ratio", spk_mbc3_comp_rat_enum), + /* R_SPKMBCATK3L PG 3 ADDR 0x1D */ + /* R_SPKMBCATK3H PG 3 ADDR 0x1E */ + SND_SOC_BYTES("Speaker MBC 3 Attack", R_SPKMBCATK3L, 3), + /* R_SPKMBCREL3L PG 3 ADDR 0x1F */ + /* R_SPKMBCREL3H PG 3 ADDR 0x20 */ + SND_SOC_BYTES("Speaker MBC 3 Release", R_SPKMBCREL3L, 3), + /* R_SPKCLECTL PG 3 ADDR 0x21 */ + SOC_ENUM("Speaker CLE Level Mode", spk_cle_lvl_mode_enum), + SOC_ENUM("Speaker CLE Window", spk_cle_win_sel_enum), + SOC_SINGLE("Speaker CLE Expander Switch", + R_SPKCLECTL, FB_SPKCLECTL_EXPEN, 1, 0), + SOC_SINGLE("Speaker CLE Limiter Switch", + R_SPKCLECTL, FB_SPKCLECTL_LIMEN, 1, 0), + SOC_SINGLE("Speaker CLE Compressor Switch", + R_SPKCLECTL, FB_SPKCLECTL_COMPEN, 1, 0), + /* R_SPKCLEMUG PG 3 ADDR 0x22 */ + SOC_SINGLE_TLV("Speaker CLE Make-Up Gain Volume", R_SPKCLEMUG, + FB_SPKCLEMUG_MUGAIN, FM_SPKCLEMUG_MUGAIN, + 0, cle_mug_tlv_arr), + /* R_SPKCOMPTHR PG 3 ADDR 0x23 */ + SOC_SINGLE_TLV("Speaker Compressor Threshold Volume", R_SPKCOMPTHR, + FB_SPKCOMPTHR_THRESH, FM_SPKCOMPTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKCOMPRAT PG 3 ADDR 0x24 */ + SOC_ENUM("Speaker Compressor Ratio", spk_comp_rat_enum), + /* R_SPKCOMPATKL PG 3 ADDR 0x25 */ + /* R_SPKCOMPATKH PG 3 ADDR 0x26 */ + SND_SOC_BYTES("Speaker Compressor Attack", R_SPKCOMPATKL, 2), + /* R_SPKCOMPRELL PG 3 ADDR 0x27 */ + /* R_SPKCOMPRELH PG 3 ADDR 0x28 */ + SND_SOC_BYTES("Speaker Compressor Release", R_SPKCOMPRELL, 2), + /* R_SPKLIMTHR PG 3 ADDR 0x29 */ + SOC_SINGLE_TLV("Speaker Limiter Threshold Volume", R_SPKLIMTHR, + FB_SPKLIMTHR_THRESH, FM_SPKLIMTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKLIMTGT PG 3 ADDR 0x2A */ + SOC_SINGLE_TLV("Speaker Limiter Target Volume", R_SPKLIMTGT, + FB_SPKLIMTGT_TARGET, FM_SPKLIMTGT_TARGET, + 0, thr_tlv_arr), + /* R_SPKLIMATKL PG 3 ADDR 0x2B */ + /* R_SPKLIMATKH PG 3 ADDR 0x2C */ + SND_SOC_BYTES("Speaker Limiter Attack", R_SPKLIMATKL, 2), + /* R_SPKLIMRELL PG 3 ADDR 0x2D */ + /* R_SPKLIMRELR PG 3 ADDR 0x2E */ + SND_SOC_BYTES("Speaker Limiter Release", R_SPKLIMRELL, 2), + /* R_SPKEXPTHR PG 3 ADDR 0x2F */ + SOC_SINGLE_TLV("Speaker Expander Threshold Volume", R_SPKEXPTHR, + FB_SPKEXPTHR_THRESH, FM_SPKEXPTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKEXPRAT PG 3 ADDR 0x30 */ + SOC_ENUM("Speaker Expander Ratio", spk_exp_rat_enum), + /* R_SPKEXPATKL PG 3 ADDR 0x31 */ + /* R_SPKEXPATKR PG 3 ADDR 0x32 */ + SND_SOC_BYTES("Speaker Expander Attack", R_SPKEXPATKL, 2), + /* R_SPKEXPRELL PG 3 ADDR 0x33 */ + /* R_SPKEXPRELR PG 3 ADDR 0x34 */ + SND_SOC_BYTES("Speaker Expander Release", R_SPKEXPRELL, 2), + /* R_SPKFXCTL PG 3 ADDR 0x35 */ + SOC_SINGLE("Speaker 3D Switch", R_SPKFXCTL, FB_SPKFXCTL_3DEN, 1, 0), + SOC_SINGLE("Speaker Treble Enhancement Switch", + R_SPKFXCTL, FB_SPKFXCTL_TEEN, 1, 0), + SOC_SINGLE("Speaker Treble NLF Switch", + R_SPKFXCTL, FB_SPKFXCTL_TNLFBYP, 1, 1), + SOC_SINGLE("Speaker Bass Enhancement Switch", + R_SPKFXCTL, FB_SPKFXCTL_BEEN, 1, 0), + SOC_SINGLE("Speaker Bass NLF Switch", + R_SPKFXCTL, FB_SPKFXCTL_BNLFBYP, 1, 1), + /* R_DACEQFILT PG 4 ADDR 0x01 */ + SOC_SINGLE("DAC EQ 2 Switch", + R_DACEQFILT, FB_DACEQFILT_EQ2EN, 1, 0), + SOC_ENUM("DAC EQ 2 Band", dac_eq_enums[0]), + SOC_SINGLE("DAC EQ 1 Switch", R_DACEQFILT, FB_DACEQFILT_EQ1EN, 1, 0), + SOC_ENUM("DAC EQ 1 Band", dac_eq_enums[1]), + /* R_DACMBCEN PG 4 ADDR 0x0A */ + SOC_SINGLE("DAC MBC 3 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN3, 1, 0), + SOC_SINGLE("DAC MBC 2 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN2, 1, 0), + SOC_SINGLE("DAC MBC 1 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN1, 1, 0), + /* R_DACMBCCTL PG 4 ADDR 0x0B */ + SOC_ENUM("DAC MBC 3 Mode", dac_mbc3_lvl_det_mode_enum), + SOC_ENUM("DAC MBC 3 Window", dac_mbc3_win_sel_enum), + SOC_ENUM("DAC MBC 2 Mode", dac_mbc2_lvl_det_mode_enum), + SOC_ENUM("DAC MBC 2 Window", dac_mbc2_win_sel_enum), + SOC_ENUM("DAC MBC 1 Mode", dac_mbc1_lvl_det_mode_enum), + SOC_ENUM("DAC MBC 1 Window", dac_mbc1_win_sel_enum), + /* R_DACMBCMUG1 PG 4 ADDR 0x0C */ + SOC_ENUM("DAC MBC 1 Phase Polarity", dac_mbc1_phase_pol_enum), + SOC_SINGLE_TLV("DAC MBC 1 Make-Up Gain Volume", R_DACMBCMUG1, + FB_DACMBCMUG_MUGAIN, FM_DACMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_DACMBCTHR1 PG 4 ADDR 0x0D */ + SOC_SINGLE_TLV("DAC MBC 1 Compressor Threshold Volume", R_DACMBCTHR1, + FB_DACMBCTHR_THRESH, FM_DACMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACMBCRAT1 PG 4 ADDR 0x0E */ + SOC_ENUM("DAC MBC 1 Compressor Ratio", dac_mbc1_comp_rat_enum), + /* R_DACMBCATK1L PG 4 ADDR 0x0F */ + /* R_DACMBCATK1H PG 4 ADDR 0x10 */ + SND_SOC_BYTES("DAC MBC 1 Attack", R_DACMBCATK1L, 2), + /* R_DACMBCREL1L PG 4 ADDR 0x11 */ + /* R_DACMBCREL1H PG 4 ADDR 0x12 */ + SND_SOC_BYTES("DAC MBC 1 Release", R_DACMBCREL1L, 2), + /* R_DACMBCMUG2 PG 4 ADDR 0x13 */ + SOC_ENUM("DAC MBC 2 Phase Polarity", dac_mbc2_phase_pol_enum), + SOC_SINGLE_TLV("DAC MBC 2 Make-Up Gain Volume", R_DACMBCMUG2, + FB_DACMBCMUG_MUGAIN, FM_DACMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_DACMBCTHR2 PG 4 ADDR 0x14 */ + SOC_SINGLE_TLV("DAC MBC 2 Compressor Threshold Volume", R_DACMBCTHR2, + FB_DACMBCTHR_THRESH, FM_DACMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACMBCRAT2 PG 4 ADDR 0x15 */ + SOC_ENUM("DAC MBC 2 Compressor Ratio", dac_mbc2_comp_rat_enum), + /* R_DACMBCATK2L PG 4 ADDR 0x16 */ + /* R_DACMBCATK2H PG 4 ADDR 0x17 */ + SND_SOC_BYTES("DAC MBC 2 Attack", R_DACMBCATK2L, 2), + /* R_DACMBCREL2L PG 4 ADDR 0x18 */ + /* R_DACMBCREL2H PG 4 ADDR 0x19 */ + SND_SOC_BYTES("DAC MBC 2 Release", R_DACMBCREL2L, 2), + /* R_DACMBCMUG3 PG 4 ADDR 0x1A */ + SOC_ENUM("DAC MBC 3 Phase Polarity", dac_mbc3_phase_pol_enum), + SOC_SINGLE_TLV("DAC MBC 3 Make-Up Gain Volume", R_DACMBCMUG3, + FB_DACMBCMUG_MUGAIN, FM_DACMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_DACMBCTHR3 PG 4 ADDR 0x1B */ + SOC_SINGLE_TLV("DAC MBC 3 Threshold Volume", R_DACMBCTHR3, + FB_DACMBCTHR_THRESH, FM_DACMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACMBCRAT3 PG 4 ADDR 0x1C */ + SOC_ENUM("DAC MBC 3 Compressor Ratio", dac_mbc3_comp_rat_enum), + /* R_DACMBCATK3L PG 4 ADDR 0x1D */ + /* R_DACMBCATK3H PG 4 ADDR 0x1E */ + SND_SOC_BYTES("DAC MBC 3 Attack", R_DACMBCATK3L, 3), + /* R_DACMBCREL3L PG 4 ADDR 0x1F */ + /* R_DACMBCREL3H PG 4 ADDR 0x20 */ + SND_SOC_BYTES("DAC MBC 3 Release", R_DACMBCREL3L, 3), + /* R_DACCLECTL PG 4 ADDR 0x21 */ + SOC_ENUM("DAC CLE Level Mode", dac_cle_lvl_mode_enum), + SOC_ENUM("DAC CLE Window", dac_cle_win_sel_enum), + SOC_SINGLE("DAC CLE Expander Switch", + R_DACCLECTL, FB_DACCLECTL_EXPEN, 1, 0), + SOC_SINGLE("DAC CLE Limiter Switch", + R_DACCLECTL, FB_DACCLECTL_LIMEN, 1, 0), + SOC_SINGLE("DAC CLE Compressor Switch", + R_DACCLECTL, FB_DACCLECTL_COMPEN, 1, 0), + /* R_DACCLEMUG PG 4 ADDR 0x22 */ + SOC_SINGLE_TLV("DAC CLE Make-Up Gain Volume", R_DACCLEMUG, + FB_DACCLEMUG_MUGAIN, FM_DACCLEMUG_MUGAIN, + 0, cle_mug_tlv_arr), + /* R_DACCOMPTHR PG 4 ADDR 0x23 */ + SOC_SINGLE_TLV("DAC Compressor Threshold Volume", R_DACCOMPTHR, + FB_DACCOMPTHR_THRESH, FM_DACCOMPTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACCOMPRAT PG 4 ADDR 0x24 */ + SOC_ENUM("DAC Compressor Ratio", dac_comp_rat_enum), + /* R_DACCOMPATKL PG 4 ADDR 0x25 */ + /* R_DACCOMPATKH PG 4 ADDR 0x26 */ + SND_SOC_BYTES("DAC Compressor Attack", R_DACCOMPATKL, 2), + /* R_DACCOMPRELL PG 4 ADDR 0x27 */ + /* R_DACCOMPRELH PG 4 ADDR 0x28 */ + SND_SOC_BYTES("DAC Compressor Release", R_DACCOMPRELL, 2), + /* R_DACLIMTHR PG 4 ADDR 0x29 */ + SOC_SINGLE_TLV("DAC Limiter Threshold Volume", R_DACLIMTHR, + FB_DACLIMTHR_THRESH, FM_DACLIMTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACLIMTGT PG 4 ADDR 0x2A */ + SOC_SINGLE_TLV("DAC Limiter Target Volume", R_DACLIMTGT, + FB_DACLIMTGT_TARGET, FM_DACLIMTGT_TARGET, + 0, thr_tlv_arr), + /* R_DACLIMATKL PG 4 ADDR 0x2B */ + /* R_DACLIMATKH PG 4 ADDR 0x2C */ + SND_SOC_BYTES("DAC Limiter Attack", R_DACLIMATKL, 2), + /* R_DACLIMRELL PG 4 ADDR 0x2D */ + /* R_DACLIMRELR PG 4 ADDR 0x2E */ + SND_SOC_BYTES("DAC Limiter Release", R_DACLIMRELL, 2), + /* R_DACEXPTHR PG 4 ADDR 0x2F */ + SOC_SINGLE_TLV("DAC Expander Threshold Volume", R_DACEXPTHR, + FB_DACEXPTHR_THRESH, FM_DACEXPTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACEXPRAT PG 4 ADDR 0x30 */ + SOC_ENUM("DAC Expander Ratio", dac_exp_rat_enum), + /* R_DACEXPATKL PG 4 ADDR 0x31 */ + /* R_DACEXPATKR PG 4 ADDR 0x32 */ + SND_SOC_BYTES("DAC Expander Attack", R_DACEXPATKL, 2), + /* R_DACEXPRELL PG 4 ADDR 0x33 */ + /* R_DACEXPRELR PG 4 ADDR 0x34 */ + SND_SOC_BYTES("DAC Expander Release", R_DACEXPRELL, 2), + /* R_DACFXCTL PG 4 ADDR 0x35 */ + SOC_SINGLE("DAC 3D Switch", R_DACFXCTL, FB_DACFXCTL_3DEN, 1, 0), + SOC_SINGLE("DAC Treble Enhancement Switch", + R_DACFXCTL, FB_DACFXCTL_TEEN, 1, 0), + SOC_SINGLE("DAC Treble NLF Switch", + R_DACFXCTL, FB_DACFXCTL_TNLFBYP, 1, 1), + SOC_SINGLE("DAC Bass Enhancement Switch", + R_DACFXCTL, FB_DACFXCTL_BEEN, 1, 0), + SOC_SINGLE("DAC Bass NLF Switch", + R_DACFXCTL, FB_DACFXCTL_BNLFBYP, 1, 1), + /* R_SUBEQFILT PG 5 ADDR 0x01 */ + SOC_SINGLE("Sub EQ 2 Switch", + R_SUBEQFILT, FB_SUBEQFILT_EQ2EN, 1, 0), + SOC_ENUM("Sub EQ 2 Band", sub_eq_enums[0]), + SOC_SINGLE("Sub EQ 1 Switch", R_SUBEQFILT, FB_SUBEQFILT_EQ1EN, 1, 0), + SOC_ENUM("Sub EQ 1 Band", sub_eq_enums[1]), + /* R_SUBMBCEN PG 5 ADDR 0x0A */ + SOC_SINGLE("Sub MBC 3 Switch", R_SUBMBCEN, FB_SUBMBCEN_MBCEN3, 1, 0), + SOC_SINGLE("Sub MBC 2 Switch", R_SUBMBCEN, FB_SUBMBCEN_MBCEN2, 1, 0), + SOC_SINGLE("Sub MBC 1 Switch", R_SUBMBCEN, FB_SUBMBCEN_MBCEN1, 1, 0), + /* R_SUBMBCCTL PG 5 ADDR 0x0B */ + SOC_ENUM("Sub MBC 3 Mode", sub_mbc3_lvl_det_mode_enum), + SOC_ENUM("Sub MBC 3 Window", sub_mbc3_win_sel_enum), + SOC_ENUM("Sub MBC 2 Mode", sub_mbc2_lvl_det_mode_enum), + SOC_ENUM("Sub MBC 2 Window", sub_mbc2_win_sel_enum), + SOC_ENUM("Sub MBC 1 Mode", sub_mbc1_lvl_det_mode_enum), + SOC_ENUM("Sub MBC 1 Window", sub_mbc1_win_sel_enum), + /* R_SUBMBCMUG1 PG 5 ADDR 0x0C */ + SOC_ENUM("Sub MBC 1 Phase Polarity", sub_mbc1_phase_pol_enum), + SOC_SINGLE_TLV("Sub MBC 1 Make-Up Gain Volume", R_SUBMBCMUG1, + FB_SUBMBCMUG_MUGAIN, FM_SUBMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SUBMBCTHR1 PG 5 ADDR 0x0D */ + SOC_SINGLE_TLV("Sub MBC 1 Compressor Threshold Volume", R_SUBMBCTHR1, + FB_SUBMBCTHR_THRESH, FM_SUBMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBMBCRAT1 PG 5 ADDR 0x0E */ + SOC_ENUM("Sub MBC 1 Compressor Ratio", sub_mbc1_comp_rat_enum), + /* R_SUBMBCATK1L PG 5 ADDR 0x0F */ + /* R_SUBMBCATK1H PG 5 ADDR 0x10 */ + SND_SOC_BYTES("Sub MBC 1 Attack", R_SUBMBCATK1L, 2), + /* R_SUBMBCREL1L PG 5 ADDR 0x11 */ + /* R_SUBMBCREL1H PG 5 ADDR 0x12 */ + SND_SOC_BYTES("Sub MBC 1 Release", R_SUBMBCREL1L, 2), + /* R_SUBMBCMUG2 PG 5 ADDR 0x13 */ + SOC_ENUM("Sub MBC 2 Phase Polarity", sub_mbc2_phase_pol_enum), + SOC_SINGLE_TLV("Sub MBC 2 Make-Up Gain Volume", R_SUBMBCMUG2, + FB_SUBMBCMUG_MUGAIN, FM_SUBMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SUBMBCTHR2 PG 5 ADDR 0x14 */ + SOC_SINGLE_TLV("Sub MBC 2 Compressor Threshold Volume", R_SUBMBCTHR2, + FB_SUBMBCTHR_THRESH, FM_SUBMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBMBCRAT2 PG 5 ADDR 0x15 */ + SOC_ENUM("Sub MBC 2 Compressor Ratio", sub_mbc2_comp_rat_enum), + /* R_SUBMBCATK2L PG 5 ADDR 0x16 */ + /* R_SUBMBCATK2H PG 5 ADDR 0x17 */ + SND_SOC_BYTES("Sub MBC 2 Attack", R_SUBMBCATK2L, 2), + /* R_SUBMBCREL2L PG 5 ADDR 0x18 */ + /* R_SUBMBCREL2H PG 5 ADDR 0x19 */ + SND_SOC_BYTES("Sub MBC 2 Release", R_SUBMBCREL2L, 2), + /* R_SUBMBCMUG3 PG 5 ADDR 0x1A */ + SOC_ENUM("Sub MBC 3 Phase Polarity", sub_mbc3_phase_pol_enum), + SOC_SINGLE_TLV("Sub MBC 3 Make-Up Gain Volume", R_SUBMBCMUG3, + FB_SUBMBCMUG_MUGAIN, FM_SUBMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SUBMBCTHR3 PG 5 ADDR 0x1B */ + SOC_SINGLE_TLV("Sub MBC 3 Threshold Volume", R_SUBMBCTHR3, + FB_SUBMBCTHR_THRESH, FM_SUBMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBMBCRAT3 PG 5 ADDR 0x1C */ + SOC_ENUM("Sub MBC 3 Compressor Ratio", sub_mbc3_comp_rat_enum), + /* R_SUBMBCATK3L PG 5 ADDR 0x1D */ + /* R_SUBMBCATK3H PG 5 ADDR 0x1E */ + SND_SOC_BYTES("Sub MBC 3 Attack", R_SUBMBCATK3L, 3), + /* R_SUBMBCREL3L PG 5 ADDR 0x1F */ + /* R_SUBMBCREL3H PG 5 ADDR 0x20 */ + SND_SOC_BYTES("Sub MBC 3 Release", R_SUBMBCREL3L, 3), + /* R_SUBCLECTL PG 5 ADDR 0x21 */ + SOC_ENUM("Sub CLE Level Mode", sub_cle_lvl_mode_enum), + SOC_ENUM("Sub CLE Window", sub_cle_win_sel_enum), + SOC_SINGLE("Sub CLE Expander Switch", + R_SUBCLECTL, FB_SUBCLECTL_EXPEN, 1, 0), + SOC_SINGLE("Sub CLE Limiter Switch", + R_SUBCLECTL, FB_SUBCLECTL_LIMEN, 1, 0), + SOC_SINGLE("Sub CLE Compressor Switch", + R_SUBCLECTL, FB_SUBCLECTL_COMPEN, 1, 0), + /* R_SUBCLEMUG PG 5 ADDR 0x22 */ + SOC_SINGLE_TLV("Sub CLE Make-Up Gain Volume", R_SUBCLEMUG, + FB_SUBCLEMUG_MUGAIN, FM_SUBCLEMUG_MUGAIN, + 0, cle_mug_tlv_arr), + /* R_SUBCOMPTHR PG 5 ADDR 0x23 */ + SOC_SINGLE_TLV("Sub Compressor Threshold Volume", R_SUBCOMPTHR, + FB_SUBCOMPTHR_THRESH, FM_SUBCOMPTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBCOMPRAT PG 5 ADDR 0x24 */ + SOC_ENUM("Sub Compressor Ratio", sub_comp_rat_enum), + /* R_SUBCOMPATKL PG 5 ADDR 0x25 */ + /* R_SUBCOMPATKH PG 5 ADDR 0x26 */ + SND_SOC_BYTES("Sub Compressor Attack", R_SUBCOMPATKL, 2), + /* R_SUBCOMPRELL PG 5 ADDR 0x27 */ + /* R_SUBCOMPRELH PG 5 ADDR 0x28 */ + SND_SOC_BYTES("Sub Compressor Release", R_SUBCOMPRELL, 2), + /* R_SUBLIMTHR PG 5 ADDR 0x29 */ + SOC_SINGLE_TLV("Sub Limiter Threshold Volume", R_SUBLIMTHR, + FB_SUBLIMTHR_THRESH, FM_SUBLIMTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBLIMTGT PG 5 ADDR 0x2A */ + SOC_SINGLE_TLV("Sub Limiter Target Volume", R_SUBLIMTGT, + FB_SUBLIMTGT_TARGET, FM_SUBLIMTGT_TARGET, + 0, thr_tlv_arr), + /* R_SUBLIMATKL PG 5 ADDR 0x2B */ + /* R_SUBLIMATKH PG 5 ADDR 0x2C */ + SND_SOC_BYTES("Sub Limiter Attack", R_SUBLIMATKL, 2), + /* R_SUBLIMRELL PG 5 ADDR 0x2D */ + /* R_SUBLIMRELR PG 5 ADDR 0x2E */ + SND_SOC_BYTES("Sub Limiter Release", R_SUBLIMRELL, 2), + /* R_SUBEXPTHR PG 5 ADDR 0x2F */ + SOC_SINGLE_TLV("Sub Expander Threshold Volume", R_SUBEXPTHR, + FB_SUBEXPTHR_THRESH, FM_SUBEXPTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBEXPRAT PG 5 ADDR 0x30 */ + SOC_ENUM("Sub Expander Ratio", sub_exp_rat_enum), + /* R_SUBEXPATKL PG 5 ADDR 0x31 */ + /* R_SUBEXPATKR PG 5 ADDR 0x32 */ + SND_SOC_BYTES("Sub Expander Attack", R_SUBEXPATKL, 2), + /* R_SUBEXPRELL PG 5 ADDR 0x33 */ + /* R_SUBEXPRELR PG 5 ADDR 0x34 */ + SND_SOC_BYTES("Sub Expander Release", R_SUBEXPRELL, 2), + /* R_SUBFXCTL PG 5 ADDR 0x35 */ + SOC_SINGLE("Sub Treble Enhancement Switch", + R_SUBFXCTL, FB_SUBFXCTL_TEEN, 1, 0), + SOC_SINGLE("Sub Treble NLF Switch", + R_SUBFXCTL, FB_SUBFXCTL_TNLFBYP, 1, 1), + SOC_SINGLE("Sub Bass Enhancement Switch", + R_SUBFXCTL, FB_SUBFXCTL_BEEN, 1, 0), + SOC_SINGLE("Sub Bass NLF Switch", + R_SUBFXCTL, FB_SUBFXCTL_BNLFBYP, 1, 1), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 1", BIQUAD_SIZE, 0x00), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 2", BIQUAD_SIZE, 0x05), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 3", BIQUAD_SIZE, 0x0a), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 4", BIQUAD_SIZE, 0x0f), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 5", BIQUAD_SIZE, 0x14), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 6", BIQUAD_SIZE, 0x19), + + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 1", BIQUAD_SIZE, 0x20), + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 2", BIQUAD_SIZE, 0x25), + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 3", BIQUAD_SIZE, 0x2a), + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 4", BIQUAD_SIZE, 0x2f), + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 5", BIQUAD_SIZE, 0x34), + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 6", BIQUAD_SIZE, 0x39), + + COEFF_RAM_CTL("DAC Cascade 1 Left Prescale", COEFF_SIZE, 0x1f), + COEFF_RAM_CTL("DAC Cascade 1 Right Prescale", COEFF_SIZE, 0x3f), + + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 1", BIQUAD_SIZE, 0x40), + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 2", BIQUAD_SIZE, 0x45), + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 3", BIQUAD_SIZE, 0x4a), + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 4", BIQUAD_SIZE, 0x4f), + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 5", BIQUAD_SIZE, 0x54), + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 6", BIQUAD_SIZE, 0x59), + + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 1", BIQUAD_SIZE, 0x60), + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 2", BIQUAD_SIZE, 0x65), + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 3", BIQUAD_SIZE, 0x6a), + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 4", BIQUAD_SIZE, 0x6f), + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 5", BIQUAD_SIZE, 0x74), + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 6", BIQUAD_SIZE, 0x79), + + COEFF_RAM_CTL("DAC Cascade 2 Left Prescale", COEFF_SIZE, 0x5f), + COEFF_RAM_CTL("DAC Cascade 2 Right Prescale", COEFF_SIZE, 0x7f), + + COEFF_RAM_CTL("DAC Bass Extraction BiQuad 1", BIQUAD_SIZE, 0x80), + COEFF_RAM_CTL("DAC Bass Extraction BiQuad 2", BIQUAD_SIZE, 0x85), + + COEFF_RAM_CTL("DAC Bass Non Linear Function 1", COEFF_SIZE, 0x8a), + COEFF_RAM_CTL("DAC Bass Non Linear Function 2", COEFF_SIZE, 0x8b), + + COEFF_RAM_CTL("DAC Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c), + + COEFF_RAM_CTL("DAC Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91), + + COEFF_RAM_CTL("DAC Bass Mix", COEFF_SIZE, 0x96), + + COEFF_RAM_CTL("DAC Treb Extraction BiQuad 1", BIQUAD_SIZE, 0x97), + COEFF_RAM_CTL("DAC Treb Extraction BiQuad 2", BIQUAD_SIZE, 0x9c), + + COEFF_RAM_CTL("DAC Treb Non Linear Function 1", COEFF_SIZE, 0xa1), + COEFF_RAM_CTL("DAC Treb Non Linear Function 2", COEFF_SIZE, 0xa2), + + COEFF_RAM_CTL("DAC Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3), + + COEFF_RAM_CTL("DAC Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8), + + COEFF_RAM_CTL("DAC Treb Mix", COEFF_SIZE, 0xad), + + COEFF_RAM_CTL("DAC 3D", COEFF_SIZE, 0xae), + + COEFF_RAM_CTL("DAC 3D Mix", COEFF_SIZE, 0xaf), + + COEFF_RAM_CTL("DAC MBC 1 BiQuad 1", BIQUAD_SIZE, 0xb0), + COEFF_RAM_CTL("DAC MBC 1 BiQuad 2", BIQUAD_SIZE, 0xb5), + + COEFF_RAM_CTL("DAC MBC 2 BiQuad 1", BIQUAD_SIZE, 0xba), + COEFF_RAM_CTL("DAC MBC 2 BiQuad 2", BIQUAD_SIZE, 0xbf), + + COEFF_RAM_CTL("DAC MBC 3 BiQuad 1", BIQUAD_SIZE, 0xc4), + COEFF_RAM_CTL("DAC MBC 3 BiQuad 2", BIQUAD_SIZE, 0xc9), + + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 1", BIQUAD_SIZE, 0x00), + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 2", BIQUAD_SIZE, 0x05), + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 3", BIQUAD_SIZE, 0x0a), + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 4", BIQUAD_SIZE, 0x0f), + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 5", BIQUAD_SIZE, 0x14), + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 6", BIQUAD_SIZE, 0x19), + + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 1", BIQUAD_SIZE, 0x20), + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 2", BIQUAD_SIZE, 0x25), + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 3", BIQUAD_SIZE, 0x2a), + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 4", BIQUAD_SIZE, 0x2f), + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 5", BIQUAD_SIZE, 0x34), + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 6", BIQUAD_SIZE, 0x39), + + COEFF_RAM_CTL("Speaker Cascade 1 Left Prescale", COEFF_SIZE, 0x1f), + COEFF_RAM_CTL("Speaker Cascade 1 Right Prescale", COEFF_SIZE, 0x3f), + + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 1", BIQUAD_SIZE, 0x40), + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 2", BIQUAD_SIZE, 0x45), + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 3", BIQUAD_SIZE, 0x4a), + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 4", BIQUAD_SIZE, 0x4f), + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 5", BIQUAD_SIZE, 0x54), + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 6", BIQUAD_SIZE, 0x59), + + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 1", BIQUAD_SIZE, 0x60), + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 2", BIQUAD_SIZE, 0x65), + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 3", BIQUAD_SIZE, 0x6a), + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 4", BIQUAD_SIZE, 0x6f), + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 5", BIQUAD_SIZE, 0x74), + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 6", BIQUAD_SIZE, 0x79), + + COEFF_RAM_CTL("Speaker Cascade 2 Left Prescale", COEFF_SIZE, 0x5f), + COEFF_RAM_CTL("Speaker Cascade 2 Right Prescale", COEFF_SIZE, 0x7f), + + COEFF_RAM_CTL("Speaker Bass Extraction BiQuad 1", BIQUAD_SIZE, 0x80), + COEFF_RAM_CTL("Speaker Bass Extraction BiQuad 2", BIQUAD_SIZE, 0x85), + + COEFF_RAM_CTL("Speaker Bass Non Linear Function 1", COEFF_SIZE, 0x8a), + COEFF_RAM_CTL("Speaker Bass Non Linear Function 2", COEFF_SIZE, 0x8b), + + COEFF_RAM_CTL("Speaker Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c), + + COEFF_RAM_CTL("Speaker Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91), + + COEFF_RAM_CTL("Speaker Bass Mix", COEFF_SIZE, 0x96), + + COEFF_RAM_CTL("Speaker Treb Extraction BiQuad 1", BIQUAD_SIZE, 0x97), + COEFF_RAM_CTL("Speaker Treb Extraction BiQuad 2", BIQUAD_SIZE, 0x9c), + + COEFF_RAM_CTL("Speaker Treb Non Linear Function 1", COEFF_SIZE, 0xa1), + COEFF_RAM_CTL("Speaker Treb Non Linear Function 2", COEFF_SIZE, 0xa2), + + COEFF_RAM_CTL("Speaker Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3), + + COEFF_RAM_CTL("Speaker Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8), + + COEFF_RAM_CTL("Speaker Treb Mix", COEFF_SIZE, 0xad), + + COEFF_RAM_CTL("Speaker 3D", COEFF_SIZE, 0xae), + + COEFF_RAM_CTL("Speaker 3D Mix", COEFF_SIZE, 0xaf), + + COEFF_RAM_CTL("Speaker MBC 1 BiQuad 1", BIQUAD_SIZE, 0xb0), + COEFF_RAM_CTL("Speaker MBC 1 BiQuad 2", BIQUAD_SIZE, 0xb5), + + COEFF_RAM_CTL("Speaker MBC 2 BiQuad 1", BIQUAD_SIZE, 0xba), + COEFF_RAM_CTL("Speaker MBC 2 BiQuad 2", BIQUAD_SIZE, 0xbf), + + COEFF_RAM_CTL("Speaker MBC 3 BiQuad 1", BIQUAD_SIZE, 0xc4), + COEFF_RAM_CTL("Speaker MBC 3 BiQuad 2", BIQUAD_SIZE, 0xc9), + + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 1", BIQUAD_SIZE, 0x00), + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 2", BIQUAD_SIZE, 0x05), + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 3", BIQUAD_SIZE, 0x0a), + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 4", BIQUAD_SIZE, 0x0f), + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 5", BIQUAD_SIZE, 0x14), + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 6", BIQUAD_SIZE, 0x19), + + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 1", BIQUAD_SIZE, 0x20), + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 2", BIQUAD_SIZE, 0x25), + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 3", BIQUAD_SIZE, 0x2a), + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 4", BIQUAD_SIZE, 0x2f), + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 5", BIQUAD_SIZE, 0x34), + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 6", BIQUAD_SIZE, 0x39), + + COEFF_RAM_CTL("Sub Cascade 1 Left Prescale", COEFF_SIZE, 0x1f), + COEFF_RAM_CTL("Sub Cascade 1 Right Prescale", COEFF_SIZE, 0x3f), + + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 1", BIQUAD_SIZE, 0x40), + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 2", BIQUAD_SIZE, 0x45), + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 3", BIQUAD_SIZE, 0x4a), + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 4", BIQUAD_SIZE, 0x4f), + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 5", BIQUAD_SIZE, 0x54), + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 6", BIQUAD_SIZE, 0x59), + + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 1", BIQUAD_SIZE, 0x60), + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 2", BIQUAD_SIZE, 0x65), + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 3", BIQUAD_SIZE, 0x6a), + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 4", BIQUAD_SIZE, 0x6f), + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 5", BIQUAD_SIZE, 0x74), + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 6", BIQUAD_SIZE, 0x79), + + COEFF_RAM_CTL("Sub Cascade 2 Left Prescale", COEFF_SIZE, 0x5f), + COEFF_RAM_CTL("Sub Cascade 2 Right Prescale", COEFF_SIZE, 0x7f), + + COEFF_RAM_CTL("Sub Bass Extraction BiQuad 1", BIQUAD_SIZE, 0x80), + COEFF_RAM_CTL("Sub Bass Extraction BiQuad 2", BIQUAD_SIZE, 0x85), + + COEFF_RAM_CTL("Sub Bass Non Linear Function 1", COEFF_SIZE, 0x8a), + COEFF_RAM_CTL("Sub Bass Non Linear Function 2", COEFF_SIZE, 0x8b), + + COEFF_RAM_CTL("Sub Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c), + + COEFF_RAM_CTL("Sub Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91), + + COEFF_RAM_CTL("Sub Bass Mix", COEFF_SIZE, 0x96), + + COEFF_RAM_CTL("Sub Treb Extraction BiQuad 1", BIQUAD_SIZE, 0x97), + COEFF_RAM_CTL("Sub Treb Extraction BiQuad 2", BIQUAD_SIZE, 0x9c), + + COEFF_RAM_CTL("Sub Treb Non Linear Function 1", COEFF_SIZE, 0xa1), + COEFF_RAM_CTL("Sub Treb Non Linear Function 2", COEFF_SIZE, 0xa2), + + COEFF_RAM_CTL("Sub Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3), + + COEFF_RAM_CTL("Sub Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8), + + COEFF_RAM_CTL("Sub Treb Mix", COEFF_SIZE, 0xad), + + COEFF_RAM_CTL("Sub 3D", COEFF_SIZE, 0xae), + + COEFF_RAM_CTL("Sub 3D Mix", COEFF_SIZE, 0xaf), + + COEFF_RAM_CTL("Sub MBC 1 BiQuad 1", BIQUAD_SIZE, 0xb0), + COEFF_RAM_CTL("Sub MBC 1 BiQuad 2", BIQUAD_SIZE, 0xb5), + + COEFF_RAM_CTL("Sub MBC 2 BiQuad 1", BIQUAD_SIZE, 0xba), + COEFF_RAM_CTL("Sub MBC 2 BiQuad 2", BIQUAD_SIZE, 0xbf), + + COEFF_RAM_CTL("Sub MBC 3 BiQuad 1", BIQUAD_SIZE, 0xc4), + COEFF_RAM_CTL("Sub MBC 3 BiQuad 2", BIQUAD_SIZE, 0xc9), +}; + +static struct snd_soc_dapm_widget const tscs454_dapm_widgets[] = { + /* R_PLLCTL PG 0 ADDR 0x15 */ + SND_SOC_DAPM_SUPPLY("PLL 1 Power", R_PLLCTL, FB_PLLCTL_PU_PLL1, 0, + pll_power_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PLL 2 Power", R_PLLCTL, FB_PLLCTL_PU_PLL2, 0, + pll_power_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + /* R_I2SPINC0 PG 0 ADDR 0x22 */ + SND_SOC_DAPM_AIF_OUT("DAI 3 Out", "DAI 3 Capture", 0, + R_I2SPINC0, FB_I2SPINC0_SDO3TRI, 1), + SND_SOC_DAPM_AIF_OUT("DAI 2 Out", "DAI 2 Capture", 0, + R_I2SPINC0, FB_I2SPINC0_SDO2TRI, 1), + SND_SOC_DAPM_AIF_OUT("DAI 1 Out", "DAI 1 Capture", 0, + R_I2SPINC0, FB_I2SPINC0_SDO1TRI, 1), + /* R_PWRM0 PG 0 ADDR 0x33 */ + SND_SOC_DAPM_ADC("Input Processor Channel 3", NULL, + R_PWRM0, FB_PWRM0_INPROC3PU, 0), + SND_SOC_DAPM_ADC("Input Processor Channel 2", NULL, + R_PWRM0, FB_PWRM0_INPROC2PU, 0), + SND_SOC_DAPM_ADC("Input Processor Channel 1", NULL, + R_PWRM0, FB_PWRM0_INPROC1PU, 0), + SND_SOC_DAPM_ADC("Input Processor Channel 0", NULL, + R_PWRM0, FB_PWRM0_INPROC0PU, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias 2", + R_PWRM0, FB_PWRM0_MICB2PU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias 1", R_PWRM0, + FB_PWRM0_MICB1PU, 0, NULL, 0), + /* R_PWRM1 PG 0 ADDR 0x34 */ + SND_SOC_DAPM_SUPPLY("Sub Power", R_PWRM1, FB_PWRM1_SUBPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Left Power", + R_PWRM1, FB_PWRM1_HPLPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Right Power", + R_PWRM1, FB_PWRM1_HPRPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Speaker Left Power", + R_PWRM1, FB_PWRM1_SPKLPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Speaker Right Power", + R_PWRM1, FB_PWRM1_SPKRPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Differential Input 2 Power", + R_PWRM1, FB_PWRM1_D2S2PU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Differential Input 1 Power", + R_PWRM1, FB_PWRM1_D2S1PU, 0, NULL, 0), + /* R_PWRM2 PG 0 ADDR 0x35 */ + SND_SOC_DAPM_SUPPLY("DAI 3 Out Power", + R_PWRM2, FB_PWRM2_I2S3OPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAI 2 Out Power", + R_PWRM2, FB_PWRM2_I2S2OPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAI 1 Out Power", + R_PWRM2, FB_PWRM2_I2S1OPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAI 3 In Power", + R_PWRM2, FB_PWRM2_I2S3IPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAI 2 In Power", + R_PWRM2, FB_PWRM2_I2S2IPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAI 1 In Power", + R_PWRM2, FB_PWRM2_I2S1IPU, 0, NULL, 0), + /* R_PWRM3 PG 0 ADDR 0x36 */ + SND_SOC_DAPM_SUPPLY("Line Out Left Power", + R_PWRM3, FB_PWRM3_LLINEPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Line Out Right Power", + R_PWRM3, FB_PWRM3_RLINEPU, 0, NULL, 0), + /* R_PWRM4 PG 0 ADDR 0x37 */ + SND_SOC_DAPM_DAC("Sub", NULL, R_PWRM4, FB_PWRM4_OPSUBPU, 0), + SND_SOC_DAPM_DAC("DAC Left", NULL, R_PWRM4, FB_PWRM4_OPDACLPU, 0), + SND_SOC_DAPM_DAC("DAC Right", NULL, R_PWRM4, FB_PWRM4_OPDACRPU, 0), + SND_SOC_DAPM_DAC("ClassD Left", NULL, R_PWRM4, FB_PWRM4_OPSPKLPU, 0), + SND_SOC_DAPM_DAC("ClassD Right", NULL, R_PWRM4, FB_PWRM4_OPSPKRPU, 0), + /* R_AUDIOMUX1 PG 0 ADDR 0x3A */ + SND_SOC_DAPM_MUX("DAI 2 Out Mux", SND_SOC_NOPM, 0, 0, + &dai2_mux_dapm_enum), + SND_SOC_DAPM_MUX("DAI 1 Out Mux", SND_SOC_NOPM, 0, 0, + &dai1_mux_dapm_enum), + /* R_AUDIOMUX2 PG 0 ADDR 0x3B */ + SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, + &dac_mux_dapm_enum), + SND_SOC_DAPM_MUX("DAI 3 Out Mux", SND_SOC_NOPM, 0, 0, + &dai3_mux_dapm_enum), + /* R_AUDIOMUX3 PG 0 ADDR 0x3C */ + SND_SOC_DAPM_MUX("Sub Mux", SND_SOC_NOPM, 0, 0, + &sub_mux_dapm_enum), + SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0, + &classd_mux_dapm_enum), + /* R_HSDCTL1 PG 1 ADDR 0x01 */ + SND_SOC_DAPM_SUPPLY("GHS Detect Power", R_HSDCTL1, + FB_HSDCTL1_CON_DET_PWD, 1, NULL, 0), + /* R_CH0AIC PG 1 ADDR 0x06 */ + SND_SOC_DAPM_MUX("Input Boost Channel 0 Mux", SND_SOC_NOPM, 0, 0, + &in_bst_mux_ch0_dapm_enum), + SND_SOC_DAPM_MUX("ADC Channel 0 Mux", SND_SOC_NOPM, 0, 0, + &adc_mux_ch0_dapm_enum), + SND_SOC_DAPM_MUX("Input Processor Channel 0 Mux", SND_SOC_NOPM, 0, 0, + &in_proc_mux_ch0_dapm_enum), + /* R_CH1AIC PG 1 ADDR 0x07 */ + SND_SOC_DAPM_MUX("Input Boost Channel 1 Mux", SND_SOC_NOPM, 0, 0, + &in_bst_mux_ch1_dapm_enum), + SND_SOC_DAPM_MUX("ADC Channel 1 Mux", SND_SOC_NOPM, 0, 0, + &adc_mux_ch1_dapm_enum), + SND_SOC_DAPM_MUX("Input Processor Channel 1 Mux", SND_SOC_NOPM, 0, 0, + &in_proc_mux_ch1_dapm_enum), + /* Virtual */ + SND_SOC_DAPM_AIF_IN("DAI 3 In", "DAI 3 Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DAI 2 In", "DAI 2 Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DAI 1 In", "DAI 1 Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("PLLs", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("Sub Out"), + SND_SOC_DAPM_OUTPUT("Headphone Left"), + SND_SOC_DAPM_OUTPUT("Headphone Right"), + SND_SOC_DAPM_OUTPUT("Speaker Left"), + SND_SOC_DAPM_OUTPUT("Speaker Right"), + SND_SOC_DAPM_OUTPUT("Line Out Left"), + SND_SOC_DAPM_OUTPUT("Line Out Right"), + SND_SOC_DAPM_INPUT("D2S 2"), + SND_SOC_DAPM_INPUT("D2S 1"), + SND_SOC_DAPM_INPUT("Line In 1 Left"), + SND_SOC_DAPM_INPUT("Line In 1 Right"), + SND_SOC_DAPM_INPUT("Line In 2 Left"), + SND_SOC_DAPM_INPUT("Line In 2 Right"), + SND_SOC_DAPM_INPUT("Line In 3 Left"), + SND_SOC_DAPM_INPUT("Line In 3 Right"), + SND_SOC_DAPM_INPUT("DMic 1"), + SND_SOC_DAPM_INPUT("DMic 2"), + + SND_SOC_DAPM_MUX("CH 0_1 Mux", SND_SOC_NOPM, 0, 0, + &ch_0_1_mux_dapm_enum), + SND_SOC_DAPM_MUX("CH 2_3 Mux", SND_SOC_NOPM, 0, 0, + &ch_2_3_mux_dapm_enum), + SND_SOC_DAPM_MUX("CH 4_5 Mux", SND_SOC_NOPM, 0, 0, + &ch_4_5_mux_dapm_enum), +}; + +static struct snd_soc_dapm_route const tscs454_intercon[] = { + /* PLLs */ + {"PLLs", NULL, "PLL 1 Power", pll_connected}, + {"PLLs", NULL, "PLL 2 Power", pll_connected}, + /* Inputs */ + {"DAI 3 In", NULL, "DAI 3 In Power"}, + {"DAI 2 In", NULL, "DAI 2 In Power"}, + {"DAI 1 In", NULL, "DAI 1 In Power"}, + /* Outputs */ + {"DAI 3 Out", NULL, "DAI 3 Out Power"}, + {"DAI 2 Out", NULL, "DAI 2 Out Power"}, + {"DAI 1 Out", NULL, "DAI 1 Out Power"}, + /* Ch Muxing */ + {"CH 0_1 Mux", "DAI 1", "DAI 1 In"}, + {"CH 0_1 Mux", "TDM 0_1", "DAI 1 In"}, + {"CH 2_3 Mux", "DAI 2", "DAI 2 In"}, + {"CH 2_3 Mux", "TDM 2_3", "DAI 1 In"}, + {"CH 4_5 Mux", "DAI 3", "DAI 2 In"}, + {"CH 4_5 Mux", "TDM 4_5", "DAI 1 In"}, + /* In/Out Muxing */ + {"DAI 1 Out Mux", "CH 0_1", "CH 0_1 Mux"}, + {"DAI 1 Out Mux", "CH 2_3", "CH 2_3 Mux"}, + {"DAI 1 Out Mux", "CH 4_5", "CH 4_5 Mux"}, + {"DAI 2 Out Mux", "CH 0_1", "CH 0_1 Mux"}, + {"DAI 2 Out Mux", "CH 2_3", "CH 2_3 Mux"}, + {"DAI 2 Out Mux", "CH 4_5", "CH 4_5 Mux"}, + {"DAI 3 Out Mux", "CH 0_1", "CH 0_1 Mux"}, + {"DAI 3 Out Mux", "CH 2_3", "CH 2_3 Mux"}, + {"DAI 3 Out Mux", "CH 4_5", "CH 4_5 Mux"}, + /****************** + * Playback Paths * + ******************/ + /* DAC Path */ + {"DAC Mux", "CH 4_5", "CH 4_5 Mux"}, + {"DAC Mux", "CH 2_3", "CH 2_3 Mux"}, + {"DAC Mux", "CH 0_1", "CH 0_1 Mux"}, + {"DAC Left", NULL, "DAC Mux"}, + {"DAC Right", NULL, "DAC Mux"}, + {"DAC Left", NULL, "PLLs"}, + {"DAC Right", NULL, "PLLs"}, + {"Headphone Left", NULL, "Headphone Left Power"}, + {"Headphone Right", NULL, "Headphone Right Power"}, + {"Headphone Left", NULL, "DAC Left"}, + {"Headphone Right", NULL, "DAC Right"}, + /* Line Out */ + {"Line Out Left", NULL, "Line Out Left Power"}, + {"Line Out Right", NULL, "Line Out Right Power"}, + {"Line Out Left", NULL, "DAC Left"}, + {"Line Out Right", NULL, "DAC Right"}, + /* ClassD Path */ + {"Speaker Mux", "CH 4_5", "CH 4_5 Mux"}, + {"Speaker Mux", "CH 2_3", "CH 2_3 Mux"}, + {"Speaker Mux", "CH 0_1", "CH 0_1 Mux"}, + {"ClassD Left", NULL, "Speaker Mux"}, + {"ClassD Right", NULL, "Speaker Mux"}, + {"ClassD Left", NULL, "PLLs"}, + {"ClassD Right", NULL, "PLLs"}, + {"Speaker Left", NULL, "Speaker Left Power"}, + {"Speaker Right", NULL, "Speaker Right Power"}, + {"Speaker Left", NULL, "ClassD Left"}, + {"Speaker Right", NULL, "ClassD Right"}, + /* Sub Path */ + {"Sub Mux", "CH 4", "CH 4_5 Mux"}, + {"Sub Mux", "CH 5", "CH 4_5 Mux"}, + {"Sub Mux", "CH 4 + 5", "CH 4_5 Mux"}, + {"Sub Mux", "CH 2", "CH 2_3 Mux"}, + {"Sub Mux", "CH 3", "CH 2_3 Mux"}, + {"Sub Mux", "CH 2 + 3", "CH 2_3 Mux"}, + {"Sub Mux", "CH 0", "CH 0_1 Mux"}, + {"Sub Mux", "CH 1", "CH 0_1 Mux"}, + {"Sub Mux", "CH 0 + 1", "CH 0_1 Mux"}, + {"Sub Mux", "ADC/DMic 1 Left", "Input Processor Channel 0"}, + {"Sub Mux", "ADC/DMic 1 Right", "Input Processor Channel 1"}, + {"Sub Mux", "ADC/DMic 1 Left Plus Right", "Input Processor Channel 0"}, + {"Sub Mux", "ADC/DMic 1 Left Plus Right", "Input Processor Channel 1"}, + {"Sub Mux", "DMic 2 Left", "DMic 2"}, + {"Sub Mux", "DMic 2 Right", "DMic 2"}, + {"Sub Mux", "DMic 2 Left Plus Right", "DMic 2"}, + {"Sub Mux", "ClassD Left", "ClassD Left"}, + {"Sub Mux", "ClassD Right", "ClassD Right"}, + {"Sub Mux", "ClassD Left Plus Right", "ClassD Left"}, + {"Sub Mux", "ClassD Left Plus Right", "ClassD Right"}, + {"Sub", NULL, "Sub Mux"}, + {"Sub", NULL, "PLLs"}, + {"Sub Out", NULL, "Sub Power"}, + {"Sub Out", NULL, "Sub"}, + /***************** + * Capture Paths * + *****************/ + {"Input Boost Channel 0 Mux", "Input 3", "Line In 3 Left"}, + {"Input Boost Channel 0 Mux", "Input 2", "Line In 2 Left"}, + {"Input Boost Channel 0 Mux", "Input 1", "Line In 1 Left"}, + {"Input Boost Channel 0 Mux", "D2S", "D2S 1"}, + + {"Input Boost Channel 1 Mux", "Input 3", "Line In 3 Right"}, + {"Input Boost Channel 1 Mux", "Input 2", "Line In 2 Right"}, + {"Input Boost Channel 1 Mux", "Input 1", "Line In 1 Right"}, + {"Input Boost Channel 1 Mux", "D2S", "D2S 2"}, + + {"ADC Channel 0 Mux", "Input 3 Boost Bypass", "Line In 3 Left"}, + {"ADC Channel 0 Mux", "Input 2 Boost Bypass", "Line In 2 Left"}, + {"ADC Channel 0 Mux", "Input 1 Boost Bypass", "Line In 1 Left"}, + {"ADC Channel 0 Mux", "Input Boost", "Input Boost Channel 0 Mux"}, + + {"ADC Channel 1 Mux", "Input 3 Boost Bypass", "Line In 3 Right"}, + {"ADC Channel 1 Mux", "Input 2 Boost Bypass", "Line In 2 Right"}, + {"ADC Channel 1 Mux", "Input 1 Boost Bypass", "Line In 1 Right"}, + {"ADC Channel 1 Mux", "Input Boost", "Input Boost Channel 1 Mux"}, + + {"Input Processor Channel 0 Mux", "ADC", "ADC Channel 0 Mux"}, + {"Input Processor Channel 0 Mux", "DMic", "DMic 1"}, + + {"Input Processor Channel 0", NULL, "PLLs"}, + {"Input Processor Channel 0", NULL, "Input Processor Channel 0 Mux"}, + + {"Input Processor Channel 1 Mux", "ADC", "ADC Channel 1 Mux"}, + {"Input Processor Channel 1 Mux", "DMic", "DMic 1"}, + + {"Input Processor Channel 1", NULL, "PLLs"}, + {"Input Processor Channel 1", NULL, "Input Processor Channel 1 Mux"}, + + {"Input Processor Channel 2", NULL, "PLLs"}, + {"Input Processor Channel 2", NULL, "DMic 2"}, + + {"Input Processor Channel 3", NULL, "PLLs"}, + {"Input Processor Channel 3", NULL, "DMic 2"}, + + {"DAI 1 Out Mux", "ADC/DMic 1", "Input Processor Channel 0"}, + {"DAI 1 Out Mux", "ADC/DMic 1", "Input Processor Channel 1"}, + {"DAI 1 Out Mux", "DMic 2", "Input Processor Channel 2"}, + {"DAI 1 Out Mux", "DMic 2", "Input Processor Channel 3"}, + + {"DAI 2 Out Mux", "ADC/DMic 1", "Input Processor Channel 0"}, + {"DAI 2 Out Mux", "ADC/DMic 1", "Input Processor Channel 1"}, + {"DAI 2 Out Mux", "DMic 2", "Input Processor Channel 2"}, + {"DAI 2 Out Mux", "DMic 2", "Input Processor Channel 3"}, + + {"DAI 3 Out Mux", "ADC/DMic 1", "Input Processor Channel 0"}, + {"DAI 3 Out Mux", "ADC/DMic 1", "Input Processor Channel 1"}, + {"DAI 3 Out Mux", "DMic 2", "Input Processor Channel 2"}, + {"DAI 3 Out Mux", "DMic 2", "Input Processor Channel 3"}, + + {"DAI 1 Out", NULL, "DAI 1 Out Mux"}, + {"DAI 2 Out", NULL, "DAI 2 Out Mux"}, + {"DAI 3 Out", NULL, "DAI 3 Out Mux"}, +}; + +/* This is used when BCLK is sourcing the PLLs */ +static int tscs454_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + unsigned int val; + int bclk_dai; + int ret; + + dev_dbg(component->dev, "%s(): freq = %u\n", __func__, freq); + + ret = snd_soc_component_read(component, R_PLLCTL, &val); + if (ret < 0) + return ret; + + bclk_dai = (val & FM_PLLCTL_BCLKSEL) >> FB_PLLCTL_BCLKSEL; + if (bclk_dai != dai->id) + return 0; + + tscs454->bclk_freq = freq; + return set_sysclk(component); +} + +static int tscs454_set_bclk_ratio(struct snd_soc_dai *dai, + unsigned int ratio) +{ + unsigned int mask; + int ret; + struct snd_soc_component *component = dai->component; + unsigned int val; + int shift; + + dev_dbg(component->dev, "set_bclk_ratio() id = %d ratio = %u\n", + dai->id, ratio); + + switch (dai->id) { + case TSCS454_DAI1_ID: + mask = FM_I2SCMC_BCMP1; + shift = FB_I2SCMC_BCMP1; + break; + case TSCS454_DAI2_ID: + mask = FM_I2SCMC_BCMP2; + shift = FB_I2SCMC_BCMP2; + break; + case TSCS454_DAI3_ID: + mask = FM_I2SCMC_BCMP3; + shift = FB_I2SCMC_BCMP3; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unknown audio interface (%d)\n", ret); + return ret; + } + + switch (ratio) { + case 32: + val = I2SCMC_BCMP_32X; + break; + case 40: + val = I2SCMC_BCMP_40X; + break; + case 64: + val = I2SCMC_BCMP_64X; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unsupported bclk ratio (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, + R_I2SCMC, mask, val << shift); + if (ret < 0) { + dev_err(component->dev, + "Failed to set DAI BCLK ratio (%d)\n", ret); + return ret; + } + + return 0; +} + +static inline int set_aif_master_from_fmt(struct snd_soc_component *component, + struct aif *aif, unsigned int fmt) +{ + int ret; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aif->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + aif->master = false; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unsupported format (%d)\n", ret); + return ret; + } + + return 0; +} + +static inline int set_aif_tdm_delay(struct snd_soc_component *component, + unsigned int dai_id, bool delay) +{ + unsigned int reg; + int ret; + + switch (dai_id) { + case TSCS454_DAI1_ID: + reg = R_TDMCTL0; + break; + case TSCS454_DAI2_ID: + reg = R_PCMP2CTL0; + break; + case TSCS454_DAI3_ID: + reg = R_PCMP3CTL0; + break; + default: + ret = -EINVAL; + dev_err(component->dev, + "DAI %d unknown (%d)\n", dai_id + 1, ret); + return ret; + } + ret = snd_soc_component_update_bits(component, + reg, FM_TDMCTL0_BDELAY, delay); + if (ret < 0) { + dev_err(component->dev, "Failed to setup tdm format (%d)\n", + ret); + return ret; + } + + return 0; +} + +static inline int set_aif_format_from_fmt(struct snd_soc_component *component, + unsigned int dai_id, unsigned int fmt) +{ + unsigned int reg; + unsigned int val; + int ret; + + switch (dai_id) { + case TSCS454_DAI1_ID: + reg = R_I2SP1CTL; + break; + case TSCS454_DAI2_ID: + reg = R_I2SP2CTL; + break; + case TSCS454_DAI3_ID: + reg = R_I2SP3CTL; + break; + default: + ret = -EINVAL; + dev_err(component->dev, + "DAI %d unknown (%d)\n", dai_id + 1, ret); + return ret; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + val = FV_FORMAT_RIGHT; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = FV_FORMAT_LEFT; + break; + case SND_SOC_DAIFMT_I2S: + val = FV_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + ret = set_aif_tdm_delay(component, dai_id, true); + if (ret < 0) + return ret; + val = FV_FORMAT_TDM; + break; + case SND_SOC_DAIFMT_DSP_B: + ret = set_aif_tdm_delay(component, dai_id, false); + if (ret < 0) + return ret; + val = FV_FORMAT_TDM; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Format unsupported (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, + reg, FM_I2SPCTL_FORMAT, val); + if (ret < 0) { + dev_err(component->dev, "Failed to set DAI %d format (%d)\n", + dai_id + 1, ret); + return ret; + } + + return 0; +} + +static inline int +set_aif_clock_format_from_fmt(struct snd_soc_component *component, + unsigned int dai_id, unsigned int fmt) +{ + unsigned int reg; + unsigned int val; + int ret; + + switch (dai_id) { + case TSCS454_DAI1_ID: + reg = R_I2SP1CTL; + break; + case TSCS454_DAI2_ID: + reg = R_I2SP2CTL; + break; + case TSCS454_DAI3_ID: + reg = R_I2SP3CTL; + break; + default: + ret = -EINVAL; + dev_err(component->dev, + "DAI %d unknown (%d)\n", dai_id + 1, ret); + return ret; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val = FV_BCLKP_NOT_INVERTED | FV_LRCLKP_NOT_INVERTED; + break; + case SND_SOC_DAIFMT_NB_IF: + val = FV_BCLKP_NOT_INVERTED | FV_LRCLKP_INVERTED; + break; + case SND_SOC_DAIFMT_IB_NF: + val = FV_BCLKP_INVERTED | FV_LRCLKP_NOT_INVERTED; + break; + case SND_SOC_DAIFMT_IB_IF: + val = FV_BCLKP_INVERTED | FV_LRCLKP_INVERTED; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Format unknown (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, reg, + FM_I2SPCTL_BCLKP | FM_I2SPCTL_LRCLKP, val); + if (ret < 0) { + dev_err(component->dev, + "Failed to set clock polarity for DAI%d (%d)\n", + dai_id + 1, ret); + return ret; + } + + return 0; +} + +static int tscs454_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct aif *aif = &tscs454->aifs[dai->id]; + int ret; + + ret = set_aif_master_from_fmt(component, aif, fmt); + if (ret < 0) + return ret; + + ret = set_aif_format_from_fmt(component, dai->id, fmt); + if (ret < 0) + return ret; + + ret = set_aif_clock_format_from_fmt(component, dai->id, fmt); + if (ret < 0) + return ret; + + return 0; +} + +static int tscs454_dai1_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; + unsigned int val; + int ret; + + if (!slots) + return 0; + + if (tx_mask >= (1 << slots) || rx_mask >= (1 << slots)) { + ret = -EINVAL; + dev_err(component->dev, "Invalid TDM slot mask (%d)\n", ret); + return ret; + } + + switch (slots) { + case 2: + val = FV_TDMSO_2 | FV_TDMSI_2; + break; + case 4: + val = FV_TDMSO_4 | FV_TDMSI_4; + break; + case 6: + val = FV_TDMSO_6 | FV_TDMSI_6; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Invalid number of slots (%d)\n", ret); + return ret; + } + + switch (slot_width) { + case 16: + val = val | FV_TDMDSS_16; + break; + case 24: + val = val | FV_TDMDSS_24; + break; + case 32: + val = val | FV_TDMDSS_32; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Invalid TDM slot width (%d)\n", ret); + return ret; + } + ret = snd_soc_component_write(component, R_TDMCTL1, val); + if (ret < 0) { + dev_err(component->dev, "Failed to set slots (%d)\n", ret); + return ret; + } + + return 0; +} + +static int tscs454_dai23_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; + unsigned int reg; + unsigned int val; + int ret; + + if (!slots) + return 0; + + if (tx_mask >= (1 << slots) || rx_mask >= (1 << slots)) { + ret = -EINVAL; + dev_err(component->dev, "Invalid TDM slot mask (%d)\n", ret); + return ret; + } + + switch (dai->id) { + case TSCS454_DAI2_ID: + reg = R_PCMP2CTL1; + break; + case TSCS454_DAI3_ID: + reg = R_PCMP3CTL1; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unrecognized interface %d (%d)\n", + dai->id, ret); + return ret; + } + + switch (slots) { + case 1: + val = FV_PCMSOP_1 | FV_PCMSIP_1; + break; + case 2: + val = FV_PCMSOP_2 | FV_PCMSIP_2; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Invalid number of slots (%d)\n", ret); + return ret; + } + + switch (slot_width) { + case 16: + val = val | FV_PCMDSSP_16; + break; + case 24: + val = val | FV_PCMDSSP_24; + break; + case 32: + val = val | FV_PCMDSSP_32; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Invalid TDM slot width (%d)\n", ret); + return ret; + } + ret = snd_soc_component_write(component, reg, val); + if (ret < 0) { + dev_err(component->dev, "Failed to set slots (%d)\n", ret); + return ret; + } + + return 0; +} + +static int set_aif_fs(struct snd_soc_component *component, + unsigned int id, + unsigned int rate) +{ + unsigned int reg; + unsigned int br; + unsigned int bm; + int ret; + + switch (rate) { + case 8000: + br = FV_I2SMBR_32; + bm = FV_I2SMBM_0PT25; + break; + case 16000: + br = FV_I2SMBR_32; + bm = FV_I2SMBM_0PT5; + break; + case 24000: + br = FV_I2SMBR_48; + bm = FV_I2SMBM_0PT5; + break; + case 32000: + br = FV_I2SMBR_32; + bm = FV_I2SMBM_1; + break; + case 48000: + br = FV_I2SMBR_48; + bm = FV_I2SMBM_1; + break; + case 96000: + br = FV_I2SMBR_48; + bm = FV_I2SMBM_2; + break; + case 11025: + br = FV_I2SMBR_44PT1; + bm = FV_I2SMBM_0PT25; + break; + case 22050: + br = FV_I2SMBR_44PT1; + bm = FV_I2SMBM_0PT5; + break; + case 44100: + br = FV_I2SMBR_44PT1; + bm = FV_I2SMBM_1; + break; + case 88200: + br = FV_I2SMBR_44PT1; + bm = FV_I2SMBM_2; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unsupported sample rate (%d)\n", ret); + return ret; + } + + switch (id) { + case TSCS454_DAI1_ID: + reg = R_I2S1MRATE; + break; + case TSCS454_DAI2_ID: + reg = R_I2S2MRATE; + break; + case TSCS454_DAI3_ID: + reg = R_I2S3MRATE; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "DAI ID not recognized (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, reg, + FM_I2SMRATE_I2SMBR | FM_I2SMRATE_I2SMBM, br|bm); + if (ret < 0) { + dev_err(component->dev, + "Failed to update register (%d)\n", ret); + return ret; + } + + return 0; +} + +static int set_aif_sample_format(struct snd_soc_component *component, + snd_pcm_format_t format, + int aif_id) +{ + unsigned int reg; + unsigned int width; + int ret; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + width = FV_WL_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + width = FV_WL_20; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + width = FV_WL_24; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + width = FV_WL_32; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unsupported format width (%d)\n", ret); + return ret; + } + + switch (aif_id) { + case TSCS454_DAI1_ID: + reg = R_I2SP1CTL; + break; + case TSCS454_DAI2_ID: + reg = R_I2SP2CTL; + break; + case TSCS454_DAI3_ID: + reg = R_I2SP3CTL; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "AIF ID not recognized (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, + reg, FM_I2SPCTL_WL, width); + if (ret < 0) { + dev_err(component->dev, + "Failed to set sample width (%d)\n", ret); + return ret; + } + + return 0; +} + +static int tscs454_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 tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + unsigned int fs = params_rate(params); + struct aif *aif = &tscs454->aifs[dai->id]; + unsigned int val; + int ret; + + mutex_lock(&tscs454->aifs_status_lock); + + dev_dbg(component->dev, "%s(): aif %d fs = %u\n", __func__, + aif->id, fs); + + if (!aif_active(&tscs454->aifs_status, aif->id)) { + if (PLL_44_1K_RATE % fs) + aif->pll = &tscs454->pll1; + else + aif->pll = &tscs454->pll2; + + dev_dbg(component->dev, "Reserving pll %d for aif %d\n", + aif->pll->id, aif->id); + + reserve_pll(aif->pll); + } + + if (!aifs_active(&tscs454->aifs_status)) { /* First active aif */ + ret = snd_soc_component_read(component, R_ISRC, &val); + if (ret < 0) + goto exit; + + if ((val & FM_ISRC_IBR) == FV_IBR_48) + tscs454->internal_rate.pll = &tscs454->pll1; + else + tscs454->internal_rate.pll = &tscs454->pll2; + + dev_dbg(component->dev, "Reserving pll %d for ir\n", + tscs454->internal_rate.pll->id); + + reserve_pll(tscs454->internal_rate.pll); + } + + ret = set_aif_fs(component, aif->id, fs); + if (ret < 0) { + dev_err(component->dev, "Failed to set aif fs (%d)\n", ret); + goto exit; + } + + ret = set_aif_sample_format(component, params_format(params), aif->id); + if (ret < 0) { + dev_err(component->dev, + "Failed to set aif sample format (%d)\n", ret); + goto exit; + } + + set_aif_status_active(&tscs454->aifs_status, aif->id, + substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + + dev_dbg(component->dev, "Set aif %d active. Streams status is 0x%x\n", + aif->id, tscs454->aifs_status.streams); + + ret = 0; +exit: + mutex_unlock(&tscs454->aifs_status_lock); + + return ret; +} + +static int tscs454_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct aif *aif = &tscs454->aifs[dai->id]; + + return aif_free(component, aif, + substream->stream == SNDRV_PCM_STREAM_PLAYBACK); +} + +static int tscs454_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + struct snd_soc_component *component = dai->component; + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct aif *aif = &tscs454->aifs[dai->id]; + + ret = aif_prepare(component, aif); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_dai_ops const tscs454_dai1_ops = { + .set_sysclk = tscs454_set_sysclk, + .set_bclk_ratio = tscs454_set_bclk_ratio, + .set_fmt = tscs454_set_dai_fmt, + .set_tdm_slot = tscs454_dai1_set_tdm_slot, + .hw_params = tscs454_hw_params, + .hw_free = tscs454_hw_free, + .prepare = tscs454_prepare, +}; + +static struct snd_soc_dai_ops const tscs454_dai23_ops = { + .set_sysclk = tscs454_set_sysclk, + .set_bclk_ratio = tscs454_set_bclk_ratio, + .set_fmt = tscs454_set_dai_fmt, + .set_tdm_slot = tscs454_dai23_set_tdm_slot, + .hw_params = tscs454_hw_params, + .hw_free = tscs454_hw_free, + .prepare = tscs454_prepare, +}; + +static int tscs454_probe(struct snd_soc_component *component) +{ + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + unsigned int val; + int ret = 0; + + switch (tscs454->sysclk_src_id) { + case PLL_INPUT_XTAL: + val = FV_PLLISEL_XTAL; + break; + case PLL_INPUT_MCLK1: + val = FV_PLLISEL_MCLK1; + break; + case PLL_INPUT_MCLK2: + val = FV_PLLISEL_MCLK2; + break; + case PLL_INPUT_BCLK: + val = FV_PLLISEL_BCLK; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Invalid sysclk src id (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, R_PLLCTL, + FM_PLLCTL_PLLISEL, val); + if (ret < 0) { + dev_err(component->dev, "Failed to set PLL input (%d)\n", ret); + return ret; + } + + if (tscs454->sysclk_src_id < PLL_INPUT_BCLK) + ret = set_sysclk(component); + + return ret; +} + +static const struct snd_soc_component_driver soc_component_dev_tscs454 = { + .probe = tscs454_probe, + .dapm_widgets = tscs454_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tscs454_dapm_widgets), + .dapm_routes = tscs454_intercon, + .num_dapm_routes = ARRAY_SIZE(tscs454_intercon), + .controls = tscs454_snd_controls, + .num_controls = ARRAY_SIZE(tscs454_snd_controls), +}; + +#define TSCS454_RATES SNDRV_PCM_RATE_8000_96000 + +#define TSCS454_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver tscs454_dais[] = { + { + .name = "tscs454-dai1", + .id = TSCS454_DAI1_ID, + .playback = { + .stream_name = "DAI 1 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .capture = { + .stream_name = "DAI 1 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .ops = &tscs454_dai1_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, + }, + { + .name = "tscs454-dai2", + .id = TSCS454_DAI2_ID, + .playback = { + .stream_name = "DAI 2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .capture = { + .stream_name = "DAI 2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .ops = &tscs454_dai23_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, + }, + { + .name = "tscs454-dai3", + .id = TSCS454_DAI3_ID, + .playback = { + .stream_name = "DAI 3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .capture = { + .stream_name = "DAI 3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .ops = &tscs454_dai23_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, + }, +}; + +static char const * const src_names[] = { + "xtal", "mclk1", "mclk2", "bclk"}; + +static int tscs454_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tscs454 *tscs454; + int src; + int ret; + + tscs454 = devm_kzalloc(&i2c->dev, sizeof(*tscs454), GFP_KERNEL); + if (!tscs454) + return -ENOMEM; + + ret = tscs454_data_init(tscs454, i2c); + if (ret < 0) + return ret; + + i2c_set_clientdata(i2c, tscs454); + + for (src = PLL_INPUT_XTAL; src < PLL_INPUT_BCLK; src++) { + tscs454->sysclk = devm_clk_get(&i2c->dev, src_names[src]); + if (!IS_ERR(tscs454->sysclk)) { + break; + } else if (PTR_ERR(tscs454->sysclk) != -ENOENT) { + ret = PTR_ERR(tscs454->sysclk); + dev_err(&i2c->dev, "Failed to get sysclk (%d)\n", ret); + return ret; + } + } + dev_dbg(&i2c->dev, "PLL input is %s\n", src_names[src]); + tscs454->sysclk_src_id = src; + + ret = regmap_write(tscs454->regmap, + R_RESET, FV_RESET_PWR_ON_DEFAULTS); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to reset the component (%d)\n", ret); + return ret; + } + regcache_mark_dirty(tscs454->regmap); + + ret = regmap_register_patch(tscs454->regmap, tscs454_patch, + ARRAY_SIZE(tscs454_patch)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to apply patch (%d)\n", ret); + return ret; + } + /* Sync pg sel reg with cache */ + regmap_write(tscs454->regmap, R_PAGESEL, 0x00); + + ret = snd_soc_register_component(&i2c->dev, &soc_component_dev_tscs454, + tscs454_dais, ARRAY_SIZE(tscs454_dais)); + if (ret) { + dev_err(&i2c->dev, "Failed to register component (%d)\n", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id tscs454_i2c_id[] = { + { "tscs454", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tscs454_i2c_id); + +static const struct of_device_id tscs454_of_match[] = { + { .compatible = "tempo,tscs454", }, + { } +}; +MODULE_DEVICE_TABLE(of, tscs454_of_match); + +static struct i2c_driver tscs454_i2c_driver = { + .driver = { + .name = "tscs454", + .of_match_table = tscs454_of_match, + }, + .probe = tscs454_i2c_probe, + .id_table = tscs454_i2c_id, +}; + +module_i2c_driver(tscs454_i2c_driver); + +MODULE_AUTHOR("Tempo Semiconductor <steven.eckhoff.opensource@gmail.com"); +MODULE_DESCRIPTION("ASoC TSCS454 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tscs454.h b/sound/soc/codecs/tscs454.h new file mode 100644 index 000000000000..1142d73d3168 --- /dev/null +++ b/sound/soc/codecs/tscs454.h @@ -0,0 +1,2323 @@ +// SPDX-License-Identifier: GPL-2.0 +// tscs454.h -- TSCS454 ALSA SoC Audio driver +// Copyright 2018 Tempo Semiconductor, Inc. +// Author: Steven Eckhoff <steven.eckhoff.opensource@gmail.com> + +#ifndef __REDWOODPUBLIC_H__ +#define __REDWOODPUBLIC_H__ + +#define VIRT_BASE 0x00 +#define PAGE_LEN 0x100 +#define VIRT_PAGE_BASE(page) (VIRT_BASE + (PAGE_LEN * page)) +#define VIRT_ADDR(page, address) (VIRT_PAGE_BASE(page) + address) +#define ADDR(page, virt_address) (virt_address - VIRT_PAGE_BASE(page)) + +#define R_PAGESEL 0x0 +#define R_RESET VIRT_ADDR(0x0, 0x1) +#define R_IRQEN VIRT_ADDR(0x0, 0x2) +#define R_IRQMASK VIRT_ADDR(0x0, 0x3) +#define R_IRQSTAT VIRT_ADDR(0x0, 0x4) +#define R_DEVADD0 VIRT_ADDR(0x0, 0x6) +#define R_DEVID VIRT_ADDR(0x0, 0x8) +#define R_DEVREV VIRT_ADDR(0x0, 0x9) +#define R_PLLSTAT VIRT_ADDR(0x0, 0x0A) +#define R_PLL1CTL VIRT_ADDR(0x0, 0x0B) +#define R_PLL1RDIV VIRT_ADDR(0x0, 0x0C) +#define R_PLL1ODIV VIRT_ADDR(0x0, 0x0D) +#define R_PLL1FDIVL VIRT_ADDR(0x0, 0x0E) +#define R_PLL1FDIVH VIRT_ADDR(0x0, 0x0F) +#define R_PLL2CTL VIRT_ADDR(0x0, 0x10) +#define R_PLL2RDIV VIRT_ADDR(0x0, 0x11) +#define R_PLL2ODIV VIRT_ADDR(0x0, 0x12) +#define R_PLL2FDIVL VIRT_ADDR(0x0, 0x13) +#define R_PLL2FDIVH VIRT_ADDR(0x0, 0x14) +#define R_PLLCTL VIRT_ADDR(0x0, 0x15) +#define R_ISRC VIRT_ADDR(0x0, 0x16) +#define R_SCLKCTL VIRT_ADDR(0x0, 0x18) +#define R_TIMEBASE VIRT_ADDR(0x0, 0x19) +#define R_I2SP1CTL VIRT_ADDR(0x0, 0x1A) +#define R_I2SP2CTL VIRT_ADDR(0x0, 0x1B) +#define R_I2SP3CTL VIRT_ADDR(0x0, 0x1C) +#define R_I2S1MRATE VIRT_ADDR(0x0, 0x1D) +#define R_I2S2MRATE VIRT_ADDR(0x0, 0x1E) +#define R_I2S3MRATE VIRT_ADDR(0x0, 0x1F) +#define R_I2SCMC VIRT_ADDR(0x0, 0x20) +#define R_MCLK2PINC VIRT_ADDR(0x0, 0x21) +#define R_I2SPINC0 VIRT_ADDR(0x0, 0x22) +#define R_I2SPINC1 VIRT_ADDR(0x0, 0x23) +#define R_I2SPINC2 VIRT_ADDR(0x0, 0x24) +#define R_GPIOCTL0 VIRT_ADDR(0x0, 0x25) +#define R_GPIOCTL1 VIRT_ADDR(0x0, 0x26) +#define R_ASRC VIRT_ADDR(0x0, 0x28) +#define R_TDMCTL0 VIRT_ADDR(0x0, 0x2D) +#define R_TDMCTL1 VIRT_ADDR(0x0, 0x2E) +#define R_PCMP2CTL0 VIRT_ADDR(0x0, 0x2F) +#define R_PCMP2CTL1 VIRT_ADDR(0x0, 0x30) +#define R_PCMP3CTL0 VIRT_ADDR(0x0, 0x31) +#define R_PCMP3CTL1 VIRT_ADDR(0x0, 0x32) +#define R_PWRM0 VIRT_ADDR(0x0, 0x33) +#define R_PWRM1 VIRT_ADDR(0x0, 0x34) +#define R_PWRM2 VIRT_ADDR(0x0, 0x35) +#define R_PWRM3 VIRT_ADDR(0x0, 0x36) +#define R_PWRM4 VIRT_ADDR(0x0, 0x37) +#define R_I2SIDCTL VIRT_ADDR(0x0, 0x38) +#define R_I2SODCTL VIRT_ADDR(0x0, 0x39) +#define R_AUDIOMUX1 VIRT_ADDR(0x0, 0x3A) +#define R_AUDIOMUX2 VIRT_ADDR(0x0, 0x3B) +#define R_AUDIOMUX3 VIRT_ADDR(0x0, 0x3C) +#define R_HSDCTL1 VIRT_ADDR(0x1, 0x1) +#define R_HSDCTL2 VIRT_ADDR(0x1, 0x2) +#define R_HSDSTAT VIRT_ADDR(0x1, 0x3) +#define R_HSDDELAY VIRT_ADDR(0x1, 0x4) +#define R_BUTCTL VIRT_ADDR(0x1, 0x5) +#define R_CH0AIC VIRT_ADDR(0x1, 0x6) +#define R_CH1AIC VIRT_ADDR(0x1, 0x7) +#define R_CH2AIC VIRT_ADDR(0x1, 0x8) +#define R_CH3AIC VIRT_ADDR(0x1, 0x9) +#define R_ICTL0 VIRT_ADDR(0x1, 0x0A) +#define R_ICTL1 VIRT_ADDR(0x1, 0x0B) +#define R_MICBIAS VIRT_ADDR(0x1, 0x0C) +#define R_PGACTL0 VIRT_ADDR(0x1, 0x0D) +#define R_PGACTL1 VIRT_ADDR(0x1, 0x0E) +#define R_PGACTL2 VIRT_ADDR(0x1, 0x0F) +#define R_PGACTL3 VIRT_ADDR(0x1, 0x10) +#define R_PGAZ VIRT_ADDR(0x1, 0x11) +#define R_ICH0VOL VIRT_ADDR(0x1, 0x12) +#define R_ICH1VOL VIRT_ADDR(0x1, 0x13) +#define R_ICH2VOL VIRT_ADDR(0x1, 0x14) +#define R_ICH3VOL VIRT_ADDR(0x1, 0x15) +#define R_ASRCILVOL VIRT_ADDR(0x1, 0x16) +#define R_ASRCIRVOL VIRT_ADDR(0x1, 0x17) +#define R_ASRCOLVOL VIRT_ADDR(0x1, 0x18) +#define R_ASRCORVOL VIRT_ADDR(0x1, 0x19) +#define R_IVOLCTLU VIRT_ADDR(0x1, 0x1C) +#define R_ALCCTL0 VIRT_ADDR(0x1, 0x1D) +#define R_ALCCTL1 VIRT_ADDR(0x1, 0x1E) +#define R_ALCCTL2 VIRT_ADDR(0x1, 0x1F) +#define R_ALCCTL3 VIRT_ADDR(0x1, 0x20) +#define R_NGATE VIRT_ADDR(0x1, 0x21) +#define R_DMICCTL VIRT_ADDR(0x1, 0x22) +#define R_DACCTL VIRT_ADDR(0x2, 0x1) +#define R_SPKCTL VIRT_ADDR(0x2, 0x2) +#define R_SUBCTL VIRT_ADDR(0x2, 0x3) +#define R_DCCTL VIRT_ADDR(0x2, 0x4) +#define R_OVOLCTLU VIRT_ADDR(0x2, 0x6) +#define R_MUTEC VIRT_ADDR(0x2, 0x7) +#define R_MVOLL VIRT_ADDR(0x2, 0x8) +#define R_MVOLR VIRT_ADDR(0x2, 0x9) +#define R_HPVOLL VIRT_ADDR(0x2, 0x0A) +#define R_HPVOLR VIRT_ADDR(0x2, 0x0B) +#define R_SPKVOLL VIRT_ADDR(0x2, 0x0C) +#define R_SPKVOLR VIRT_ADDR(0x2, 0x0D) +#define R_SUBVOL VIRT_ADDR(0x2, 0x10) +#define R_COP0 VIRT_ADDR(0x2, 0x11) +#define R_COP1 VIRT_ADDR(0x2, 0x12) +#define R_COPSTAT VIRT_ADDR(0x2, 0x13) +#define R_PWM0 VIRT_ADDR(0x2, 0x14) +#define R_PWM1 VIRT_ADDR(0x2, 0x15) +#define R_PWM2 VIRT_ADDR(0x2, 0x16) +#define R_PWM3 VIRT_ADDR(0x2, 0x17) +#define R_HPSW VIRT_ADDR(0x2, 0x18) +#define R_THERMTS VIRT_ADDR(0x2, 0x19) +#define R_THERMSPK1 VIRT_ADDR(0x2, 0x1A) +#define R_THERMSTAT VIRT_ADDR(0x2, 0x1B) +#define R_SCSTAT VIRT_ADDR(0x2, 0x1C) +#define R_SDMON VIRT_ADDR(0x2, 0x1D) +#define R_SPKEQFILT VIRT_ADDR(0x3, 0x1) +#define R_SPKCRWDL VIRT_ADDR(0x3, 0x2) +#define R_SPKCRWDM VIRT_ADDR(0x3, 0x3) +#define R_SPKCRWDH VIRT_ADDR(0x3, 0x4) +#define R_SPKCRRDL VIRT_ADDR(0x3, 0x5) +#define R_SPKCRRDM VIRT_ADDR(0x3, 0x6) +#define R_SPKCRRDH VIRT_ADDR(0x3, 0x7) +#define R_SPKCRADD VIRT_ADDR(0x3, 0x8) +#define R_SPKCRS VIRT_ADDR(0x3, 0x9) +#define R_SPKMBCEN VIRT_ADDR(0x3, 0x0A) +#define R_SPKMBCCTL VIRT_ADDR(0x3, 0x0B) +#define R_SPKMBCMUG1 VIRT_ADDR(0x3, 0x0C) +#define R_SPKMBCTHR1 VIRT_ADDR(0x3, 0x0D) +#define R_SPKMBCRAT1 VIRT_ADDR(0x3, 0x0E) +#define R_SPKMBCATK1L VIRT_ADDR(0x3, 0x0F) +#define R_SPKMBCATK1H VIRT_ADDR(0x3, 0x10) +#define R_SPKMBCREL1L VIRT_ADDR(0x3, 0x11) +#define R_SPKMBCREL1H VIRT_ADDR(0x3, 0x12) +#define R_SPKMBCMUG2 VIRT_ADDR(0x3, 0x13) +#define R_SPKMBCTHR2 VIRT_ADDR(0x3, 0x14) +#define R_SPKMBCRAT2 VIRT_ADDR(0x3, 0x15) +#define R_SPKMBCATK2L VIRT_ADDR(0x3, 0x16) +#define R_SPKMBCATK2H VIRT_ADDR(0x3, 0x17) +#define R_SPKMBCREL2L VIRT_ADDR(0x3, 0x18) +#define R_SPKMBCREL2H VIRT_ADDR(0x3, 0x19) +#define R_SPKMBCMUG3 VIRT_ADDR(0x3, 0x1A) +#define R_SPKMBCTHR3 VIRT_ADDR(0x3, 0x1B) +#define R_SPKMBCRAT3 VIRT_ADDR(0x3, 0x1C) +#define R_SPKMBCATK3L VIRT_ADDR(0x3, 0x1D) +#define R_SPKMBCATK3H VIRT_ADDR(0x3, 0x1E) +#define R_SPKMBCREL3L VIRT_ADDR(0x3, 0x1F) +#define R_SPKMBCREL3H VIRT_ADDR(0x3, 0x20) +#define R_SPKCLECTL VIRT_ADDR(0x3, 0x21) +#define R_SPKCLEMUG VIRT_ADDR(0x3, 0x22) +#define R_SPKCOMPTHR VIRT_ADDR(0x3, 0x23) +#define R_SPKCOMPRAT VIRT_ADDR(0x3, 0x24) +#define R_SPKCOMPATKL VIRT_ADDR(0x3, 0x25) +#define R_SPKCOMPATKH VIRT_ADDR(0x3, 0x26) +#define R_SPKCOMPRELL VIRT_ADDR(0x3, 0x27) +#define R_SPKCOMPRELH VIRT_ADDR(0x3, 0x28) +#define R_SPKLIMTHR VIRT_ADDR(0x3, 0x29) +#define R_SPKLIMTGT VIRT_ADDR(0x3, 0x2A) +#define R_SPKLIMATKL VIRT_ADDR(0x3, 0x2B) +#define R_SPKLIMATKH VIRT_ADDR(0x3, 0x2C) +#define R_SPKLIMRELL VIRT_ADDR(0x3, 0x2D) +#define R_SPKLIMRELH VIRT_ADDR(0x3, 0x2E) +#define R_SPKEXPTHR VIRT_ADDR(0x3, 0x2F) +#define R_SPKEXPRAT VIRT_ADDR(0x3, 0x30) +#define R_SPKEXPATKL VIRT_ADDR(0x3, 0x31) +#define R_SPKEXPATKH VIRT_ADDR(0x3, 0x32) +#define R_SPKEXPRELL VIRT_ADDR(0x3, 0x33) +#define R_SPKEXPRELH VIRT_ADDR(0x3, 0x34) +#define R_SPKFXCTL VIRT_ADDR(0x3, 0x35) +#define R_DACEQFILT VIRT_ADDR(0x4, 0x1) +#define R_DACCRWDL VIRT_ADDR(0x4, 0x2) +#define R_DACCRWDM VIRT_ADDR(0x4, 0x3) +#define R_DACCRWDH VIRT_ADDR(0x4, 0x4) +#define R_DACCRRDL VIRT_ADDR(0x4, 0x5) +#define R_DACCRRDM VIRT_ADDR(0x4, 0x6) +#define R_DACCRRDH VIRT_ADDR(0x4, 0x7) +#define R_DACCRADD VIRT_ADDR(0x4, 0x8) +#define R_DACCRS VIRT_ADDR(0x4, 0x9) +#define R_DACMBCEN VIRT_ADDR(0x4, 0x0A) +#define R_DACMBCCTL VIRT_ADDR(0x4, 0x0B) +#define R_DACMBCMUG1 VIRT_ADDR(0x4, 0x0C) +#define R_DACMBCTHR1 VIRT_ADDR(0x4, 0x0D) +#define R_DACMBCRAT1 VIRT_ADDR(0x4, 0x0E) +#define R_DACMBCATK1L VIRT_ADDR(0x4, 0x0F) +#define R_DACMBCATK1H VIRT_ADDR(0x4, 0x10) +#define R_DACMBCREL1L VIRT_ADDR(0x4, 0x11) +#define R_DACMBCREL1H VIRT_ADDR(0x4, 0x12) +#define R_DACMBCMUG2 VIRT_ADDR(0x4, 0x13) +#define R_DACMBCTHR2 VIRT_ADDR(0x4, 0x14) +#define R_DACMBCRAT2 VIRT_ADDR(0x4, 0x15) +#define R_DACMBCATK2L VIRT_ADDR(0x4, 0x16) +#define R_DACMBCATK2H VIRT_ADDR(0x4, 0x17) +#define R_DACMBCREL2L VIRT_ADDR(0x4, 0x18) +#define R_DACMBCREL2H VIRT_ADDR(0x4, 0x19) +#define R_DACMBCMUG3 VIRT_ADDR(0x4, 0x1A) +#define R_DACMBCTHR3 VIRT_ADDR(0x4, 0x1B) +#define R_DACMBCRAT3 VIRT_ADDR(0x4, 0x1C) +#define R_DACMBCATK3L VIRT_ADDR(0x4, 0x1D) +#define R_DACMBCATK3H VIRT_ADDR(0x4, 0x1E) +#define R_DACMBCREL3L VIRT_ADDR(0x4, 0x1F) +#define R_DACMBCREL3H VIRT_ADDR(0x4, 0x20) +#define R_DACCLECTL VIRT_ADDR(0x4, 0x21) +#define R_DACCLEMUG VIRT_ADDR(0x4, 0x22) +#define R_DACCOMPTHR VIRT_ADDR(0x4, 0x23) +#define R_DACCOMPRAT VIRT_ADDR(0x4, 0x24) +#define R_DACCOMPATKL VIRT_ADDR(0x4, 0x25) +#define R_DACCOMPATKH VIRT_ADDR(0x4, 0x26) +#define R_DACCOMPRELL VIRT_ADDR(0x4, 0x27) +#define R_DACCOMPRELH VIRT_ADDR(0x4, 0x28) +#define R_DACLIMTHR VIRT_ADDR(0x4, 0x29) +#define R_DACLIMTGT VIRT_ADDR(0x4, 0x2A) +#define R_DACLIMATKL VIRT_ADDR(0x4, 0x2B) +#define R_DACLIMATKH VIRT_ADDR(0x4, 0x2C) +#define R_DACLIMRELL VIRT_ADDR(0x4, 0x2D) +#define R_DACLIMRELH VIRT_ADDR(0x4, 0x2E) +#define R_DACEXPTHR VIRT_ADDR(0x4, 0x2F) +#define R_DACEXPRAT VIRT_ADDR(0x4, 0x30) +#define R_DACEXPATKL VIRT_ADDR(0x4, 0x31) +#define R_DACEXPATKH VIRT_ADDR(0x4, 0x32) +#define R_DACEXPRELL VIRT_ADDR(0x4, 0x33) +#define R_DACEXPRELH VIRT_ADDR(0x4, 0x34) +#define R_DACFXCTL VIRT_ADDR(0x4, 0x35) +#define R_SUBEQFILT VIRT_ADDR(0x5, 0x1) +#define R_SUBCRWDL VIRT_ADDR(0x5, 0x2) +#define R_SUBCRWDM VIRT_ADDR(0x5, 0x3) +#define R_SUBCRWDH VIRT_ADDR(0x5, 0x4) +#define R_SUBCRRDL VIRT_ADDR(0x5, 0x5) +#define R_SUBCRRDM VIRT_ADDR(0x5, 0x6) +#define R_SUBCRRDH VIRT_ADDR(0x5, 0x7) +#define R_SUBCRADD VIRT_ADDR(0x5, 0x8) +#define R_SUBCRS VIRT_ADDR(0x5, 0x9) +#define R_SUBMBCEN VIRT_ADDR(0x5, 0x0A) +#define R_SUBMBCCTL VIRT_ADDR(0x5, 0x0B) +#define R_SUBMBCMUG1 VIRT_ADDR(0x5, 0x0C) +#define R_SUBMBCTHR1 VIRT_ADDR(0x5, 0x0D) +#define R_SUBMBCRAT1 VIRT_ADDR(0x5, 0x0E) +#define R_SUBMBCATK1L VIRT_ADDR(0x5, 0x0F) +#define R_SUBMBCATK1H VIRT_ADDR(0x5, 0x10) +#define R_SUBMBCREL1L VIRT_ADDR(0x5, 0x11) +#define R_SUBMBCREL1H VIRT_ADDR(0x5, 0x12) +#define R_SUBMBCMUG2 VIRT_ADDR(0x5, 0x13) +#define R_SUBMBCTHR2 VIRT_ADDR(0x5, 0x14) +#define R_SUBMBCRAT2 VIRT_ADDR(0x5, 0x15) +#define R_SUBMBCATK2L VIRT_ADDR(0x5, 0x16) +#define R_SUBMBCATK2H VIRT_ADDR(0x5, 0x17) +#define R_SUBMBCREL2L VIRT_ADDR(0x5, 0x18) +#define R_SUBMBCREL2H VIRT_ADDR(0x5, 0x19) +#define R_SUBMBCMUG3 VIRT_ADDR(0x5, 0x1A) +#define R_SUBMBCTHR3 VIRT_ADDR(0x5, 0x1B) +#define R_SUBMBCRAT3 VIRT_ADDR(0x5, 0x1C) +#define R_SUBMBCATK3L VIRT_ADDR(0x5, 0x1D) +#define R_SUBMBCATK3H VIRT_ADDR(0x5, 0x1E) +#define R_SUBMBCREL3L VIRT_ADDR(0x5, 0x1F) +#define R_SUBMBCREL3H VIRT_ADDR(0x5, 0x20) +#define R_SUBCLECTL VIRT_ADDR(0x5, 0x21) +#define R_SUBCLEMUG VIRT_ADDR(0x5, 0x22) +#define R_SUBCOMPTHR VIRT_ADDR(0x5, 0x23) +#define R_SUBCOMPRAT VIRT_ADDR(0x5, 0x24) +#define R_SUBCOMPATKL VIRT_ADDR(0x5, 0x25) +#define R_SUBCOMPATKH VIRT_ADDR(0x5, 0x26) +#define R_SUBCOMPRELL VIRT_ADDR(0x5, 0x27) +#define R_SUBCOMPRELH VIRT_ADDR(0x5, 0x28) +#define R_SUBLIMTHR VIRT_ADDR(0x5, 0x29) +#define R_SUBLIMTGT VIRT_ADDR(0x5, 0x2A) +#define R_SUBLIMATKL VIRT_ADDR(0x5, 0x2B) +#define R_SUBLIMATKH VIRT_ADDR(0x5, 0x2C) +#define R_SUBLIMRELL VIRT_ADDR(0x5, 0x2D) +#define R_SUBLIMRELH VIRT_ADDR(0x5, 0x2E) +#define R_SUBEXPTHR VIRT_ADDR(0x5, 0x2F) +#define R_SUBEXPRAT VIRT_ADDR(0x5, 0x30) +#define R_SUBEXPATKL VIRT_ADDR(0x5, 0x31) +#define R_SUBEXPATKH VIRT_ADDR(0x5, 0x32) +#define R_SUBEXPRELL VIRT_ADDR(0x5, 0x33) +#define R_SUBEXPRELH VIRT_ADDR(0x5, 0x34) +#define R_SUBFXCTL VIRT_ADDR(0x5, 0x35) + +// *** PLLCTL *** +#define FB_PLLCTL_VCCI_PLL 6 +#define FM_PLLCTL_VCCI_PLL 0xC0 + +#define FB_PLLCTL_RZ_PLL 3 +#define FM_PLLCTL_RZ_PLL 0x38 + +#define FB_PLLCTL_CP_PLL 0 +#define FM_PLLCTL_CP_PLL 0x7 + +// *** PLLRDIV *** +#define FB_PLLRDIV_REFDIV_PLL 0 +#define FM_PLLRDIV_REFDIV_PLL 0xFF + +// *** PLLODIV *** +#define FB_PLLODIV_OUTDIV_PLL 0 +#define FM_PLLODIV_OUTDIV_PLL 0xFF + +// *** PLLFDIVL *** +#define FB_PLLFDIVL_FBDIVL_PLL 0 +#define FM_PLLFDIVL_FBDIVL_PLL 0xFF + +// *** PLLFDIVH *** +#define FB_PLLFDIVH_FBDIVH_PLL 0 +#define FM_PLLFDIVH_FBDIVH_PLL 0xF + +// *** I2SPCTL *** +#define FB_I2SPCTL_BCLKSTAT 7 +#define FM_I2SPCTL_BCLKSTAT 0x80 +#define FV_BCLKSTAT_LOST 0x80 +#define FV_BCLKSTAT_NOT_LOST 0x0 + +#define FB_I2SPCTL_BCLKP 6 +#define FM_I2SPCTL_BCLKP 0x40 +#define FV_BCLKP_NOT_INVERTED 0x0 +#define FV_BCLKP_INVERTED 0x40 + +#define FB_I2SPCTL_PORTMS 5 +#define FM_I2SPCTL_PORTMS 0x20 +#define FV_PORTMS_SLAVE 0x0 +#define FV_PORTMS_MASTER 0x20 + +#define FB_I2SPCTL_LRCLKP 4 +#define FM_I2SPCTL_LRCLKP 0x10 +#define FV_LRCLKP_NOT_INVERTED 0x0 +#define FV_LRCLKP_INVERTED 0x10 + +#define FB_I2SPCTL_WL 2 +#define FM_I2SPCTL_WL 0xC +#define FV_WL_16 0x0 +#define FV_WL_20 0x4 +#define FV_WL_24 0x8 +#define FV_WL_32 0xC + +#define FB_I2SPCTL_FORMAT 0 +#define FM_I2SPCTL_FORMAT 0x3 +#define FV_FORMAT_RIGHT 0x0 +#define FV_FORMAT_LEFT 0x1 +#define FV_FORMAT_I2S 0x2 +#define FV_FORMAT_TDM 0x3 + +// *** I2SMRATE *** +#define FB_I2SMRATE_I2SMCLKHALF 7 +#define FM_I2SMRATE_I2SMCLKHALF 0x80 +#define FV_I2SMCLKHALF_I2S1MCLKDIV_DIV_2 0x0 +#define FV_I2SMCLKHALF_I2S1MCLKDIV_ONLY 0x80 + +#define FB_I2SMRATE_I2SMCLKDIV 5 +#define FM_I2SMRATE_I2SMCLKDIV 0x60 +#define FV_I2SMCLKDIV_125 0x0 +#define FV_I2SMCLKDIV_128 0x20 +#define FV_I2SMCLKDIV_136 0x40 +#define FV_I2SMCLKDIV_192 0x60 + +#define FB_I2SMRATE_I2SMBR 3 +#define FM_I2SMRATE_I2SMBR 0x18 +#define FV_I2SMBR_32 0x0 +#define FV_I2SMBR_44PT1 0x8 +#define FV_I2SMBR_48 0x10 +#define FV_I2SMBR_MCLK_MODE 0x18 + +#define FB_I2SMRATE_I2SMBM 0 +#define FM_I2SMRATE_I2SMBM 0x3 +#define FV_I2SMBM_0PT25 0x0 +#define FV_I2SMBM_0PT5 0x1 +#define FV_I2SMBM_1 0x2 +#define FV_I2SMBM_2 0x3 + +// *** PCMPCTL0 *** +#define FB_PCMPCTL0_PCMFLENP 2 +#define FM_PCMPCTL0_PCMFLENP 0x4 +#define FV_PCMFLENP_128 0x0 +#define FV_PCMFLENP_256 0x4 + +#define FB_PCMPCTL0_SLSYNCP 1 +#define FM_PCMPCTL0_SLSYNCP 0x2 +#define FV_SLSYNCP_SHORT 0x0 +#define FV_SLSYNCP_LONG 0x2 + +#define FB_PCMPCTL0_BDELAYP 0 +#define FM_PCMPCTL0_BDELAYP 0x1 +#define FV_BDELAYP_NO_DELAY 0x0 +#define FV_BDELAYP_1BCLK_DELAY 0x1 + +// *** PCMPCTL1 *** +#define FB_PCMPCTL1_PCMMOMP 6 +#define FM_PCMPCTL1_PCMMOMP 0x40 + +#define FB_PCMPCTL1_PCMSOP 5 +#define FM_PCMPCTL1_PCMSOP 0x20 +#define FV_PCMSOP_1 0x0 +#define FV_PCMSOP_2 0x20 + +#define FB_PCMPCTL1_PCMDSSP 3 +#define FM_PCMPCTL1_PCMDSSP 0x18 +#define FV_PCMDSSP_16 0x0 +#define FV_PCMDSSP_24 0x8 +#define FV_PCMDSSP_32 0x10 + +#define FB_PCMPCTL1_PCMMIMP 1 +#define FM_PCMPCTL1_PCMMIMP 0x2 + +#define FB_PCMPCTL1_PCMSIP 0 +#define FM_PCMPCTL1_PCMSIP 0x1 +#define FV_PCMSIP_1 0x0 +#define FV_PCMSIP_2 0x1 + +// *** CHAIC *** +#define FB_CHAIC_MICBST 4 +#define FM_CHAIC_MICBST 0x30 + +// *** PGACTL *** +#define FB_PGACTL_PGAMUTE 7 +#define FM_PGACTL_PGAMUTE 0x80 + +#define FB_PGACTL_PGAVOL 0 +#define FM_PGACTL_PGAVOL 0x3F + +// *** ICHVOL *** +#define FB_ICHVOL_ICHVOL 0 +#define FM_ICHVOL_ICHVOL 0xFF + +// *** SPKMBCMUG *** +#define FB_SPKMBCMUG_PHASE 5 +#define FM_SPKMBCMUG_PHASE 0x20 + +#define FB_SPKMBCMUG_MUGAIN 0 +#define FM_SPKMBCMUG_MUGAIN 0x1F + +// *** SPKMBCTHR *** +#define FB_SPKMBCTHR_THRESH 0 +#define FM_SPKMBCTHR_THRESH 0xFF + +// *** SPKMBCRAT *** +#define FB_SPKMBCRAT_RATIO 0 +#define FM_SPKMBCRAT_RATIO 0x1F + +// *** SPKMBCATKL *** +#define FB_SPKMBCATKL_TCATKL 0 +#define FM_SPKMBCATKL_TCATKL 0xFF + +// *** SPKMBCATKH *** +#define FB_SPKMBCATKH_TCATKH 0 +#define FM_SPKMBCATKH_TCATKH 0xFF + +// *** SPKMBCRELL *** +#define FB_SPKMBCRELL_TCRELL 0 +#define FM_SPKMBCRELL_TCRELL 0xFF + +// *** SPKMBCRELH *** +#define FB_SPKMBCRELH_TCRELH 0 +#define FM_SPKMBCRELH_TCRELH 0xFF + +// *** DACMBCMUG *** +#define FB_DACMBCMUG_PHASE 5 +#define FM_DACMBCMUG_PHASE 0x20 + +#define FB_DACMBCMUG_MUGAIN 0 +#define FM_DACMBCMUG_MUGAIN 0x1F + +// *** DACMBCTHR *** +#define FB_DACMBCTHR_THRESH 0 +#define FM_DACMBCTHR_THRESH 0xFF + +// *** DACMBCRAT *** +#define FB_DACMBCRAT_RATIO 0 +#define FM_DACMBCRAT_RATIO 0x1F + +// *** DACMBCATKL *** +#define FB_DACMBCATKL_TCATKL 0 +#define FM_DACMBCATKL_TCATKL 0xFF + +// *** DACMBCATKH *** +#define FB_DACMBCATKH_TCATKH 0 +#define FM_DACMBCATKH_TCATKH 0xFF + +// *** DACMBCRELL *** +#define FB_DACMBCRELL_TCRELL 0 +#define FM_DACMBCRELL_TCRELL 0xFF + +// *** DACMBCRELH *** +#define FB_DACMBCRELH_TCRELH 0 +#define FM_DACMBCRELH_TCRELH 0xFF + +// *** SUBMBCMUG *** +#define FB_SUBMBCMUG_PHASE 5 +#define FM_SUBMBCMUG_PHASE 0x20 + +#define FB_SUBMBCMUG_MUGAIN 0 +#define FM_SUBMBCMUG_MUGAIN 0x1F + +// *** SUBMBCTHR *** +#define FB_SUBMBCTHR_THRESH 0 +#define FM_SUBMBCTHR_THRESH 0xFF + +// *** SUBMBCRAT *** +#define FB_SUBMBCRAT_RATIO 0 +#define FM_SUBMBCRAT_RATIO 0x1F + +// *** SUBMBCATKL *** +#define FB_SUBMBCATKL_TCATKL 0 +#define FM_SUBMBCATKL_TCATKL 0xFF + +// *** SUBMBCATKH *** +#define FB_SUBMBCATKH_TCATKH 0 +#define FM_SUBMBCATKH_TCATKH 0xFF + +// *** SUBMBCRELL *** +#define FB_SUBMBCRELL_TCRELL 0 +#define FM_SUBMBCRELL_TCRELL 0xFF + +// *** SUBMBCRELH *** +#define FB_SUBMBCRELH_TCRELH 0 +#define FM_SUBMBCRELH_TCRELH 0xFF + +// *** PAGESEL *** +#define FB_PAGESEL_PAGESEL 0 +#define FM_PAGESEL_PAGESEL 0xFF + +// *** RESET *** +#define FB_RESET_RESET 0 +#define FM_RESET_RESET 0xFF +#define FV_RESET_PWR_ON_DEFAULTS 0x85 + +// *** IRQEN *** +#define FB_IRQEN_THRMINTEN 6 +#define FM_IRQEN_THRMINTEN 0x40 +#define FV_THRMINTEN_ENABLED 0x40 +#define FV_THRMINTEN_DISABLED 0x0 + +#define FB_IRQEN_HBPINTEN 5 +#define FM_IRQEN_HBPINTEN 0x20 +#define FV_HBPINTEN_ENABLED 0x20 +#define FV_HBPINTEN_DISABLED 0x0 + +#define FB_IRQEN_HSDINTEN 4 +#define FM_IRQEN_HSDINTEN 0x10 +#define FV_HSDINTEN_ENABLED 0x10 +#define FV_HSDINTEN_DISABLED 0x0 + +#define FB_IRQEN_HPDINTEN 3 +#define FM_IRQEN_HPDINTEN 0x8 +#define FV_HPDINTEN_ENABLED 0x8 +#define FV_HPDINTEN_DISABLED 0x0 + +#define FB_IRQEN_GPIO3INTEN 1 +#define FM_IRQEN_GPIO3INTEN 0x2 +#define FV_GPIO3INTEN_ENABLED 0x2 +#define FV_GPIO3INTEN_DISABLED 0x0 + +#define FB_IRQEN_GPIO2INTEN 0 +#define FM_IRQEN_GPIO2INTEN 0x1 +#define FV_GPIO2INTEN_ENABLED 0x1 +#define FV_GPIO2INTEN_DISABLED 0x0 + +#define IRQEN_GPIOINTEN_ENABLED 0x1 +#define IRQEN_GPIOINTEN_DISABLED 0x0 + +// *** IRQMASK *** +#define FB_IRQMASK_THRMIM 6 +#define FM_IRQMASK_THRMIM 0x40 +#define FV_THRMIM_MASKED 0x0 +#define FV_THRMIM_NOT_MASKED 0x40 + +#define FB_IRQMASK_HBPIM 5 +#define FM_IRQMASK_HBPIM 0x20 +#define FV_HBPIM_MASKED 0x0 +#define FV_HBPIM_NOT_MASKED 0x20 + +#define FB_IRQMASK_HSDIM 4 +#define FM_IRQMASK_HSDIM 0x10 +#define FV_HSDIM_MASKED 0x0 +#define FV_HSDIM_NOT_MASKED 0x10 + +#define FB_IRQMASK_HPDIM 3 +#define FM_IRQMASK_HPDIM 0x8 +#define FV_HPDIM_MASKED 0x0 +#define FV_HPDIM_NOT_MASKED 0x8 + +#define FB_IRQMASK_GPIO3M 1 +#define FM_IRQMASK_GPIO3M 0x2 +#define FV_GPIO3M_MASKED 0x0 +#define FV_GPIO3M_NOT_MASKED 0x2 + +#define FB_IRQMASK_GPIO2M 0 +#define FM_IRQMASK_GPIO2M 0x1 +#define FV_GPIO2M_MASKED 0x0 +#define FV_GPIO2M_NOT_MASKED 0x1 + +#define IRQMASK_GPIOM_MASKED 0x0 +#define IRQMASK_GPIOM_NOT_MASKED 0x1 + +// *** IRQSTAT *** +#define FB_IRQSTAT_THRMINT 6 +#define FM_IRQSTAT_THRMINT 0x40 +#define FV_THRMINT_INTERRUPTED 0x40 +#define FV_THRMINT_NOT_INTERRUPTED 0x0 + +#define FB_IRQSTAT_HBPINT 5 +#define FM_IRQSTAT_HBPINT 0x20 +#define FV_HBPINT_INTERRUPTED 0x20 +#define FV_HBPINT_NOT_INTERRUPTED 0x0 + +#define FB_IRQSTAT_HSDINT 4 +#define FM_IRQSTAT_HSDINT 0x10 +#define FV_HSDINT_INTERRUPTED 0x10 +#define FV_HSDINT_NOT_INTERRUPTED 0x0 + +#define FB_IRQSTAT_HPDINT 3 +#define FM_IRQSTAT_HPDINT 0x8 +#define FV_HPDINT_INTERRUPTED 0x8 +#define FV_HPDINT_NOT_INTERRUPTED 0x0 + +#define FB_IRQSTAT_GPIO3INT 1 +#define FM_IRQSTAT_GPIO3INT 0x2 +#define FV_GPIO3INT_INTERRUPTED 0x2 +#define FV_GPIO3INT_NOT_INTERRUPTED 0x0 + +#define FB_IRQSTAT_GPIO2INT 0 +#define FM_IRQSTAT_GPIO2INT 0x1 +#define FV_GPIO2INT_INTERRUPTED 0x1 +#define FV_GPIO2INT_NOT_INTERRUPTED 0x0 + +#define IRQSTAT_GPIOINT_INTERRUPTED 0x1 +#define IRQSTAT_GPIOINT_NOT_INTERRUPTED 0x0 + +// *** DEVADD0 *** +#define FB_DEVADD0_DEVADD0 1 +#define FM_DEVADD0_DEVADD0 0xFE + +#define FB_DEVADD0_I2C_ADDRLK 0 +#define FM_DEVADD0_I2C_ADDRLK 0x1 +#define FV_I2C_ADDRLK_LOCK 0x1 + +// *** DEVID *** +#define FB_DEVID_DEV_ID 0 +#define FM_DEVID_DEV_ID 0xFF + +// *** DEVREV *** +#define FB_DEVREV_MAJ_REV 4 +#define FM_DEVREV_MAJ_REV 0xF0 + +#define FB_DEVREV_MIN_REV 0 +#define FM_DEVREV_MIN_REV 0xF + +// *** PLLSTAT *** +#define FB_PLLSTAT_PLL2LK 1 +#define FM_PLLSTAT_PLL2LK 0x2 +#define FV_PLL2LK_LOCKED 0x2 +#define FV_PLL2LK_UNLOCKED 0x0 + +#define FB_PLLSTAT_PLL1LK 0 +#define FM_PLLSTAT_PLL1LK 0x1 +#define FV_PLL1LK_LOCKED 0x1 +#define FV_PLL1LK_UNLOCKED 0x0 + +#define PLLSTAT_PLLLK_LOCKED 0x1 +#define PLLSTAT_PLLLK_UNLOCKED 0x0 + +// *** PLLCTL *** +#define FB_PLLCTL_PU_PLL2 7 +#define FM_PLLCTL_PU_PLL2 0x80 +#define FV_PU_PLL2_PWR_UP 0x80 +#define FV_PU_PLL2_PWR_DWN 0x0 + +#define FB_PLLCTL_PU_PLL1 6 +#define FM_PLLCTL_PU_PLL1 0x40 +#define FV_PU_PLL1_PWR_UP 0x40 +#define FV_PU_PLL1_PWR_DWN 0x0 + +#define FB_PLLCTL_PLL2CLKEN 5 +#define FM_PLLCTL_PLL2CLKEN 0x20 +#define FV_PLL2CLKEN_ENABLE 0x20 +#define FV_PLL2CLKEN_DISABLE 0x0 + +#define FB_PLLCTL_PLL1CLKEN 4 +#define FM_PLLCTL_PLL1CLKEN 0x10 +#define FV_PLL1CLKEN_ENABLE 0x10 +#define FV_PLL1CLKEN_DISABLE 0x0 + +#define FB_PLLCTL_BCLKSEL 2 +#define FM_PLLCTL_BCLKSEL 0xC +#define FV_BCLKSEL_BCLK1 0x0 +#define FV_BCLKSEL_BCLK2 0x4 +#define FV_BCLKSEL_BCLK3 0x8 + +#define FB_PLLCTL_PLLISEL 0 +#define FM_PLLCTL_PLLISEL 0x3 +#define FV_PLLISEL_XTAL 0x0 +#define FV_PLLISEL_MCLK1 0x1 +#define FV_PLLISEL_MCLK2 0x2 +#define FV_PLLISEL_BCLK 0x3 + +#define PLLCTL_PU_PLL_PWR_UP 0x1 +#define PLLCTL_PU_PLL_PWR_DWN 0x0 +#define PLLCTL_PLLCLKEN_ENABLE 0x1 +#define PLLCTL_PLLCLKEN_DISABLE 0x0 + +// *** ISRC *** +#define FB_ISRC_IBR 2 +#define FM_ISRC_IBR 0x4 +#define FV_IBR_44PT1 0x0 +#define FV_IBR_48 0x4 + +#define FB_ISRC_IBM 0 +#define FM_ISRC_IBM 0x3 +#define FV_IBM_0PT25 0x0 +#define FV_IBM_0PT5 0x1 +#define FV_IBM_1 0x2 +#define FV_IBM_2 0x3 + +// *** SCLKCTL *** +#define FB_SCLKCTL_ASDM 6 +#define FM_SCLKCTL_ASDM 0xC0 +#define FV_ASDM_HALF 0x40 +#define FV_ASDM_FULL 0x80 +#define FV_ASDM_AUTO 0xC0 + +#define FB_SCLKCTL_DSDM 4 +#define FM_SCLKCTL_DSDM 0x30 +#define FV_DSDM_HALF 0x10 +#define FV_DSDM_FULL 0x20 +#define FV_DSDM_AUTO 0x30 + +// *** TIMEBASE *** +#define FB_TIMEBASE_TIMEBASE 0 +#define FM_TIMEBASE_TIMEBASE 0xFF + +// *** I2SCMC *** +#define FB_I2SCMC_BCMP3 4 +#define FM_I2SCMC_BCMP3 0x30 +#define FV_BCMP3_AUTO 0x0 +#define FV_BCMP3_32X 0x10 +#define FV_BCMP3_40X 0x20 +#define FV_BCMP3_64X 0x30 + +#define FB_I2SCMC_BCMP2 2 +#define FM_I2SCMC_BCMP2 0xC +#define FV_BCMP2_AUTO 0x0 +#define FV_BCMP2_32X 0x4 +#define FV_BCMP2_40X 0x8 +#define FV_BCMP2_64X 0xC + +#define FB_I2SCMC_BCMP1 0 +#define FM_I2SCMC_BCMP1 0x3 +#define FV_BCMP1_AUTO 0x0 +#define FV_BCMP1_32X 0x1 +#define FV_BCMP1_40X 0x2 +#define FV_BCMP1_64X 0x3 + +#define I2SCMC_BCMP_AUTO 0x0 +#define I2SCMC_BCMP_32X 0x1 +#define I2SCMC_BCMP_40X 0x2 +#define I2SCMC_BCMP_64X 0x3 + +// *** MCLK2PINC *** +#define FB_MCLK2PINC_SLEWOUT 4 +#define FM_MCLK2PINC_SLEWOUT 0xF0 + +#define FB_MCLK2PINC_MCLK2IO 2 +#define FM_MCLK2PINC_MCLK2IO 0x4 +#define FV_MCLK2IO_INPUT 0x0 +#define FV_MCLK2IO_OUTPUT 0x4 + +#define FB_MCLK2PINC_MCLK2OS 0 +#define FM_MCLK2PINC_MCLK2OS 0x3 +#define FV_MCLK2OS_24PT576 0x0 +#define FV_MCLK2OS_22PT5792 0x1 +#define FV_MCLK2OS_PLL2 0x2 + +// *** I2SPINC0 *** +#define FB_I2SPINC0_SDO3TRI 7 +#define FM_I2SPINC0_SDO3TRI 0x80 + +#define FB_I2SPINC0_SDO2TRI 6 +#define FM_I2SPINC0_SDO2TRI 0x40 + +#define FB_I2SPINC0_SDO1TRI 5 +#define FM_I2SPINC0_SDO1TRI 0x20 + +#define FB_I2SPINC0_PCM3TRI 2 +#define FM_I2SPINC0_PCM3TRI 0x4 + +#define FB_I2SPINC0_PCM2TRI 1 +#define FM_I2SPINC0_PCM2TRI 0x2 + +#define FB_I2SPINC0_PCM1TRI 0 +#define FM_I2SPINC0_PCM1TRI 0x1 + +// *** I2SPINC1 *** +#define FB_I2SPINC1_SDO3PDD 2 +#define FM_I2SPINC1_SDO3PDD 0x4 + +#define FB_I2SPINC1_SDO2PDD 1 +#define FM_I2SPINC1_SDO2PDD 0x2 + +#define FB_I2SPINC1_SDO1PDD 0 +#define FM_I2SPINC1_SDO1PDD 0x1 + +// *** I2SPINC2 *** +#define FB_I2SPINC2_LR3PDD 5 +#define FM_I2SPINC2_LR3PDD 0x20 + +#define FB_I2SPINC2_BC3PDD 4 +#define FM_I2SPINC2_BC3PDD 0x10 + +#define FB_I2SPINC2_LR2PDD 3 +#define FM_I2SPINC2_LR2PDD 0x8 + +#define FB_I2SPINC2_BC2PDD 2 +#define FM_I2SPINC2_BC2PDD 0x4 + +#define FB_I2SPINC2_LR1PDD 1 +#define FM_I2SPINC2_LR1PDD 0x2 + +#define FB_I2SPINC2_BC1PDD 0 +#define FM_I2SPINC2_BC1PDD 0x1 + +// *** GPIOCTL0 *** +#define FB_GPIOCTL0_GPIO3INTP 7 +#define FM_GPIOCTL0_GPIO3INTP 0x80 + +#define FB_GPIOCTL0_GPIO2INTP 6 +#define FM_GPIOCTL0_GPIO2INTP 0x40 + +#define FB_GPIOCTL0_GPIO3CFG 5 +#define FM_GPIOCTL0_GPIO3CFG 0x20 + +#define FB_GPIOCTL0_GPIO2CFG 4 +#define FM_GPIOCTL0_GPIO2CFG 0x10 + +#define FB_GPIOCTL0_GPIO3IO 3 +#define FM_GPIOCTL0_GPIO3IO 0x8 + +#define FB_GPIOCTL0_GPIO2IO 2 +#define FM_GPIOCTL0_GPIO2IO 0x4 + +#define FB_GPIOCTL0_GPIO1IO 1 +#define FM_GPIOCTL0_GPIO1IO 0x2 + +#define FB_GPIOCTL0_GPIO0IO 0 +#define FM_GPIOCTL0_GPIO0IO 0x1 + +// *** GPIOCTL1 *** +#define FB_GPIOCTL1_GPIO3 7 +#define FM_GPIOCTL1_GPIO3 0x80 + +#define FB_GPIOCTL1_GPIO2 6 +#define FM_GPIOCTL1_GPIO2 0x40 + +#define FB_GPIOCTL1_GPIO1 5 +#define FM_GPIOCTL1_GPIO1 0x20 + +#define FB_GPIOCTL1_GPIO0 4 +#define FM_GPIOCTL1_GPIO0 0x10 + +#define FB_GPIOCTL1_GPIO3RD 3 +#define FM_GPIOCTL1_GPIO3RD 0x8 + +#define FB_GPIOCTL1_GPIO2RD 2 +#define FM_GPIOCTL1_GPIO2RD 0x4 + +#define FB_GPIOCTL1_GPIO1RD 1 +#define FM_GPIOCTL1_GPIO1RD 0x2 + +#define FB_GPIOCTL1_GPIO0RD 0 +#define FM_GPIOCTL1_GPIO0RD 0x1 + +// *** ASRC *** +#define FB_ASRC_ASRCOBW 7 +#define FM_ASRC_ASRCOBW 0x80 + +#define FB_ASRC_ASRCIBW 6 +#define FM_ASRC_ASRCIBW 0x40 + +#define FB_ASRC_ASRCOB 5 +#define FM_ASRC_ASRCOB 0x20 +#define FV_ASRCOB_ACTIVE 0x0 +#define FV_ASRCOB_BYPASSED 0x20 + +#define FB_ASRC_ASRCIB 4 +#define FM_ASRC_ASRCIB 0x10 +#define FV_ASRCIB_ACTIVE 0x0 +#define FV_ASRCIB_BYPASSED 0x10 + +#define FB_ASRC_ASRCOL 3 +#define FM_ASRC_ASRCOL 0x8 + +#define FB_ASRC_ASRCIL 2 +#define FM_ASRC_ASRCIL 0x4 + +// *** TDMCTL0 *** +#define FB_TDMCTL0_TDMMD 2 +#define FM_TDMCTL0_TDMMD 0x4 +#define FV_TDMMD_200 0x0 +#define FV_TDMMD_256 0x4 + +#define FB_TDMCTL0_SLSYNC 1 +#define FM_TDMCTL0_SLSYNC 0x2 +#define FV_SLSYNC_SHORT 0x0 +#define FV_SLSYNC_LONG 0x2 + +#define FB_TDMCTL0_BDELAY 0 +#define FM_TDMCTL0_BDELAY 0x1 +#define FV_BDELAY_NO_DELAY 0x0 +#define FV_BDELAY_1BCLK_DELAY 0x1 + +// *** TDMCTL1 *** +#define FB_TDMCTL1_TDMSO 5 +#define FM_TDMCTL1_TDMSO 0x60 +#define FV_TDMSO_2 0x0 +#define FV_TDMSO_4 0x20 +#define FV_TDMSO_6 0x40 + +#define FB_TDMCTL1_TDMDSS 3 +#define FM_TDMCTL1_TDMDSS 0x18 +#define FV_TDMDSS_16 0x0 +#define FV_TDMDSS_24 0x10 +#define FV_TDMDSS_32 0x18 + +#define FB_TDMCTL1_TDMSI 0 +#define FM_TDMCTL1_TDMSI 0x3 +#define FV_TDMSI_2 0x0 +#define FV_TDMSI_4 0x1 +#define FV_TDMSI_6 0x2 + +// *** PWRM0 *** +#define FB_PWRM0_INPROC3PU 6 +#define FM_PWRM0_INPROC3PU 0x40 + +#define FB_PWRM0_INPROC2PU 5 +#define FM_PWRM0_INPROC2PU 0x20 + +#define FB_PWRM0_INPROC1PU 4 +#define FM_PWRM0_INPROC1PU 0x10 + +#define FB_PWRM0_INPROC0PU 3 +#define FM_PWRM0_INPROC0PU 0x8 + +#define FB_PWRM0_MICB2PU 2 +#define FM_PWRM0_MICB2PU 0x4 + +#define FB_PWRM0_MICB1PU 1 +#define FM_PWRM0_MICB1PU 0x2 + +#define FB_PWRM0_MCLKPEN 0 +#define FM_PWRM0_MCLKPEN 0x1 + +// *** PWRM1 *** +#define FB_PWRM1_SUBPU 7 +#define FM_PWRM1_SUBPU 0x80 + +#define FB_PWRM1_HPLPU 6 +#define FM_PWRM1_HPLPU 0x40 + +#define FB_PWRM1_HPRPU 5 +#define FM_PWRM1_HPRPU 0x20 + +#define FB_PWRM1_SPKLPU 4 +#define FM_PWRM1_SPKLPU 0x10 + +#define FB_PWRM1_SPKRPU 3 +#define FM_PWRM1_SPKRPU 0x8 + +#define FB_PWRM1_D2S2PU 2 +#define FM_PWRM1_D2S2PU 0x4 + +#define FB_PWRM1_D2S1PU 1 +#define FM_PWRM1_D2S1PU 0x2 + +#define FB_PWRM1_VREFPU 0 +#define FM_PWRM1_VREFPU 0x1 + +// *** PWRM2 *** +#define FB_PWRM2_I2S3OPU 5 +#define FM_PWRM2_I2S3OPU 0x20 +#define FV_I2S3OPU_PWR_DOWN 0x0 +#define FV_I2S3OPU_PWR_UP 0x20 + +#define FB_PWRM2_I2S2OPU 4 +#define FM_PWRM2_I2S2OPU 0x10 +#define FV_I2S2OPU_PWR_DOWN 0x0 +#define FV_I2S2OPU_PWR_UP 0x10 + +#define FB_PWRM2_I2S1OPU 3 +#define FM_PWRM2_I2S1OPU 0x8 +#define FV_I2S1OPU_PWR_DOWN 0x0 +#define FV_I2S1OPU_PWR_UP 0x8 + +#define FB_PWRM2_I2S3IPU 2 +#define FM_PWRM2_I2S3IPU 0x4 +#define FV_I2S3IPU_PWR_DOWN 0x0 +#define FV_I2S3IPU_PWR_UP 0x4 + +#define FB_PWRM2_I2S2IPU 1 +#define FM_PWRM2_I2S2IPU 0x2 +#define FV_I2S2IPU_PWR_DOWN 0x0 +#define FV_I2S2IPU_PWR_UP 0x2 + +#define FB_PWRM2_I2S1IPU 0 +#define FM_PWRM2_I2S1IPU 0x1 +#define FV_I2S1IPU_PWR_DOWN 0x0 +#define FV_I2S1IPU_PWR_UP 0x1 + +#define PWRM2_I2SOPU_PWR_DOWN 0x0 +#define PWRM2_I2SOPU_PWR_UP 0x1 +#define PWRM2_I2SIPU_PWR_DOWN 0x0 +#define PWRM2_I2SIPU_PWR_UP 0x1 + +// *** PWRM3 *** +#define FB_PWRM3_BGSBUP 6 +#define FM_PWRM3_BGSBUP 0x40 +#define FV_BGSBUP_ON 0x0 +#define FV_BGSBUP_OFF 0x40 + +#define FB_PWRM3_VGBAPU 5 +#define FM_PWRM3_VGBAPU 0x20 +#define FV_VGBAPU_ON 0x0 +#define FV_VGBAPU_OFF 0x20 + +#define FB_PWRM3_LLINEPU 4 +#define FM_PWRM3_LLINEPU 0x10 + +#define FB_PWRM3_RLINEPU 3 +#define FM_PWRM3_RLINEPU 0x8 + +// *** PWRM4 *** +#define FB_PWRM4_OPSUBPU 4 +#define FM_PWRM4_OPSUBPU 0x10 + +#define FB_PWRM4_OPDACLPU 3 +#define FM_PWRM4_OPDACLPU 0x8 + +#define FB_PWRM4_OPDACRPU 2 +#define FM_PWRM4_OPDACRPU 0x4 + +#define FB_PWRM4_OPSPKLPU 1 +#define FM_PWRM4_OPSPKLPU 0x2 + +#define FB_PWRM4_OPSPKRPU 0 +#define FM_PWRM4_OPSPKRPU 0x1 + +// *** I2SIDCTL *** +#define FB_I2SIDCTL_I2SI3DCTL 4 +#define FM_I2SIDCTL_I2SI3DCTL 0x30 + +#define FB_I2SIDCTL_I2SI2DCTL 2 +#define FM_I2SIDCTL_I2SI2DCTL 0xC + +#define FB_I2SIDCTL_I2SI1DCTL 0 +#define FM_I2SIDCTL_I2SI1DCTL 0x3 + +// *** I2SODCTL *** +#define FB_I2SODCTL_I2SO3DCTL 4 +#define FM_I2SODCTL_I2SO3DCTL 0x30 + +#define FB_I2SODCTL_I2SO2DCTL 2 +#define FM_I2SODCTL_I2SO2DCTL 0xC + +#define FB_I2SODCTL_I2SO1DCTL 0 +#define FM_I2SODCTL_I2SO1DCTL 0x3 + +// *** AUDIOMUX1 *** +#define FB_AUDIOMUX1_ASRCIMUX 6 +#define FM_AUDIOMUX1_ASRCIMUX 0xC0 +#define FV_ASRCIMUX_NONE 0x0 +#define FV_ASRCIMUX_I2S1 0x40 +#define FV_ASRCIMUX_I2S2 0x80 +#define FV_ASRCIMUX_I2S3 0xC0 + +#define FB_AUDIOMUX1_I2S2MUX 3 +#define FM_AUDIOMUX1_I2S2MUX 0x38 +#define FV_I2S2MUX_I2S1 0x0 +#define FV_I2S2MUX_I2S2 0x8 +#define FV_I2S2MUX_I2S3 0x10 +#define FV_I2S2MUX_ADC_DMIC 0x18 +#define FV_I2S2MUX_DMIC2 0x20 +#define FV_I2S2MUX_CLASSD_DSP 0x28 +#define FV_I2S2MUX_DAC_DSP 0x30 +#define FV_I2S2MUX_SUB_DSP 0x38 + +#define FB_AUDIOMUX1_I2S1MUX 0 +#define FM_AUDIOMUX1_I2S1MUX 0x7 +#define FV_I2S1MUX_I2S1 0x0 +#define FV_I2S1MUX_I2S2 0x1 +#define FV_I2S1MUX_I2S3 0x2 +#define FV_I2S1MUX_ADC_DMIC 0x3 +#define FV_I2S1MUX_DMIC2 0x4 +#define FV_I2S1MUX_CLASSD_DSP 0x5 +#define FV_I2S1MUX_DAC_DSP 0x6 +#define FV_I2S1MUX_SUB_DSP 0x7 + +#define AUDIOMUX1_I2SMUX_I2S1 0x0 +#define AUDIOMUX1_I2SMUX_I2S2 0x1 +#define AUDIOMUX1_I2SMUX_I2S3 0x2 +#define AUDIOMUX1_I2SMUX_ADC_DMIC 0x3 +#define AUDIOMUX1_I2SMUX_DMIC2 0x4 +#define AUDIOMUX1_I2SMUX_CLASSD_DSP 0x5 +#define AUDIOMUX1_I2SMUX_DAC_DSP 0x6 +#define AUDIOMUX1_I2SMUX_SUB_DSP 0x7 + +// *** AUDIOMUX2 *** +#define FB_AUDIOMUX2_ASRCOMUX 6 +#define FM_AUDIOMUX2_ASRCOMUX 0xC0 +#define FV_ASRCOMUX_NONE 0x0 +#define FV_ASRCOMUX_I2S1 0x40 +#define FV_ASRCOMUX_I2S2 0x80 +#define FV_ASRCOMUX_I2S3 0xC0 + +#define FB_AUDIOMUX2_DACMUX 3 +#define FM_AUDIOMUX2_DACMUX 0x38 +#define FV_DACMUX_I2S1 0x0 +#define FV_DACMUX_I2S2 0x8 +#define FV_DACMUX_I2S3 0x10 +#define FV_DACMUX_ADC_DMIC 0x18 +#define FV_DACMUX_DMIC2 0x20 +#define FV_DACMUX_CLASSD_DSP 0x28 +#define FV_DACMUX_DAC_DSP 0x30 +#define FV_DACMUX_SUB_DSP 0x38 + +#define FB_AUDIOMUX2_I2S3MUX 0 +#define FM_AUDIOMUX2_I2S3MUX 0x7 +#define FV_I2S3MUX_I2S1 0x0 +#define FV_I2S3MUX_I2S2 0x1 +#define FV_I2S3MUX_I2S3 0x2 +#define FV_I2S3MUX_ADC_DMIC 0x3 +#define FV_I2S3MUX_DMIC2 0x4 +#define FV_I2S3MUX_CLASSD_DSP 0x5 +#define FV_I2S3MUX_DAC_DSP 0x6 +#define FV_I2S3MUX_SUB_DSP 0x7 + +// *** AUDIOMUX3 *** +#define FB_AUDIOMUX3_SUBMUX 3 +#define FM_AUDIOMUX3_SUBMUX 0xF8 +#define FV_SUBMUX_I2S1_L 0x0 +#define FV_SUBMUX_I2S1_R 0x8 +#define FV_SUBMUX_I2S1_LR 0x10 +#define FV_SUBMUX_I2S2_L 0x18 +#define FV_SUBMUX_I2S2_R 0x20 +#define FV_SUBMUX_I2S2_LR 0x28 +#define FV_SUBMUX_I2S3_L 0x30 +#define FV_SUBMUX_I2S3_R 0x38 +#define FV_SUBMUX_I2S3_LR 0x40 +#define FV_SUBMUX_ADC_DMIC_L 0x48 +#define FV_SUBMUX_ADC_DMIC_R 0x50 +#define FV_SUBMUX_ADC_DMIC_LR 0x58 +#define FV_SUBMUX_DMIC_L 0x60 +#define FV_SUBMUX_DMIC_R 0x68 +#define FV_SUBMUX_DMIC_LR 0x70 +#define FV_SUBMUX_CLASSD_DSP_L 0x78 +#define FV_SUBMUX_CLASSD_DSP_R 0x80 +#define FV_SUBMUX_CLASSD_DSP_LR 0x88 + +#define FB_AUDIOMUX3_CLSSDMUX 0 +#define FM_AUDIOMUX3_CLSSDMUX 0x7 +#define FV_CLSSDMUX_I2S1 0x0 +#define FV_CLSSDMUX_I2S2 0x1 +#define FV_CLSSDMUX_I2S3 0x2 +#define FV_CLSSDMUX_ADC_DMIC 0x3 +#define FV_CLSSDMUX_DMIC2 0x4 +#define FV_CLSSDMUX_CLASSD_DSP 0x5 +#define FV_CLSSDMUX_DAC_DSP 0x6 +#define FV_CLSSDMUX_SUB_DSP 0x7 + +// *** HSDCTL1 *** +#define FB_HSDCTL1_HPJKTYPE 7 +#define FM_HSDCTL1_HPJKTYPE 0x80 + +#define FB_HSDCTL1_CON_DET_PWD 6 +#define FM_HSDCTL1_CON_DET_PWD 0x40 + +#define FB_HSDCTL1_DETCYC 4 +#define FM_HSDCTL1_DETCYC 0x30 + +#define FB_HSDCTL1_HPDLYBYP 3 +#define FM_HSDCTL1_HPDLYBYP 0x8 + +#define FB_HSDCTL1_HSDETPOL 2 +#define FM_HSDCTL1_HSDETPOL 0x4 + +#define FB_HSDCTL1_HPID_EN 1 +#define FM_HSDCTL1_HPID_EN 0x2 + +#define FB_HSDCTL1_GBLHS_EN 0 +#define FM_HSDCTL1_GBLHS_EN 0x1 + +// *** HSDCTL2 *** +#define FB_HSDCTL2_FMICBIAS1 6 +#define FM_HSDCTL2_FMICBIAS1 0xC0 + +#define FB_HSDCTL2_MB1MODE 5 +#define FM_HSDCTL2_MB1MODE 0x20 +#define FV_MB1MODE_AUTO 0x0 +#define FV_MB1MODE_MANUAL 0x20 + +#define FB_HSDCTL2_FORCETRG 4 +#define FM_HSDCTL2_FORCETRG 0x10 + +#define FB_HSDCTL2_SWMODE 3 +#define FM_HSDCTL2_SWMODE 0x8 + +#define FB_HSDCTL2_GHSHIZ 2 +#define FM_HSDCTL2_GHSHIZ 0x4 + +#define FB_HSDCTL2_FPLUGTYPE 0 +#define FM_HSDCTL2_FPLUGTYPE 0x3 + +// *** HSDSTAT *** +#define FB_HSDSTAT_MBIAS1DRV 5 +#define FM_HSDSTAT_MBIAS1DRV 0x60 + +#define FB_HSDSTAT_HSDETSTAT 3 +#define FM_HSDSTAT_HSDETSTAT 0x8 + +#define FB_HSDSTAT_PLUGTYPE 1 +#define FM_HSDSTAT_PLUGTYPE 0x6 + +#define FB_HSDSTAT_HSDETDONE 0 +#define FM_HSDSTAT_HSDETDONE 0x1 + +// *** HSDDELAY *** +#define FB_HSDDELAY_T_STABLE 0 +#define FM_HSDDELAY_T_STABLE 0x7 + +// *** BUTCTL *** +#define FB_BUTCTL_BPUSHSTAT 7 +#define FM_BUTCTL_BPUSHSTAT 0x80 + +#define FB_BUTCTL_BPUSHDET 6 +#define FM_BUTCTL_BPUSHDET 0x40 + +#define FB_BUTCTL_BPUSHEN 5 +#define FM_BUTCTL_BPUSHEN 0x20 + +#define FB_BUTCTL_BSTABLE_L 3 +#define FM_BUTCTL_BSTABLE_L 0x18 + +#define FB_BUTCTL_BSTABLE_S 0 +#define FM_BUTCTL_BSTABLE_S 0x7 + +// *** CH0AIC *** +#define FB_CH0AIC_INSELL 6 +#define FM_CH0AIC_INSELL 0xC0 + +#define FB_CH0AIC_MICBST0 4 +#define FM_CH0AIC_MICBST0 0x30 + +#define FB_CH0AIC_LADCIN 2 +#define FM_CH0AIC_LADCIN 0xC + +#define FB_CH0AIC_IN_BYPS_L_SEL 1 +#define FM_CH0AIC_IN_BYPS_L_SEL 0x2 + +#define FB_CH0AIC_IPCH0S 0 +#define FM_CH0AIC_IPCH0S 0x1 + +// *** CH1AIC *** +#define FB_CH1AIC_INSELR 6 +#define FM_CH1AIC_INSELR 0xC0 + +#define FB_CH1AIC_MICBST1 4 +#define FM_CH1AIC_MICBST1 0x30 + +#define FB_CH1AIC_RADCIN 2 +#define FM_CH1AIC_RADCIN 0xC + +#define FB_CH1AIC_IN_BYPS_R_SEL 1 +#define FM_CH1AIC_IN_BYPS_R_SEL 0x2 + +#define FB_CH1AIC_IPCH1S 0 +#define FM_CH1AIC_IPCH1S 0x1 + +// *** ICTL0 *** +#define FB_ICTL0_IN1POL 7 +#define FM_ICTL0_IN1POL 0x80 + +#define FB_ICTL0_IN0POL 6 +#define FM_ICTL0_IN0POL 0x40 + +#define FB_ICTL0_INPCH10SEL 4 +#define FM_ICTL0_INPCH10SEL 0x30 + +#define FB_ICTL0_IN1MUTE 3 +#define FM_ICTL0_IN1MUTE 0x8 + +#define FB_ICTL0_IN0MUTE 2 +#define FM_ICTL0_IN0MUTE 0x4 + +#define FB_ICTL0_IN1HP 1 +#define FM_ICTL0_IN1HP 0x2 + +#define FB_ICTL0_IN0HP 0 +#define FM_ICTL0_IN0HP 0x1 + +// *** ICTL1 *** +#define FB_ICTL1_IN3POL 7 +#define FM_ICTL1_IN3POL 0x80 + +#define FB_ICTL1_IN2POL 6 +#define FM_ICTL1_IN2POL 0x40 + +#define FB_ICTL1_INPCH32SEL 4 +#define FM_ICTL1_INPCH32SEL 0x30 + +#define FB_ICTL1_IN3MUTE 3 +#define FM_ICTL1_IN3MUTE 0x8 + +#define FB_ICTL1_IN2MUTE 2 +#define FM_ICTL1_IN2MUTE 0x4 + +#define FB_ICTL1_IN3HP 1 +#define FM_ICTL1_IN3HP 0x2 + +#define FB_ICTL1_IN2HP 0 +#define FM_ICTL1_IN2HP 0x1 + +// *** MICBIAS *** +#define FB_MICBIAS_MICBOV2 4 +#define FM_MICBIAS_MICBOV2 0x30 + +#define FB_MICBIAS_MICBOV1 6 +#define FM_MICBIAS_MICBOV1 0xC0 + +#define FB_MICBIAS_SPARE1 2 +#define FM_MICBIAS_SPARE1 0xC + +#define FB_MICBIAS_SPARE2 0 +#define FM_MICBIAS_SPARE2 0x3 + +// *** PGAZ *** +#define FB_PGAZ_INHPOR 1 +#define FM_PGAZ_INHPOR 0x2 + +#define FB_PGAZ_TOEN 0 +#define FM_PGAZ_TOEN 0x1 + +// *** ASRCILVOL *** +#define FB_ASRCILVOL_ASRCILVOL 0 +#define FM_ASRCILVOL_ASRCILVOL 0xFF + +// *** ASRCIRVOL *** +#define FB_ASRCIRVOL_ASRCIRVOL 0 +#define FM_ASRCIRVOL_ASRCIRVOL 0xFF + +// *** ASRCOLVOL *** +#define FB_ASRCOLVOL_ASRCOLVOL 0 +#define FM_ASRCOLVOL_ASRCOLVOL 0xFF + +// *** ASRCORVOL *** +#define FB_ASRCORVOL_ASRCOLVOL 0 +#define FM_ASRCORVOL_ASRCOLVOL 0xFF + +// *** IVOLCTLU *** +#define FB_IVOLCTLU_IFADE 3 +#define FM_IVOLCTLU_IFADE 0x8 + +#define FB_IVOLCTLU_INPVOLU 2 +#define FM_IVOLCTLU_INPVOLU 0x4 + +#define FB_IVOLCTLU_PGAVOLU 1 +#define FM_IVOLCTLU_PGAVOLU 0x2 + +#define FB_IVOLCTLU_ASRCVOLU 0 +#define FM_IVOLCTLU_ASRCVOLU 0x1 + +// *** ALCCTL0 *** +#define FB_ALCCTL0_ALCMODE 7 +#define FM_ALCCTL0_ALCMODE 0x80 + +#define FB_ALCCTL0_ALCREF 4 +#define FM_ALCCTL0_ALCREF 0x70 + +#define FB_ALCCTL0_ALCEN3 3 +#define FM_ALCCTL0_ALCEN3 0x8 + +#define FB_ALCCTL0_ALCEN2 2 +#define FM_ALCCTL0_ALCEN2 0x4 + +#define FB_ALCCTL0_ALCEN1 1 +#define FM_ALCCTL0_ALCEN1 0x2 + +#define FB_ALCCTL0_ALCEN0 0 +#define FM_ALCCTL0_ALCEN0 0x1 + +// *** ALCCTL1 *** +#define FB_ALCCTL1_MAXGAIN 4 +#define FM_ALCCTL1_MAXGAIN 0x70 + +#define FB_ALCCTL1_ALCL 0 +#define FM_ALCCTL1_ALCL 0xF + +// *** ALCCTL2 *** +#define FB_ALCCTL2_ALCZC 7 +#define FM_ALCCTL2_ALCZC 0x80 + +#define FB_ALCCTL2_MINGAIN 4 +#define FM_ALCCTL2_MINGAIN 0x70 + +#define FB_ALCCTL2_HLD 0 +#define FM_ALCCTL2_HLD 0xF + +// *** ALCCTL3 *** +#define FB_ALCCTL3_DCY 4 +#define FM_ALCCTL3_DCY 0xF0 + +#define FB_ALCCTL3_ATK 0 +#define FM_ALCCTL3_ATK 0xF + +// *** NGATE *** +#define FB_NGATE_NGTH 3 +#define FM_NGATE_NGTH 0xF8 + +#define FB_NGATE_NGG 1 +#define FM_NGATE_NGG 0x6 + +#define FB_NGATE_NGAT 0 +#define FM_NGATE_NGAT 0x1 + +// *** DMICCTL *** +#define FB_DMICCTL_DMIC2EN 7 +#define FM_DMICCTL_DMIC2EN 0x80 + +#define FB_DMICCTL_DMIC1EN 6 +#define FM_DMICCTL_DMIC1EN 0x40 + +#define FB_DMICCTL_DMONO 4 +#define FM_DMICCTL_DMONO 0x10 + +#define FB_DMICCTL_DMDCLK 2 +#define FM_DMICCTL_DMDCLK 0xC + +#define FB_DMICCTL_DMRATE 0 +#define FM_DMICCTL_DMRATE 0x3 + +// *** DACCTL *** +#define FB_DACCTL_DACPOLR 7 +#define FM_DACCTL_DACPOLR 0x80 +#define FV_DACPOLR_NORMAL 0x0 +#define FV_DACPOLR_INVERTED 0x80 + +#define FB_DACCTL_DACPOLL 6 +#define FM_DACCTL_DACPOLL 0x40 +#define FV_DACPOLL_NORMAL 0x0 +#define FV_DACPOLL_INVERTED 0x40 + +#define FB_DACCTL_DACDITH 4 +#define FM_DACCTL_DACDITH 0x30 +#define FV_DACDITH_DYNAMIC_HALF 0x0 +#define FV_DACDITH_DYNAMIC_FULL 0x10 +#define FV_DACDITH_DISABLED 0x20 +#define FV_DACDITH_STATIC 0x30 + +#define FB_DACCTL_DACMUTE 3 +#define FM_DACCTL_DACMUTE 0x8 +#define FV_DACMUTE_ENABLE 0x8 +#define FV_DACMUTE_DISABLE 0x0 + +#define FB_DACCTL_DACDEM 2 +#define FM_DACCTL_DACDEM 0x4 +#define FV_DACDEM_ENABLE 0x4 +#define FV_DACDEM_DISABLE 0x0 + +#define FB_DACCTL_ABYPASS 0 +#define FM_DACCTL_ABYPASS 0x1 + +// *** SPKCTL *** +#define FB_SPKCTL_SPKPOLR 7 +#define FM_SPKCTL_SPKPOLR 0x80 +#define FV_SPKPOLR_NORMAL 0x0 +#define FV_SPKPOLR_INVERTED 0x80 + +#define FB_SPKCTL_SPKPOLL 6 +#define FM_SPKCTL_SPKPOLL 0x40 +#define FV_SPKPOLL_NORMAL 0x0 +#define FV_SPKPOLL_INVERTED 0x40 + +#define FB_SPKCTL_SPKMUTE 3 +#define FM_SPKCTL_SPKMUTE 0x8 +#define FV_SPKMUTE_ENABLE 0x8 +#define FV_SPKMUTE_DISABLE 0x0 + +#define FB_SPKCTL_SPKDEM 2 +#define FM_SPKCTL_SPKDEM 0x4 +#define FV_SPKDEM_ENABLE 0x4 +#define FV_SPKDEM_DISABLE 0x0 + +// *** SUBCTL *** +#define FB_SUBCTL_SUBPOL 7 +#define FM_SUBCTL_SUBPOL 0x80 + +#define FB_SUBCTL_SUBMUTE 3 +#define FM_SUBCTL_SUBMUTE 0x8 + +#define FB_SUBCTL_SUBDEM 2 +#define FM_SUBCTL_SUBDEM 0x4 + +#define FB_SUBCTL_SUBMUX 1 +#define FM_SUBCTL_SUBMUX 0x2 + +#define FB_SUBCTL_SUBILMDIS 0 +#define FM_SUBCTL_SUBILMDIS 0x1 + +// *** DCCTL *** +#define FB_DCCTL_SUBDCBYP 7 +#define FM_DCCTL_SUBDCBYP 0x80 + +#define FB_DCCTL_DACDCBYP 6 +#define FM_DCCTL_DACDCBYP 0x40 + +#define FB_DCCTL_SPKDCBYP 5 +#define FM_DCCTL_SPKDCBYP 0x20 + +#define FB_DCCTL_DCCOEFSEL 0 +#define FM_DCCTL_DCCOEFSEL 0x7 + +// *** OVOLCTLU *** +#define FB_OVOLCTLU_OFADE 4 +#define FM_OVOLCTLU_OFADE 0x10 + +#define FB_OVOLCTLU_SUBVOLU 3 +#define FM_OVOLCTLU_SUBVOLU 0x8 + +#define FB_OVOLCTLU_MVOLU 2 +#define FM_OVOLCTLU_MVOLU 0x4 + +#define FB_OVOLCTLU_SPKVOLU 1 +#define FM_OVOLCTLU_SPKVOLU 0x2 + +#define FB_OVOLCTLU_HPVOLU 0 +#define FM_OVOLCTLU_HPVOLU 0x1 + +// *** MUTEC *** +#define FB_MUTEC_ZDSTAT 7 +#define FM_MUTEC_ZDSTAT 0x80 + +#define FB_MUTEC_ZDLEN 4 +#define FM_MUTEC_ZDLEN 0x30 + +#define FB_MUTEC_APWD 3 +#define FM_MUTEC_APWD 0x8 + +#define FB_MUTEC_AMUTE 2 +#define FM_MUTEC_AMUTE 0x4 + +// *** MVOLL *** +#define FB_MVOLL_MVOL_L 0 +#define FM_MVOLL_MVOL_L 0xFF + +// *** MVOLR *** +#define FB_MVOLR_MVOL_R 0 +#define FM_MVOLR_MVOL_R 0xFF + +// *** HPVOLL *** +#define FB_HPVOLL_HPVOL_L 0 +#define FM_HPVOLL_HPVOL_L 0x7F + +// *** HPVOLR *** +#define FB_HPVOLR_HPVOL_R 0 +#define FM_HPVOLR_HPVOL_R 0x7F + +// *** SPKVOLL *** +#define FB_SPKVOLL_SPKVOL_L 0 +#define FM_SPKVOLL_SPKVOL_L 0x7F + +// *** SPKVOLR *** +#define FB_SPKVOLR_SPKVOL_R 0 +#define FM_SPKVOLR_SPKVOL_R 0x7F + +// *** SUBVOL *** +#define FB_SUBVOL_SUBVOL 0 +#define FM_SUBVOL_SUBVOL 0x7F + +// *** COP0 *** +#define FB_COP0_COPATTEN 7 +#define FM_COP0_COPATTEN 0x80 + +#define FB_COP0_COPGAIN 6 +#define FM_COP0_COPGAIN 0x40 + +#define FB_COP0_HDELTAEN 5 +#define FM_COP0_HDELTAEN 0x20 + +#define FB_COP0_COPTARGET 0 +#define FM_COP0_COPTARGET 0x1F + +// *** COP1 *** +#define FB_COP1_HDCOMPMODE 6 +#define FM_COP1_HDCOMPMODE 0x40 + +#define FB_COP1_AVGLENGTH 2 +#define FM_COP1_AVGLENGTH 0x3C + +#define FB_COP1_MONRATE 0 +#define FM_COP1_MONRATE 0x3 + +// *** COPSTAT *** +#define FB_COPSTAT_HDELTADET 7 +#define FM_COPSTAT_HDELTADET 0x80 + +#define FB_COPSTAT_UV 6 +#define FM_COPSTAT_UV 0x40 + +#define FB_COPSTAT_COPADJ 0 +#define FM_COPSTAT_COPADJ 0x3F + +// *** PWM0 *** +#define FB_PWM0_SCTO 6 +#define FM_PWM0_SCTO 0xC0 + +#define FB_PWM0_UVLO 5 +#define FM_PWM0_UVLO 0x20 + +#define FB_PWM0_BFDIS 3 +#define FM_PWM0_BFDIS 0x8 + +#define FB_PWM0_PWMMODE 2 +#define FM_PWM0_PWMMODE 0x4 + +#define FB_PWM0_NOOFFSET 0 +#define FM_PWM0_NOOFFSET 0x1 + +// *** PWM1 *** +#define FB_PWM1_DITHPOS 4 +#define FM_PWM1_DITHPOS 0x70 + +#define FB_PWM1_DYNDITH 1 +#define FM_PWM1_DYNDITH 0x2 + +#define FB_PWM1_DITHDIS 0 +#define FM_PWM1_DITHDIS 0x1 + +// *** PWM2 *** +// *** PWM3 *** +#define FB_PWM3_PWMMUX 6 +#define FM_PWM3_PWMMUX 0xC0 + +#define FB_PWM3_CVALUE 0 +#define FM_PWM3_CVALUE 0x7 + +// *** HPSW *** +#define FB_HPSW_HPDETSTATE 4 +#define FM_HPSW_HPDETSTATE 0x10 + +#define FB_HPSW_HPSWEN 2 +#define FM_HPSW_HPSWEN 0xC + +#define FB_HPSW_HPSWPOL 1 +#define FM_HPSW_HPSWPOL 0x2 + +#define FB_HPSW_TSDEN 0 +#define FM_HPSW_TSDEN 0x1 + +// *** THERMTS *** +#define FB_THERMTS_TRIPHS 7 +#define FM_THERMTS_TRIPHS 0x80 + +#define FB_THERMTS_TRIPLS 6 +#define FM_THERMTS_TRIPLS 0x40 + +#define FB_THERMTS_TRIPSPLIT 4 +#define FM_THERMTS_TRIPSPLIT 0x30 + +#define FB_THERMTS_TRIPSHIFT 2 +#define FM_THERMTS_TRIPSHIFT 0xC + +#define FB_THERMTS_TSPOLL 0 +#define FM_THERMTS_TSPOLL 0x3 + +// *** THERMSPK1 *** +#define FB_THERMSPK1_FORCEPWD 7 +#define FM_THERMSPK1_FORCEPWD 0x80 + +#define FB_THERMSPK1_INSTCUTMODE 6 +#define FM_THERMSPK1_INSTCUTMODE 0x40 + +#define FB_THERMSPK1_INCRATIO 4 +#define FM_THERMSPK1_INCRATIO 0x30 + +#define FB_THERMSPK1_INCSTEP 2 +#define FM_THERMSPK1_INCSTEP 0xC + +#define FB_THERMSPK1_DECSTEP 0 +#define FM_THERMSPK1_DECSTEP 0x3 + +// *** THERMSTAT *** +#define FB_THERMSTAT_FPWDS 7 +#define FM_THERMSTAT_FPWDS 0x80 + +#define FB_THERMSTAT_VOLSTAT 0 +#define FM_THERMSTAT_VOLSTAT 0x7F + +// *** SCSTAT *** +#define FB_SCSTAT_ESDF 3 +#define FM_SCSTAT_ESDF 0x18 + +#define FB_SCSTAT_CPF 2 +#define FM_SCSTAT_CPF 0x4 + +#define FB_SCSTAT_CLSDF 0 +#define FM_SCSTAT_CLSDF 0x3 + +// *** SDMON *** +#define FB_SDMON_SDFORCE 7 +#define FM_SDMON_SDFORCE 0x80 + +#define FB_SDMON_SDVALUE 0 +#define FM_SDMON_SDVALUE 0x1F + +// *** SPKEQFILT *** +#define FB_SPKEQFILT_EQ2EN 7 +#define FM_SPKEQFILT_EQ2EN 0x80 +#define FV_EQ2EN_ENABLE 0x80 +#define FV_EQ2EN_DISABLE 0x0 + +#define FB_SPKEQFILT_EQ2BE 4 +#define FM_SPKEQFILT_EQ2BE 0x70 + +#define FB_SPKEQFILT_EQ1EN 3 +#define FM_SPKEQFILT_EQ1EN 0x8 +#define FV_EQ1EN_ENABLE 0x8 +#define FV_EQ1EN_DISABLE 0x0 + +#define FB_SPKEQFILT_EQ1BE 0 +#define FM_SPKEQFILT_EQ1BE 0x7 + +#define SPKEQFILT_EQEN_ENABLE 0x1 +#define SPKEQFILT_EQEN_DISABLE 0x0 + +// *** SPKCRWDL *** +#define FB_SPKCRWDL_WDATA_L 0 +#define FM_SPKCRWDL_WDATA_L 0xFF + +// *** SPKCRWDM *** +#define FB_SPKCRWDM_WDATA_M 0 +#define FM_SPKCRWDM_WDATA_M 0xFF + +// *** SPKCRWDH *** +#define FB_SPKCRWDH_WDATA_H 0 +#define FM_SPKCRWDH_WDATA_H 0xFF + +// *** SPKCRRDL *** +#define FB_SPKCRRDL_RDATA_L 0 +#define FM_SPKCRRDL_RDATA_L 0xFF + +// *** SPKCRRDM *** +#define FB_SPKCRRDM_RDATA_M 0 +#define FM_SPKCRRDM_RDATA_M 0xFF + +// *** SPKCRRDH *** +#define FB_SPKCRRDH_RDATA_H 0 +#define FM_SPKCRRDH_RDATA_H 0xFF + +// *** SPKCRADD *** +#define FB_SPKCRADD_ADDRESS 0 +#define FM_SPKCRADD_ADDRESS 0xFF + +// *** SPKCRS *** +#define FB_SPKCRS_ACCSTAT 7 +#define FM_SPKCRS_ACCSTAT 0x80 + +// *** SPKMBCEN *** +#define FB_SPKMBCEN_MBCEN3 2 +#define FM_SPKMBCEN_MBCEN3 0x4 +#define FV_MBCEN3_ENABLE 0x4 +#define FV_MBCEN3_DISABLE 0x0 + +#define FB_SPKMBCEN_MBCEN2 1 +#define FM_SPKMBCEN_MBCEN2 0x2 +#define FV_MBCEN2_ENABLE 0x2 +#define FV_MBCEN2_DISABLE 0x0 + +#define FB_SPKMBCEN_MBCEN1 0 +#define FM_SPKMBCEN_MBCEN1 0x1 +#define FV_MBCEN1_ENABLE 0x1 +#define FV_MBCEN1_DISABLE 0x0 + +#define SPKMBCEN_MBCEN_ENABLE 0x1 +#define SPKMBCEN_MBCEN_DISABLE 0x0 + +// *** SPKMBCCTL *** +#define FB_SPKMBCCTL_LVLMODE3 5 +#define FM_SPKMBCCTL_LVLMODE3 0x20 + +#define FB_SPKMBCCTL_WINSEL3 4 +#define FM_SPKMBCCTL_WINSEL3 0x10 + +#define FB_SPKMBCCTL_LVLMODE2 3 +#define FM_SPKMBCCTL_LVLMODE2 0x8 + +#define FB_SPKMBCCTL_WINSEL2 2 +#define FM_SPKMBCCTL_WINSEL2 0x4 + +#define FB_SPKMBCCTL_LVLMODE1 1 +#define FM_SPKMBCCTL_LVLMODE1 0x2 + +#define FB_SPKMBCCTL_WINSEL1 0 +#define FM_SPKMBCCTL_WINSEL1 0x1 + +// *** SPKCLECTL *** +#define FB_SPKCLECTL_LVLMODE 4 +#define FM_SPKCLECTL_LVLMODE 0x10 + +#define FB_SPKCLECTL_WINSEL 3 +#define FM_SPKCLECTL_WINSEL 0x8 + +#define FB_SPKCLECTL_EXPEN 2 +#define FM_SPKCLECTL_EXPEN 0x4 +#define FV_EXPEN_ENABLE 0x4 +#define FV_EXPEN_DISABLE 0x0 + +#define FB_SPKCLECTL_LIMEN 1 +#define FM_SPKCLECTL_LIMEN 0x2 +#define FV_LIMEN_ENABLE 0x2 +#define FV_LIMEN_DISABLE 0x0 + +#define FB_SPKCLECTL_COMPEN 0 +#define FM_SPKCLECTL_COMPEN 0x1 +#define FV_COMPEN_ENABLE 0x1 +#define FV_COMPEN_DISABLE 0x0 + +// *** SPKCLEMUG *** +#define FB_SPKCLEMUG_MUGAIN 0 +#define FM_SPKCLEMUG_MUGAIN 0x1F + +// *** SPKCOMPTHR *** +#define FB_SPKCOMPTHR_THRESH 0 +#define FM_SPKCOMPTHR_THRESH 0xFF + +// *** SPKCOMPRAT *** +#define FB_SPKCOMPRAT_RATIO 0 +#define FM_SPKCOMPRAT_RATIO 0x1F + +// *** SPKCOMPATKL *** +#define FB_SPKCOMPATKL_TCATKL 0 +#define FM_SPKCOMPATKL_TCATKL 0xFF + +// *** SPKCOMPATKH *** +#define FB_SPKCOMPATKH_TCATKH 0 +#define FM_SPKCOMPATKH_TCATKH 0xFF + +// *** SPKCOMPRELL *** +#define FB_SPKCOMPRELL_TCRELL 0 +#define FM_SPKCOMPRELL_TCRELL 0xFF + +// *** SPKCOMPRELH *** +#define FB_SPKCOMPRELH_TCRELH 0 +#define FM_SPKCOMPRELH_TCRELH 0xFF + +// *** SPKLIMTHR *** +#define FB_SPKLIMTHR_THRESH 0 +#define FM_SPKLIMTHR_THRESH 0xFF + +// *** SPKLIMTGT *** +#define FB_SPKLIMTGT_TARGET 0 +#define FM_SPKLIMTGT_TARGET 0xFF + +// *** SPKLIMATKL *** +#define FB_SPKLIMATKL_TCATKL 0 +#define FM_SPKLIMATKL_TCATKL 0xFF + +// *** SPKLIMATKH *** +#define FB_SPKLIMATKH_TCATKH 0 +#define FM_SPKLIMATKH_TCATKH 0xFF + +// *** SPKLIMRELL *** +#define FB_SPKLIMRELL_TCRELL 0 +#define FM_SPKLIMRELL_TCRELL 0xFF + +// *** SPKLIMRELH *** +#define FB_SPKLIMRELH_TCRELH 0 +#define FM_SPKLIMRELH_TCRELH 0xFF + +// *** SPKEXPTHR *** +#define FB_SPKEXPTHR_THRESH 0 +#define FM_SPKEXPTHR_THRESH 0xFF + +// *** SPKEXPRAT *** +#define FB_SPKEXPRAT_RATIO 0 +#define FM_SPKEXPRAT_RATIO 0x7 + +// *** SPKEXPATKL *** +#define FB_SPKEXPATKL_TCATKL 0 +#define FM_SPKEXPATKL_TCATKL 0xFF + +// *** SPKEXPATKH *** +#define FB_SPKEXPATKH_TCATKH 0 +#define FM_SPKEXPATKH_TCATKH 0xFF + +// *** SPKEXPRELL *** +#define FB_SPKEXPRELL_TCRELL 0 +#define FM_SPKEXPRELL_TCRELL 0xFF + +// *** SPKEXPRELH *** +#define FB_SPKEXPRELH_TCRELH 0 +#define FM_SPKEXPRELH_TCRELH 0xFF + +// *** SPKFXCTL *** +#define FB_SPKFXCTL_3DEN 4 +#define FM_SPKFXCTL_3DEN 0x10 + +#define FB_SPKFXCTL_TEEN 3 +#define FM_SPKFXCTL_TEEN 0x8 + +#define FB_SPKFXCTL_TNLFBYP 2 +#define FM_SPKFXCTL_TNLFBYP 0x4 + +#define FB_SPKFXCTL_BEEN 1 +#define FM_SPKFXCTL_BEEN 0x2 + +#define FB_SPKFXCTL_BNLFBYP 0 +#define FM_SPKFXCTL_BNLFBYP 0x1 + +// *** DACEQFILT *** +#define FB_DACEQFILT_EQ2EN 7 +#define FM_DACEQFILT_EQ2EN 0x80 +#define FV_EQ2EN_ENABLE 0x80 +#define FV_EQ2EN_DISABLE 0x0 + +#define FB_DACEQFILT_EQ2BE 4 +#define FM_DACEQFILT_EQ2BE 0x70 + +#define FB_DACEQFILT_EQ1EN 3 +#define FM_DACEQFILT_EQ1EN 0x8 +#define FV_EQ1EN_ENABLE 0x8 +#define FV_EQ1EN_DISABLE 0x0 + +#define FB_DACEQFILT_EQ1BE 0 +#define FM_DACEQFILT_EQ1BE 0x7 + +#define DACEQFILT_EQEN_ENABLE 0x1 +#define DACEQFILT_EQEN_DISABLE 0x0 + +// *** DACCRWDL *** +#define FB_DACCRWDL_WDATA_L 0 +#define FM_DACCRWDL_WDATA_L 0xFF + +// *** DACCRWDM *** +#define FB_DACCRWDM_WDATA_M 0 +#define FM_DACCRWDM_WDATA_M 0xFF + +// *** DACCRWDH *** +#define FB_DACCRWDH_WDATA_H 0 +#define FM_DACCRWDH_WDATA_H 0xFF + +// *** DACCRRDL *** +#define FB_DACCRRDL_RDATA_L 0 +#define FM_DACCRRDL_RDATA_L 0xFF + +// *** DACCRRDM *** +#define FB_DACCRRDM_RDATA_M 0 +#define FM_DACCRRDM_RDATA_M 0xFF + +// *** DACCRRDH *** +#define FB_DACCRRDH_RDATA_H 0 +#define FM_DACCRRDH_RDATA_H 0xFF + +// *** DACCRADD *** +#define FB_DACCRADD_ADDRESS 0 +#define FM_DACCRADD_ADDRESS 0xFF + +// *** DACCRS *** +#define FB_DACCRS_ACCSTAT 7 +#define FM_DACCRS_ACCSTAT 0x80 + +// *** DACMBCEN *** +#define FB_DACMBCEN_MBCEN3 2 +#define FM_DACMBCEN_MBCEN3 0x4 +#define FV_MBCEN3_ENABLE 0x4 +#define FV_MBCEN3_DISABLE 0x0 + +#define FB_DACMBCEN_MBCEN2 1 +#define FM_DACMBCEN_MBCEN2 0x2 +#define FV_MBCEN2_ENABLE 0x2 +#define FV_MBCEN2_DISABLE 0x0 + +#define FB_DACMBCEN_MBCEN1 0 +#define FM_DACMBCEN_MBCEN1 0x1 +#define FV_MBCEN1_ENABLE 0x1 +#define FV_MBCEN1_DISABLE 0x0 + +#define DACMBCEN_MBCEN_ENABLE 0x1 +#define DACMBCEN_MBCEN_DISABLE 0x0 + +// *** DACMBCCTL *** +#define FB_DACMBCCTL_LVLMODE3 5 +#define FM_DACMBCCTL_LVLMODE3 0x20 + +#define FB_DACMBCCTL_WINSEL3 4 +#define FM_DACMBCCTL_WINSEL3 0x10 + +#define FB_DACMBCCTL_LVLMODE2 3 +#define FM_DACMBCCTL_LVLMODE2 0x8 + +#define FB_DACMBCCTL_WINSEL2 2 +#define FM_DACMBCCTL_WINSEL2 0x4 + +#define FB_DACMBCCTL_LVLMODE1 1 +#define FM_DACMBCCTL_LVLMODE1 0x2 + +#define FB_DACMBCCTL_WINSEL1 0 +#define FM_DACMBCCTL_WINSEL1 0x1 + +// *** DACCLECTL *** +#define FB_DACCLECTL_LVLMODE 4 +#define FM_DACCLECTL_LVLMODE 0x10 + +#define FB_DACCLECTL_WINSEL 3 +#define FM_DACCLECTL_WINSEL 0x8 + +#define FB_DACCLECTL_EXPEN 2 +#define FM_DACCLECTL_EXPEN 0x4 +#define FV_EXPEN_ENABLE 0x4 +#define FV_EXPEN_DISABLE 0x0 + +#define FB_DACCLECTL_LIMEN 1 +#define FM_DACCLECTL_LIMEN 0x2 +#define FV_LIMEN_ENABLE 0x2 +#define FV_LIMEN_DISABLE 0x0 + +#define FB_DACCLECTL_COMPEN 0 +#define FM_DACCLECTL_COMPEN 0x1 +#define FV_COMPEN_ENABLE 0x1 +#define FV_COMPEN_DISABLE 0x0 + +// *** DACCLEMUG *** +#define FB_DACCLEMUG_MUGAIN 0 +#define FM_DACCLEMUG_MUGAIN 0x1F + +// *** DACCOMPTHR *** +#define FB_DACCOMPTHR_THRESH 0 +#define FM_DACCOMPTHR_THRESH 0xFF + +// *** DACCOMPRAT *** +#define FB_DACCOMPRAT_RATIO 0 +#define FM_DACCOMPRAT_RATIO 0x1F + +// *** DACCOMPATKL *** +#define FB_DACCOMPATKL_TCATKL 0 +#define FM_DACCOMPATKL_TCATKL 0xFF + +// *** DACCOMPATKH *** +#define FB_DACCOMPATKH_TCATKH 0 +#define FM_DACCOMPATKH_TCATKH 0xFF + +// *** DACCOMPRELL *** +#define FB_DACCOMPRELL_TCRELL 0 +#define FM_DACCOMPRELL_TCRELL 0xFF + +// *** DACCOMPRELH *** +#define FB_DACCOMPRELH_TCRELH 0 +#define FM_DACCOMPRELH_TCRELH 0xFF + +// *** DACLIMTHR *** +#define FB_DACLIMTHR_THRESH 0 +#define FM_DACLIMTHR_THRESH 0xFF + +// *** DACLIMTGT *** +#define FB_DACLIMTGT_TARGET 0 +#define FM_DACLIMTGT_TARGET 0xFF + +// *** DACLIMATKL *** +#define FB_DACLIMATKL_TCATKL 0 +#define FM_DACLIMATKL_TCATKL 0xFF + +// *** DACLIMATKH *** +#define FB_DACLIMATKH_TCATKH 0 +#define FM_DACLIMATKH_TCATKH 0xFF + +// *** DACLIMRELL *** +#define FB_DACLIMRELL_TCRELL 0 +#define FM_DACLIMRELL_TCRELL 0xFF + +// *** DACLIMRELH *** +#define FB_DACLIMRELH_TCRELH 0 +#define FM_DACLIMRELH_TCRELH 0xFF + +// *** DACEXPTHR *** +#define FB_DACEXPTHR_THRESH 0 +#define FM_DACEXPTHR_THRESH 0xFF + +// *** DACEXPRAT *** +#define FB_DACEXPRAT_RATIO 0 +#define FM_DACEXPRAT_RATIO 0x7 + +// *** DACEXPATKL *** +#define FB_DACEXPATKL_TCATKL 0 +#define FM_DACEXPATKL_TCATKL 0xFF + +// *** DACEXPATKH *** +#define FB_DACEXPATKH_TCATKH 0 +#define FM_DACEXPATKH_TCATKH 0xFF + +// *** DACEXPRELL *** +#define FB_DACEXPRELL_TCRELL 0 +#define FM_DACEXPRELL_TCRELL 0xFF + +// *** DACEXPRELH *** +#define FB_DACEXPRELH_TCRELH 0 +#define FM_DACEXPRELH_TCRELH 0xFF + +// *** DACFXCTL *** +#define FB_DACFXCTL_3DEN 4 +#define FM_DACFXCTL_3DEN 0x10 + +#define FB_DACFXCTL_TEEN 3 +#define FM_DACFXCTL_TEEN 0x8 + +#define FB_DACFXCTL_TNLFBYP 2 +#define FM_DACFXCTL_TNLFBYP 0x4 + +#define FB_DACFXCTL_BEEN 1 +#define FM_DACFXCTL_BEEN 0x2 + +#define FB_DACFXCTL_BNLFBYP 0 +#define FM_DACFXCTL_BNLFBYP 0x1 + +// *** SUBEQFILT *** +#define FB_SUBEQFILT_EQ2EN 7 +#define FM_SUBEQFILT_EQ2EN 0x80 +#define FV_EQ2EN_ENABLE 0x80 +#define FV_EQ2EN_DISABLE 0x0 + +#define FB_SUBEQFILT_EQ2BE 4 +#define FM_SUBEQFILT_EQ2BE 0x70 + +#define FB_SUBEQFILT_EQ1EN 3 +#define FM_SUBEQFILT_EQ1EN 0x8 +#define FV_EQ1EN_ENABLE 0x8 +#define FV_EQ1EN_DISABLE 0x0 + +#define FB_SUBEQFILT_EQ1BE 0 +#define FM_SUBEQFILT_EQ1BE 0x7 + +#define SUBEQFILT_EQEN_ENABLE 0x1 +#define SUBEQFILT_EQEN_DISABLE 0x0 + +// *** SUBCRWDL *** +#define FB_SUBCRWDL_WDATA_L 0 +#define FM_SUBCRWDL_WDATA_L 0xFF + +// *** SUBCRWDM *** +#define FB_SUBCRWDM_WDATA_M 0 +#define FM_SUBCRWDM_WDATA_M 0xFF + +// *** SUBCRWDH *** +#define FB_SUBCRWDH_WDATA_H 0 +#define FM_SUBCRWDH_WDATA_H 0xFF + +// *** SUBCRRDL *** +#define FB_SUBCRRDL_RDATA_L 0 +#define FM_SUBCRRDL_RDATA_L 0xFF + +// *** SUBCRRDM *** +#define FB_SUBCRRDM_RDATA_M 0 +#define FM_SUBCRRDM_RDATA_M 0xFF + +// *** SUBCRRDH *** +#define FB_SUBCRRDH_RDATA_H 0 +#define FM_SUBCRRDH_RDATA_H 0xFF + +// *** SUBCRADD *** +#define FB_SUBCRADD_ADDRESS 0 +#define FM_SUBCRADD_ADDRESS 0xFF + +// *** SUBCRS *** +#define FB_SUBCRS_ACCSTAT 7 +#define FM_SUBCRS_ACCSTAT 0x80 + +// *** SUBMBCEN *** +#define FB_SUBMBCEN_MBCEN3 2 +#define FM_SUBMBCEN_MBCEN3 0x4 +#define FV_MBCEN3_ENABLE 0x4 +#define FV_MBCEN3_DISABLE 0x0 + +#define FB_SUBMBCEN_MBCEN2 1 +#define FM_SUBMBCEN_MBCEN2 0x2 +#define FV_MBCEN2_ENABLE 0x2 +#define FV_MBCEN2_DISABLE 0x0 + +#define FB_SUBMBCEN_MBCEN1 0 +#define FM_SUBMBCEN_MBCEN1 0x1 +#define FV_MBCEN1_ENABLE 0x1 +#define FV_MBCEN1_DISABLE 0x0 + +#define SUBMBCEN_MBCEN_ENABLE 0x1 +#define SUBMBCEN_MBCEN_DISABLE 0x0 + +// *** SUBMBCCTL *** +#define FB_SUBMBCCTL_LVLMODE3 5 +#define FM_SUBMBCCTL_LVLMODE3 0x20 + +#define FB_SUBMBCCTL_WINSEL3 4 +#define FM_SUBMBCCTL_WINSEL3 0x10 + +#define FB_SUBMBCCTL_LVLMODE2 3 +#define FM_SUBMBCCTL_LVLMODE2 0x8 + +#define FB_SUBMBCCTL_WINSEL2 2 +#define FM_SUBMBCCTL_WINSEL2 0x4 + +#define FB_SUBMBCCTL_LVLMODE1 1 +#define FM_SUBMBCCTL_LVLMODE1 0x2 + +#define FB_SUBMBCCTL_WINSEL1 0 +#define FM_SUBMBCCTL_WINSEL1 0x1 + +// *** SUBCLECTL *** +#define FB_SUBCLECTL_LVLMODE 4 +#define FM_SUBCLECTL_LVLMODE 0x10 + +#define FB_SUBCLECTL_WINSEL 3 +#define FM_SUBCLECTL_WINSEL 0x8 + +#define FB_SUBCLECTL_EXPEN 2 +#define FM_SUBCLECTL_EXPEN 0x4 +#define FV_EXPEN_ENABLE 0x4 +#define FV_EXPEN_DISABLE 0x0 + +#define FB_SUBCLECTL_LIMEN 1 +#define FM_SUBCLECTL_LIMEN 0x2 +#define FV_LIMEN_ENABLE 0x2 +#define FV_LIMEN_DISABLE 0x0 + +#define FB_SUBCLECTL_COMPEN 0 +#define FM_SUBCLECTL_COMPEN 0x1 +#define FV_COMPEN_ENABLE 0x1 +#define FV_COMPEN_DISABLE 0x0 + +// *** SUBCLEMUG *** +#define FB_SUBCLEMUG_MUGAIN 0 +#define FM_SUBCLEMUG_MUGAIN 0x1F + +// *** SUBCOMPTHR *** +#define FB_SUBCOMPTHR_THRESH 0 +#define FM_SUBCOMPTHR_THRESH 0xFF + +// *** SUBCOMPRAT *** +#define FB_SUBCOMPRAT_RATIO 0 +#define FM_SUBCOMPRAT_RATIO 0x1F + +// *** SUBCOMPATKL *** +#define FB_SUBCOMPATKL_TCATKL 0 +#define FM_SUBCOMPATKL_TCATKL 0xFF + +// *** SUBCOMPATKH *** +#define FB_SUBCOMPATKH_TCATKH 0 +#define FM_SUBCOMPATKH_TCATKH 0xFF + +// *** SUBCOMPRELL *** +#define FB_SUBCOMPRELL_TCRELL 0 +#define FM_SUBCOMPRELL_TCRELL 0xFF + +// *** SUBCOMPRELH *** +#define FB_SUBCOMPRELH_TCRELH 0 +#define FM_SUBCOMPRELH_TCRELH 0xFF + +// *** SUBLIMTHR *** +#define FB_SUBLIMTHR_THRESH 0 +#define FM_SUBLIMTHR_THRESH 0xFF + +// *** SUBLIMTGT *** +#define FB_SUBLIMTGT_TARGET 0 +#define FM_SUBLIMTGT_TARGET 0xFF + +// *** SUBLIMATKL *** +#define FB_SUBLIMATKL_TCATKL 0 +#define FM_SUBLIMATKL_TCATKL 0xFF + +// *** SUBLIMATKH *** +#define FB_SUBLIMATKH_TCATKH 0 +#define FM_SUBLIMATKH_TCATKH 0xFF + +// *** SUBLIMRELL *** +#define FB_SUBLIMRELL_TCRELL 0 +#define FM_SUBLIMRELL_TCRELL 0xFF + +// *** SUBLIMRELH *** +#define FB_SUBLIMRELH_TCRELH 0 +#define FM_SUBLIMRELH_TCRELH 0xFF + +// *** SUBEXPTHR *** +#define FB_SUBEXPTHR_THRESH 0 +#define FM_SUBEXPTHR_THRESH 0xFF + +// *** SUBEXPRAT *** +#define FB_SUBEXPRAT_RATIO 0 +#define FM_SUBEXPRAT_RATIO 0x7 + +// *** SUBEXPATKL *** +#define FB_SUBEXPATKL_TCATKL 0 +#define FM_SUBEXPATKL_TCATKL 0xFF + +// *** SUBEXPATKH *** +#define FB_SUBEXPATKH_TCATKH 0 +#define FM_SUBEXPATKH_TCATKH 0xFF + +// *** SUBEXPRELL *** +#define FB_SUBEXPRELL_TCRELL 0 +#define FM_SUBEXPRELL_TCRELL 0xFF + +// *** SUBEXPRELH *** +#define FB_SUBEXPRELH_TCRELH 0 +#define FM_SUBEXPRELH_TCRELH 0xFF + +// *** SUBFXCTL *** +#define FB_SUBFXCTL_TEEN 3 +#define FM_SUBFXCTL_TEEN 0x8 + +#define FB_SUBFXCTL_TNLFBYP 2 +#define FM_SUBFXCTL_TNLFBYP 0x4 + +#define FB_SUBFXCTL_BEEN 1 +#define FM_SUBFXCTL_BEEN 0x2 + +#define FB_SUBFXCTL_BNLFBYP 0 +#define FM_SUBFXCTL_BNLFBYP 0x1 + +#endif /* __REDWOODPUBLIC_H__ */ diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index d5f4bbf27182..3663b9fd4d65 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1155,8 +1155,8 @@ SOC_DOUBLE_R_TLV("IN3 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_3L, SND_SOC_BYTES_MASK("EQL Coefficients", WM2200_EQL_1, 20, WM2200_EQL_ENA), SND_SOC_BYTES_MASK("EQR Coefficients", WM2200_EQR_1, 20, WM2200_EQR_ENA), -SND_SOC_BYTES("LHPF1 Coefficeints", WM2200_HPLPF1_2, 1), -SND_SOC_BYTES("LHPF2 Coefficeints", WM2200_HPLPF2_2, 1), +SND_SOC_BYTES("LHPF1 Coefficients", WM2200_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficients", WM2200_HPLPF2_2, 1), SOC_SINGLE("OUT1 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_1L, WM2200_OUT1_OSR_SHIFT, 1, 0), diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 87f9a99ce978..ba89d9d711f7 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -573,10 +573,10 @@ SND_SOC_BYTES_MASK("EQ4 Coefficients", WM5100_EQ4_1, 20, WM5100_EQ4_ENA), SND_SOC_BYTES_MASK("DRC Coefficients", WM5100_DRC1_CTRL1, 5, WM5100_DRCL_ENA | WM5100_DRCR_ENA), -SND_SOC_BYTES("LHPF1 Coefficeints", WM5100_HPLPF1_2, 1), -SND_SOC_BYTES("LHPF2 Coefficeints", WM5100_HPLPF2_2, 1), -SND_SOC_BYTES("LHPF3 Coefficeints", WM5100_HPLPF3_2, 1), -SND_SOC_BYTES("LHPF4 Coefficeints", WM5100_HPLPF4_2, 1), +SND_SOC_BYTES("LHPF1 Coefficients", WM5100_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficients", WM5100_HPLPF2_2, 1), +SND_SOC_BYTES("LHPF3 Coefficients", WM5100_HPLPF3_2, 1), +SND_SOC_BYTES("LHPF4 Coefficients", WM5100_HPLPF4_2, 1), SOC_SINGLE("HPOUT1 High Performance Switch", WM5100_OUT_VOLUME_1L, WM5100_OUT1_OSR_SHIFT, 1, 0), diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c index 7949703f3d04..317db9a149a7 100644 --- a/sound/soc/codecs/wm8782.c +++ b/sound/soc/codecs/wm8782.c @@ -67,9 +67,18 @@ static int wm8782_probe(struct platform_device *pdev) &soc_component_dev_wm8782, &wm8782_dai, 1); } +#ifdef CONFIG_OF +static const struct of_device_id wm8782_of_match[] = { + { .compatible = "wlf,wm8782", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8782_of_match); +#endif + static struct platform_driver wm8782_codec_driver = { .driver = { .name = "wm8782", + .of_match_table = of_match_ptr(wm8782_of_match), }, .probe = wm8782_probe, }; diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 20fdcae06c6b..f13ef334c0d7 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -596,7 +596,7 @@ static const struct snd_kcontrol_new wm8904_adc_snd_controls[] = { SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8904_ADC_DIGITAL_VOLUME_LEFT, WM8904_ADC_DIGITAL_VOLUME_RIGHT, 1, 119, 0, digital_tlv), -SOC_ENUM("Left Caputure Mode", lin_mode), +SOC_ENUM("Left Capture Mode", lin_mode), SOC_ENUM("Right Capture Mode", rin_mode), /* No TLV since it depends on mode */ diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 82b0927e6ed7..2175dccdf388 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -627,22 +627,21 @@ static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, if (!root) goto err; - if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted)) + if (!debugfs_create_bool("booted", 0444, root, &dsp->booted)) goto err; - if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) + if (!debugfs_create_bool("running", 0444, root, &dsp->running)) goto err; - if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id)) + if (!debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id)) goto err; - if (!debugfs_create_x32("fw_version", S_IRUGO, root, - &dsp->fw_id_version)) + if (!debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version)) goto err; for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, - S_IRUGO, root, dsp, + 0444, root, dsp, &wm_adsp_debugfs_fops[i].fops)) goto err; } @@ -2666,9 +2665,9 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, dsp->preloaded = ucontrol->value.integer.value[0]; if (ucontrol->value.integer.value[0]) - snd_soc_dapm_force_enable_pin(dapm, preload); + snd_soc_component_force_enable_pin(component, preload); else - snd_soc_dapm_disable_pin(dapm, preload); + snd_soc_component_disable_pin(component, preload); snd_soc_dapm_sync(dapm); @@ -2852,11 +2851,11 @@ EXPORT_SYMBOL_GPL(wm_adsp2_event); int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) { - struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); char preload[32]; snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", dsp->num); - snd_soc_dapm_disable_pin(dapm, preload); + + snd_soc_component_disable_pin(component, preload); wm_adsp2_init_debugfs(dsp, component); diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 6b732d8e5896..778faff28e0e 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -24,7 +24,7 @@ config SND_DAVINCI_SOC_I2S config SND_DAVINCI_SOC_MCASP tristate "Multichannel Audio Serial Port (McASP) support" - depends on SND_OMAP_SOC || SND_EDMA_SOC + depends on SND_SDMA_SOC || SND_EDMA_SOC help Say Y or M here if you want to have support for McASP IP found in various Texas Instruments SoCs like: diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 03ba218160ca..1f96c9dbe9c4 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -36,9 +36,9 @@ #include <sound/initval.h> #include <sound/soc.h> #include <sound/dmaengine_pcm.h> -#include <sound/omap-pcm.h> #include "edma-pcm.h" +#include "../omap/sdma-pcm.h" #include "davinci-mcasp.h" #define MCASP_MAX_AFIFO_DEPTH 64 @@ -789,7 +789,7 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, rx_ser < max_active_serializers) { mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i)); rx_ser++; - } else { + } else if (mcasp->serial_dir[i] == INACTIVE_MODE) { mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), SRMOD_INACTIVE, SRMOD_MASK); } @@ -2048,10 +2048,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) #endif break; case PCM_SDMA: -#if IS_BUILTIN(CONFIG_SND_OMAP_SOC) || \ +#if IS_BUILTIN(CONFIG_SND_SDMA_SOC) || \ (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ - IS_MODULE(CONFIG_SND_OMAP_SOC)) - ret = omap_pcm_platform_register(&pdev->dev); + IS_MODULE(CONFIG_SND_SDMA_SOC)) + ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL); #else dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n"); ret = -EINVAL; diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index da8fd98c7f51..8f43110373b8 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -1,12 +1,8 @@ -/* - * Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver - * - * Copyright (C) 2014 Freescale Semiconductor, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver +// +// Copyright (C) 2014 Freescale Semiconductor, Inc. #include <linux/clk.h> #include <linux/dmaengine.h> @@ -226,6 +222,12 @@ static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned long clk_rate; int ret; + if (freq == 0) { + dev_err(dai->dev, "%sput freq of HCK%c should not be 0Hz\n", + in ? "in" : "out", tx ? 'T' : 'R'); + return -EINVAL; + } + /* Bypass divider settings if the requirement doesn't change */ if (freq == esai_priv->hck_rate[tx] && dir == esai_priv->hck_dir[tx]) return 0; diff --git a/sound/soc/fsl/fsl_esai.h b/sound/soc/fsl/fsl_esai.h index 5e793bbb6b02..f873588d9045 100644 --- a/sound/soc/fsl/fsl_esai.h +++ b/sound/soc/fsl/fsl_esai.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * fsl_esai.h - ALSA ESAI interface for the Freescale i.MX SoC * * Copyright (C) 2014 Freescale Semiconductor, Inc. * * Author: Nicolin Chen <Guangyu.Chen@freescale.com> - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _FSL_ESAI_DAI_H diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 18e5ce81527d..4163f2cfc06f 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1,14 +1,8 @@ -/* - * Freescale ALSA SoC Digital Audio Interface (SAI) driver. - * - * Copyright 2012-2015 Freescale Semiconductor, Inc. - * - * This program is free software, you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation, either version 2 of the License, or(at your - * option) any later version. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Freescale ALSA SoC Digital Audio Interface (SAI) driver. +// +// Copyright 2012-2015 Freescale Semiconductor, Inc. #include <linux/clk.h> #include <linux/delay.h> diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index d9ed7be8cb34..24cb156bf995 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -1,9 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright 2012-2013 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef __FSL_SAI_H diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 4f7469c1864c..9b59d87b61bf 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1,17 +1,13 @@ -/* - * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver - * - * Copyright (C) 2013 Freescale Semiconductor, Inc. - * - * Based on stmp3xxx_spdif_dai.c - * Vladimir Barinov <vbarinov@embeddedalley.com> - * Copyright 2008 SigmaTel, Inc - * Copyright 2008 Embedded Alley Solutions, Inc - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver +// +// Copyright (C) 2013 Freescale Semiconductor, Inc. +// +// Based on stmp3xxx_spdif_dai.c +// Vladimir Barinov <vbarinov@embeddedalley.com> +// Copyright 2008 SigmaTel, Inc +// Copyright 2008 Embedded Alley Solutions, Inc #include <linux/bitrev.h> #include <linux/clk.h> diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h index 00bd3514c610..7666dabaccfd 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * fsl_spdif.h - ALSA S/PDIF interface for the Freescale i.MX SoC * @@ -8,10 +9,6 @@ * Based on fsl_ssi.h * Author: Timur Tabi <timur@freescale.com> * Copyright 2007-2008 Freescale Semiconductor, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _FSL_SPDIF_DAI_H diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 89df2d9f63d7..0a648229e643 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1,34 +1,29 @@ -/* - * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver - * - * Author: Timur Tabi <timur@freescale.com> - * - * Copyright 2007-2010 Freescale Semiconductor, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * - * - * Some notes why imx-pcm-fiq is used instead of DMA on some boards: - * - * The i.MX SSI core has some nasty limitations in AC97 mode. While most - * sane processor vendors have a FIFO per AC97 slot, the i.MX has only - * one FIFO which combines all valid receive slots. We cannot even select - * which slots we want to receive. The WM9712 with which this driver - * was developed with always sends GPIO status data in slot 12 which - * we receive in our (PCM-) data stream. The only chance we have is to - * manually skip this data in the FIQ handler. With sampling rates different - * from 48000Hz not every frame has valid receive data, so the ratio - * between pcm data and GPIO status data changes. Our FIQ handler is not - * able to handle this, hence this driver only works with 48000Hz sampling - * rate. - * Reading and writing AC97 registers is another challenge. The core - * provides us status bits when the read register is updated with *another* - * value. When we read the same register two times (and the register still - * contains the same value) these status bits are not set. We work - * around this by not polling these bits but only wait a fixed delay. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver +// +// Author: Timur Tabi <timur@freescale.com> +// +// Copyright 2007-2010 Freescale Semiconductor, Inc. +// +// Some notes why imx-pcm-fiq is used instead of DMA on some boards: +// +// The i.MX SSI core has some nasty limitations in AC97 mode. While most +// sane processor vendors have a FIFO per AC97 slot, the i.MX has only +// one FIFO which combines all valid receive slots. We cannot even select +// which slots we want to receive. The WM9712 with which this driver +// was developed with always sends GPIO status data in slot 12 which +// we receive in our (PCM-) data stream. The only chance we have is to +// manually skip this data in the FIQ handler. With sampling rates different +// from 48000Hz not every frame has valid receive data, so the ratio +// between pcm data and GPIO status data changes. Our FIQ handler is not +// able to handle this, hence this driver only works with 48000Hz sampling +// rate. +// Reading and writing AC97 registers is another challenge. The core +// provides us status bits when the read register is updated with *another* +// value. When we read the same register two times (and the register still +// contains the same value) these status bits are not set. We work +// around this by not polling these bits but only wait a fixed delay. #include <linux/init.h> #include <linux/io.h> @@ -385,8 +380,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) { struct fsl_ssi *ssi = dev_id; struct regmap *regs = ssi->regs; - __be32 sisr; - __be32 sisr2; + u32 sisr, sisr2; regmap_read(regs, REG_SSI_SISR, &sisr); diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h index 18f8dd5209d5..0bdda608d414 100644 --- a/sound/soc/fsl/fsl_ssi.h +++ b/sound/soc/fsl/fsl_ssi.h @@ -1,12 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 and i.MX SoC * * Author: Timur Tabi <timur@freescale.com> * - * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed - * under the terms of the GNU General Public License version 2. This - * program is licensed "as is" without any warranty of any kind, whether - * express or implied. + * Copyright 2007-2008 Freescale Semiconductor, Inc. */ #ifndef _MPC8610_I2S_H diff --git a/sound/soc/fsl/fsl_ssi_dbg.c b/sound/soc/fsl/fsl_ssi_dbg.c index 7aac63e2c561..1255dfe19eef 100644 --- a/sound/soc/fsl/fsl_ssi_dbg.c +++ b/sound/soc/fsl/fsl_ssi_dbg.c @@ -1,14 +1,10 @@ -/* - * Freescale SSI ALSA SoC Digital Audio Interface (DAI) debugging functions - * - * Copyright 2014 Markus Pargmann <mpa@pengutronix.de>, Pengutronix - * - * Splitted from fsl_ssi.c - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// Freescale SSI ALSA SoC Digital Audio Interface (DAI) debugging functions +// +// Copyright 2014 Markus Pargmann <mpa@pengutronix.de>, Pengutronix +// +// Split from fsl_ssi.c #include <linux/debugfs.h> #include <linux/device.h> @@ -146,7 +142,7 @@ int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev) if (!ssi_dbg->dbg_dir) return -ENOMEM; - ssi_dbg->dbg_stats = debugfs_create_file("stats", S_IRUGO, + ssi_dbg->dbg_stats = debugfs_create_file("stats", 0444, ssi_dbg->dbg_dir, ssi_dbg, &fsl_ssi_stats_ops); if (!ssi_dbg->dbg_stats) { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 6959a74a6f49..4a516c428b3d 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -135,6 +135,18 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) asoc_simple_card_clk_disable(&dai_props->codec_dai); } +static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai, + unsigned long rate) +{ + if (!simple_dai->clk) + return 0; + + if (clk_get_rate(simple_dai->clk) == rate) + return 0; + + return clk_set_rate(simple_dai->clk, rate); +} + static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -154,6 +166,15 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, if (mclk_fs) { mclk = params_rate(params) * mclk_fs; + + ret = asoc_simple_set_clk_rate(&dai_props->codec_dai, mclk); + if (ret < 0) + return ret; + + ret = asoc_simple_set_clk_rate(&dai_props->cpu_dai, mclk); + if (ret < 0) + return ret; + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); if (ret && ret != -ENOTSUPP) diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c index 07a57209e055..53344a3b7a60 100644 --- a/sound/soc/hisilicon/hi6210-i2s.c +++ b/sound/soc/hisilicon/hi6210-i2s.c @@ -498,7 +498,7 @@ static int hi6210_i2s_trigger(struct snd_pcm_substream *substream, int cmd, hi6210_i2s_txctrl(cpu_dai, 0); break; default: - dev_err(cpu_dai->dev, "uknown cmd\n"); + dev_err(cpu_dai->dev, "unknown cmd\n"); return -EINVAL; } return 0; diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index addac2a8e52a..0caa1f4eb94d 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -61,7 +61,7 @@ config SND_SOC_INTEL_HASWELL config SND_SOC_INTEL_BAYTRAIL tristate "Baytrail (legacy) Platforms" - depends on DMADEVICES && ACPI + depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 668c0934e942..40eb979d5ac1 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -571,7 +571,7 @@ static int broxton_audio_probe(struct platform_device *pdev) { struct bxt_card_private *ctx; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index c7e9024e65ef..b68c289558a8 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -593,7 +593,7 @@ static int broxton_audio_probe(struct platform_device *pdev) } } - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/byt-max98090.c b/sound/soc/intel/boards/byt-max98090.c index 0f8b8209c020..f1283634b22b 100644 --- a/sound/soc/intel/boards/byt-max98090.c +++ b/sound/soc/intel/boards/byt-max98090.c @@ -151,7 +151,7 @@ static int byt_max98090_probe(struct platform_device *pdev) struct byt_max98090_private *priv; int ret_val; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(&pdev->dev, "allocation failed\n"); return -ENOMEM; diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 305e7f4fe55a..adc26dfc7d65 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -243,7 +243,7 @@ static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) int i; int ret = 0; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a8d8bff788e7..33065ba294a9 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -17,6 +17,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include <linux/i2c.h> #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -25,6 +26,7 @@ #include <linux/clk.h> #include <linux/device.h> #include <linux/dmi.h> +#include <linux/input.h> #include <linux/slab.h> #include <asm/cpu_device_id.h> #include <asm/platform_sst_audio.h> @@ -33,6 +35,7 @@ #include <sound/soc.h> #include <sound/jack.h> #include <sound/soc-acpi.h> +#include <dt-bindings/sound/rt5640.h> #include "../../codecs/rt5640.h" #include "../atom/sst-atom-controls.h" #include "../common/sst-dsp.h" @@ -44,17 +47,53 @@ enum { BYT_RT5640_IN3_MAP, }; -#define BYT_RT5640_MAP(quirk) ((quirk) & GENMASK(7, 0)) -#define BYT_RT5640_DMIC_EN BIT(16) -#define BYT_RT5640_MONO_SPEAKER BIT(17) -#define BYT_RT5640_DIFF_MIC BIT(18) /* defaut is single-ended */ -#define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */ -#define BYT_RT5640_SSP0_AIF1 BIT(20) -#define BYT_RT5640_SSP0_AIF2 BIT(21) -#define BYT_RT5640_MCLK_EN BIT(22) -#define BYT_RT5640_MCLK_25MHZ BIT(23) +enum { + BYT_RT5640_JD_SRC_GPIO1 = (RT5640_JD_SRC_GPIO1 << 4), + BYT_RT5640_JD_SRC_JD1_IN4P = (RT5640_JD_SRC_JD1_IN4P << 4), + BYT_RT5640_JD_SRC_JD2_IN4N = (RT5640_JD_SRC_JD2_IN4N << 4), + BYT_RT5640_JD_SRC_GPIO2 = (RT5640_JD_SRC_GPIO2 << 4), + BYT_RT5640_JD_SRC_GPIO3 = (RT5640_JD_SRC_GPIO3 << 4), + BYT_RT5640_JD_SRC_GPIO4 = (RT5640_JD_SRC_GPIO4 << 4), +}; + +enum { + BYT_RT5640_OVCD_TH_600UA = (6 << 8), + BYT_RT5640_OVCD_TH_1500UA = (15 << 8), + BYT_RT5640_OVCD_TH_2000UA = (20 << 8), +}; + +enum { + BYT_RT5640_OVCD_SF_0P5 = (RT5640_OVCD_SF_0P5 << 13), + BYT_RT5640_OVCD_SF_0P75 = (RT5640_OVCD_SF_0P75 << 13), + BYT_RT5640_OVCD_SF_1P0 = (RT5640_OVCD_SF_1P0 << 13), + BYT_RT5640_OVCD_SF_1P5 = (RT5640_OVCD_SF_1P5 << 13), +}; + +#define BYT_RT5640_MAP(quirk) ((quirk) & GENMASK(3, 0)) +#define BYT_RT5640_JDSRC(quirk) (((quirk) & GENMASK(7, 4)) >> 4) +#define BYT_RT5640_OVCD_TH(quirk) (((quirk) & GENMASK(12, 8)) >> 8) +#define BYT_RT5640_OVCD_SF(quirk) (((quirk) & GENMASK(14, 13)) >> 13) +#define BYT_RT5640_JD_NOT_INV BIT(16) +#define BYT_RT5640_MONO_SPEAKER BIT(17) +#define BYT_RT5640_DIFF_MIC BIT(18) /* default is single-ended */ +#define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */ +#define BYT_RT5640_SSP0_AIF1 BIT(20) +#define BYT_RT5640_SSP0_AIF2 BIT(21) +#define BYT_RT5640_MCLK_EN BIT(22) +#define BYT_RT5640_MCLK_25MHZ BIT(23) + +#define BYTCR_INPUT_DEFAULTS \ + (BYT_RT5640_IN3_MAP | \ + BYT_RT5640_JD_SRC_JD1_IN4P | \ + BYT_RT5640_OVCD_TH_2000UA | \ + BYT_RT5640_OVCD_SF_0P75 | \ + BYT_RT5640_DIFF_MIC) + +/* in-diff or dmic-pin + jdsrc + ovcd-th + -sf + jd-inv + terminating entry */ +#define MAX_NO_PROPS 6 struct byt_rt5640_private { + struct snd_soc_jack jack; struct clk *mclk; }; static bool is_bytcr; @@ -67,7 +106,6 @@ MODULE_PARM_DESC(quirk, "Board-specific quirk override"); static void log_quirks(struct device *dev) { int map; - bool has_dmic = false; bool has_mclk = false; bool has_ssp0 = false; bool has_ssp0_aif1 = false; @@ -78,11 +116,9 @@ static void log_quirks(struct device *dev) switch (map) { case BYT_RT5640_DMIC1_MAP: dev_info(dev, "quirk DMIC1_MAP enabled\n"); - has_dmic = true; break; case BYT_RT5640_DMIC2_MAP: dev_info(dev, "quirk DMIC2_MAP enabled\n"); - has_dmic = true; break; case BYT_RT5640_IN1_MAP: dev_info(dev, "quirk IN1_MAP enabled\n"); @@ -94,20 +130,20 @@ static void log_quirks(struct device *dev) dev_err(dev, "quirk map 0x%x is not supported, microphone input will not work\n", map); break; } - if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) { - if (has_dmic) - dev_info(dev, "quirk DMIC enabled\n"); - else - dev_err(dev, "quirk DMIC enabled but no DMIC input set, will be ignored\n"); + if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) { + dev_info(dev, "quirk realtek,jack-detect-source %ld\n", + BYT_RT5640_JDSRC(byt_rt5640_quirk)); + dev_info(dev, "quirk realtek,over-current-threshold-microamp %ld\n", + BYT_RT5640_OVCD_TH(byt_rt5640_quirk) * 100); + dev_info(dev, "quirk realtek,over-current-scale-factor %ld\n", + BYT_RT5640_OVCD_SF(byt_rt5640_quirk)); } + if (byt_rt5640_quirk & BYT_RT5640_JD_NOT_INV) + dev_info(dev, "quirk JD_NOT_INV enabled\n"); if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) dev_info(dev, "quirk MONO_SPEAKER enabled\n"); - if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) { - if (!has_dmic) - dev_info(dev, "quirk DIFF_MIC enabled\n"); - else - dev_info(dev, "quirk DIFF_MIC enabled but DMIC input selected, will be ignored\n"); - } + if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) + dev_info(dev, "quirk DIFF_MIC enabled\n"); if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) { dev_info(dev, "quirk SSP0_AIF1 enabled\n"); has_ssp0 = true; @@ -141,6 +177,52 @@ static void log_quirks(struct device *dev) } } +static int byt_rt5640_prepare_and_enable_pll1(struct snd_soc_dai *codec_dai, + int rate) +{ + int ret; + + /* Configure the PLL before selecting it */ + if (!(byt_rt5640_quirk & BYT_RT5640_MCLK_EN)) { + /* use bitclock as PLL input */ + if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { + /* 2x16 bit slots on SSP0 */ + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_BCLK1, + rate * 32, rate * 512); + } else { + /* 2x15 bit slots on SSP2 */ + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_BCLK1, + rate * 50, rate * 512); + } + } else { + if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) { + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_MCLK, + 25000000, rate * 512); + } else { + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_MCLK, + 19200000, rate * 512); + } + } + + if (ret < 0) { + dev_err(codec_dai->component->dev, "can't set pll: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, + rate * 512, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->component->dev, "can't set clock %d\n", ret); + return ret; + } + + return 0; +} #define BYT_CODEC_DAI1 "rt5640-aif1" #define BYT_CODEC_DAI2 "rt5640-aif2" @@ -173,9 +255,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return ret; } } - ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, - 48000 * 512, - SND_SOC_CLOCK_IN); + ret = byt_rt5640_prepare_and_enable_pll1(codec_dai, 48000); } else { /* * Set codec clock source to internal clock before @@ -295,79 +375,72 @@ static const struct snd_kcontrol_new byt_rt5640_controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; +static struct snd_soc_jack_pin rt5640_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret; + struct snd_soc_dai *dai = rtd->codec_dai; - ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, - params_rate(params) * 512, - SND_SOC_CLOCK_IN); - - if (ret < 0) { - dev_err(rtd->dev, "can't set codec clock %d\n", ret); - return ret; - } - - if (!(byt_rt5640_quirk & BYT_RT5640_MCLK_EN)) { - /* use bitclock as PLL input */ - if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || - (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { - - /* 2x16 bit slots on SSP0 */ - ret = snd_soc_dai_set_pll(codec_dai, 0, - RT5640_PLL1_S_BCLK1, - params_rate(params) * 32, - params_rate(params) * 512); - } else { - /* 2x15 bit slots on SSP2 */ - ret = snd_soc_dai_set_pll(codec_dai, 0, - RT5640_PLL1_S_BCLK1, - params_rate(params) * 50, - params_rate(params) * 512); - } - } else { - if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) { - ret = snd_soc_dai_set_pll(codec_dai, 0, - RT5640_PLL1_S_MCLK, - 25000000, - params_rate(params) * 512); - } else { - ret = snd_soc_dai_set_pll(codec_dai, 0, - RT5640_PLL1_S_MCLK, - 19200000, - params_rate(params) * 512); - } - } - - if (ret < 0) { - dev_err(rtd->dev, "can't set codec pll: %d\n", ret); - return ret; - } - - return 0; -} - -static int byt_rt5640_quirk_cb(const struct dmi_system_id *id) -{ - byt_rt5640_quirk = (unsigned long)id->driver_data; - return 1; + return byt_rt5640_prepare_and_enable_pll1(dai, params_rate(params)); } +/* Please keep this list alphabetically sorted */ static const struct dmi_system_id byt_rt5640_quirk_table[] = { + { /* Acer Iconia Tab 8 W1-810 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Iconia W1-810"), + }, + .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | + BYT_RT5640_JD_SRC_JD1_IN4P | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN | + BYT_RT5640_SSP0_AIF1), + + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ARCHOS 80 Cesium"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { - .callback = byt_rt5640_quirk_cb, .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), }, .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | BYT_RT5640_MCLK_EN), }, { - .callback = byt_rt5640_quirk_cb, .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"), @@ -378,18 +451,38 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, + { /* Chuwi Vi8 (CWI506) */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "i86"), + /* The above are too generic, also match BIOS info */ + DMI_MATCH(DMI_BIOS_VERSION, "CHUWI.D86JLBNR"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { - .callback = byt_rt5640_quirk_cb, .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "DellInc."), + DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), + DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), + }, + .driver_data = (void *)(BYT_RT5640_DMIC1_MAP), + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), }, - .driver_data = (void *)(BYT_RT5640_DMIC2_MAP | - BYT_RT5640_DMIC_EN | + .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_MONO_SPEAKER | BYT_RT5640_MCLK_EN), }, { - .callback = byt_rt5640_quirk_cb, .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), @@ -397,17 +490,112 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { .driver_data = (void *)(BYT_RT5640_IN1_MAP | BYT_RT5640_MCLK_EN), }, - { - .callback = byt_rt5640_quirk_cb, + { /* HP Pavilion x2 10-n000nd */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), - DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), }, .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | - BYT_RT5640_DMIC_EN), + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_1500UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { /* HP Stream 7 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Stream 7 Tablet"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_JD_NOT_INV | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { /* I.T.Works TW891 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "TW891"), + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { /* Lamina I8270 / T701BR.SE */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Lamina"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "T701BR.SE"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_JD_NOT_INV | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { /* MSI S100 tablet */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "S100"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_MCLK_EN), + }, + { /* Pipo W4 */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Aptio CRB"), + /* The above are too generic, also match BIOS info */ + DMI_MATCH(DMI_BIOS_VERSION, "V8L_WIN32_CHIPHD"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { /* Point of View Mobii TAB-P800W (V2.0) */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Aptio CRB"), + /* The above are too generic, also match BIOS info */ + DMI_EXACT_MATCH(DMI_BIOS_VERSION, "3BAIR1014"), + DMI_EXACT_MATCH(DMI_BIOS_DATE, "10/24/2014"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF2 | + BYT_RT5640_MCLK_EN), + }, + { /* Point of View Mobii TAB-P800W (V2.1) */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Aptio CRB"), + /* The above are too generic, also match BIOS info */ + DMI_EXACT_MATCH(DMI_BIOS_VERSION, "3BAIR1013"), + DMI_EXACT_MATCH(DMI_BIOS_DATE, "08/22/2014"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF2 | + BYT_RT5640_MCLK_EN), }, { - .callback = byt_rt5640_quirk_cb, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), DMI_MATCH(DMI_BOARD_NAME, "tPAD"), @@ -416,23 +604,23 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_MCLK_EN | BYT_RT5640_SSP0_AIF1), }, - { - .callback = byt_rt5640_quirk_cb, + { /* Toshiba Satellite Click Mini L9W-B */ .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SATELLITE Click Mini L9W-B"), }, - .driver_data = (void *)(BYT_RT5640_IN1_MAP | - BYT_RT5640_MCLK_EN | - BYT_RT5640_SSP0_AIF1), - + .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_1500UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), }, - { - .callback = byt_rt5640_quirk_cb, + { /* Catch-all for generic Insyde tablets, must be last */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), }, - .driver_data = (void *)(BYT_RT5640_IN3_MAP | + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | BYT_RT5640_MCLK_EN | BYT_RT5640_SSP0_AIF1), @@ -440,6 +628,64 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { {} }; +/* + * Note this MUST be called before snd_soc_register_card(), so that the props + * are in place before the codec component driver's probe function parses them. + */ +static int byt_rt5640_add_codec_device_props(const char *i2c_dev_name) +{ + struct property_entry props[MAX_NO_PROPS] = {}; + struct device *i2c_dev; + int ret, cnt = 0; + + i2c_dev = bus_find_device_by_name(&i2c_bus_type, NULL, i2c_dev_name); + if (!i2c_dev) + return -EPROBE_DEFER; + + switch (BYT_RT5640_MAP(byt_rt5640_quirk)) { + case BYT_RT5640_DMIC1_MAP: + props[cnt++] = PROPERTY_ENTRY_U32("realtek,dmic1-data-pin", + RT5640_DMIC1_DATA_PIN_IN1P); + break; + case BYT_RT5640_DMIC2_MAP: + props[cnt++] = PROPERTY_ENTRY_U32("realtek,dmic2-data-pin", + RT5640_DMIC2_DATA_PIN_IN1N); + break; + case BYT_RT5640_IN1_MAP: + if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) + props[cnt++] = + PROPERTY_ENTRY_BOOL("realtek,in1-differential"); + break; + case BYT_RT5640_IN3_MAP: + if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) + props[cnt++] = + PROPERTY_ENTRY_BOOL("realtek,in3-differential"); + break; + } + + if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) { + props[cnt++] = PROPERTY_ENTRY_U32( + "realtek,jack-detect-source", + BYT_RT5640_JDSRC(byt_rt5640_quirk)); + + props[cnt++] = PROPERTY_ENTRY_U32( + "realtek,over-current-threshold-microamp", + BYT_RT5640_OVCD_TH(byt_rt5640_quirk) * 100); + + props[cnt++] = PROPERTY_ENTRY_U32( + "realtek,over-current-scale-factor", + BYT_RT5640_OVCD_SF(byt_rt5640_quirk)); + } + + if (byt_rt5640_quirk & BYT_RT5640_JD_NOT_INV) + props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,jack-detect-not-inverted"); + + ret = device_add_properties(i2c_dev, props); + put_device(i2c_dev); + + return ret; +} + static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; @@ -451,6 +697,11 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) card->dapm.idle_bias_off = true; + /* Start with RC clk for jack-detect (we disable MCLK below) */ + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) + snd_soc_component_update_bits(component, RT5640_GLB_CLK, + RT5640_SCLK_SRC_MASK, RT5640_SCLK_SRC_RCCLK); + rt5640_sel_asrc_clk_src(component, RT5640_DA_STEREO_FILTER | RT5640_DA_MONO_L_FILTER | @@ -521,17 +772,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; - if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) { - snd_soc_component_update_bits(component, RT5640_IN1_IN2, RT5640_IN_DF1, - RT5640_IN_DF1); - } - - if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) { - ret = rt5640_dmic_enable(component, 0, 0); - if (ret) - return ret; - } - snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); @@ -555,11 +795,27 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) else ret = clk_set_rate(priv->mclk, 19200000); - if (ret) + if (ret) { dev_err(card->dev, "unable to set MCLK rate\n"); + return ret; + } } - return ret; + if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) { + ret = snd_soc_card_jack_new(card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &priv->jack, rt5640_pins, + ARRAY_SIZE(rt5640_pins)); + if (ret) { + dev_err(card->dev, "Jack creation failed %d\n", ret); + return ret; + } + snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, + KEY_PLAYPAUSE); + snd_soc_component_set_jack(component, &priv->jack, NULL); + } + + return 0; } static const struct snd_soc_pcm_stream byt_rt5640_dai_params = { @@ -701,6 +957,48 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { }; /* SoC card */ +static char byt_rt5640_codec_name[SND_ACPI_I2C_ID_LEN]; +static char byt_rt5640_codec_aif_name[12]; /* = "rt5640-aif[1|2]" */ +static char byt_rt5640_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ +static char byt_rt5640_long_name[40]; /* = "bytcr-rt5640-*-spk-*-mic" */ + +static int byt_rt5640_suspend(struct snd_soc_card *card) +{ + struct snd_soc_component *component; + + if (!BYT_RT5640_JDSRC(byt_rt5640_quirk)) + return 0; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strcmp(component->name, byt_rt5640_codec_name)) { + dev_dbg(component->dev, "disabling jack detect before suspend\n"); + snd_soc_component_set_jack(component, NULL, NULL); + break; + } + } + + return 0; +} + +static int byt_rt5640_resume(struct snd_soc_card *card) +{ + struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component; + + if (!BYT_RT5640_JDSRC(byt_rt5640_quirk)) + return 0; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strcmp(component->name, byt_rt5640_codec_name)) { + dev_dbg(component->dev, "re-enabling jack detect after resume\n"); + snd_soc_component_set_jack(component, &priv->jack, NULL); + break; + } + } + + return 0; +} + static struct snd_soc_card byt_rt5640_card = { .name = "bytcr-rt5640", .owner = THIS_MODULE, @@ -711,12 +1009,10 @@ static struct snd_soc_card byt_rt5640_card = { .dapm_routes = byt_rt5640_audio_map, .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map), .fully_routed = true, + .suspend_pre = byt_rt5640_suspend, + .resume_post = byt_rt5640_resume, }; -static char byt_rt5640_codec_name[SND_ACPI_I2C_ID_LEN]; -static char byt_rt5640_codec_aif_name[12]; /* = "rt5640-aif[1|2]" */ -static char byt_rt5640_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ - static bool is_valleyview(void) { static const struct x86_cpu_id cpu_ids[] = { @@ -736,6 +1032,8 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { + const char * const map_name[] = { "dmic1", "dmic2", "in1", "in3" }; + const struct dmi_system_id *dmi_id; struct byt_rt5640_private *priv; struct snd_soc_acpi_mach *mach; const char *i2c_name = NULL; @@ -744,7 +1042,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) int i; is_bytcr = false; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -829,20 +1127,29 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) } /* change defaults for Baytrail-CR capture */ - byt_rt5640_quirk |= BYT_RT5640_IN1_MAP; - byt_rt5640_quirk |= BYT_RT5640_DIFF_MIC; + byt_rt5640_quirk |= BYTCR_INPUT_DEFAULTS; } else { - byt_rt5640_quirk |= (BYT_RT5640_DMIC1_MAP | - BYT_RT5640_DMIC_EN); + byt_rt5640_quirk |= BYT_RT5640_DMIC1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75; } /* check quirks before creating card */ - dmi_check_system(byt_rt5640_quirk_table); + dmi_id = dmi_first_match(byt_rt5640_quirk_table); + if (dmi_id) + byt_rt5640_quirk = (unsigned long)dmi_id->driver_data; if (quirk_override) { dev_info(&pdev->dev, "Overriding quirk 0x%x => 0x%x\n", (unsigned int)byt_rt5640_quirk, quirk_override); byt_rt5640_quirk = quirk_override; } + + /* Must be called before register_card, also see declaration comment. */ + ret_val = byt_rt5640_add_codec_device_props(byt_rt5640_codec_name); + if (ret_val) + return ret_val; + log_quirks(&pdev->dev); if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) || @@ -889,6 +1196,13 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) } } + snprintf(byt_rt5640_long_name, sizeof(byt_rt5640_long_name), + "bytcr-rt5640-%s-spk-%s-mic", + (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) ? + "mono" : "stereo", + map_name[BYT_RT5640_MAP(byt_rt5640_quirk)]); + byt_rt5640_card.long_name = byt_rt5640_long_name; + ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); if (ret_val) { diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 1b1997f1d60c..987720e203f9 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -706,6 +706,7 @@ static struct snd_soc_card byt_rt5651_card = { static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN]; static char byt_rt5651_codec_aif_name[12]; /* = "rt5651-aif[1|2]" */ static char byt_rt5651_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ +static char byt_rt5651_long_name[40]; /* = "bytcr-rt5651-*-spk-*-mic" */ static bool is_valleyview(void) { @@ -726,6 +727,10 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) { + const char * const intmic_name[] = + { "dmic", "in1", "in2", "in12", "in1", "in2" }; + const char * const hsmic_name[] = + { "in2", "in2", "in1", "in3", "in3", "in3" }; struct byt_rt5651_private *priv; struct snd_soc_acpi_mach *mach; const char *i2c_name = NULL; @@ -734,7 +739,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) int dai_index = 0; int i; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -856,9 +861,10 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); if (IS_ERR(priv->mclk)) { + ret_val = PTR_ERR(priv->mclk); dev_err(&pdev->dev, - "Failed to get MCLK from pmc_plt_clk_3: %ld\n", - PTR_ERR(priv->mclk)); + "Failed to get MCLK from pmc_plt_clk_3: %d\n", + ret_val); /* * Fall back to bit clock usage for -ENOENT (clock not * available likely due to missing dependencies), bail @@ -870,6 +876,12 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) } } + snprintf(byt_rt5651_long_name, sizeof(byt_rt5651_long_name), + "bytcr-rt5651-%s-intmic-%s-hsmic", + intmic_name[BYT_RT5651_MAP(byt_rt5651_quirk)], + hsmic_name[BYT_RT5651_MAP(byt_rt5651_quirk)]); + byt_rt5651_card.long_name = byt_rt5651_long_name; + ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card); if (ret_val) { diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index d3e1c7e12004..db6976f4ddaa 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -391,7 +391,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) int ret_val = 0; struct cht_mc_private *drv; - drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); if (!drv) return -ENOMEM; diff --git a/sound/soc/intel/boards/cht_bsw_nau8824.c b/sound/soc/intel/boards/cht_bsw_nau8824.c index 680f2b3a24f9..30c46977d53c 100644 --- a/sound/soc/intel/boards/cht_bsw_nau8824.c +++ b/sound/soc/intel/boards/cht_bsw_nau8824.c @@ -120,7 +120,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) * KEY_VOLUMEUP * KEY_VOLUMEDOWN */ - jack_type = SND_JACK_HEADPHONE | SND_JACK_BTN_0 | SND_JACK_BTN_1 | + jack_type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3; ret = snd_soc_card_jack_new(runtime->card, "Headset", jack_type, jack, cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins)); @@ -248,7 +248,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) struct cht_mc_private *drv; int ret_val; - drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); if (!drv) return -ENOMEM; snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 49ba1a956a06..f5a5ea6a093c 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -539,7 +539,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) int ret_val = 0; int i; - drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); if (!drv) return -ENOMEM; diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index e68ec32720a4..e5aa13058dd7 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -189,13 +189,6 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) if (devm_acpi_dev_add_driver_gpios(component->dev, cht_rt5672_gpios)) dev_warn(runtime->dev, "Unable to add GPIO mapping table\n"); - /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); - if (ret < 0) { - dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret); - return ret; - } - /* Select codec ASRC clock source to track I2S1 clock, because codec * is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot * be supported by RT5672. Otherwise, ASRC will be disabled and cause @@ -252,6 +245,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int ret; /* The DSP will covert the FE rate to 48k, stereo, 24bits */ rate->min = rate->max = 48000; @@ -259,6 +253,26 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, /* set SSP2 to 24-bit */ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + + /* + * Default mode for SSP configuration is TDM 4 slot + */ + ret = snd_soc_dai_set_fmt(rtd->codec_dai, + SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to TDM %d\n", ret); + return ret; + } + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(rtd->codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret); + return ret; + } + return 0; } @@ -315,8 +329,6 @@ static struct snd_soc_dai_link cht_dailink[] = { .nonatomic = true, .codec_dai_name = "rt5670-aif1", .codec_name = "i2c-10EC5670:00", - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBS_CFS, .init = cht_codec_init, .be_hw_params_fixup = cht_codec_fixup, .dpcm_playback = 1, diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index e84baafd5f63..94294c27d1db 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -65,14 +65,6 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return -EIO; } - /* Configure sysclk for codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, 24576000, - SND_SOC_CLOCK_IN); - if (ret) { - dev_err(card->dev, "can't set codec sysclk configuration\n"); - return ret; - } - if (SND_SOC_DAPM_EVENT_OFF(event)) { ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0); @@ -169,9 +161,18 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) { struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_jack *jack; int ret; + /* Configure sysclk for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, 24576000, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "can't set codec sysclk configuration\n"); + return ret; + } + /* * Headset buttons map to the google Reference headset. * These can be configured by userspace. @@ -572,7 +573,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_codec_private *ctx; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 0e6b7e79441c..3a61252fe450 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -299,7 +299,8 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - rt5663_set_jack_detect(component, &ctx->kabylake_headset); + snd_soc_component_set_jack(component, &ctx->kabylake_headset, NULL); + return ret; } @@ -972,7 +973,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) struct skl_machine_pdata *pdata; int ret; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index 69c3d8446f06..92f5fb2ae0a3 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -200,7 +200,7 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - rt5663_set_jack_detect(component, &ctx->kabylake_headset); + snd_soc_component_set_jack(component, &ctx->kabylake_headset, NULL); ret = snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "DMIC"); if (ret) @@ -651,7 +651,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) struct kbl_codec_private *ctx; struct skl_machine_pdata *pdata; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 9a7a0646bffe..3ff6646cfa21 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -643,7 +643,7 @@ static int skylake_audio_probe(struct platform_device *pdev) struct skl_nau8825_private *ctx; struct skl_machine_pdata *pdata; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 212ac8971e55..b0610bba3cfa 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -696,7 +696,7 @@ static int skylake_audio_probe(struct platform_device *pdev) struct skl_nau88125_private *ctx; struct skl_machine_pdata *pdata; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 737d5615b0ef..38a1495c29cf 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -527,7 +527,7 @@ static int skylake_audio_probe(struct platform_device *pdev) { struct skl_rt286_private *ctx; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index a016455a6ddb..5d7ac2ee7a3c 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -15,10 +15,10 @@ #include <linux/pci.h> #include <linux/debugfs.h> +#include <uapi/sound/skl-tplg-interface.h> #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" -#include "skl-tplg-interface.h" #include "skl-topology.h" #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -142,8 +142,8 @@ static ssize_t module_read(struct file *file, char __user *user_buf, mconfig->max_out_queue, ret, false); ret += snprintf(buf + ret, MOD_BUF - ret, - "Other:\n\tDomain %d\n\tHomogenous Input %s\n\t" - "Homogenous Output %s\n\tIn Queue Mask %d\n\t" + "Other:\n\tDomain %d\n\tHomogeneous Input %s\n\t" + "Homogeneous Output %s\n\tIn Queue Mask %d\n\t" "Out Queue Mask %d\n\tDMA ID %d\n\tMem Pages %d\n\t" "Module Type %d\n\tModule State %d\n", mconfig->domain, diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 57d4a58522a6..d5f9c30eba32 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -21,6 +21,7 @@ #include <linux/pci.h> #include <sound/core.h> #include <sound/pcm.h> +#include <uapi/sound/skl-tplg-interface.h> #include "skl-sst-dsp.h" #include "cnl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -28,7 +29,6 @@ #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" #include "skl-topology.h" -#include "skl-tplg-interface.h" static int skl_alloc_dma_buf(struct device *dev, struct snd_dma_buffer *dmab, size_t size) @@ -225,7 +225,7 @@ static const struct skl_dsp_ops dsp_ops[] = { .id = 0x9d71, .num_cores = 2, .loader_ops = skl_get_loader_ops, - .init = kbl_sst_dsp_init, + .init = skl_sst_dsp_init, .init_fw = skl_sst_init_fw, .cleanup = skl_sst_dsp_cleanup }, diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 15cb8ac3e374..afa86b9e4dcf 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -268,15 +268,31 @@ static int skl_pcm_prepare(struct snd_pcm_substream *substream, { struct skl *skl = get_skl_ctx(dai->dev); struct skl_module_cfg *mconfig; + int ret; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); - /* In case of XRUN recovery, reset the FW pipe to clean state */ - if (mconfig && (substream->runtime->status->state == - SNDRV_PCM_STATE_XRUN)) - skl_reset_pipe(skl->skl_sst, mconfig->pipe); + /* + * In case of XRUN recovery or in the case when the application + * calls prepare another time, reset the FW pipe to clean state + */ + if (mconfig && + (substream->runtime->status->state == SNDRV_PCM_STATE_XRUN || + mconfig->pipe->state == SKL_PIPE_CREATED || + mconfig->pipe->state == SKL_PIPE_PAUSED)) { + + ret = skl_reset_pipe(skl->skl_sst, mconfig->pipe); + + if (ret < 0) + return ret; + + ret = skl_pcm_host_dma_prepare(dai->dev, + mconfig->pipe->p_params); + if (ret < 0) + return ret; + } return 0; } @@ -366,9 +382,21 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, { struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_module_cfg *mconfig; + int ret; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + + if (mconfig) { + ret = skl_reset_pipe(skl->skl_sst, mconfig->pipe); + if (ret < 0) + dev_err(dai->dev, "%s:Reset failed ret =%d", + __func__, ret); + } + snd_hdac_stream_cleanup(hdac_stream(stream)); hdac_stream(stream)->prepared = 0; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 12fc9a73dc8a..e1d6f6719f7e 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -231,9 +231,6 @@ int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp); -int kbl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp); int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 5a7e41b65ef3..5951bbdf1f1a 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -390,7 +390,7 @@ out: } static int -kbl_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) +skl_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) { struct skl_sst *skl = ctx->thread_context; struct firmware stripped_fw; @@ -508,16 +508,7 @@ static const struct skl_dsp_fw_ops skl_fw_ops = { .set_state_D3 = skl_set_dsp_D3, .load_fw = skl_load_base_firmware, .get_fw_errcode = skl_get_errorcode, - .load_mod = skl_load_module, - .unload_mod = skl_unload_module, -}; - -static const struct skl_dsp_fw_ops kbl_fw_ops = { - .set_state_D0 = skl_set_dsp_D0, - .set_state_D3 = skl_set_dsp_D3, - .load_fw = skl_load_base_firmware, - .get_fw_errcode = skl_get_errorcode, - .load_library = kbl_load_library, + .load_library = skl_load_library, .load_mod = skl_load_module, .unload_mod = skl_unload_module, }; @@ -573,27 +564,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, } EXPORT_SYMBOL_GPL(skl_sst_dsp_init); -int kbl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp) -{ - struct sst_dsp *sst; - int ret; - - ret = skl_sst_dsp_init(dev, mmio_base, irq, fw_name, dsp_ops, dsp); - if (ret < 0) { - dev_err(dev, "%s: Init failed %d\n", __func__, ret); - return ret; - } - - sst = (*dsp)->dsp; - sst->fw_ops = kbl_fw_ops; - - return 0; - -} -EXPORT_SYMBOL_GPL(kbl_sst_dsp_init); - int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx) { int ret; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 3b1dca419883..2c5129782959 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -19,14 +19,15 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/firmware.h> +#include <linux/uuid.h> #include <sound/soc.h> #include <sound/soc-topology.h> #include <uapi/sound/snd_sst_tokens.h> +#include <uapi/sound/skl-tplg-interface.h> #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" #include "skl-topology.h" #include "skl.h" -#include "skl-tplg-interface.h" #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -2724,6 +2725,167 @@ static int skl_tplg_get_desc_blocks(struct device *dev, return -EINVAL; } +/* Functions to parse private data from configuration file format v4 */ + +/* + * Add pipeline from topology binary into driver pipeline list + * + * If already added we return that instance + * Otherwise we create a new instance and add into driver list + */ +static int skl_tplg_add_pipe_v4(struct device *dev, + struct skl_module_cfg *mconfig, struct skl *skl, + struct skl_dfw_v4_pipe *dfw_pipe) +{ + struct skl_pipeline *ppl; + struct skl_pipe *pipe; + struct skl_pipe_params *params; + + list_for_each_entry(ppl, &skl->ppl_list, node) { + if (ppl->pipe->ppl_id == dfw_pipe->pipe_id) { + mconfig->pipe = ppl->pipe; + return 0; + } + } + + ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL); + if (!ppl) + return -ENOMEM; + + pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL); + if (!pipe) + return -ENOMEM; + + params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + + pipe->ppl_id = dfw_pipe->pipe_id; + pipe->memory_pages = dfw_pipe->memory_pages; + pipe->pipe_priority = dfw_pipe->pipe_priority; + pipe->conn_type = dfw_pipe->conn_type; + pipe->state = SKL_PIPE_INVALID; + pipe->p_params = params; + INIT_LIST_HEAD(&pipe->w_list); + + ppl->pipe = pipe; + list_add(&ppl->node, &skl->ppl_list); + + mconfig->pipe = pipe; + + return 0; +} + +static void skl_fill_module_pin_info_v4(struct skl_dfw_v4_module_pin *dfw_pin, + struct skl_module_pin *m_pin, + bool is_dynamic, int max_pin) +{ + int i; + + for (i = 0; i < max_pin; i++) { + m_pin[i].id.module_id = dfw_pin[i].module_id; + m_pin[i].id.instance_id = dfw_pin[i].instance_id; + m_pin[i].in_use = false; + m_pin[i].is_dynamic = is_dynamic; + m_pin[i].pin_state = SKL_PIN_UNBIND; + } +} + +static void skl_tplg_fill_fmt_v4(struct skl_module_pin_fmt *dst_fmt, + struct skl_dfw_v4_module_fmt *src_fmt, + int pins) +{ + int i; + + for (i = 0; i < pins; i++) { + dst_fmt[i].fmt.channels = src_fmt[i].channels; + dst_fmt[i].fmt.s_freq = src_fmt[i].freq; + dst_fmt[i].fmt.bit_depth = src_fmt[i].bit_depth; + dst_fmt[i].fmt.valid_bit_depth = src_fmt[i].valid_bit_depth; + dst_fmt[i].fmt.ch_cfg = src_fmt[i].ch_cfg; + dst_fmt[i].fmt.ch_map = src_fmt[i].ch_map; + dst_fmt[i].fmt.interleaving_style = + src_fmt[i].interleaving_style; + dst_fmt[i].fmt.sample_type = src_fmt[i].sample_type; + } +} + +static int skl_tplg_get_pvt_data_v4(struct snd_soc_tplg_dapm_widget *tplg_w, + struct skl *skl, struct device *dev, + struct skl_module_cfg *mconfig) +{ + struct skl_dfw_v4_module *dfw = + (struct skl_dfw_v4_module *)tplg_w->priv.data; + int ret; + + dev_dbg(dev, "Parsing Skylake v4 widget topology data\n"); + + ret = guid_parse(dfw->uuid, (guid_t *)mconfig->guid); + if (ret) + return ret; + mconfig->id.module_id = -1; + mconfig->id.instance_id = dfw->instance_id; + mconfig->module->resources[0].cps = dfw->max_mcps; + mconfig->module->resources[0].ibs = dfw->ibs; + mconfig->module->resources[0].obs = dfw->obs; + mconfig->core_id = dfw->core_id; + mconfig->module->max_input_pins = dfw->max_in_queue; + mconfig->module->max_output_pins = dfw->max_out_queue; + mconfig->module->loadable = dfw->is_loadable; + skl_tplg_fill_fmt_v4(mconfig->module->formats[0].inputs, dfw->in_fmt, + MAX_IN_QUEUE); + skl_tplg_fill_fmt_v4(mconfig->module->formats[0].outputs, dfw->out_fmt, + MAX_OUT_QUEUE); + + mconfig->params_fixup = dfw->params_fixup; + mconfig->converter = dfw->converter; + mconfig->m_type = dfw->module_type; + mconfig->vbus_id = dfw->vbus_id; + mconfig->module->resources[0].is_pages = dfw->mem_pages; + + ret = skl_tplg_add_pipe_v4(dev, mconfig, skl, &dfw->pipe); + if (ret) + return ret; + + mconfig->dev_type = dfw->dev_type; + mconfig->hw_conn_type = dfw->hw_conn_type; + mconfig->time_slot = dfw->time_slot; + mconfig->formats_config.caps_size = dfw->caps.caps_size; + + mconfig->m_in_pin = devm_kzalloc(dev, + MAX_IN_QUEUE * sizeof(*mconfig->m_in_pin), + GFP_KERNEL); + if (!mconfig->m_in_pin) + return -ENOMEM; + + mconfig->m_out_pin = devm_kzalloc(dev, + MAX_OUT_QUEUE * sizeof(*mconfig->m_out_pin), + GFP_KERNEL); + if (!mconfig->m_out_pin) + return -ENOMEM; + + skl_fill_module_pin_info_v4(dfw->in_pin, mconfig->m_in_pin, + dfw->is_dynamic_in_pin, + mconfig->module->max_input_pins); + skl_fill_module_pin_info_v4(dfw->out_pin, mconfig->m_out_pin, + dfw->is_dynamic_out_pin, + mconfig->module->max_output_pins); + + if (mconfig->formats_config.caps_size) { + mconfig->formats_config.set_params = dfw->caps.set_params; + mconfig->formats_config.param_id = dfw->caps.param_id; + mconfig->formats_config.caps = + devm_kzalloc(dev, mconfig->formats_config.caps_size, + GFP_KERNEL); + if (!mconfig->formats_config.caps) + return -ENOMEM; + memcpy(mconfig->formats_config.caps, dfw->caps.caps, + dfw->caps.caps_size); + } + + return 0; +} + /* * Parse the private data for the token and corresponding value. * The private data can have multiple data blocks. So, a data block @@ -2739,6 +2901,13 @@ static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w, char *data; int ret; + /* + * v4 configuration files have a valid UUID at the start of + * the widget's private data. + */ + if (uuid_is_valid((char *)tplg_w->priv.data)) + return skl_tplg_get_pvt_data_v4(tplg_w, skl, dev, mconfig); + /* Read the NUM_DATA_BLOCKS descriptor */ array = (struct snd_soc_tplg_vendor_array *)tplg_w->priv.data; ret = skl_tplg_get_desc_blocks(dev, array); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index b1e0667c0ae0..6d7e0569695f 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -25,8 +25,8 @@ #include <sound/hdaudio_ext.h> #include <sound/soc.h> +#include <uapi/sound/skl-tplg-interface.h> #include "skl.h" -#include "skl-tplg-interface.h" #define BITS_PER_BYTE 8 #define MAX_TS_GROUPS 8 diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h deleted file mode 100644 index f8d1749a2e0c..000000000000 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * skl-tplg-interface.h - Intel DSP FW private data interface - * - * Copyright (C) 2015 Intel Corp - * Author: Jeeja KP <jeeja.kp@intel.com> - * Nilofer, Samreen <samreen.nilofer@intel.com> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#ifndef __HDA_TPLG_INTERFACE_H__ -#define __HDA_TPLG_INTERFACE_H__ - -/* - * Default types range from 0~12. type can range from 0 to 0xff - * SST types start at higher to avoid any overlapping in future - */ -#define SKL_CONTROL_TYPE_BYTE_TLV 0x100 -#define SKL_CONTROL_TYPE_MIC_SELECT 0x102 - -#define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ -#define MAX_IN_QUEUE 8 -#define MAX_OUT_QUEUE 8 - -#define SKL_UUID_STR_SZ 40 -/* Event types goes here */ -/* Reserve event type 0 for no event handlers */ -enum skl_event_types { - SKL_EVENT_NONE = 0, - SKL_MIXER_EVENT, - SKL_MUX_EVENT, - SKL_VMIXER_EVENT, - SKL_PGA_EVENT -}; - -/** - * enum skl_ch_cfg - channel configuration - * - * @SKL_CH_CFG_MONO: One channel only - * @SKL_CH_CFG_STEREO: L & R - * @SKL_CH_CFG_2_1: L, R & LFE - * @SKL_CH_CFG_3_0: L, C & R - * @SKL_CH_CFG_3_1: L, C, R & LFE - * @SKL_CH_CFG_QUATRO: L, R, Ls & Rs - * @SKL_CH_CFG_4_0: L, C, R & Cs - * @SKL_CH_CFG_5_0: L, C, R, Ls & Rs - * @SKL_CH_CFG_5_1: L, C, R, Ls, Rs & LFE - * @SKL_CH_CFG_DUAL_MONO: One channel replicated in two - * @SKL_CH_CFG_I2S_DUAL_STEREO_0: Stereo(L,R) in 4 slots, 1st stream:[ L, R, -, - ] - * @SKL_CH_CFG_I2S_DUAL_STEREO_1: Stereo(L,R) in 4 slots, 2nd stream:[ -, -, L, R ] - * @SKL_CH_CFG_INVALID: Invalid - */ -enum skl_ch_cfg { - SKL_CH_CFG_MONO = 0, - SKL_CH_CFG_STEREO = 1, - SKL_CH_CFG_2_1 = 2, - SKL_CH_CFG_3_0 = 3, - SKL_CH_CFG_3_1 = 4, - SKL_CH_CFG_QUATRO = 5, - SKL_CH_CFG_4_0 = 6, - SKL_CH_CFG_5_0 = 7, - SKL_CH_CFG_5_1 = 8, - SKL_CH_CFG_DUAL_MONO = 9, - SKL_CH_CFG_I2S_DUAL_STEREO_0 = 10, - SKL_CH_CFG_I2S_DUAL_STEREO_1 = 11, - SKL_CH_CFG_4_CHANNEL = 12, - SKL_CH_CFG_INVALID -}; - -enum skl_module_type { - SKL_MODULE_TYPE_MIXER = 0, - SKL_MODULE_TYPE_COPIER, - SKL_MODULE_TYPE_UPDWMIX, - SKL_MODULE_TYPE_SRCINT, - SKL_MODULE_TYPE_ALGO, - SKL_MODULE_TYPE_BASE_OUTFMT, - SKL_MODULE_TYPE_KPB, - SKL_MODULE_TYPE_MIC_SELECT, -}; - -enum skl_core_affinity { - SKL_AFFINITY_CORE_0 = 0, - SKL_AFFINITY_CORE_1, - SKL_AFFINITY_CORE_MAX -}; - -enum skl_pipe_conn_type { - SKL_PIPE_CONN_TYPE_NONE = 0, - SKL_PIPE_CONN_TYPE_FE, - SKL_PIPE_CONN_TYPE_BE -}; - -enum skl_hw_conn_type { - SKL_CONN_NONE = 0, - SKL_CONN_SOURCE = 1, - SKL_CONN_SINK = 2 -}; - -enum skl_dev_type { - SKL_DEVICE_BT = 0x0, - SKL_DEVICE_DMIC = 0x1, - SKL_DEVICE_I2S = 0x2, - SKL_DEVICE_SLIMBUS = 0x3, - SKL_DEVICE_HDALINK = 0x4, - SKL_DEVICE_HDAHOST = 0x5, - SKL_DEVICE_NONE -}; - -/** - * enum skl_interleaving - interleaving style - * - * @SKL_INTERLEAVING_PER_CHANNEL: [s1_ch1...s1_chN,...,sM_ch1...sM_chN] - * @SKL_INTERLEAVING_PER_SAMPLE: [s1_ch1...sM_ch1,...,s1_chN...sM_chN] - */ -enum skl_interleaving { - SKL_INTERLEAVING_PER_CHANNEL = 0, - SKL_INTERLEAVING_PER_SAMPLE = 1, -}; - -enum skl_sample_type { - SKL_SAMPLE_TYPE_INT_MSB = 0, - SKL_SAMPLE_TYPE_INT_LSB = 1, - SKL_SAMPLE_TYPE_INT_SIGNED = 2, - SKL_SAMPLE_TYPE_INT_UNSIGNED = 3, - SKL_SAMPLE_TYPE_FLOAT = 4 -}; - -enum module_pin_type { - /* All pins of the module takes same PCM inputs or outputs - * e.g. mixout - */ - SKL_PIN_TYPE_HOMOGENEOUS, - /* All pins of the module takes different PCM inputs or outputs - * e.g mux - */ - SKL_PIN_TYPE_HETEROGENEOUS, -}; - -enum skl_module_param_type { - SKL_PARAM_DEFAULT = 0, - SKL_PARAM_INIT, - SKL_PARAM_SET, - SKL_PARAM_BIND -}; - -struct skl_dfw_algo_data { - u32 set_params:2; - u32 rsvd:30; - u32 param_id; - u32 max; - char params[0]; -} __packed; - -enum skl_tkn_dir { - SKL_DIR_IN, - SKL_DIR_OUT -}; - -enum skl_tuple_type { - SKL_TYPE_TUPLE, - SKL_TYPE_DATA -}; - -#endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index abf324747b29..f0d9793f872a 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -127,10 +127,17 @@ static void skl_clock_power_gating(struct device *dev, bool enable) */ static int skl_init_chip(struct hdac_bus *bus, bool full_reset) { + struct hdac_ext_bus *ebus = hbus_to_ebus(bus); + struct hdac_ext_link *hlink; int ret; skl_enable_miscbdcge(bus->dev, false); ret = snd_hdac_bus_init_chip(bus, full_reset); + + /* Reset stream-to-link mapping */ + list_for_each_entry(hlink, &ebus->hlink_list, list) + bus->io_ops->reg_writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); + skl_enable_miscbdcge(bus->dev, true); return ret; diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig index bc3c7b5ac752..132bb83f8e99 100644 --- a/sound/soc/kirkwood/Kconfig +++ b/sound/soc/kirkwood/Kconfig @@ -1,7 +1,6 @@ config SND_KIRKWOOD_SOC tristate "SoC Audio for the Marvell Kirkwood and Dove chips" depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST - depends on HAS_DMA help Say Y or M if you want to add support for codecs attached to the Kirkwood I2S interface. You will also need to select the diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 5c68797f36c4..e731d40afcce 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -32,6 +32,26 @@ config SND_SOC_MT2701_WM8960 Select Y if you have such device. If unsure select "N". +config SND_SOC_MT6797 + tristate "ASoC support for Mediatek MT6797 chip" + depends on ARCH_MEDIATEK + select SND_SOC_MEDIATEK + help + This adds ASoC driver for Mediatek MT6797 boards + that can be used with other codecs. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MT6797_MT6351 + tristate "ASoc Audio driver for MT6797 with MT6351 codec" + depends on SND_SOC_MT6797 && MTK_PMIC_WRAP + select SND_SOC_MT6351 + help + This adds ASoC driver for Mediatek MT6797 boards + with the MT6351 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 6bcab35dc828..3bb2c47532f4 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SND_SOC_MEDIATEK) += common/ obj-$(CONFIG_SND_SOC_MT2701) += mt2701/ +obj-$(CONFIG_SND_SOC_MT6797) += mt6797/ obj-$(CONFIG_SND_SOC_MT8173) += mt8173/ diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile index a55d33bc7b01..cdadabc5fd16 100644 --- a/sound/soc/mediatek/common/Makefile +++ b/sound/soc/mediatek/common/Makefile @@ -1,16 +1,4 @@ -# -# Copyright (C) 2015 MediaTek Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# - +# SPDX-License-Identifier: GPL-2.0 # platform driver snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c index c91e5f4cd902..cf4978be062f 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtk-afe-fe-dais.c -- Mediatek afe fe dai operator * * Copyright (c) 2016 MediaTek Inc. * Author: Garlic Tseng <garlic.tseng@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> @@ -44,8 +36,7 @@ int mtk_afe_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct snd_pcm_runtime *runtime = substream->runtime; int memif_num = rtd->cpu_dai->id; struct mtk_base_afe_memif *memif = &afe->memif[memif_num]; @@ -107,8 +98,7 @@ void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; int irq_id; @@ -131,8 +121,7 @@ int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; int msb_at_bit33 = 0; int ret, fs = 0; @@ -196,8 +185,7 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime * const runtime = substream->runtime; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage]; const struct mtk_base_irq_data *irq_data = irqs->irq_data; @@ -260,8 +248,7 @@ int mtk_afe_fe_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; int hd_audio = 0; @@ -333,7 +320,7 @@ EXPORT_SYMBOL_GPL(mtk_dynamic_irq_release); int mtk_afe_dai_suspend(struct snd_soc_dai *dai) { - struct mtk_base_afe *afe = dev_get_drvdata(dai->dev); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct device *dev = afe->dev; struct regmap *regmap = afe->regmap; int i; @@ -358,7 +345,7 @@ EXPORT_SYMBOL_GPL(mtk_afe_dai_suspend); int mtk_afe_dai_resume(struct snd_soc_dai *dai) { - struct mtk_base_afe *afe = dev_get_drvdata(dai->dev); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct device *dev = afe->dev; struct regmap *regmap = afe->regmap; int i = 0; @@ -383,4 +370,3 @@ EXPORT_SYMBOL_GPL(mtk_afe_dai_resume); MODULE_DESCRIPTION("Mediatek simple fe dai operator"); MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>"); MODULE_LICENSE("GPL v2"); - diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h index 28cb17854da1..55074fb9861a 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.h +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * mtk-afe-fe-dais.h -- Mediatek afe fe dai operator definition * * Copyright (c) 2016 MediaTek Inc. * Author: Garlic Tseng <garlic.tseng@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _MTK_AFE_FE_DAI_H_ diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c index 53215b52e4f2..51ec4ff6ed95 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtk-afe-platform-driver.c -- Mediatek afe platform driver * * Copyright (c) 2016 MediaTek Inc. * Author: Garlic Tseng <garlic.tseng@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> @@ -21,6 +13,86 @@ #include "mtk-afe-platform-driver.h" #include "mtk-base-afe.h" +int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe) +{ + struct snd_soc_dai_driver *sub_dai_drivers; + size_t num_dai_drivers = 0, dai_idx = 0; + int i; + + if (!afe->sub_dais) { + dev_err(afe->dev, "%s(), sub_dais == NULL\n", __func__); + return -EINVAL; + } + + /* calcualte total dai driver size */ + for (i = 0; i < afe->num_sub_dais; i++) { + if (afe->sub_dais[i].dai_drivers && + afe->sub_dais[i].num_dai_drivers != 0) + num_dai_drivers += afe->sub_dais[i].num_dai_drivers; + } + + dev_info(afe->dev, "%s(), num of dai %zd\n", __func__, num_dai_drivers); + + /* combine sub_dais */ + afe->num_dai_drivers = num_dai_drivers; + afe->dai_drivers = devm_kcalloc(afe->dev, + num_dai_drivers, + sizeof(struct snd_soc_dai_driver), + GFP_KERNEL); + if (!afe->dai_drivers) + return -ENOMEM; + + for (i = 0; i < afe->num_sub_dais; i++) { + if (afe->sub_dais[i].dai_drivers && + afe->sub_dais[i].num_dai_drivers != 0) { + sub_dai_drivers = afe->sub_dais[i].dai_drivers; + /* dai driver */ + memcpy(&afe->dai_drivers[dai_idx], + sub_dai_drivers, + afe->sub_dais[i].num_dai_drivers * + sizeof(struct snd_soc_dai_driver)); + dai_idx += afe->sub_dais[i].num_dai_drivers; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(mtk_afe_combine_sub_dai); + +int mtk_afe_add_sub_dai_control(struct snd_soc_component *component) +{ + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + int i; + + if (!afe->sub_dais) { + dev_err(afe->dev, "%s(), sub_dais == NULL\n", __func__); + return -EINVAL; + } + + for (i = 0; i < afe->num_sub_dais; i++) { + if (afe->sub_dais[i].controls) + snd_soc_add_component_controls(component, + afe->sub_dais[i].controls, + afe->sub_dais[i].num_controls); + + if (afe->sub_dais[i].dapm_widgets) + snd_soc_dapm_new_controls(&component->dapm, + afe->sub_dais[i].dapm_widgets, + afe->sub_dais[i].num_dapm_widgets); + + if (afe->sub_dais[i].dapm_routes) + snd_soc_dapm_add_routes(&component->dapm, + afe->sub_dais[i].dapm_routes, + afe->sub_dais[i].num_dapm_routes); + } + + snd_soc_dapm_new_widgets(component->dapm.card); + + return 0; + +} +EXPORT_SYMBOL_GPL(mtk_afe_add_sub_dai_control); + static snd_pcm_uframes_t mtk_afe_pcm_pointer (struct snd_pcm_substream *substream) { @@ -56,28 +128,31 @@ POINTER_RETURN_FRAMES: return bytes_to_frames(substream->runtime, pcm_ptr_bytes); } -static const struct snd_pcm_ops mtk_afe_pcm_ops = { +const struct snd_pcm_ops mtk_afe_pcm_ops = { .ioctl = snd_pcm_lib_ioctl, .pointer = mtk_afe_pcm_pointer, }; +EXPORT_SYMBOL_GPL(mtk_afe_pcm_ops); -static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd) +int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd) { size_t size; - struct snd_card *card = rtd->card->snd_card; struct snd_pcm *pcm = rtd->pcm; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); size = afe->mtk_afe_hardware->buffer_bytes_max; return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - card->dev, size, size); + afe->dev, + size, size); } +EXPORT_SYMBOL_GPL(mtk_afe_pcm_new); -static void mtk_afe_pcm_free(struct snd_pcm *pcm) +void mtk_afe_pcm_free(struct snd_pcm *pcm) { snd_pcm_lib_preallocate_free_for_all(pcm); } +EXPORT_SYMBOL_GPL(mtk_afe_pcm_free); const struct snd_soc_component_driver mtk_afe_pcm_platform = { .name = AFE_PCM_NAME, diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h index 8dcdbed959ea..88df6797732f 100644 --- a/sound/soc/mediatek/common/mtk-afe-platform-driver.h +++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h @@ -1,24 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * mtk-afe-platform-driver.h -- Mediatek afe platform driver definition * * Copyright (c) 2016 MediaTek Inc. * Author: Garlic Tseng <garlic.tseng@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _MTK_AFE_PLATFORM_DRIVER_H_ #define _MTK_AFE_PLATFORM_DRIVER_H_ #define AFE_PCM_NAME "mtk-afe-pcm" +extern const struct snd_pcm_ops mtk_afe_pcm_ops; extern const struct snd_soc_component_driver mtk_afe_pcm_platform; +struct mtk_base_afe; +struct snd_pcm; +struct snd_soc_component; +struct snd_soc_pcm_runtime; + + +int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd); +void mtk_afe_pcm_free(struct snd_pcm *pcm); + +int mtk_afe_combine_sub_dai(struct mtk_base_afe *afe); +int mtk_afe_add_sub_dai_control(struct snd_soc_component *component); #endif diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h index 3a78f6f17195..bcf562f029b6 100644 --- a/sound/soc/mediatek/common/mtk-base-afe.h +++ b/sound/soc/mediatek/common/mtk-base-afe.h @@ -1,22 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * mtk-base-afe.h -- Mediatek base afe structure * * Copyright (c) 2016 MediaTek Inc. * Author: Garlic Tseng <garlic.tseng@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _MTK_BASE_AFE_H_ #define _MTK_BASE_AFE_H_ +#define MTK_STREAM_NUM (SNDRV_PCM_STREAM_LAST + 1) + struct mtk_base_memif_data { int id; const char *name; @@ -54,6 +48,7 @@ struct mtk_base_irq_data { struct device; struct mtk_base_afe_memif; struct mtk_base_afe_irq; +struct mtk_base_afe_dai; struct regmap; struct snd_pcm_substream; struct snd_soc_dai; @@ -77,6 +72,11 @@ struct mtk_base_afe { struct mtk_base_afe_irq *irqs; int irqs_size; + struct mtk_base_afe_dai *sub_dais; + int num_sub_dais; + struct snd_soc_dai_driver *dai_drivers; + unsigned int num_dai_drivers; + const struct snd_pcm_hardware *mtk_afe_hardware; int (*memif_fs)(struct snd_pcm_substream *substream, unsigned int rate); @@ -100,5 +100,17 @@ struct mtk_base_afe_irq { int irq_occupyed; }; +struct mtk_base_afe_dai { + struct snd_soc_dai_driver *dai_drivers; + unsigned int num_dai_drivers; + + const struct snd_kcontrol_new *controls; + unsigned int num_controls; + const struct snd_soc_dapm_widget *dapm_widgets; + unsigned int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + unsigned int num_dapm_routes; +}; + #endif diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile index c91deb6aca21..21d5e697cfa7 100644 --- a/sound/soc/mediatek/mt2701/Makefile +++ b/sound/soc/mediatek/mt2701/Makefile @@ -1,16 +1,4 @@ -# -# Copyright (C) 2015 MediaTek Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# - +# SPDX-License-Identifier: GPL-2.0 # platform driver snd-soc-mt2701-afe-objs := mt2701-afe-pcm.o mt2701-afe-clock-ctrl.o obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c index 949fc3a1d025..ae620890bb3a 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c @@ -1,17 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mt2701-afe-clock-ctrl.c -- Mediatek 2701 afe clock ctrl * * Copyright (c) 2016 MediaTek Inc. * Author: Garlic Tseng <garlic.tseng@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Ryder Lee <ryder.lee@mediatek.com> */ #include "mt2701-afe-common.h" @@ -43,8 +36,9 @@ int mt2701_init_clock(struct mtk_base_afe *afe) } /* Get I2S related clocks */ - for (i = 0; i < MT2701_I2S_NUM; i++) { + for (i = 0; i < afe_priv->soc->i2s_num; i++) { struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i]; + struct clk *i2s_ck; char name[13]; snprintf(name, sizeof(name), "i2s%d_src_sel", i); @@ -69,18 +63,20 @@ int mt2701_init_clock(struct mtk_base_afe *afe) } snprintf(name, sizeof(name), "i2so%d_hop_ck", i); - i2s_path->hop_ck[I2S_OUT] = devm_clk_get(afe->dev, name); - if (IS_ERR(i2s_path->hop_ck[I2S_OUT])) { + i2s_ck = devm_clk_get(afe->dev, name); + if (IS_ERR(i2s_ck)) { dev_err(afe->dev, "failed to get %s\n", name); - return PTR_ERR(i2s_path->hop_ck[I2S_OUT]); + return PTR_ERR(i2s_ck); } + i2s_path->hop_ck[SNDRV_PCM_STREAM_PLAYBACK] = i2s_ck; snprintf(name, sizeof(name), "i2si%d_hop_ck", i); - i2s_path->hop_ck[I2S_IN] = devm_clk_get(afe->dev, name); - if (IS_ERR(i2s_path->hop_ck[I2S_IN])) { + i2s_ck = devm_clk_get(afe->dev, name); + if (IS_ERR(i2s_ck)) { dev_err(afe->dev, "failed to get %s\n", name); - return PTR_ERR(i2s_path->hop_ck[I2S_IN]); + return PTR_ERR(i2s_ck); } + i2s_path->hop_ck[SNDRV_PCM_STREAM_CAPTURE] = i2s_ck; snprintf(name, sizeof(name), "asrc%d_out_ck", i); i2s_path->asrco_ck = devm_clk_get(afe->dev, name); @@ -102,10 +98,10 @@ int mt2701_init_clock(struct mtk_base_afe *afe) return 0; } -int mt2701_afe_enable_i2s(struct mtk_base_afe *afe, int id, int dir) +int mt2701_afe_enable_i2s(struct mtk_base_afe *afe, + struct mt2701_i2s_path *i2s_path, + int dir) { - struct mt2701_afe_private *afe_priv = afe->platform_priv; - struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id]; int ret; ret = clk_prepare_enable(i2s_path->asrco_ck); @@ -128,11 +124,10 @@ err_hop_ck: return ret; } -void mt2701_afe_disable_i2s(struct mtk_base_afe *afe, int id, int dir) +void mt2701_afe_disable_i2s(struct mtk_base_afe *afe, + struct mt2701_i2s_path *i2s_path, + int dir) { - struct mt2701_afe_private *afe_priv = afe->platform_priv; - struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id]; - clk_disable_unprepare(i2s_path->hop_ck[dir]); clk_disable_unprepare(i2s_path->asrco_ck); } @@ -272,27 +267,32 @@ int mt2701_afe_disable_clock(struct mtk_base_afe *afe) return 0; } -void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain, - int mclk) +int mt2701_mclk_configuration(struct mtk_base_afe *afe, int id) + { struct mt2701_afe_private *priv = afe->platform_priv; struct mt2701_i2s_path *i2s_path = &priv->i2s_path[id]; - int ret; + int ret = -EINVAL; /* Set mclk source */ - if (domain == 0) + if (!(MT2701_PLL_DOMAIN_0_RATE % i2s_path->mclk_rate)) ret = clk_set_parent(i2s_path->sel_ck, priv->base_ck[MT2701_TOP_AUD_MCLK_SRC0]); - else + else if (!(MT2701_PLL_DOMAIN_1_RATE % i2s_path->mclk_rate)) ret = clk_set_parent(i2s_path->sel_ck, priv->base_ck[MT2701_TOP_AUD_MCLK_SRC1]); - if (ret) - dev_err(afe->dev, "failed to set domain%d mclk source %d\n", - domain, ret); + if (ret) { + dev_err(afe->dev, "failed to set mclk source\n"); + return ret; + } /* Set mclk divider */ - ret = clk_set_rate(i2s_path->div_ck, mclk); - if (ret) + ret = clk_set_rate(i2s_path->div_ck, i2s_path->mclk_rate); + if (ret) { dev_err(afe->dev, "failed to set mclk divider %d\n", ret); + return ret; + } + + return 0; } diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h index 15417d9d6597..580fead2ab05 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h +++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h @@ -1,37 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * mt2701-afe-clock-ctrl.h -- Mediatek 2701 afe clock ctrl definition * * Copyright (c) 2016 MediaTek Inc. * Author: Garlic Tseng <garlic.tseng@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Ryder Lee <ryder.lee@mediatek.com> */ #ifndef _MT2701_AFE_CLOCK_CTRL_H_ #define _MT2701_AFE_CLOCK_CTRL_H_ struct mtk_base_afe; +struct mt2701_i2s_path; int mt2701_init_clock(struct mtk_base_afe *afe); int mt2701_afe_enable_clock(struct mtk_base_afe *afe); int mt2701_afe_disable_clock(struct mtk_base_afe *afe); -int mt2701_afe_enable_i2s(struct mtk_base_afe *afe, int id, int dir); -void mt2701_afe_disable_i2s(struct mtk_base_afe *afe, int id, int dir); +int mt2701_afe_enable_i2s(struct mtk_base_afe *afe, + struct mt2701_i2s_path *path, + int dir); +void mt2701_afe_disable_i2s(struct mtk_base_afe *afe, + struct mt2701_i2s_path *path, + int dir); int mt2701_afe_enable_mclk(struct mtk_base_afe *afe, int id); void mt2701_afe_disable_mclk(struct mtk_base_afe *afe, int id); int mt2701_enable_btmrg_clk(struct mtk_base_afe *afe); void mt2701_disable_btmrg_clk(struct mtk_base_afe *afe); -void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain, - int mclk); +int mt2701_mclk_configuration(struct mtk_base_afe *afe, int id); #endif diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h index ae8ddeacfbfe..d44faba27d3c 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-common.h +++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * mt2701-afe-common.h -- Mediatek 2701 audio driver definitions * * Copyright (c) 2016 MediaTek Inc. * Author: Garlic Tseng <garlic.tseng@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _MT_2701_AFE_COMMON_H_ @@ -23,10 +15,8 @@ #include "mt2701-reg.h" #include "../common/mtk-base-afe.h" -#define MT2701_STREAM_DIR_NUM (SNDRV_PCM_STREAM_LAST + 1) #define MT2701_PLL_DOMAIN_0_RATE 98304000 #define MT2701_PLL_DOMAIN_1_RATE 90316800 -#define MT2701_I2S_NUM 4 enum { MT2701_MEMIF_DL1, @@ -100,30 +90,30 @@ struct mt2701_i2s_data { int i2s_asrc_fs_mask; }; -enum mt2701_i2s_dir { - I2S_OUT, - I2S_IN, - I2S_DIR_NUM, -}; - struct mt2701_i2s_path { - int dai_id; int mclk_rate; - int on[I2S_DIR_NUM]; - int occupied[I2S_DIR_NUM]; - const struct mt2701_i2s_data *i2s_data[I2S_DIR_NUM]; - struct clk *hop_ck[I2S_DIR_NUM]; + int on[MTK_STREAM_NUM]; + int occupied[MTK_STREAM_NUM]; + const struct mt2701_i2s_data *i2s_data[MTK_STREAM_NUM]; + struct clk *hop_ck[MTK_STREAM_NUM]; struct clk *sel_ck; struct clk *div_ck; struct clk *mclk_ck; struct clk *asrco_ck; }; +struct mt2701_soc_variants { + bool has_one_heart_mode; + int i2s_num; +}; + struct mt2701_afe_private { - struct mt2701_i2s_path i2s_path[MT2701_I2S_NUM]; + struct mt2701_i2s_path *i2s_path; struct clk *base_ck[MT2701_BASE_CLK_NUM]; struct clk *mrgif_ck; - bool mrg_enable[MT2701_STREAM_DIR_NUM]; + bool mrg_enable[MTK_STREAM_NUM]; + + const struct mt2701_soc_variants *soc; }; #endif diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c index 73cce33fa439..828d11c30c6a 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c @@ -1,18 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Mediatek ALSA SoC AFE platform driver for 2701 * * Copyright (c) 2016 MediaTek Inc. * Author: Garlic Tseng <garlic.tseng@mediatek.com> - * Ir Lian <ir.lian@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Ir Lian <ir.lian@mediatek.com> + * Ryder Lee <ryder.lee@mediatek.com> */ #include <linux/delay.h> @@ -20,6 +13,7 @@ #include <linux/mfd/syscon.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/pm_runtime.h> #include "mt2701-afe-common.h" @@ -36,7 +30,7 @@ static const struct snd_pcm_hardware mt2701_afe_hardware = { .period_bytes_max = 1024 * 256, .periods_min = 4, .periods_max = 1024, - .buffer_bytes_max = 1024 * 1024 * 16, + .buffer_bytes_max = 1024 * 1024, .fifo_size = 0, }; @@ -68,9 +62,10 @@ static const struct mt2701_afe_rate mt2701_afe_i2s_rates[] = { static int mt2701_dai_num_to_i2s(struct mtk_base_afe *afe, int num) { + struct mt2701_afe_private *afe_priv = afe->platform_priv; int val = num - MT2701_IO_I2S; - if (val < 0 || val >= MT2701_I2S_NUM) { + if (val < 0 || val >= afe_priv->soc->i2s_num) { dev_err(afe->dev, "%s, num not available, num %d, val %d\n", __func__, num, val); return -EINVAL; @@ -92,44 +87,26 @@ static int mt2701_afe_i2s_fs(unsigned int sample_rate) static int mt2701_afe_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt2701_afe_private *afe_priv = afe->platform_priv; int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id); + bool mode = afe_priv->soc->has_one_heart_mode; if (i2s_num < 0) return i2s_num; - return mt2701_afe_enable_mclk(afe, i2s_num); + return mt2701_afe_enable_mclk(afe, mode ? 1 : i2s_num); } -static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai, - int i2s_num, - int dir_invert) +static int mt2701_afe_i2s_path_disable(struct mtk_base_afe *afe, + struct mt2701_i2s_path *i2s_path, + int stream_dir) { - 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); - struct mt2701_afe_private *afe_priv = afe->platform_priv; - struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i2s_num]; - const struct mt2701_i2s_data *i2s_data; - int stream_dir = substream->stream; - - if (dir_invert) { - if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) - stream_dir = SNDRV_PCM_STREAM_CAPTURE; - else - stream_dir = SNDRV_PCM_STREAM_PLAYBACK; - } - i2s_data = i2s_path->i2s_data[stream_dir]; + const struct mt2701_i2s_data *i2s_data = i2s_path->i2s_data[stream_dir]; - i2s_path->on[stream_dir]--; - if (i2s_path->on[stream_dir] < 0) { - dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n", - i2s_path->on[stream_dir], stream_dir); + if (--i2s_path->on[stream_dir] < 0) i2s_path->on[stream_dir] = 0; - } + if (i2s_path->on[stream_dir]) return 0; @@ -137,7 +114,7 @@ static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream, regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, ASYS_I2S_CON_I2S_EN, 0); - mt2701_afe_disable_i2s(afe, i2s_num, stream_dir); + mt2701_afe_disable_i2s(afe, i2s_path, stream_dir); return 0; } @@ -145,12 +122,11 @@ static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream, static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt2701_afe_private *afe_priv = afe->platform_priv; int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id); struct mt2701_i2s_path *i2s_path; + bool mode = afe_priv->soc->has_one_heart_mode; if (i2s_num < 0) return; @@ -160,50 +136,33 @@ static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream, if (i2s_path->occupied[substream->stream]) i2s_path->occupied[substream->stream] = 0; else - goto I2S_UNSTART; + goto exit; - mt2701_afe_i2s_path_shutdown(substream, dai, i2s_num, 0); + mt2701_afe_i2s_path_disable(afe, i2s_path, substream->stream); /* need to disable i2s-out path when disable i2s-in */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - mt2701_afe_i2s_path_shutdown(substream, dai, i2s_num, 1); + mt2701_afe_i2s_path_disable(afe, i2s_path, !substream->stream); -I2S_UNSTART: +exit: /* disable mclk */ - mt2701_afe_disable_mclk(afe, i2s_num); + mt2701_afe_disable_mclk(afe, mode ? 1 : i2s_num); } -static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai, - int i2s_num, - int dir_invert) +static int mt2701_i2s_path_enable(struct mtk_base_afe *afe, + struct mt2701_i2s_path *i2s_path, + int stream_dir, 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); + const struct mt2701_i2s_data *i2s_data = i2s_path->i2s_data[stream_dir]; struct mt2701_afe_private *afe_priv = afe->platform_priv; - struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i2s_num]; - const struct mt2701_i2s_data *i2s_data; - struct snd_pcm_runtime * const runtime = substream->runtime; int reg, fs, w_len = 1; /* now we support bck 64bits only */ - int stream_dir = substream->stream; - unsigned int mask = 0, val = 0; - - if (dir_invert) { - if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) - stream_dir = SNDRV_PCM_STREAM_CAPTURE; - else - stream_dir = SNDRV_PCM_STREAM_PLAYBACK; - } - i2s_data = i2s_path->i2s_data[stream_dir]; + unsigned int mask, val; /* no need to enable if already done */ - i2s_path->on[stream_dir]++; - - if (i2s_path->on[stream_dir] != 1) + if (++i2s_path->on[stream_dir] != 1) return 0; - fs = mt2701_afe_i2s_fs(runtime->rate); + fs = mt2701_afe_i2s_fs(rate); mask = ASYS_I2S_CON_FS | ASYS_I2S_CON_I2S_COUPLE_MODE | /* 0 */ @@ -217,22 +176,24 @@ static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream, if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) { mask |= ASYS_I2S_IN_PHASE_FIX; val |= ASYS_I2S_IN_PHASE_FIX; + reg = ASMI_TIMING_CON1; + } else { + if (afe_priv->soc->has_one_heart_mode) { + mask |= ASYS_I2S_CON_ONE_HEART_MODE; + val |= ASYS_I2S_CON_ONE_HEART_MODE; + } + reg = ASMO_TIMING_CON1; } regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val); - if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) - reg = ASMO_TIMING_CON1; - else - reg = ASMI_TIMING_CON1; - regmap_update_bits(afe->regmap, reg, i2s_data->i2s_asrc_fs_mask << i2s_data->i2s_asrc_fs_shift, fs << i2s_data->i2s_asrc_fs_shift); /* enable i2s */ - mt2701_afe_enable_i2s(afe, i2s_num, stream_dir); + mt2701_afe_enable_i2s(afe, i2s_path, stream_dir); /* reset i2s hw status before enable */ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, @@ -249,45 +210,33 @@ static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream, static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int clk_domain; - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt2701_afe_private *afe_priv = afe->platform_priv; - int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id); + int ret, i2s_num = mt2701_dai_num_to_i2s(afe, dai->id); struct mt2701_i2s_path *i2s_path; - int mclk_rate; + bool mode = afe_priv->soc->has_one_heart_mode; if (i2s_num < 0) return i2s_num; i2s_path = &afe_priv->i2s_path[i2s_num]; - mclk_rate = i2s_path->mclk_rate; if (i2s_path->occupied[substream->stream]) return -EBUSY; + + ret = mt2701_mclk_configuration(afe, mode ? 1 : i2s_num); + if (ret) + return ret; + i2s_path->occupied[substream->stream] = 1; - if (MT2701_PLL_DOMAIN_0_RATE % mclk_rate == 0) { - clk_domain = 0; - } else if (MT2701_PLL_DOMAIN_1_RATE % mclk_rate == 0) { - clk_domain = 1; - } else { - dev_err(dai->dev, "%s() bad mclk rate %d\n", - __func__, mclk_rate); - return -EINVAL; - } - mt2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate); + /* need to enable i2s-out path when enable i2s-in */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + mt2701_i2s_path_enable(afe, i2s_path, !substream->stream, + substream->runtime->rate); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - mt2701_i2s_path_prepare_enable(substream, dai, i2s_num, 0); - } else { - /* need to enable i2s-out path when enable i2s-in */ - /* prepare for another direction "out" */ - mt2701_i2s_path_prepare_enable(substream, dai, i2s_num, 1); - /* prepare for "in" */ - mt2701_i2s_path_prepare_enable(substream, dai, i2s_num, 0); - } + mt2701_i2s_path_enable(afe, i2s_path, substream->stream, + substream->runtime->rate); return 0; } @@ -295,30 +244,29 @@ static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream, static int mt2701_afe_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { - struct mtk_base_afe *afe = dev_get_drvdata(dai->dev); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt2701_afe_private *afe_priv = afe->platform_priv; int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id); + bool mode = afe_priv->soc->has_one_heart_mode; if (i2s_num < 0) return i2s_num; /* mclk */ if (dir == SND_SOC_CLOCK_IN) { - dev_warn(dai->dev, - "%s() warning: mt2701 doesn't support mclk input\n", - __func__); + dev_warn(dai->dev, "The SoCs doesn't support mclk input\n"); return -EINVAL; } - afe_priv->i2s_path[i2s_num].mclk_rate = freq; + + afe_priv->i2s_path[mode ? 1 : i2s_num].mclk_rate = freq; + return 0; } static int mt2701_btmrg_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt2701_afe_private *afe_priv = afe->platform_priv; int ret; @@ -327,6 +275,7 @@ static int mt2701_btmrg_startup(struct snd_pcm_substream *substream, return ret; afe_priv->mrg_enable[substream->stream] = 1; + return 0; } @@ -334,17 +283,14 @@ static int mt2701_btmrg_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); int stream_fs; u32 val, msk; stream_fs = params_rate(params); - if ((stream_fs != 8000) && (stream_fs != 16000)) { - dev_err(afe->dev, "%s() btmgr not support this stream_fs %d\n", - __func__, stream_fs); + if (stream_fs != 8000 && stream_fs != 16000) { + dev_err(afe->dev, "unsupported rate %d\n", stream_fs); return -EINVAL; } @@ -378,9 +324,7 @@ static int mt2701_btmrg_hw_params(struct snd_pcm_substream *substream, static void mt2701_btmrg_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt2701_afe_private *afe_priv = afe->platform_priv; /* if the other direction stream is not occupied */ @@ -393,28 +337,26 @@ static void mt2701_btmrg_shutdown(struct snd_pcm_substream *substream, AFE_MRGIF_CON_MRG_I2S_EN, 0); mt2701_disable_btmrg_clk(afe); } + afe_priv->mrg_enable[substream->stream] = 0; } static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); - int stream_dir = substream->stream; - int memif_num = rtd->cpu_dai->id; + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mtk_base_afe_memif *memif_tmp; + int stream_dir = substream->stream; /* can't run single DL & DLM at the same time */ if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) { memif_tmp = &afe->memif[MT2701_MEMIF_DLM]; if (memif_tmp->substream) { - dev_warn(afe->dev, "%s memif is not available, stream_dir %d, memif_num %d\n", - __func__, stream_dir, memif_num); + dev_warn(afe->dev, "memif is not available"); return -EBUSY; } } + return mtk_afe_fe_startup(substream, dai); } @@ -422,27 +364,23 @@ static int mt2701_simple_fe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); int stream_dir = substream->stream; /* single DL use PAIR_INTERLEAVE */ - if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) { + if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) regmap_update_bits(afe->regmap, AFE_MEMIF_PBUF_SIZE, AFE_MEMIF_PBUF_SIZE_DLM_MASK, AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE); - } + return mtk_afe_fe_hw_params(substream, params, dai); } static int mt2701_dlm_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mtk_base_afe_memif *memif_tmp; const struct mtk_base_memif_data *memif_data; int i; @@ -468,9 +406,7 @@ static int mt2701_dlm_fe_startup(struct snd_pcm_substream *substream, static void mt2701_dlm_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); const struct mtk_base_memif_data *memif_data; int i; @@ -481,6 +417,7 @@ static void mt2701_dlm_fe_shutdown(struct snd_pcm_substream *substream, 1 << memif_data->agent_disable_shift, 1 << memif_data->agent_disable_shift); } + return mtk_afe_fe_shutdown(substream, dai); } @@ -488,9 +425,7 @@ static int mt2701_dlm_fe_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); int channels = params_channels(params); regmap_update_bits(afe->regmap, @@ -512,9 +447,7 @@ static int mt2701_dlm_fe_hw_params(struct snd_pcm_substream *substream, static int mt2701_dlm_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mtk_base_afe_memif *memif_tmp = &afe->memif[MT2701_MEMIF_DL1]; switch (cmd) { @@ -547,6 +480,7 @@ static int mt2701_memif_fs(struct snd_pcm_substream *substream, fs = mt2701_afe_i2s_fs(rate); else fs = (rate == 16000 ? 1 : 0); + return fs; } @@ -1024,7 +958,17 @@ static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = { { "O31", "I35 Switch", "I35" }, }; +static int mt2701_afe_pcm_probe(struct snd_soc_component *component) +{ + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + + snd_soc_component_init_regmap(component, afe->regmap); + + return 0; +} + static const struct snd_soc_component_driver mt2701_afe_pcm_dai_component = { + .probe = mt2701_afe_pcm_probe, .name = "mt2701-afe-pcm-dai", .dapm_widgets = mt2701_afe_pcm_widgets, .num_dapm_widgets = ARRAY_SIZE(mt2701_afe_pcm_widgets), @@ -1324,63 +1268,24 @@ static const struct mtk_base_irq_data irq_data[MT2701_IRQ_ASYS_END] = { } }; -static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = { +static const struct mt2701_i2s_data mt2701_i2s_data[][2] = { { - { - .i2s_ctrl_reg = ASYS_I2SO1_CON, - .i2s_asrc_fs_shift = 0, - .i2s_asrc_fs_mask = 0x1f, - - }, - { - .i2s_ctrl_reg = ASYS_I2SIN1_CON, - .i2s_asrc_fs_shift = 0, - .i2s_asrc_fs_mask = 0x1f, - - }, + { ASYS_I2SO1_CON, 0, 0x1f }, + { ASYS_I2SIN1_CON, 0, 0x1f }, }, { - { - .i2s_ctrl_reg = ASYS_I2SO2_CON, - .i2s_asrc_fs_shift = 5, - .i2s_asrc_fs_mask = 0x1f, - - }, - { - .i2s_ctrl_reg = ASYS_I2SIN2_CON, - .i2s_asrc_fs_shift = 5, - .i2s_asrc_fs_mask = 0x1f, - - }, + { ASYS_I2SO2_CON, 5, 0x1f }, + { ASYS_I2SIN2_CON, 5, 0x1f }, }, { - { - .i2s_ctrl_reg = ASYS_I2SO3_CON, - .i2s_asrc_fs_shift = 10, - .i2s_asrc_fs_mask = 0x1f, - - }, - { - .i2s_ctrl_reg = ASYS_I2SIN3_CON, - .i2s_asrc_fs_shift = 10, - .i2s_asrc_fs_mask = 0x1f, - - }, + { ASYS_I2SO3_CON, 10, 0x1f }, + { ASYS_I2SIN3_CON, 10, 0x1f }, }, { - { - .i2s_ctrl_reg = ASYS_I2SO4_CON, - .i2s_asrc_fs_shift = 15, - .i2s_asrc_fs_mask = 0x1f, - - }, - { - .i2s_ctrl_reg = ASYS_I2SIN4_CON, - .i2s_asrc_fs_shift = 15, - .i2s_asrc_fs_mask = 0x1f, - - }, + { ASYS_I2SO4_CON, 15, 0x1f }, + { ASYS_I2SIN4_CON, 15, 0x1f }, }, + /* TODO - extend control registers supported by newer SoCs */ }; static irqreturn_t mt2701_asys_isr(int irq_id, void *dev) @@ -1398,10 +1303,12 @@ static irqreturn_t mt2701_asys_isr(int irq_id, void *dev) memif = &afe->memif[id]; if (memif->irq_usage < 0) continue; + irq = &afe->irqs[memif->irq_usage]; - if (status & 1 << (irq->irq_data->irq_clr_shift)) + if (status & 1 << irq->irq_data->irq_clr_shift) snd_pcm_period_elapsed(memif->substream); } + return IRQ_HANDLED; } @@ -1419,22 +1326,6 @@ static int mt2701_afe_runtime_resume(struct device *dev) return mt2701_afe_enable_clock(afe); } -static int mt2701_afe_add_component(struct mtk_base_afe *afe) -{ - struct snd_soc_component *component; - - component = kzalloc(sizeof(*component), GFP_KERNEL); - if (!component) - return -ENOMEM; - - component->regmap = afe->regmap; - - return snd_soc_add_component(afe->dev, component, - &mt2701_afe_pcm_dai_component, - mt2701_afe_pcm_dais, - ARRAY_SIZE(mt2701_afe_pcm_dais)); -} - static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; @@ -1452,9 +1343,16 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) return -ENOMEM; afe_priv = afe->platform_priv; + afe_priv->soc = of_device_get_match_data(&pdev->dev); afe->dev = &pdev->dev; dev = afe->dev; + afe_priv->i2s_path = devm_kzalloc(dev, afe_priv->soc->i2s_num * + sizeof(struct mt2701_i2s_path), + GFP_KERNEL); + if (!afe_priv->i2s_path) + return -ENOMEM; + irq_id = platform_get_irq_byname(pdev, "asys"); if (irq_id < 0) { dev_err(dev, "unable to get ASYS IRQ\n"); @@ -1499,11 +1397,11 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) afe->irqs[i].irq_data = &irq_data[i]; /* I2S initialize */ - for (i = 0; i < MT2701_I2S_NUM; i++) { - afe_priv->i2s_path[i].i2s_data[I2S_OUT] - = &mt2701_i2s_data[i][I2S_OUT]; - afe_priv->i2s_path[i].i2s_data[I2S_IN] - = &mt2701_i2s_data[i][I2S_IN]; + for (i = 0; i < afe_priv->soc->i2s_num; i++) { + afe_priv->i2s_path[i].i2s_data[SNDRV_PCM_STREAM_PLAYBACK] = + &mt2701_i2s_data[i][SNDRV_PCM_STREAM_PLAYBACK]; + afe_priv->i2s_path[i].i2s_data[SNDRV_PCM_STREAM_CAPTURE] = + &mt2701_i2s_data[i][SNDRV_PCM_STREAM_CAPTURE]; } afe->mtk_afe_hardware = &mt2701_afe_hardware; @@ -1538,7 +1436,10 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev) goto err_platform; } - ret = mt2701_afe_add_component(afe); + ret = devm_snd_soc_register_component(&pdev->dev, + &mt2701_afe_pcm_dai_component, + mt2701_afe_pcm_dais, + ARRAY_SIZE(mt2701_afe_pcm_dais)); if (ret) { dev_warn(dev, "err_dai_component\n"); goto err_platform; @@ -1564,8 +1465,18 @@ static int mt2701_afe_pcm_dev_remove(struct platform_device *pdev) return 0; } +static const struct mt2701_soc_variants mt2701_soc_v1 = { + .i2s_num = 4, +}; + +static const struct mt2701_soc_variants mt2701_soc_v2 = { + .has_one_heart_mode = true, + .i2s_num = 4, +}; + static const struct of_device_id mt2701_afe_pcm_dt_match[] = { - { .compatible = "mediatek,mt2701-audio", }, + { .compatible = "mediatek,mt2701-audio", .data = &mt2701_soc_v1 }, + { .compatible = "mediatek,mt7622-audio", .data = &mt2701_soc_v2 }, {}, }; MODULE_DEVICE_TABLE(of, mt2701_afe_pcm_dt_match); diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c index 70f61d53fe05..666282b865a8 100644 --- a/sound/soc/mediatek/mt2701/mt2701-cs42448.c +++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mt2701-cs42448.c -- MT2701 CS42448 ALSA SoC machine driver * * Copyright (c) 2016 MediaTek Inc. * Author: Ir Lian <ir.lian@mediatek.com> - * Garlic Tseng <garlic.tseng@mediatek.com> - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Garlic Tseng <garlic.tseng@mediatek.com> */ #include <linux/module.h> diff --git a/sound/soc/mediatek/mt2701/mt2701-reg.h b/sound/soc/mediatek/mt2701/mt2701-reg.h index 18e676974f22..c84d14cdd7ae 100644 --- a/sound/soc/mediatek/mt2701/mt2701-reg.h +++ b/sound/soc/mediatek/mt2701/mt2701-reg.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * mt2701-reg.h -- Mediatek 2701 audio driver reg definition * * Copyright (c) 2016 MediaTek Inc. * Author: Garlic Tseng <garlic.tseng@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _MT2701_REG_H_ @@ -138,6 +130,7 @@ #define ASYS_I2S_CON_FS_SET(x) ((x) << 8) #define ASYS_I2S_CON_RESET (0x1 << 30) #define ASYS_I2S_CON_I2S_EN (0x1 << 0) +#define ASYS_I2S_CON_ONE_HEART_MODE (0x1 << 16) #define ASYS_I2S_CON_I2S_COUPLE_MODE (0x1 << 17) /* 0:EIAJ 1:I2S */ #define ASYS_I2S_CON_I2S_MODE (0x1 << 3) diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c index a08ce2323bdc..89f34efd9747 100644 --- a/sound/soc/mediatek/mt2701/mt2701-wm8960.c +++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mt2701-wm8960.c -- MT2701 WM8960 ALSA SoC machine driver * * Copyright (c) 2017 MediaTek Inc. * Author: Ryder Lee <ryder.lee@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> diff --git a/sound/soc/mediatek/mt6797/Makefile b/sound/soc/mediatek/mt6797/Makefile new file mode 100644 index 000000000000..bf6e179ea93f --- /dev/null +++ b/sound/soc/mediatek/mt6797/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 + +# platform driver +snd-soc-mt6797-afe-objs := \ + mt6797-afe-pcm.o \ + mt6797-afe-clk.o \ + mt6797-dai-pcm.o \ + mt6797-dai-hostless.o \ + mt6797-dai-adda.o + +obj-$(CONFIG_SND_SOC_MT6797) += snd-soc-mt6797-afe.o + +# machine driver +obj-$(CONFIG_SND_SOC_MT6797_MT6351) += mt6797-mt6351.o diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-clk.c b/sound/soc/mediatek/mt6797/mt6797-afe-clk.c new file mode 100644 index 000000000000..6f3e6acfcfab --- /dev/null +++ b/sound/soc/mediatek/mt6797/mt6797-afe-clk.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt6797-afe-clk.c -- Mediatek 6797 afe clock ctrl +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/clk.h> + +#include "mt6797-afe-common.h" +#include "mt6797-afe-clk.h" + +enum { + CLK_INFRA_SYS_AUD, + CLK_INFRA_SYS_AUD_26M, + CLK_TOP_MUX_AUD, + CLK_TOP_MUX_AUD_BUS, + CLK_TOP_SYSPLL3_D4, + CLK_TOP_SYSPLL1_D4, + CLK_CLK26M, + CLK_NUM +}; + +static const char *aud_clks[CLK_NUM] = { + [CLK_INFRA_SYS_AUD] = "infra_sys_audio_clk", + [CLK_INFRA_SYS_AUD_26M] = "infra_sys_audio_26m", + [CLK_TOP_MUX_AUD] = "top_mux_audio", + [CLK_TOP_MUX_AUD_BUS] = "top_mux_aud_intbus", + [CLK_TOP_SYSPLL3_D4] = "top_sys_pll3_d4", + [CLK_TOP_SYSPLL1_D4] = "top_sys_pll1_d4", + [CLK_CLK26M] = "top_clk26m_clk", +}; + +int mt6797_init_clock(struct mtk_base_afe *afe) +{ + struct mt6797_afe_private *afe_priv = afe->platform_priv; + int i; + + afe_priv->clk = devm_kcalloc(afe->dev, CLK_NUM, sizeof(*afe_priv->clk), + GFP_KERNEL); + if (!afe_priv->clk) + return -ENOMEM; + + for (i = 0; i < CLK_NUM; i++) { + afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]); + if (IS_ERR(afe_priv->clk[i])) { + dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n", + __func__, aud_clks[i], + PTR_ERR(afe_priv->clk[i])); + return PTR_ERR(afe_priv->clk[i]); + } + } + + return 0; +} + +int mt6797_afe_enable_clock(struct mtk_base_afe *afe) +{ + struct mt6797_afe_private *afe_priv = afe->platform_priv; + int ret; + + ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUD]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_INFRA_SYS_AUD], ret); + goto CLK_INFRA_SYS_AUDIO_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUD_26M]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_INFRA_SYS_AUD_26M], ret); + goto CLK_INFRA_SYS_AUD_26M_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD], ret); + goto CLK_MUX_AUDIO_ERR; + } + + ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD], + afe_priv->clk[CLK_CLK26M]); + if (ret) { + dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD], + aud_clks[CLK_CLK26M], ret); + goto CLK_MUX_AUDIO_ERR; + } + + ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_BUS]); + if (ret) { + dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n", + __func__, aud_clks[CLK_TOP_MUX_AUD_BUS], ret); + goto CLK_MUX_AUDIO_INTBUS_ERR; + } + + return ret; + +CLK_MUX_AUDIO_INTBUS_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_BUS]); +CLK_MUX_AUDIO_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD]); +CLK_INFRA_SYS_AUD_26M_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUD_26M]); +CLK_INFRA_SYS_AUDIO_ERR: + clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUD]); + + return 0; +} + +int mt6797_afe_disable_clock(struct mtk_base_afe *afe) +{ + struct mt6797_afe_private *afe_priv = afe->platform_priv; + + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_BUS]); + clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD]); + clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUD_26M]); + clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUD]); + + return 0; +} diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-clk.h b/sound/soc/mediatek/mt6797/mt6797-afe-clk.h new file mode 100644 index 000000000000..a6f0cb572711 --- /dev/null +++ b/sound/soc/mediatek/mt6797/mt6797-afe-clk.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt6797-afe-clk.h -- Mediatek 6797 afe clock ctrl definition + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + */ + +#ifndef _MT6797_AFE_CLK_H_ +#define _MT6797_AFE_CLK_H_ + +struct mtk_base_afe; + +int mt6797_init_clock(struct mtk_base_afe *afe); +int mt6797_afe_enable_clock(struct mtk_base_afe *afe); +int mt6797_afe_disable_clock(struct mtk_base_afe *afe); +#endif diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-common.h b/sound/soc/mediatek/mt6797/mt6797-afe-common.h new file mode 100644 index 000000000000..22eb7b455cf1 --- /dev/null +++ b/sound/soc/mediatek/mt6797/mt6797-afe-common.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt6797-afe-common.h -- Mediatek 6797 audio driver definitions + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + */ + +#ifndef _MT_6797_AFE_COMMON_H_ +#define _MT_6797_AFE_COMMON_H_ + +#include <sound/soc.h> +#include <linux/regmap.h> +#include "../common/mtk-base-afe.h" + +enum { + MT6797_MEMIF_DL1, + MT6797_MEMIF_DL2, + MT6797_MEMIF_DL3, + MT6797_MEMIF_VUL, + MT6797_MEMIF_AWB, + MT6797_MEMIF_VUL12, + MT6797_MEMIF_DAI, + MT6797_MEMIF_MOD_DAI, + MT6797_MEMIF_NUM, + MT6797_DAI_ADDA = MT6797_MEMIF_NUM, + MT6797_DAI_PCM_1, + MT6797_DAI_PCM_2, + MT6797_DAI_HOSTLESS_LPBK, + MT6797_DAI_HOSTLESS_SPEECH, + MT6797_DAI_NUM, +}; + +enum { + MT6797_IRQ_1, + MT6797_IRQ_2, + MT6797_IRQ_3, + MT6797_IRQ_4, + MT6797_IRQ_7, + MT6797_IRQ_NUM, +}; + +struct clk; + +struct mt6797_afe_private { + struct clk **clk; +}; + +unsigned int mt6797_general_rate_transform(struct device *dev, + unsigned int rate); +unsigned int mt6797_rate_transform(struct device *dev, + unsigned int rate, int aud_blk); + +/* dai register */ +int mt6797_dai_adda_register(struct mtk_base_afe *afe); +int mt6797_dai_pcm_register(struct mtk_base_afe *afe); +int mt6797_dai_hostless_register(struct mtk_base_afe *afe); +#endif diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c new file mode 100644 index 000000000000..6c5dd9fc9976 --- /dev/null +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -0,0 +1,914 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Mediatek ALSA SoC AFE platform driver for 6797 +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/pm_runtime.h> + +#include "mt6797-afe-common.h" +#include "mt6797-afe-clk.h" +#include "mt6797-interconnection.h" +#include "mt6797-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_384K = 3, + MTK_AFE_RATE_16K = 4, + MTK_AFE_RATE_22K = 5, + MTK_AFE_RATE_24K = 6, + MTK_AFE_RATE_130K = 7, + MTK_AFE_RATE_32K = 8, + MTK_AFE_RATE_44K = 9, + MTK_AFE_RATE_48K = 10, + MTK_AFE_RATE_88K = 11, + MTK_AFE_RATE_96K = 12, + MTK_AFE_RATE_174K = 13, + MTK_AFE_RATE_192K = 14, + MTK_AFE_RATE_260K = 15, +}; + +enum { + MTK_AFE_DAI_MEMIF_RATE_8K = 0, + MTK_AFE_DAI_MEMIF_RATE_16K = 1, + MTK_AFE_DAI_MEMIF_RATE_32K = 2, +}; + +enum { + MTK_AFE_PCM_RATE_8K = 0, + MTK_AFE_PCM_RATE_16K = 1, + MTK_AFE_PCM_RATE_32K = 2, + MTK_AFE_PCM_RATE_48K = 3, +}; + +unsigned int mt6797_general_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 130000: + return MTK_AFE_RATE_130K; + case 176400: + return MTK_AFE_RATE_174K; + case 192000: + return MTK_AFE_RATE_192K; + case 260000: + return MTK_AFE_RATE_260K; + default: + dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n", + __func__, rate, MTK_AFE_RATE_48K); + return MTK_AFE_RATE_48K; + } +} + +static unsigned int dai_memif_rate_transform(struct device *dev, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_DAI_MEMIF_RATE_8K; + case 16000: + return MTK_AFE_DAI_MEMIF_RATE_16K; + case 32000: + return MTK_AFE_DAI_MEMIF_RATE_32K; + default: + dev_warn(dev, "%s(), rate %u invalid, use %d!!!\n", + __func__, rate, MTK_AFE_DAI_MEMIF_RATE_16K); + return MTK_AFE_DAI_MEMIF_RATE_16K; + } +} + +unsigned int mt6797_rate_transform(struct device *dev, + unsigned int rate, int aud_blk) +{ + switch (aud_blk) { + case MT6797_MEMIF_DAI: + case MT6797_MEMIF_MOD_DAI: + return dai_memif_rate_transform(dev, rate); + default: + return mt6797_general_rate_transform(dev, rate); + } +} + +static const struct snd_pcm_hardware mt6797_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 mt6797_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); + int id = rtd->cpu_dai->id; + + return mt6797_rate_transform(afe->dev, rate, id); +} + +static int mt6797_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 mt6797_general_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_DAI_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000) + +#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 mt6797_memif_dai_driver[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL1", + .id = MT6797_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 = "DL2", + .id = MT6797_MEMIF_DL2, + .playback = { + .stream_name = "DL2", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "DL3", + .id = MT6797_MEMIF_DL3, + .playback = { + .stream_name = "DL3", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL1", + .id = MT6797_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, + }, + { + .name = "UL2", + .id = MT6797_MEMIF_AWB, + .capture = { + .stream_name = "UL2", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL3", + .id = MT6797_MEMIF_VUL, + .capture = { + .stream_name = "UL3", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL_MONO_1", + .id = MT6797_MEMIF_MOD_DAI, + .capture = { + .stream_name = "UL_MONO_1", + .channels_min = 1, + .channels_max = 1, + .rates = MTK_PCM_DAI_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL_MONO_2", + .id = MT6797_MEMIF_DAI, + .capture = { + .stream_name = "UL_MONO_2", + .channels_min = 1, + .channels_max = 1, + .rates = MTK_PCM_DAI_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, +}; + +/* dma widget & routes*/ +static const struct snd_kcontrol_new memif_ul1_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN21, + I_ADDA_UL_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul1_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN22, + I_ADDA_UL_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul2_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN5, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN5, + I_DL1_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN5, + I_DL2_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN5, + I_DL3_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul2_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN6, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN6, + I_DL1_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN6, + I_DL2_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN6, + I_DL3_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul3_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN9, + I_ADDA_UL_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul3_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN10, + I_ADDA_UL_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul_mono_1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN12, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN12, + I_ADDA_UL_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new memif_ul_mono_2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN11, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN11, + I_ADDA_UL_CH2, 1, 0), +}; + +static const struct snd_soc_dapm_widget mt6797_memif_widgets[] = { + /* memif */ + SND_SOC_DAPM_MIXER("UL1_CH1", SND_SOC_NOPM, 0, 0, + memif_ul1_ch1_mix, ARRAY_SIZE(memif_ul1_ch1_mix)), + SND_SOC_DAPM_MIXER("UL1_CH2", SND_SOC_NOPM, 0, 0, + memif_ul1_ch2_mix, ARRAY_SIZE(memif_ul1_ch2_mix)), + + SND_SOC_DAPM_MIXER("UL2_CH1", SND_SOC_NOPM, 0, 0, + memif_ul2_ch1_mix, ARRAY_SIZE(memif_ul2_ch1_mix)), + SND_SOC_DAPM_MIXER("UL2_CH2", SND_SOC_NOPM, 0, 0, + memif_ul2_ch2_mix, ARRAY_SIZE(memif_ul2_ch2_mix)), + + SND_SOC_DAPM_MIXER("UL3_CH1", SND_SOC_NOPM, 0, 0, + memif_ul3_ch1_mix, ARRAY_SIZE(memif_ul3_ch1_mix)), + SND_SOC_DAPM_MIXER("UL3_CH2", SND_SOC_NOPM, 0, 0, + memif_ul3_ch2_mix, ARRAY_SIZE(memif_ul3_ch2_mix)), + + SND_SOC_DAPM_MIXER("UL_MONO_1_CH1", SND_SOC_NOPM, 0, 0, + memif_ul_mono_1_mix, + ARRAY_SIZE(memif_ul_mono_1_mix)), + + SND_SOC_DAPM_MIXER("UL_MONO_2_CH1", SND_SOC_NOPM, 0, 0, + memif_ul_mono_2_mix, + ARRAY_SIZE(memif_ul_mono_2_mix)), +}; + +static const struct snd_soc_dapm_route mt6797_memif_routes[] = { + /* capture */ + {"UL1", NULL, "UL1_CH1"}, + {"UL1", NULL, "UL1_CH2"}, + {"UL1_CH1", "ADDA_UL_CH1", "ADDA Capture"}, + {"UL1_CH2", "ADDA_UL_CH2", "ADDA Capture"}, + + {"UL2", NULL, "UL2_CH1"}, + {"UL2", NULL, "UL2_CH2"}, + {"UL2_CH1", "ADDA_UL_CH1", "ADDA Capture"}, + {"UL2_CH2", "ADDA_UL_CH2", "ADDA Capture"}, + + {"UL3", NULL, "UL3_CH1"}, + {"UL3", NULL, "UL3_CH2"}, + {"UL3_CH1", "ADDA_UL_CH1", "ADDA Capture"}, + {"UL3_CH2", "ADDA_UL_CH2", "ADDA Capture"}, + + {"UL_MONO_1", NULL, "UL_MONO_1_CH1"}, + {"UL_MONO_1_CH1", "ADDA_UL_CH1", "ADDA Capture"}, + {"UL_MONO_1_CH1", "ADDA_UL_CH2", "ADDA Capture"}, + + {"UL_MONO_2", NULL, "UL_MONO_2_CH1"}, + {"UL_MONO_2_CH1", "ADDA_UL_CH1", "ADDA Capture"}, + {"UL_MONO_2_CH1", "ADDA_UL_CH2", "ADDA Capture"}, +}; + +static const struct snd_soc_component_driver mt6797_afe_pcm_dai_component = { + .name = "mt6797-afe-pcm-dai", +}; + +static const struct mtk_base_memif_data memif_data[MT6797_MEMIF_NUM] = { + [MT6797_MEMIF_DL1] = { + .name = "DL1", + .id = MT6797_MEMIF_DL1, + .reg_ofs_base = AFE_DL1_BASE, + .reg_ofs_cur = AFE_DL1_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = DL1_MODE_SFT, + .fs_maskbit = DL1_MODE_MASK, + .mono_reg = AFE_DAC_CON1, + .mono_shift = DL1_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = DL1_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = DL1_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT6797_MEMIF_DL2] = { + .name = "DL2", + .id = MT6797_MEMIF_DL2, + .reg_ofs_base = AFE_DL2_BASE, + .reg_ofs_cur = AFE_DL2_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = DL2_MODE_SFT, + .fs_maskbit = DL2_MODE_MASK, + .mono_reg = AFE_DAC_CON1, + .mono_shift = DL2_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = DL2_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = DL2_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT6797_MEMIF_DL3] = { + .name = "DL3", + .id = MT6797_MEMIF_DL3, + .reg_ofs_base = AFE_DL3_BASE, + .reg_ofs_cur = AFE_DL3_CUR, + .fs_reg = AFE_DAC_CON0, + .fs_shift = DL3_MODE_SFT, + .fs_maskbit = DL3_MODE_MASK, + .mono_reg = AFE_DAC_CON1, + .mono_shift = DL3_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = DL3_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = DL3_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT6797_MEMIF_VUL] = { + .name = "VUL", + .id = MT6797_MEMIF_VUL, + .reg_ofs_base = AFE_VUL_BASE, + .reg_ofs_cur = AFE_VUL_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = VUL_MODE_SFT, + .fs_maskbit = VUL_MODE_MASK, + .mono_reg = AFE_DAC_CON1, + .mono_shift = VUL_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = VUL_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = VUL_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT6797_MEMIF_AWB] = { + .name = "AWB", + .id = MT6797_MEMIF_AWB, + .reg_ofs_base = AFE_AWB_BASE, + .reg_ofs_cur = AFE_AWB_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = AWB_MODE_SFT, + .fs_maskbit = AWB_MODE_MASK, + .mono_reg = AFE_DAC_CON1, + .mono_shift = AWB_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = AWB_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = AWB_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT6797_MEMIF_VUL12] = { + .name = "VUL12", + .id = MT6797_MEMIF_VUL12, + .reg_ofs_base = AFE_VUL_D2_BASE, + .reg_ofs_cur = AFE_VUL_D2_CUR, + .fs_reg = AFE_DAC_CON0, + .fs_shift = VUL_DATA2_MODE_SFT, + .fs_maskbit = VUL_DATA2_MODE_MASK, + .mono_reg = AFE_DAC_CON0, + .mono_shift = VUL_DATA2_DATA_SFT, + .enable_reg = AFE_DAC_CON0, + .enable_shift = VUL_DATA2_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = VUL_DATA2_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT6797_MEMIF_DAI] = { + .name = "DAI", + .id = MT6797_MEMIF_DAI, + .reg_ofs_base = AFE_DAI_BASE, + .reg_ofs_cur = AFE_DAI_CUR, + .fs_reg = AFE_DAC_CON0, + .fs_shift = DAI_MODE_SFT, + .fs_maskbit = DAI_MODE_MASK, + .mono_reg = -1, + .mono_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = DAI_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = DAI_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, + [MT6797_MEMIF_MOD_DAI] = { + .name = "MOD_DAI", + .id = MT6797_MEMIF_MOD_DAI, + .reg_ofs_base = AFE_MOD_DAI_BASE, + .reg_ofs_cur = AFE_MOD_DAI_CUR, + .fs_reg = AFE_DAC_CON1, + .fs_shift = MOD_DAI_MODE_SFT, + .fs_maskbit = MOD_DAI_MODE_MASK, + .mono_reg = -1, + .mono_shift = 0, + .enable_reg = AFE_DAC_CON0, + .enable_shift = MOD_DAI_ON_SFT, + .hd_reg = AFE_MEMIF_HD_MODE, + .hd_shift = MOD_DAI_HD_SFT, + .agent_disable_reg = -1, + .agent_disable_shift = -1, + .msb_reg = -1, + .msb_shift = -1, + }, +}; + +static const struct mtk_base_irq_data irq_data[MT6797_IRQ_NUM] = { + [MT6797_IRQ_1] = { + .id = MT6797_IRQ_1, + .irq_cnt_reg = AFE_IRQ_MCU_CNT1, + .irq_cnt_shift = AFE_IRQ_MCU_CNT1_SFT, + .irq_cnt_maskbit = AFE_IRQ_MCU_CNT1_MASK, + .irq_fs_reg = AFE_IRQ_MCU_CON, + .irq_fs_shift = IRQ1_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ1_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON, + .irq_en_shift = IRQ1_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ1_MCU_CLR_SFT, + }, + [MT6797_IRQ_2] = { + .id = MT6797_IRQ_2, + .irq_cnt_reg = AFE_IRQ_MCU_CNT2, + .irq_cnt_shift = AFE_IRQ_MCU_CNT2_SFT, + .irq_cnt_maskbit = AFE_IRQ_MCU_CNT2_MASK, + .irq_fs_reg = AFE_IRQ_MCU_CON, + .irq_fs_shift = IRQ2_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ2_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON, + .irq_en_shift = IRQ2_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ2_MCU_CLR_SFT, + }, + [MT6797_IRQ_3] = { + .id = MT6797_IRQ_3, + .irq_cnt_reg = AFE_IRQ_MCU_CNT3, + .irq_cnt_shift = AFE_IRQ_MCU_CNT3_SFT, + .irq_cnt_maskbit = AFE_IRQ_MCU_CNT3_MASK, + .irq_fs_reg = AFE_IRQ_MCU_CON, + .irq_fs_shift = IRQ3_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ3_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON, + .irq_en_shift = IRQ3_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ3_MCU_CLR_SFT, + }, + [MT6797_IRQ_4] = { + .id = MT6797_IRQ_4, + .irq_cnt_reg = AFE_IRQ_MCU_CNT4, + .irq_cnt_shift = AFE_IRQ_MCU_CNT4_SFT, + .irq_cnt_maskbit = AFE_IRQ_MCU_CNT4_MASK, + .irq_fs_reg = AFE_IRQ_MCU_CON, + .irq_fs_shift = IRQ4_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ4_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON, + .irq_en_shift = IRQ4_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ4_MCU_CLR_SFT, + }, + [MT6797_IRQ_7] = { + .id = MT6797_IRQ_7, + .irq_cnt_reg = AFE_IRQ_MCU_CNT7, + .irq_cnt_shift = AFE_IRQ_MCU_CNT7_SFT, + .irq_cnt_maskbit = AFE_IRQ_MCU_CNT7_MASK, + .irq_fs_reg = AFE_IRQ_MCU_CON, + .irq_fs_shift = IRQ7_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ7_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ_MCU_CON, + .irq_en_shift = IRQ7_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ7_MCU_CLR_SFT, + }, +}; + +static const struct regmap_config mt6797_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = AFE_MAX_REGISTER, +}; + +static irqreturn_t mt6797_afe_irq_handler(int irq_id, void *dev) +{ + struct mtk_base_afe *afe = dev; + struct mtk_base_afe_irq *irq; + unsigned int status; + unsigned int mcu_en; + int ret; + int i; + 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); + if (ret || (status & mcu_en) == 0) { + dev_err(afe->dev, "%s(), irq status err, ret %d, status 0x%x, mcu_en 0x%x\n", + __func__, ret, status, mcu_en); + + /* only clear IRQ which is sent to MCU */ + status = mcu_en & AFE_IRQ_STATUS_BITS; + + irq_ret = IRQ_NONE; + goto err_irq; + } + + for (i = 0; i < MT6797_MEMIF_NUM; i++) { + struct mtk_base_afe_memif *memif = &afe->memif[i]; + + if (!memif->substream) + continue; + + irq = &afe->irqs[memif->irq_usage]; + + if (status & (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 & AFE_IRQ_STATUS_BITS); + + return irq_ret; +} + +static int mt6797_afe_runtime_suspend(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + unsigned int afe_on_retm; + int retry = 0; + + /* disable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, AFE_ON_MASK_SFT, 0x0); + do { + regmap_read(afe->regmap, AFE_DAC_CON0, &afe_on_retm); + if ((afe_on_retm & AFE_ON_RETM_MASK_SFT) == 0) + break; + + udelay(10); + } while (++retry < 100000); + + if (retry) + dev_warn(afe->dev, "%s(), retry %d\n", __func__, retry); + + /* make sure all irq status are cleared */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CLR, 0xffff, 0xffff); + + return mt6797_afe_disable_clock(afe); +} + +static int mt6797_afe_runtime_resume(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + int ret; + + ret = mt6797_afe_enable_clock(afe); + if (ret) + return ret; + + /* irq signal to mcu only */ + regmap_write(afe->regmap, AFE_IRQ_MCU_EN, AFE_IRQ_MCU_EN_MASK_SFT); + + /* force all memif use normal mode */ + regmap_update_bits(afe->regmap, AFE_MEMIF_HDALIGN, + 0x7ff << 16, 0x7ff << 16); + /* force cpu use normal mode when access sram data */ + regmap_update_bits(afe->regmap, AFE_MEMIF_MSB, + CPU_COMPACT_MODE_MASK_SFT, 0); + /* force cpu use 8_24 format when writing 32bit data */ + regmap_update_bits(afe->regmap, AFE_MEMIF_MSB, + CPU_HD_ALIGN_MASK_SFT, 0); + + /* set all output port to 24bit */ + regmap_update_bits(afe->regmap, AFE_CONN_24BIT, + 0x3fffffff, 0x3fffffff); + + /* enable AFE */ + regmap_update_bits(afe->regmap, AFE_DAC_CON0, + AFE_ON_MASK_SFT, + 0x1 << AFE_ON_SFT); + + return 0; +} + +static int mt6797_afe_component_probe(struct snd_soc_component *component) +{ + return mtk_afe_add_sub_dai_control(component); +} + +static const struct snd_soc_component_driver mt6797_afe_component = { + .name = AFE_PCM_NAME, + .ops = &mtk_afe_pcm_ops, + .pcm_new = mtk_afe_pcm_new, + .pcm_free = mtk_afe_pcm_free, + .probe = mt6797_afe_component_probe, +}; + +static int mt6797_afe_pcm_dev_probe(struct platform_device *pdev) +{ + struct mtk_base_afe *afe; + struct mt6797_afe_private *afe_priv; + struct resource *res; + struct device *dev; + int i, irq_id, ret; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + + 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; + + /* initial audio related clock */ + ret = mt6797_init_clock(afe); + if (ret) { + dev_err(dev, "init clock error\n"); + return ret; + } + + /* regmap init */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + afe->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mt6797_afe_regmap_config); + if (IS_ERR(afe->regmap)) + return PTR_ERR(afe->regmap); + + /* init memif */ + afe->memif_size = MT6797_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 = MT6797_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) { + dev_err(dev, "%s no irq found\n", dev->of_node->name); + return -ENXIO; + } + ret = devm_request_irq(dev, irq_id, mt6797_afe_irq_handler, + IRQF_TRIGGER_NONE, "asys-isr", (void *)afe); + if (ret) { + dev_err(dev, "could not request_irq for asys-isr\n"); + return ret; + } + + /* init sub_dais */ + afe->num_sub_dais = MT6797_DAI_NUM; + afe->sub_dais = devm_kcalloc(dev, afe->num_sub_dais, + sizeof(*afe->sub_dais), + GFP_KERNEL); + if (!afe->sub_dais) + return -ENOMEM; + + mt6797_dai_adda_register(afe); + mt6797_dai_pcm_register(afe); + mt6797_dai_hostless_register(afe); + + afe->sub_dais[MT6797_MEMIF_DL1].dai_drivers = mt6797_memif_dai_driver; + afe->sub_dais[MT6797_MEMIF_DL1].num_dai_drivers = + ARRAY_SIZE(mt6797_memif_dai_driver); + afe->sub_dais[MT6797_MEMIF_DL1].dapm_widgets = mt6797_memif_widgets; + afe->sub_dais[MT6797_MEMIF_DL1].num_dapm_widgets = + ARRAY_SIZE(mt6797_memif_widgets); + afe->sub_dais[MT6797_MEMIF_DL1].dapm_routes = mt6797_memif_routes; + afe->sub_dais[MT6797_MEMIF_DL1].num_dapm_routes = + ARRAY_SIZE(mt6797_memif_routes); + + /* init dai_driver and component_driver */ + mtk_afe_combine_sub_dai(afe); + + afe->mtk_afe_hardware = &mt6797_afe_hardware; + afe->memif_fs = mt6797_memif_fs; + afe->irq_fs = mt6797_irq_fs; + + afe->runtime_resume = mt6797_afe_runtime_resume; + afe->runtime_suspend = mt6797_afe_runtime_suspend; + + platform_set_drvdata(pdev, afe); + + pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) + goto err_pm_disable; + pm_runtime_get_sync(&pdev->dev); + + /* register component */ + ret = devm_snd_soc_register_component(dev, &mt6797_afe_component, + NULL, 0); + if (ret) { + dev_warn(dev, "err_platform\n"); + goto err_pm_disable; + } + + ret = devm_snd_soc_register_component(afe->dev, + &mt6797_afe_pcm_dai_component, + afe->dai_drivers, + afe->num_dai_drivers); + if (ret) { + dev_warn(dev, "err_dai_component\n"); + goto err_pm_disable; + } + + return 0; + +err_pm_disable: + pm_runtime_disable(dev); + + return ret; +} + +static int mt6797_afe_pcm_dev_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + mt6797_afe_runtime_suspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); + + return 0; +} + +static const struct of_device_id mt6797_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt6797-audio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt6797_afe_pcm_dt_match); + +static const struct dev_pm_ops mt6797_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mt6797_afe_runtime_suspend, + mt6797_afe_runtime_resume, NULL) +}; + +static struct platform_driver mt6797_afe_pcm_driver = { + .driver = { + .name = "mt6797-audio", + .of_match_table = mt6797_afe_pcm_dt_match, +#ifdef CONFIG_PM + .pm = &mt6797_afe_pm_ops, +#endif + }, + .probe = mt6797_afe_pcm_dev_probe, + .remove = mt6797_afe_pcm_dev_remove, +}; + +module_platform_driver(mt6797_afe_pcm_driver); + +MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 6797"); +MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-adda.c b/sound/soc/mediatek/mt6797/mt6797-dai-adda.c new file mode 100644 index 000000000000..ad083265ce94 --- /dev/null +++ b/sound/soc/mediatek/mt6797/mt6797-dai-adda.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MediaTek ALSA SoC Audio DAI ADDA Control +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/regmap.h> +#include <linux/delay.h> +#include "mt6797-afe-common.h" +#include "mt6797-interconnection.h" +#include "mt6797-reg.h" + +enum { + MTK_AFE_ADDA_DL_RATE_8K = 0, + MTK_AFE_ADDA_DL_RATE_11K = 1, + MTK_AFE_ADDA_DL_RATE_12K = 2, + MTK_AFE_ADDA_DL_RATE_16K = 3, + MTK_AFE_ADDA_DL_RATE_22K = 4, + MTK_AFE_ADDA_DL_RATE_24K = 5, + MTK_AFE_ADDA_DL_RATE_32K = 6, + MTK_AFE_ADDA_DL_RATE_44K = 7, + MTK_AFE_ADDA_DL_RATE_48K = 8, + MTK_AFE_ADDA_DL_RATE_96K = 9, + MTK_AFE_ADDA_DL_RATE_192K = 10, +}; + +enum { + MTK_AFE_ADDA_UL_RATE_8K = 0, + MTK_AFE_ADDA_UL_RATE_16K = 1, + MTK_AFE_ADDA_UL_RATE_32K = 2, + MTK_AFE_ADDA_UL_RATE_48K = 3, + MTK_AFE_ADDA_UL_RATE_96K = 4, + MTK_AFE_ADDA_UL_RATE_192K = 5, + MTK_AFE_ADDA_UL_RATE_48K_HD = 6, +}; + +static unsigned int adda_dl_rate_transform(struct mtk_base_afe *afe, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_ADDA_DL_RATE_8K; + case 11025: + return MTK_AFE_ADDA_DL_RATE_11K; + case 12000: + return MTK_AFE_ADDA_DL_RATE_12K; + case 16000: + return MTK_AFE_ADDA_DL_RATE_16K; + case 22050: + return MTK_AFE_ADDA_DL_RATE_22K; + case 24000: + return MTK_AFE_ADDA_DL_RATE_24K; + case 32000: + return MTK_AFE_ADDA_DL_RATE_32K; + case 44100: + return MTK_AFE_ADDA_DL_RATE_44K; + case 48000: + return MTK_AFE_ADDA_DL_RATE_48K; + case 96000: + return MTK_AFE_ADDA_DL_RATE_96K; + case 192000: + return MTK_AFE_ADDA_DL_RATE_192K; + default: + dev_warn(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n", + __func__, rate); + return MTK_AFE_ADDA_DL_RATE_48K; + } +} + +static unsigned int adda_ul_rate_transform(struct mtk_base_afe *afe, + unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_ADDA_UL_RATE_8K; + case 16000: + return MTK_AFE_ADDA_UL_RATE_16K; + case 32000: + return MTK_AFE_ADDA_UL_RATE_32K; + case 48000: + return MTK_AFE_ADDA_UL_RATE_48K; + case 96000: + return MTK_AFE_ADDA_UL_RATE_96K; + case 192000: + return MTK_AFE_ADDA_UL_RATE_192K; + default: + dev_warn(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n", + __func__, rate); + return MTK_AFE_ADDA_UL_RATE_48K; + } +} + +/* dai component */ +static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN3, I_DL1_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN3, I_DL2_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN3, I_DL3_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN3, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN3, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN3, + I_PCM_1_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN3, + I_PCM_2_CAP_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN4, I_DL1_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN4, I_DL1_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN4, I_DL2_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN4, I_DL2_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN4, I_DL3_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN4, I_DL3_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN4, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN4, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN4, + I_PCM_1_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN4, + I_PCM_2_CAP_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN4, + I_PCM_1_CAP_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN4, + I_PCM_2_CAP_CH2, 1, 0), +}; + +static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + /* should delayed 1/fs(smallest is 8k) = 125us before afe off */ + usleep_range(125, 135); + break; + default: + break; + } + + return 0; +} + +enum { + SUPPLY_SEQ_AUD_TOP_PDN, + SUPPLY_SEQ_ADDA_AFE_ON, + SUPPLY_SEQ_ADDA_DL_ON, + SUPPLY_SEQ_ADDA_UL_ON, +}; + +static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = { + /* adda */ + SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0, + mtk_adda_dl_ch1_mix, + ARRAY_SIZE(mtk_adda_dl_ch1_mix)), + SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0, + mtk_adda_dl_ch2_mix, + ARRAY_SIZE(mtk_adda_dl_ch2_mix)), + + SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON, + AFE_ADDA_UL_DL_CON0, ADDA_AFE_ON_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON, + AFE_ADDA_DL_SRC2_CON0, + DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON, + AFE_ADDA_UL_SRC_CON0, + UL_SRC_ON_TMP_CTL_SFT, 0, + mtk_adda_ul_event, + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("aud_dac_clk", SUPPLY_SEQ_AUD_TOP_PDN, + AUDIO_TOP_CON0, PDN_DAC_SFT, 1, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("aud_dac_predis_clk", SUPPLY_SEQ_AUD_TOP_PDN, + AUDIO_TOP_CON0, PDN_DAC_PREDIS_SFT, 1, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("aud_adc_clk", SUPPLY_SEQ_AUD_TOP_PDN, + AUDIO_TOP_CON0, PDN_ADC_SFT, 1, + NULL, 0), + + SND_SOC_DAPM_CLOCK_SUPPLY("mtkaif_26m_clk"), +}; + +static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = { + /* playback */ + {"ADDA_DL_CH1", "DL1_CH1", "DL1"}, + {"ADDA_DL_CH2", "DL1_CH1", "DL1"}, + {"ADDA_DL_CH2", "DL1_CH2", "DL1"}, + + {"ADDA_DL_CH1", "DL2_CH1", "DL2"}, + {"ADDA_DL_CH2", "DL2_CH1", "DL2"}, + {"ADDA_DL_CH2", "DL2_CH2", "DL2"}, + + {"ADDA_DL_CH1", "DL3_CH1", "DL3"}, + {"ADDA_DL_CH2", "DL3_CH1", "DL3"}, + {"ADDA_DL_CH2", "DL3_CH2", "DL3"}, + + {"ADDA Playback", NULL, "ADDA_DL_CH1"}, + {"ADDA Playback", NULL, "ADDA_DL_CH2"}, + + /* adda enable */ + {"ADDA Playback", NULL, "ADDA Enable"}, + {"ADDA Playback", NULL, "ADDA Playback Enable"}, + {"ADDA Capture", NULL, "ADDA Enable"}, + {"ADDA Capture", NULL, "ADDA Capture Enable"}, + + /* clk */ + {"ADDA Playback", NULL, "mtkaif_26m_clk"}, + {"ADDA Playback", NULL, "aud_dac_clk"}, + {"ADDA Playback", NULL, "aud_dac_predis_clk"}, + + {"ADDA Capture", NULL, "mtkaif_26m_clk"}, + {"ADDA Capture", NULL, "aud_adc_clk"}, +}; + +/* dai ops */ +static int mtk_dai_adda_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); + unsigned int rate = params_rate(params); + + dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n", + __func__, dai->id, substream->stream, rate); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + unsigned int dl_src2_con0 = 0; + unsigned int dl_src2_con1 = 0; + + /* clean predistortion */ + regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON0, 0); + regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON1, 0); + + /* set input sampling rate */ + dl_src2_con0 = adda_dl_rate_transform(afe, rate) << 28; + + /* set output mode */ + switch (rate) { + case 192000: + dl_src2_con0 |= (0x1 << 24); /* UP_SAMPLING_RATE_X2 */ + dl_src2_con0 |= 1 << 14; + break; + case 96000: + dl_src2_con0 |= (0x2 << 24); /* UP_SAMPLING_RATE_X4 */ + dl_src2_con0 |= 1 << 14; + break; + default: + dl_src2_con0 |= (0x3 << 24); /* UP_SAMPLING_RATE_X8 */ + break; + } + + /* turn off mute function */ + dl_src2_con0 |= (0x03 << 11); + + /* set voice input data if input sample rate is 8k or 16k */ + if (rate == 8000 || rate == 16000) + dl_src2_con0 |= 0x01 << 5; + + if (rate < 96000) { + /* SA suggest apply -0.3db to audio/speech path */ + dl_src2_con1 = 0xf74f0000; + } else { + /* SA suggest apply -0.3db to audio/speech path + * with DL gain set to half, + * 0xFFFF = 0dB -> 0x8000 = 0dB when 96k, 192k + */ + dl_src2_con1 = 0x7ba70000; + } + + /* turn on down-link gain */ + dl_src2_con0 = dl_src2_con0 | (0x01 << 1); + + regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON0, dl_src2_con0); + regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON1, dl_src2_con1); + } else { + unsigned int voice_mode = 0; + unsigned int ul_src_con0 = 0; /* default value */ + + /* Using Internal ADC */ + regmap_update_bits(afe->regmap, + AFE_ADDA_TOP_CON0, + 0x1 << 0, + 0x0 << 0); + + voice_mode = adda_ul_rate_transform(afe, rate); + + ul_src_con0 |= (voice_mode << 17) & (0x7 << 17); + + /* up8x txif sat on */ + regmap_write(afe->regmap, AFE_ADDA_NEWIF_CFG0, 0x03F87201); + + if (rate >= 96000) { /* hires */ + /* use hires format [1 0 23] */ + regmap_update_bits(afe->regmap, + AFE_ADDA_NEWIF_CFG0, + 0x1 << 5, + 0x1 << 5); + + regmap_update_bits(afe->regmap, + AFE_ADDA_NEWIF_CFG2, + 0xf << 28, + voice_mode << 28); + } else { /* normal 8~48k */ + /* use fixed 260k anc path */ + regmap_update_bits(afe->regmap, + AFE_ADDA_NEWIF_CFG2, + 0xf << 28, + 8 << 28); + + /* ul_use_cic_out */ + ul_src_con0 |= 0x1 << 20; + } + + regmap_update_bits(afe->regmap, + AFE_ADDA_NEWIF_CFG2, + 0xf << 28, + 8 << 28); + + regmap_update_bits(afe->regmap, + AFE_ADDA_UL_SRC_CON0, + 0xfffffffe, + ul_src_con0); + } + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_adda_ops = { + .hw_params = mtk_dai_adda_hw_params, +}; + +/* dai driver */ +#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { + { + .name = "ADDA", + .id = MT6797_DAI_ADDA, + .playback = { + .stream_name = "ADDA Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ADDA_PLAYBACK_RATES, + .formats = MTK_ADDA_FORMATS, + }, + .capture = { + .stream_name = "ADDA Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ADDA_CAPTURE_RATES, + .formats = MTK_ADDA_FORMATS, + }, + .ops = &mtk_dai_adda_ops, + }, +}; + +int mt6797_dai_adda_register(struct mtk_base_afe *afe) +{ + int id = MT6797_DAI_ADDA; + + afe->sub_dais[id].dai_drivers = mtk_dai_adda_driver; + afe->sub_dais[id].num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver); + + afe->sub_dais[id].dapm_widgets = mtk_dai_adda_widgets; + afe->sub_dais[id].num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets); + afe->sub_dais[id].dapm_routes = mtk_dai_adda_routes; + afe->sub_dais[id].num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes); + return 0; +} diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c b/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c new file mode 100644 index 000000000000..4cf985b15a11 --- /dev/null +++ b/sound/soc/mediatek/mt6797/mt6797-dai-hostless.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MediaTek ALSA SoC Audio DAI Hostless Control +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include "mt6797-afe-common.h" + +/* dai component */ +static const struct snd_soc_dapm_route mtk_dai_hostless_routes[] = { + /* Hostless ADDA Loopback */ + {"ADDA_DL_CH1", "ADDA_UL_CH1", "Hostless LPBK DL"}, + {"ADDA_DL_CH1", "ADDA_UL_CH2", "Hostless LPBK DL"}, + {"ADDA_DL_CH2", "ADDA_UL_CH1", "Hostless LPBK DL"}, + {"ADDA_DL_CH2", "ADDA_UL_CH2", "Hostless LPBK DL"}, + {"Hostless LPBK UL", NULL, "ADDA Capture"}, + + /* Hostless Speech */ + {"ADDA_DL_CH1", "PCM_1_CAP_CH1", "Hostless Speech DL"}, + {"ADDA_DL_CH2", "PCM_1_CAP_CH1", "Hostless Speech DL"}, + {"ADDA_DL_CH2", "PCM_1_CAP_CH2", "Hostless Speech DL"}, + {"ADDA_DL_CH1", "PCM_2_CAP_CH1", "Hostless Speech DL"}, + {"ADDA_DL_CH2", "PCM_2_CAP_CH1", "Hostless Speech DL"}, + {"ADDA_DL_CH2", "PCM_2_CAP_CH2", "Hostless Speech DL"}, + {"PCM_1_PB_CH1", "ADDA_UL_CH1", "Hostless Speech DL"}, + {"PCM_1_PB_CH2", "ADDA_UL_CH2", "Hostless Speech DL"}, + {"PCM_2_PB_CH1", "ADDA_UL_CH1", "Hostless Speech DL"}, + {"PCM_2_PB_CH2", "ADDA_UL_CH2", "Hostless Speech DL"}, + + {"Hostless Speech UL", NULL, "PCM 1 Capture"}, + {"Hostless Speech UL", NULL, "PCM 2 Capture"}, + {"Hostless Speech UL", NULL, "ADDA Capture"}, +}; + +/* dai ops */ +static int mtk_dai_hostless_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + return snd_soc_set_runtime_hwparams(substream, afe->mtk_afe_hardware); +} + +static const struct snd_soc_dai_ops mtk_dai_hostless_ops = { + .startup = mtk_dai_hostless_startup, +}; + +/* dai driver */ +#define MTK_HOSTLESS_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_HOSTLESS_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_hostless_driver[] = { + { + .name = "Hostless LPBK DAI", + .id = MT6797_DAI_HOSTLESS_LPBK, + .playback = { + .stream_name = "Hostless LPBK DL", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_HOSTLESS_RATES, + .formats = MTK_HOSTLESS_FORMATS, + }, + .capture = { + .stream_name = "Hostless LPBK UL", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_HOSTLESS_RATES, + .formats = MTK_HOSTLESS_FORMATS, + }, + .ops = &mtk_dai_hostless_ops, + }, + { + .name = "Hostless Speech DAI", + .id = MT6797_DAI_HOSTLESS_SPEECH, + .playback = { + .stream_name = "Hostless Speech DL", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_HOSTLESS_RATES, + .formats = MTK_HOSTLESS_FORMATS, + }, + .capture = { + .stream_name = "Hostless Speech UL", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_HOSTLESS_RATES, + .formats = MTK_HOSTLESS_FORMATS, + }, + .ops = &mtk_dai_hostless_ops, + }, +}; + +int mt6797_dai_hostless_register(struct mtk_base_afe *afe) +{ + int id = MT6797_DAI_HOSTLESS_LPBK; + + afe->sub_dais[id].dai_drivers = mtk_dai_hostless_driver; + afe->sub_dais[id].num_dai_drivers = ARRAY_SIZE(mtk_dai_hostless_driver); + + afe->sub_dais[id].dapm_routes = mtk_dai_hostless_routes; + afe->sub_dais[id].num_dapm_routes = ARRAY_SIZE(mtk_dai_hostless_routes); + + return 0; +} diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c new file mode 100644 index 000000000000..16d5b5067204 --- /dev/null +++ b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MediaTek ALSA SoC Audio DAI I2S Control +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include "mt6797-afe-common.h" +#include "mt6797-interconnection.h" +#include "mt6797-reg.h" + +enum AUD_TX_LCH_RPT { + AUD_TX_LCH_RPT_NO_REPEAT = 0, + AUD_TX_LCH_RPT_REPEAT = 1 +}; + +enum AUD_VBT_16K_MODE { + AUD_VBT_16K_MODE_DISABLE = 0, + AUD_VBT_16K_MODE_ENABLE = 1 +}; + +enum AUD_EXT_MODEM { + AUD_EXT_MODEM_SELECT_INTERNAL = 0, + AUD_EXT_MODEM_SELECT_EXTERNAL = 1 +}; + +enum AUD_PCM_SYNC_TYPE { + /* bck sync length = 1 */ + AUD_PCM_ONE_BCK_CYCLE_SYNC = 0, + /* bck sync length = PCM_INTF_CON1[9:13] */ + AUD_PCM_EXTENDED_BCK_CYCLE_SYNC = 1 +}; + +enum AUD_BT_MODE { + AUD_BT_MODE_DUAL_MIC_ON_TX = 0, + AUD_BT_MODE_SINGLE_MIC_ON_TX = 1 +}; + +enum AUD_PCM_AFIFO_SRC { + /* slave mode & external modem uses different crystal */ + AUD_PCM_AFIFO_ASRC = 0, + /* slave mode & external modem uses the same crystal */ + AUD_PCM_AFIFO_AFIFO = 1 +}; + +enum AUD_PCM_CLOCK_SOURCE { + AUD_PCM_CLOCK_MASTER_MODE = 0, + AUD_PCM_CLOCK_SLAVE_MODE = 1 +}; + +enum AUD_PCM_WLEN { + AUD_PCM_WLEN_PCM_32_BCK_CYCLES = 0, + AUD_PCM_WLEN_PCM_64_BCK_CYCLES = 1 +}; + +enum AUD_PCM_MODE { + AUD_PCM_MODE_PCM_MODE_8K = 0, + AUD_PCM_MODE_PCM_MODE_16K = 1, + AUD_PCM_MODE_PCM_MODE_32K = 2, + AUD_PCM_MODE_PCM_MODE_48K = 3, +}; + +enum AUD_PCM_FMT { + AUD_PCM_FMT_I2S = 0, + AUD_PCM_FMT_EIAJ = 1, + AUD_PCM_FMT_PCM_MODE_A = 2, + AUD_PCM_FMT_PCM_MODE_B = 3 +}; + +enum AUD_BCLK_OUT_INV { + AUD_BCLK_OUT_INV_NO_INVERSE = 0, + AUD_BCLK_OUT_INV_INVERSE = 1 +}; + +enum AUD_PCM_EN { + AUD_PCM_EN_DISABLE = 0, + AUD_PCM_EN_ENABLE = 1 +}; + +/* dai component */ +static const struct snd_kcontrol_new mtk_pcm_1_playback_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN7, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN7, + I_DL2_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_pcm_1_playback_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN8, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN8, + I_DL2_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_pcm_1_playback_ch4_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN27, + I_DL1_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_pcm_2_playback_ch1_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN17, + I_ADDA_UL_CH1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN17, + I_DL2_CH1, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_pcm_2_playback_ch2_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN18, + I_ADDA_UL_CH2, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN18, + I_DL2_CH2, 1, 0), +}; + +static const struct snd_kcontrol_new mtk_pcm_2_playback_ch4_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN24, + I_DL1_CH1, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = { + /* inter-connections */ + SND_SOC_DAPM_MIXER("PCM_1_PB_CH1", SND_SOC_NOPM, 0, 0, + mtk_pcm_1_playback_ch1_mix, + ARRAY_SIZE(mtk_pcm_1_playback_ch1_mix)), + SND_SOC_DAPM_MIXER("PCM_1_PB_CH2", SND_SOC_NOPM, 0, 0, + mtk_pcm_1_playback_ch2_mix, + ARRAY_SIZE(mtk_pcm_1_playback_ch2_mix)), + SND_SOC_DAPM_MIXER("PCM_1_PB_CH4", SND_SOC_NOPM, 0, 0, + mtk_pcm_1_playback_ch4_mix, + ARRAY_SIZE(mtk_pcm_1_playback_ch4_mix)), + SND_SOC_DAPM_MIXER("PCM_2_PB_CH1", SND_SOC_NOPM, 0, 0, + mtk_pcm_2_playback_ch1_mix, + ARRAY_SIZE(mtk_pcm_2_playback_ch1_mix)), + SND_SOC_DAPM_MIXER("PCM_2_PB_CH2", SND_SOC_NOPM, 0, 0, + mtk_pcm_2_playback_ch2_mix, + ARRAY_SIZE(mtk_pcm_2_playback_ch2_mix)), + SND_SOC_DAPM_MIXER("PCM_2_PB_CH4", SND_SOC_NOPM, 0, 0, + mtk_pcm_2_playback_ch4_mix, + ARRAY_SIZE(mtk_pcm_2_playback_ch4_mix)), + + SND_SOC_DAPM_SUPPLY("PCM_1_EN", PCM_INTF_CON1, PCM_EN_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY("PCM_2_EN", PCM2_INTF_CON, PCM2_EN_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_INPUT("MD1_TO_AFE"), + SND_SOC_DAPM_INPUT("MD2_TO_AFE"), + SND_SOC_DAPM_OUTPUT("AFE_TO_MD1"), + SND_SOC_DAPM_OUTPUT("AFE_TO_MD2"), +}; + +static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = { + {"PCM 1 Playback", NULL, "PCM_1_PB_CH1"}, + {"PCM 1 Playback", NULL, "PCM_1_PB_CH2"}, + {"PCM 1 Playback", NULL, "PCM_1_PB_CH4"}, + {"PCM 2 Playback", NULL, "PCM_2_PB_CH1"}, + {"PCM 2 Playback", NULL, "PCM_2_PB_CH2"}, + {"PCM 2 Playback", NULL, "PCM_2_PB_CH4"}, + + {"PCM 1 Playback", NULL, "PCM_1_EN"}, + {"PCM 2 Playback", NULL, "PCM_2_EN"}, + {"PCM 1 Capture", NULL, "PCM_1_EN"}, + {"PCM 2 Capture", NULL, "PCM_2_EN"}, + + {"AFE_TO_MD1", NULL, "PCM 2 Playback"}, + {"AFE_TO_MD2", NULL, "PCM 1 Playback"}, + {"PCM 2 Capture", NULL, "MD1_TO_AFE"}, + {"PCM 1 Capture", NULL, "MD2_TO_AFE"}, + + {"PCM_1_PB_CH1", "DL2_CH1", "DL2"}, + {"PCM_1_PB_CH2", "DL2_CH2", "DL2"}, + {"PCM_1_PB_CH4", "DL1_CH1", "DL1"}, + {"PCM_2_PB_CH1", "DL2_CH1", "DL2"}, + {"PCM_2_PB_CH2", "DL2_CH2", "DL2"}, + {"PCM_2_PB_CH4", "DL1_CH1", "DL1"}, +}; + +/* dai ops */ +static int mtk_dai_pcm_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); + unsigned int rate = params_rate(params); + unsigned int rate_reg = mt6797_rate_transform(afe->dev, rate, dai->id); + unsigned int pcm_con = 0; + + dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d, rate_reg %d, widget active p %d, c %d\n", + __func__, + dai->id, + substream->stream, + rate, + rate_reg, + dai->playback_widget->active, + dai->capture_widget->active); + + if (dai->playback_widget->active || dai->capture_widget->active) + return 0; + + switch (dai->id) { + case MT6797_DAI_PCM_1: + pcm_con |= AUD_BCLK_OUT_INV_NO_INVERSE << PCM_BCLK_OUT_INV_SFT; + pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM_TX_LCH_RPT_SFT; + pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM_VBT_16K_MODE_SFT; + pcm_con |= AUD_EXT_MODEM_SELECT_INTERNAL << PCM_EXT_MODEM_SFT; + pcm_con |= 0 << PCM_SYNC_LENGTH_SFT; + pcm_con |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM_SYNC_TYPE_SFT; + pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM_BT_MODE_SFT; + pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM_BYP_ASRC_SFT; + pcm_con |= AUD_PCM_CLOCK_SLAVE_MODE << PCM_SLAVE_SFT; + pcm_con |= rate_reg << PCM_MODE_SFT; + pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM_FMT_SFT; + + regmap_update_bits(afe->regmap, PCM_INTF_CON1, + 0xfffffffe, pcm_con); + break; + case MT6797_DAI_PCM_2: + pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM2_TX_LCH_RPT_SFT; + pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM2_VBT_16K_MODE_SFT; + pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM2_BT_MODE_SFT; + pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM2_AFIFO_SFT; + pcm_con |= AUD_PCM_WLEN_PCM_32_BCK_CYCLES << PCM2_WLEN_SFT; + pcm_con |= rate_reg << PCM2_MODE_SFT; + pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM2_FMT_SFT; + + regmap_update_bits(afe->regmap, PCM2_INTF_CON, + 0xfffffffe, pcm_con); + break; + default: + dev_warn(afe->dev, "%s(), id %d not support\n", + __func__, dai->id); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_pcm_ops = { + .hw_params = mtk_dai_pcm_hw_params, +}; + +/* dai driver */ +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_48000) + +#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 mtk_dai_pcm_driver[] = { + { + .name = "PCM 1", + .id = MT6797_DAI_PCM_1, + .playback = { + .stream_name = "PCM 1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .capture = { + .stream_name = "PCM 1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_dai_pcm_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, + { + .name = "PCM 2", + .id = MT6797_DAI_PCM_2, + .playback = { + .stream_name = "PCM 2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .capture = { + .stream_name = "PCM 2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_dai_pcm_ops, + .symmetric_rates = 1, + .symmetric_samplebits = 1, + }, +}; + +int mt6797_dai_pcm_register(struct mtk_base_afe *afe) +{ + int id = MT6797_DAI_PCM_1; + + afe->sub_dais[id].dai_drivers = mtk_dai_pcm_driver; + afe->sub_dais[id].num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver); + + afe->sub_dais[id].dapm_widgets = mtk_dai_pcm_widgets; + afe->sub_dais[id].num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets); + afe->sub_dais[id].dapm_routes = mtk_dai_pcm_routes; + afe->sub_dais[id].num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes); + + return 0; +} diff --git a/sound/soc/mediatek/mt6797/mt6797-interconnection.h b/sound/soc/mediatek/mt6797/mt6797-interconnection.h new file mode 100644 index 000000000000..07b759b20079 --- /dev/null +++ b/sound/soc/mediatek/mt6797/mt6797-interconnection.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Mediatek MT6797 audio driver interconnection definition + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + */ + +#ifndef _MT6797_INTERCONNECTION_H_ +#define _MT6797_INTERCONNECTION_H_ + +#define I_I2S0_CH1 0 +#define I_I2S0_CH2 1 +#define I_ADDA_UL_CH1 3 +#define I_ADDA_UL_CH2 4 +#define I_DL1_CH1 5 +#define I_DL1_CH2 6 +#define I_DL2_CH1 7 +#define I_DL2_CH2 8 +#define I_PCM_1_CAP_CH1 9 +#define I_GAIN1_OUT_CH1 10 +#define I_GAIN1_OUT_CH2 11 +#define I_GAIN2_OUT_CH1 12 +#define I_GAIN2_OUT_CH2 13 +#define I_PCM_2_CAP_CH1 14 +#define I_PCM_2_CAP_CH2 21 +#define I_PCM_1_CAP_CH2 22 +#define I_DL3_CH1 23 +#define I_DL3_CH2 24 +#define I_I2S2_CH1 25 +#define I_I2S2_CH2 26 + +#endif diff --git a/sound/soc/mediatek/mt6797/mt6797-mt6351.c b/sound/soc/mediatek/mt6797/mt6797-mt6351.c new file mode 100644 index 000000000000..b1558c57b9ca --- /dev/null +++ b/sound/soc/mediatek/mt6797/mt6797-mt6351.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt6797-mt6351.c -- MT6797 MT6351 ALSA SoC machine driver +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + +#include <linux/module.h> +#include <sound/soc.h> + +#include "mt6797-afe-common.h" + +static struct snd_soc_dai_link mt6797_mt6351_dai_links[] = { + /* FE */ + { + .name = "Playback_1", + .stream_name = "Playback_1", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE}, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "Playback_2", + .stream_name = "Playback_2", + .cpu_dai_name = "DL2", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE}, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "Playback_3", + .stream_name = "Playback_3", + .cpu_dai_name = "DL3", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE}, + .dynamic = 1, + .dpcm_playback = 1, + }, + { + .name = "Capture_1", + .stream_name = "Capture_1", + .cpu_dai_name = "UL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE}, + .dynamic = 1, + .dpcm_capture = 1, + }, + { + .name = "Capture_2", + .stream_name = "Capture_2", + .cpu_dai_name = "UL2", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE}, + .dynamic = 1, + .dpcm_capture = 1, + }, + { + .name = "Capture_3", + .stream_name = "Capture_3", + .cpu_dai_name = "UL3", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE}, + .dynamic = 1, + .dpcm_capture = 1, + }, + { + .name = "Capture_Mono_1", + .stream_name = "Capture_Mono_1", + .cpu_dai_name = "UL_MONO_1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE}, + .dynamic = 1, + .dpcm_capture = 1, + }, + { + .name = "Hostless_LPBK", + .stream_name = "Hostless_LPBK", + .cpu_dai_name = "Hostless LPBK DAI", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE}, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + { + .name = "Hostless_Speech", + .stream_name = "Hostless_Speech", + .cpu_dai_name = "Hostless Speech DAI", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_PRE, + SND_SOC_DPCM_TRIGGER_PRE}, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + /* BE */ + { + .name = "Primary Codec", + .cpu_dai_name = "ADDA", + .codec_dai_name = "mt6351-snd-codec-aif1", + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + { + .name = "PCM 1", + .cpu_dai_name = "PCM 1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ignore_suspend = 1, + }, + { + .name = "PCM 2", + .cpu_dai_name = "PCM 2", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_card mt6797_mt6351_card = { + .name = "mt6797-mt6351", + .owner = THIS_MODULE, + .dai_link = mt6797_mt6351_dai_links, + .num_links = ARRAY_SIZE(mt6797_mt6351_dai_links), +}; + +static int mt6797_mt6351_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt6797_mt6351_card; + struct device_node *platform_node, *codec_node; + int ret, i; + + card->dev = &pdev->dev; + + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + for (i = 0; i < card->num_links; i++) { + if (mt6797_mt6351_dai_links[i].platform_name) + continue; + mt6797_mt6351_dai_links[i].platform_of_node = platform_node; + } + + codec_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,audio-codec", 0); + if (!codec_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + for (i = 0; i < card->num_links; i++) { + if (mt6797_mt6351_dai_links[i].codec_name) + continue; + mt6797_mt6351_dai_links[i].codec_of_node = codec_node; + } + + 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); + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id mt6797_mt6351_dt_match[] = { + {.compatible = "mediatek,mt6797-mt6351-sound",}, + {} +}; +#endif + +static struct platform_driver mt6797_mt6351_driver = { + .driver = { + .name = "mt6797-mt6351", + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = mt6797_mt6351_dt_match, +#endif + }, + .probe = mt6797_mt6351_dev_probe, +}; + +module_platform_driver(mt6797_mt6351_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT6797 MT6351 ALSA SoC machine driver"); +MODULE_AUTHOR("KaiChieh Chuang <kaichieh.chuang@mediatek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("mt6797 mt6351 soc card"); + diff --git a/sound/soc/mediatek/mt6797/mt6797-reg.h b/sound/soc/mediatek/mt6797/mt6797-reg.h new file mode 100644 index 000000000000..978f146c143c --- /dev/null +++ b/sound/soc/mediatek/mt6797/mt6797-reg.h @@ -0,0 +1,1015 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt6797-reg.h -- Mediatek 6797 audio driver reg definition + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> + */ + +#ifndef _MT6797_REG_H_ +#define _MT6797_REG_H_ + +#define AUDIO_TOP_CON0 0x0000 +#define AUDIO_TOP_CON1 0x0004 +#define AUDIO_TOP_CON3 0x000c +#define AFE_DAC_CON0 0x0010 +#define AFE_DAC_CON1 0x0014 +#define AFE_I2S_CON 0x0018 +#define AFE_DAIBT_CON0 0x001c +#define AFE_CONN0 0x0020 +#define AFE_CONN1 0x0024 +#define AFE_CONN2 0x0028 +#define AFE_CONN3 0x002c +#define AFE_CONN4 0x0030 +#define AFE_I2S_CON1 0x0034 +#define AFE_I2S_CON2 0x0038 +#define AFE_MRGIF_CON 0x003c +#define AFE_DL1_BASE 0x0040 +#define AFE_DL1_CUR 0x0044 +#define AFE_DL1_END 0x0048 +#define AFE_I2S_CON3 0x004c +#define AFE_DL2_BASE 0x0050 +#define AFE_DL2_CUR 0x0054 +#define AFE_DL2_END 0x0058 +#define AFE_CONN5 0x005c +#define AFE_CONN_24BIT 0x006c +#define AFE_AWB_BASE 0x0070 +#define AFE_AWB_END 0x0078 +#define AFE_AWB_CUR 0x007c +#define AFE_VUL_BASE 0x0080 +#define AFE_VUL_END 0x0088 +#define AFE_VUL_CUR 0x008c +#define AFE_DAI_BASE 0x0090 +#define AFE_DAI_END 0x0098 +#define AFE_DAI_CUR 0x009c +#define AFE_CONN6 0x00bc +#define AFE_MEMIF_MSB 0x00cc +#define AFE_MEMIF_MON0 0x00d0 +#define AFE_MEMIF_MON1 0x00d4 +#define AFE_MEMIF_MON2 0x00d8 +#define AFE_MEMIF_MON4 0x00e0 +#define AFE_ADDA_DL_SRC2_CON0 0x0108 +#define AFE_ADDA_DL_SRC2_CON1 0x010c +#define AFE_ADDA_UL_SRC_CON0 0x0114 +#define AFE_ADDA_UL_SRC_CON1 0x0118 +#define AFE_ADDA_TOP_CON0 0x0120 +#define AFE_ADDA_UL_DL_CON0 0x0124 +#define AFE_ADDA_SRC_DEBUG 0x012c +#define AFE_ADDA_SRC_DEBUG_MON0 0x0130 +#define AFE_ADDA_SRC_DEBUG_MON1 0x0134 +#define AFE_ADDA_NEWIF_CFG0 0x0138 +#define AFE_ADDA_NEWIF_CFG1 0x013c +#define AFE_ADDA_NEWIF_CFG2 0x0140 +#define AFE_DMA_CTL 0x0150 +#define AFE_DMA_MON0 0x0154 +#define AFE_DMA_MON1 0x0158 +#define AFE_SIDETONE_DEBUG 0x01d0 +#define AFE_SIDETONE_MON 0x01d4 +#define AFE_SIDETONE_CON0 0x01e0 +#define AFE_SIDETONE_COEFF 0x01e4 +#define AFE_SIDETONE_CON1 0x01e8 +#define AFE_SIDETONE_GAIN 0x01ec +#define AFE_SGEN_CON0 0x01f0 +#define AFE_SINEGEN_CON_TDM 0x01fc +#define AFE_TOP_CON0 0x0200 +#define AFE_ADDA_PREDIS_CON0 0x0260 +#define AFE_ADDA_PREDIS_CON1 0x0264 +#define AFE_MRGIF_MON0 0x0270 +#define AFE_MRGIF_MON1 0x0274 +#define AFE_MRGIF_MON2 0x0278 +#define AFE_I2S_MON 0x027c +#define AFE_MOD_DAI_BASE 0x0330 +#define AFE_MOD_DAI_END 0x0338 +#define AFE_MOD_DAI_CUR 0x033c +#define AFE_VUL_D2_BASE 0x0350 +#define AFE_VUL_D2_END 0x0358 +#define AFE_VUL_D2_CUR 0x035c +#define AFE_DL3_BASE 0x0360 +#define AFE_DL3_CUR 0x0364 +#define AFE_DL3_END 0x0368 +#define AFE_HDMI_OUT_CON0 0x0370 +#define AFE_HDMI_BASE 0x0374 +#define AFE_HDMI_CUR 0x0378 +#define AFE_HDMI_END 0x037c +#define AFE_HDMI_CONN0 0x0390 +#define AFE_IRQ3_MCU_CNT_MON 0x0398 +#define AFE_IRQ4_MCU_CNT_MON 0x039c +#define AFE_IRQ_MCU_CON 0x03a0 +#define AFE_IRQ_MCU_STATUS 0x03a4 +#define AFE_IRQ_MCU_CLR 0x03a8 +#define AFE_IRQ_MCU_CNT1 0x03ac +#define AFE_IRQ_MCU_CNT2 0x03b0 +#define AFE_IRQ_MCU_EN 0x03b4 +#define AFE_IRQ_MCU_MON2 0x03b8 +#define AFE_IRQ_MCU_CNT5 0x03bc +#define AFE_IRQ1_MCU_CNT_MON 0x03c0 +#define AFE_IRQ2_MCU_CNT_MON 0x03c4 +#define AFE_IRQ1_MCU_EN_CNT_MON 0x03c8 +#define AFE_IRQ5_MCU_CNT_MON 0x03cc +#define AFE_MEMIF_MINLEN 0x03d0 +#define AFE_MEMIF_MAXLEN 0x03d4 +#define AFE_MEMIF_PBUF_SIZE 0x03d8 +#define AFE_IRQ_MCU_CNT7 0x03dc +#define AFE_IRQ7_MCU_CNT_MON 0x03e0 +#define AFE_IRQ_MCU_CNT3 0x03e4 +#define AFE_IRQ_MCU_CNT4 0x03e8 +#define AFE_APLL1_TUNER_CFG 0x03f0 +#define AFE_APLL2_TUNER_CFG 0x03f4 +#define AFE_MEMIF_HD_MODE 0x03f8 +#define AFE_MEMIF_HDALIGN 0x03fc +#define AFE_GAIN1_CON0 0x0410 +#define AFE_GAIN1_CON1 0x0414 +#define AFE_GAIN1_CON2 0x0418 +#define AFE_GAIN1_CON3 0x041c +#define AFE_CONN7 0x0420 +#define AFE_GAIN1_CUR 0x0424 +#define AFE_GAIN2_CON0 0x0428 +#define AFE_GAIN2_CON1 0x042c +#define AFE_GAIN2_CON2 0x0430 +#define AFE_GAIN2_CON3 0x0434 +#define AFE_CONN8 0x0438 +#define AFE_GAIN2_CUR 0x043c +#define AFE_CONN9 0x0440 +#define AFE_CONN10 0x0444 +#define AFE_CONN11 0x0448 +#define AFE_CONN12 0x044c +#define AFE_CONN13 0x0450 +#define AFE_CONN14 0x0454 +#define AFE_CONN15 0x0458 +#define AFE_CONN16 0x045c +#define AFE_CONN17 0x0460 +#define AFE_CONN18 0x0464 +#define AFE_CONN19 0x0468 +#define AFE_CONN20 0x046c +#define AFE_CONN21 0x0470 +#define AFE_CONN22 0x0474 +#define AFE_CONN23 0x0478 +#define AFE_CONN24 0x047c +#define AFE_CONN_RS 0x0494 +#define AFE_CONN_DI 0x0498 +#define AFE_CONN25 0x04b0 +#define AFE_CONN26 0x04b4 +#define AFE_CONN27 0x04b8 +#define AFE_CONN28 0x04bc +#define AFE_CONN29 0x04c0 +#define AFE_SRAM_DELSEL_CON0 0x04f0 +#define AFE_SRAM_DELSEL_CON1 0x04f4 +#define AFE_ASRC_CON0 0x0500 +#define AFE_ASRC_CON1 0x0504 +#define AFE_ASRC_CON2 0x0508 +#define AFE_ASRC_CON3 0x050c +#define AFE_ASRC_CON4 0x0510 +#define AFE_ASRC_CON5 0x0514 +#define AFE_ASRC_CON6 0x0518 +#define AFE_ASRC_CON7 0x051c +#define AFE_ASRC_CON8 0x0520 +#define AFE_ASRC_CON9 0x0524 +#define AFE_ASRC_CON10 0x0528 +#define AFE_ASRC_CON11 0x052c +#define PCM_INTF_CON1 0x0530 +#define PCM_INTF_CON2 0x0538 +#define PCM2_INTF_CON 0x053c +#define AFE_TDM_CON1 0x0548 +#define AFE_TDM_CON2 0x054c +#define AFE_ASRC_CON13 0x0550 +#define AFE_ASRC_CON14 0x0554 +#define AFE_ASRC_CON15 0x0558 +#define AFE_ASRC_CON16 0x055c +#define AFE_ASRC_CON17 0x0560 +#define AFE_ASRC_CON18 0x0564 +#define AFE_ASRC_CON19 0x0568 +#define AFE_ASRC_CON20 0x056c +#define AFE_ASRC_CON21 0x0570 +#define CLK_AUDDIV_0 0x05a0 +#define CLK_AUDDIV_1 0x05a4 +#define CLK_AUDDIV_2 0x05a8 +#define CLK_AUDDIV_3 0x05ac +#define AUDIO_TOP_DBG_CON 0x05c8 +#define AUDIO_TOP_DBG_MON0 0x05cc +#define AUDIO_TOP_DBG_MON1 0x05d0 +#define AUDIO_TOP_DBG_MON2 0x05d4 +#define AFE_ADDA2_TOP_CON0 0x0600 +#define AFE_ASRC4_CON0 0x06c0 +#define AFE_ASRC4_CON1 0x06c4 +#define AFE_ASRC4_CON2 0x06c8 +#define AFE_ASRC4_CON3 0x06cc +#define AFE_ASRC4_CON4 0x06d0 +#define AFE_ASRC4_CON5 0x06d4 +#define AFE_ASRC4_CON6 0x06d8 +#define AFE_ASRC4_CON7 0x06dc +#define AFE_ASRC4_CON8 0x06e0 +#define AFE_ASRC4_CON9 0x06e4 +#define AFE_ASRC4_CON10 0x06e8 +#define AFE_ASRC4_CON11 0x06ec +#define AFE_ASRC4_CON12 0x06f0 +#define AFE_ASRC4_CON13 0x06f4 +#define AFE_ASRC4_CON14 0x06f8 +#define AFE_ASRC2_CON0 0x0700 +#define AFE_ASRC2_CON1 0x0704 +#define AFE_ASRC2_CON2 0x0708 +#define AFE_ASRC2_CON3 0x070c +#define AFE_ASRC2_CON4 0x0710 +#define AFE_ASRC2_CON5 0x0714 +#define AFE_ASRC2_CON6 0x0718 +#define AFE_ASRC2_CON7 0x071c +#define AFE_ASRC2_CON8 0x0720 +#define AFE_ASRC2_CON9 0x0724 +#define AFE_ASRC2_CON10 0x0728 +#define AFE_ASRC2_CON11 0x072c +#define AFE_ASRC2_CON12 0x0730 +#define AFE_ASRC2_CON13 0x0734 +#define AFE_ASRC2_CON14 0x0738 +#define AFE_ASRC3_CON0 0x0740 +#define AFE_ASRC3_CON1 0x0744 +#define AFE_ASRC3_CON2 0x0748 +#define AFE_ASRC3_CON3 0x074c +#define AFE_ASRC3_CON4 0x0750 +#define AFE_ASRC3_CON5 0x0754 +#define AFE_ASRC3_CON6 0x0758 +#define AFE_ASRC3_CON7 0x075c +#define AFE_ASRC3_CON8 0x0760 +#define AFE_ASRC3_CON9 0x0764 +#define AFE_ASRC3_CON10 0x0768 +#define AFE_ASRC3_CON11 0x076c +#define AFE_ASRC3_CON12 0x0770 +#define AFE_ASRC3_CON13 0x0774 +#define AFE_ASRC3_CON14 0x0778 +#define AFE_GENERAL_REG0 0x0800 +#define AFE_GENERAL_REG1 0x0804 +#define AFE_GENERAL_REG2 0x0808 +#define AFE_GENERAL_REG3 0x080c +#define AFE_GENERAL_REG4 0x0810 +#define AFE_GENERAL_REG5 0x0814 +#define AFE_GENERAL_REG6 0x0818 +#define AFE_GENERAL_REG7 0x081c +#define AFE_GENERAL_REG8 0x0820 +#define AFE_GENERAL_REG9 0x0824 +#define AFE_GENERAL_REG10 0x0828 +#define AFE_GENERAL_REG11 0x082c +#define AFE_GENERAL_REG12 0x0830 +#define AFE_GENERAL_REG13 0x0834 +#define AFE_GENERAL_REG14 0x0838 +#define AFE_GENERAL_REG15 0x083c +#define AFE_CBIP_CFG0 0x0840 +#define AFE_CBIP_MON0 0x0844 +#define AFE_CBIP_SLV_MUX_MON0 0x0848 +#define AFE_CBIP_SLV_DECODER_MON0 0x084c + +#define AFE_MAX_REGISTER AFE_CBIP_SLV_DECODER_MON0 +#define AFE_IRQ_STATUS_BITS 0x5f + +/* AUDIO_TOP_CON0 */ +#define AHB_IDLE_EN_INT_SFT 30 +#define AHB_IDLE_EN_INT_MASK 0x1 +#define AHB_IDLE_EN_INT_MASK_SFT (0x1 << 30) +#define AHB_IDLE_EN_EXT_SFT 29 +#define AHB_IDLE_EN_EXT_MASK 0x1 +#define AHB_IDLE_EN_EXT_MASK_SFT (0x1 << 29) +#define PDN_TML_SFT 27 +#define PDN_TML_MASK 0x1 +#define PDN_TML_MASK_SFT (0x1 << 27) +#define PDN_DAC_PREDIS_SFT 26 +#define PDN_DAC_PREDIS_MASK 0x1 +#define PDN_DAC_PREDIS_MASK_SFT (0x1 << 26) +#define PDN_DAC_SFT 25 +#define PDN_DAC_MASK 0x1 +#define PDN_DAC_MASK_SFT (0x1 << 25) +#define PDN_ADC_SFT 24 +#define PDN_ADC_MASK 0x1 +#define PDN_ADC_MASK_SFT (0x1 << 24) +#define PDN_TDM_CK_SFT 20 +#define PDN_TDM_CK_MASK 0x1 +#define PDN_TDM_CK_MASK_SFT (0x1 << 20) +#define PDN_APLL_TUNER_SFT 19 +#define PDN_APLL_TUNER_MASK 0x1 +#define PDN_APLL_TUNER_MASK_SFT (0x1 << 19) +#define PDN_APLL2_TUNER_SFT 18 +#define PDN_APLL2_TUNER_MASK 0x1 +#define PDN_APLL2_TUNER_MASK_SFT (0x1 << 18) +#define APB3_SEL_SFT 14 +#define APB3_SEL_MASK 0x1 +#define APB3_SEL_MASK_SFT (0x1 << 14) +#define APB_R2T_SFT 13 +#define APB_R2T_MASK 0x1 +#define APB_R2T_MASK_SFT (0x1 << 13) +#define APB_W2T_SFT 12 +#define APB_W2T_MASK 0x1 +#define APB_W2T_MASK_SFT (0x1 << 12) +#define PDN_24M_SFT 9 +#define PDN_24M_MASK 0x1 +#define PDN_24M_MASK_SFT (0x1 << 9) +#define PDN_22M_SFT 8 +#define PDN_22M_MASK 0x1 +#define PDN_22M_MASK_SFT (0x1 << 8) +#define PDN_ADDA4_ADC_SFT 7 +#define PDN_ADDA4_ADC_MASK 0x1 +#define PDN_ADDA4_ADC_MASK_SFT (0x1 << 7) +#define PDN_I2S_SFT 6 +#define PDN_I2S_MASK 0x1 +#define PDN_I2S_MASK_SFT (0x1 << 6) +#define PDN_AFE_SFT 2 +#define PDN_AFE_MASK 0x1 +#define PDN_AFE_MASK_SFT (0x1 << 2) + +/* AUDIO_TOP_CON1 */ +#define PDN_ADC_HIRES_TML_SFT 17 +#define PDN_ADC_HIRES_TML_MASK 0x1 +#define PDN_ADC_HIRES_TML_MASK_SFT (0x1 << 17) +#define PDN_ADC_HIRES_SFT 16 +#define PDN_ADC_HIRES_MASK 0x1 +#define PDN_ADC_HIRES_MASK_SFT (0x1 << 16) +#define I2S4_BCLK_SW_CG_SFT 7 +#define I2S4_BCLK_SW_CG_MASK 0x1 +#define I2S4_BCLK_SW_CG_MASK_SFT (0x1 << 7) +#define I2S3_BCLK_SW_CG_SFT 6 +#define I2S3_BCLK_SW_CG_MASK 0x1 +#define I2S3_BCLK_SW_CG_MASK_SFT (0x1 << 6) +#define I2S2_BCLK_SW_CG_SFT 5 +#define I2S2_BCLK_SW_CG_MASK 0x1 +#define I2S2_BCLK_SW_CG_MASK_SFT (0x1 << 5) +#define I2S1_BCLK_SW_CG_SFT 4 +#define I2S1_BCLK_SW_CG_MASK 0x1 +#define I2S1_BCLK_SW_CG_MASK_SFT (0x1 << 4) +#define I2S_SOFT_RST2_SFT 2 +#define I2S_SOFT_RST2_MASK 0x1 +#define I2S_SOFT_RST2_MASK_SFT (0x1 << 2) +#define I2S_SOFT_RST_SFT 1 +#define I2S_SOFT_RST_MASK 0x1 +#define I2S_SOFT_RST_MASK_SFT (0x1 << 1) + +/* AFE_DAC_CON0 */ +#define AFE_AWB_RETM_SFT 31 +#define AFE_AWB_RETM_MASK 0x1 +#define AFE_AWB_RETM_MASK_SFT (0x1 << 31) +#define AFE_DL1_DATA2_RETM_SFT 30 +#define AFE_DL1_DATA2_RETM_MASK 0x1 +#define AFE_DL1_DATA2_RETM_MASK_SFT (0x1 << 30) +#define AFE_DL2_RETM_SFT 29 +#define AFE_DL2_RETM_MASK 0x1 +#define AFE_DL2_RETM_MASK_SFT (0x1 << 29) +#define AFE_DL1_RETM_SFT 28 +#define AFE_DL1_RETM_MASK 0x1 +#define AFE_DL1_RETM_MASK_SFT (0x1 << 28) +#define AFE_ON_RETM_SFT 27 +#define AFE_ON_RETM_MASK 0x1 +#define AFE_ON_RETM_MASK_SFT (0x1 << 27) +#define MOD_DAI_DUP_WR_SFT 26 +#define MOD_DAI_DUP_WR_MASK 0x1 +#define MOD_DAI_DUP_WR_MASK_SFT (0x1 << 26) +#define DAI_MODE_SFT 24 +#define DAI_MODE_MASK 0x3 +#define DAI_MODE_MASK_SFT (0x3 << 24) +#define VUL_DATA2_MODE_SFT 20 +#define VUL_DATA2_MODE_MASK 0xf +#define VUL_DATA2_MODE_MASK_SFT (0xf << 20) +#define DL1_DATA2_MODE_SFT 16 +#define DL1_DATA2_MODE_MASK 0xf +#define DL1_DATA2_MODE_MASK_SFT (0xf << 16) +#define DL3_MODE_SFT 12 +#define DL3_MODE_MASK 0xf +#define DL3_MODE_MASK_SFT (0xf << 12) +#define VUL_DATA2_R_MONO_SFT 11 +#define VUL_DATA2_R_MONO_MASK 0x1 +#define VUL_DATA2_R_MONO_MASK_SFT (0x1 << 11) +#define VUL_DATA2_DATA_SFT 10 +#define VUL_DATA2_DATA_MASK 0x1 +#define VUL_DATA2_DATA_MASK_SFT (0x1 << 10) +#define VUL_DATA2_ON_SFT 9 +#define VUL_DATA2_ON_MASK 0x1 +#define VUL_DATA2_ON_MASK_SFT (0x1 << 9) +#define DL1_DATA2_ON_SFT 8 +#define DL1_DATA2_ON_MASK 0x1 +#define DL1_DATA2_ON_MASK_SFT (0x1 << 8) +#define MOD_DAI_ON_SFT 7 +#define MOD_DAI_ON_MASK 0x1 +#define MOD_DAI_ON_MASK_SFT (0x1 << 7) +#define AWB_ON_SFT 6 +#define AWB_ON_MASK 0x1 +#define AWB_ON_MASK_SFT (0x1 << 6) +#define DL3_ON_SFT 5 +#define DL3_ON_MASK 0x1 +#define DL3_ON_MASK_SFT (0x1 << 5) +#define DAI_ON_SFT 4 +#define DAI_ON_MASK 0x1 +#define DAI_ON_MASK_SFT (0x1 << 4) +#define VUL_ON_SFT 3 +#define VUL_ON_MASK 0x1 +#define VUL_ON_MASK_SFT (0x1 << 3) +#define DL2_ON_SFT 2 +#define DL2_ON_MASK 0x1 +#define DL2_ON_MASK_SFT (0x1 << 2) +#define DL1_ON_SFT 1 +#define DL1_ON_MASK 0x1 +#define DL1_ON_MASK_SFT (0x1 << 1) +#define AFE_ON_SFT 0 +#define AFE_ON_MASK 0x1 +#define AFE_ON_MASK_SFT (0x1 << 0) + +/* AFE_DAC_CON1 */ +#define MOD_DAI_MODE_SFT 30 +#define MOD_DAI_MODE_MASK 0x3 +#define MOD_DAI_MODE_MASK_SFT (0x3 << 30) +#define DAI_DUP_WR_SFT 29 +#define DAI_DUP_WR_MASK 0x1 +#define DAI_DUP_WR_MASK_SFT (0x1 << 29) +#define VUL_R_MONO_SFT 28 +#define VUL_R_MONO_MASK 0x1 +#define VUL_R_MONO_MASK_SFT (0x1 << 28) +#define VUL_DATA_SFT 27 +#define VUL_DATA_MASK 0x1 +#define VUL_DATA_MASK_SFT (0x1 << 27) +#define AXI_2X1_CG_DISABLE_SFT 26 +#define AXI_2X1_CG_DISABLE_MASK 0x1 +#define AXI_2X1_CG_DISABLE_MASK_SFT (0x1 << 26) +#define AWB_R_MONO_SFT 25 +#define AWB_R_MONO_MASK 0x1 +#define AWB_R_MONO_MASK_SFT (0x1 << 25) +#define AWB_DATA_SFT 24 +#define AWB_DATA_MASK 0x1 +#define AWB_DATA_MASK_SFT (0x1 << 24) +#define DL3_DATA_SFT 23 +#define DL3_DATA_MASK 0x1 +#define DL3_DATA_MASK_SFT (0x1 << 23) +#define DL2_DATA_SFT 22 +#define DL2_DATA_MASK 0x1 +#define DL2_DATA_MASK_SFT (0x1 << 22) +#define DL1_DATA_SFT 21 +#define DL1_DATA_MASK 0x1 +#define DL1_DATA_MASK_SFT (0x1 << 21) +#define DL1_DATA2_DATA_SFT 20 +#define DL1_DATA2_DATA_MASK 0x1 +#define DL1_DATA2_DATA_MASK_SFT (0x1 << 20) +#define VUL_MODE_SFT 16 +#define VUL_MODE_MASK 0xf +#define VUL_MODE_MASK_SFT (0xf << 16) +#define AWB_MODE_SFT 12 +#define AWB_MODE_MASK 0xf +#define AWB_MODE_MASK_SFT (0xf << 12) +#define I2S_MODE_SFT 8 +#define I2S_MODE_MASK 0xf +#define I2S_MODE_MASK_SFT (0xf << 8) +#define DL2_MODE_SFT 4 +#define DL2_MODE_MASK 0xf +#define DL2_MODE_MASK_SFT (0xf << 4) +#define DL1_MODE_SFT 0 +#define DL1_MODE_MASK 0xf +#define DL1_MODE_MASK_SFT (0xf << 0) + +/* AFE_ADDA_DL_SRC2_CON0 */ +#define DL_2_INPUT_MODE_CTL_SFT 28 +#define DL_2_INPUT_MODE_CTL_MASK 0xf +#define DL_2_INPUT_MODE_CTL_MASK_SFT (0xf << 28) +#define DL_2_CH1_SATURATION_EN_CTL_SFT 27 +#define DL_2_CH1_SATURATION_EN_CTL_MASK 0x1 +#define DL_2_CH1_SATURATION_EN_CTL_MASK_SFT (0x1 << 27) +#define DL_2_CH2_SATURATION_EN_CTL_SFT 26 +#define DL_2_CH2_SATURATION_EN_CTL_MASK 0x1 +#define DL_2_CH2_SATURATION_EN_CTL_MASK_SFT (0x1 << 26) +#define DL_2_OUTPUT_SEL_CTL_SFT 24 +#define DL_2_OUTPUT_SEL_CTL_MASK 0x3 +#define DL_2_OUTPUT_SEL_CTL_MASK_SFT (0x3 << 24) +#define DL_2_FADEIN_0START_EN_SFT 16 +#define DL_2_FADEIN_0START_EN_MASK 0x3 +#define DL_2_FADEIN_0START_EN_MASK_SFT (0x3 << 16) +#define DL_DISABLE_HW_CG_CTL_SFT 15 +#define DL_DISABLE_HW_CG_CTL_MASK 0x1 +#define DL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 15) +#define C_DATA_EN_SEL_CTL_PRE_SFT 14 +#define C_DATA_EN_SEL_CTL_PRE_MASK 0x1 +#define C_DATA_EN_SEL_CTL_PRE_MASK_SFT (0x1 << 14) +#define DL_2_SIDE_TONE_ON_CTL_PRE_SFT 13 +#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK 0x1 +#define DL_2_SIDE_TONE_ON_CTL_PRE_MASK_SFT (0x1 << 13) +#define DL_2_MUTE_CH1_OFF_CTL_PRE_SFT 12 +#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK 0x1 +#define DL_2_MUTE_CH1_OFF_CTL_PRE_MASK_SFT (0x1 << 12) +#define DL_2_MUTE_CH2_OFF_CTL_PRE_SFT 11 +#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK 0x1 +#define DL_2_MUTE_CH2_OFF_CTL_PRE_MASK_SFT (0x1 << 11) +#define DL2_ARAMPSP_CTL_PRE_SFT 9 +#define DL2_ARAMPSP_CTL_PRE_MASK 0x3 +#define DL2_ARAMPSP_CTL_PRE_MASK_SFT (0x3 << 9) +#define DL_2_IIRMODE_CTL_PRE_SFT 6 +#define DL_2_IIRMODE_CTL_PRE_MASK 0x7 +#define DL_2_IIRMODE_CTL_PRE_MASK_SFT (0x7 << 6) +#define DL_2_VOICE_MODE_CTL_PRE_SFT 5 +#define DL_2_VOICE_MODE_CTL_PRE_MASK 0x1 +#define DL_2_VOICE_MODE_CTL_PRE_MASK_SFT (0x1 << 5) +#define D2_2_MUTE_CH1_ON_CTL_PRE_SFT 4 +#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK 0x1 +#define D2_2_MUTE_CH1_ON_CTL_PRE_MASK_SFT (0x1 << 4) +#define D2_2_MUTE_CH2_ON_CTL_PRE_SFT 3 +#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK 0x1 +#define D2_2_MUTE_CH2_ON_CTL_PRE_MASK_SFT (0x1 << 3) +#define DL_2_IIR_ON_CTL_PRE_SFT 2 +#define DL_2_IIR_ON_CTL_PRE_MASK 0x1 +#define DL_2_IIR_ON_CTL_PRE_MASK_SFT (0x1 << 2) +#define DL_2_GAIN_ON_CTL_PRE_SFT 1 +#define DL_2_GAIN_ON_CTL_PRE_MASK 0x1 +#define DL_2_GAIN_ON_CTL_PRE_MASK_SFT (0x1 << 1) +#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_DL_SRC2_CON1 */ +#define DL_2_GAIN_CTL_PRE_SFT 16 +#define DL_2_GAIN_CTL_PRE_MASK 0xffff +#define DL_2_GAIN_CTL_PRE_MASK_SFT (0xffff << 16) +#define DL_2_GAIN_MODE_CTL_SFT 0 +#define DL_2_GAIN_MODE_CTL_MASK 0x1 +#define DL_2_GAIN_MODE_CTL_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_UL_SRC_CON0 */ +#define C_COMB_OUT_SIN_GEN_CTL_SFT 31 +#define C_COMB_OUT_SIN_GEN_CTL_MASK 0x1 +#define C_COMB_OUT_SIN_GEN_CTL_MASK_SFT (0x1 << 31) +#define C_BASEBAND_SIN_GEN_CTL_SFT 30 +#define C_BASEBAND_SIN_GEN_CTL_MASK 0x1 +#define C_BASEBAND_SIN_GEN_CTL_MASK_SFT (0x1 << 30) +#define C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 27 +#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7 +#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 27) +#define C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 24 +#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7 +#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 24) +#define C_TWO_DIGITAL_MIC_CTL_SFT 23 +#define C_TWO_DIGITAL_MIC_CTL_MASK 0x1 +#define C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 23) +#define UL_MODE_3P25M_CH2_CTL_SFT 22 +#define UL_MODE_3P25M_CH2_CTL_MASK 0x1 +#define UL_MODE_3P25M_CH2_CTL_MASK_SFT (0x1 << 22) +#define UL_MODE_3P25M_CH1_CTL_SFT 21 +#define UL_MODE_3P25M_CH1_CTL_MASK 0x1 +#define UL_MODE_3P25M_CH1_CTL_MASK_SFT (0x1 << 21) +#define UL_SRC_USE_CIC_OUT_CTL_SFT 20 +#define UL_SRC_USE_CIC_OUT_CTL_MASK 0x1 +#define UL_SRC_USE_CIC_OUT_CTL_MASK_SFT (0x1 << 20) +#define UL_VOICE_MODE_CH1_CH2_CTL_SFT 17 +#define UL_VOICE_MODE_CH1_CH2_CTL_MASK 0x7 +#define UL_VOICE_MODE_CH1_CH2_CTL_MASK_SFT (0x7 << 17) +#define DMIC_LOW_POWER_MODE_CTL_SFT 14 +#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3 +#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14) +#define DMIC_48K_SEL_CTL_SFT 13 +#define DMIC_48K_SEL_CTL_MASK 0x1 +#define DMIC_48K_SEL_CTL_MASK_SFT (0x1 << 13) +#define UL_DISABLE_HW_CG_CTL_SFT 12 +#define UL_DISABLE_HW_CG_CTL_MASK 0x1 +#define UL_DISABLE_HW_CG_CTL_MASK_SFT (0x1 << 12) +#define UL_IIR_ON_TMP_CTL_SFT 10 +#define UL_IIR_ON_TMP_CTL_MASK 0x1 +#define UL_IIR_ON_TMP_CTL_MASK_SFT (0x1 << 10) +#define UL_IIRMODE_CTL_SFT 7 +#define UL_IIRMODE_CTL_MASK 0x7 +#define UL_IIRMODE_CTL_MASK_SFT (0x7 << 7) +#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5) +#define AGC_260K_SEL_CH2_CTL_SFT 4 +#define AGC_260K_SEL_CH2_CTL_MASK 0x1 +#define AGC_260K_SEL_CH2_CTL_MASK_SFT (0x1 << 4) +#define AGC_260K_SEL_CH1_CTL_SFT 3 +#define AGC_260K_SEL_CH1_CTL_MASK 0x1 +#define AGC_260K_SEL_CH1_CTL_MASK_SFT (0x1 << 3) +#define UL_LOOP_BACK_MODE_CTL_SFT 2 +#define UL_LOOP_BACK_MODE_CTL_MASK 0x1 +#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2) +#define UL_SDM_3_LEVEL_CTL_SFT 1 +#define UL_SDM_3_LEVEL_CTL_MASK 0x1 +#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1) +#define UL_SRC_ON_TMP_CTL_SFT 0 +#define UL_SRC_ON_TMP_CTL_MASK 0x1 +#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_UL_SRC_CON1 */ +#define C_SDM_RESET_CTL_SFT 31 +#define C_SDM_RESET_CTL_MASK 0x1 +#define C_SDM_RESET_CTL_MASK_SFT (0x1 << 31) +#define ADITHON_CTL_SFT 30 +#define ADITHON_CTL_MASK 0x1 +#define ADITHON_CTL_MASK_SFT (0x1 << 30) +#define ADITHVAL_CTL_SFT 28 +#define ADITHVAL_CTL_MASK 0x3 +#define ADITHVAL_CTL_MASK_SFT (0x3 << 28) +#define C_DAC_EN_CTL_SFT 27 +#define C_DAC_EN_CTL_MASK 0x1 +#define C_DAC_EN_CTL_MASK_SFT (0x1 << 27) +#define C_MUTE_SW_CTL_SFT 26 +#define C_MUTE_SW_CTL_MASK 0x1 +#define C_MUTE_SW_CTL_MASK_SFT (0x1 << 26) +#define ASDM_SRC_SEL_CTL_SFT 25 +#define ASDM_SRC_SEL_CTL_MASK 0x1 +#define ASDM_SRC_SEL_CTL_MASK_SFT (0x1 << 25) +#define C_AMP_DIV_CH2_CTL_SFT 21 +#define C_AMP_DIV_CH2_CTL_MASK 0x7 +#define C_AMP_DIV_CH2_CTL_MASK_SFT (0x7 << 21) +#define C_FREQ_DIV_CH2_CTL_SFT 16 +#define C_FREQ_DIV_CH2_CTL_MASK 0x1f +#define C_FREQ_DIV_CH2_CTL_MASK_SFT (0x1f << 16) +#define C_SINE_MODE_CH2_CTL_SFT 12 +#define C_SINE_MODE_CH2_CTL_MASK 0xf +#define C_SINE_MODE_CH2_CTL_MASK_SFT (0xf << 12) +#define C_AMP_DIV_CH1_CTL_SFT 9 +#define C_AMP_DIV_CH1_CTL_MASK 0x7 +#define C_AMP_DIV_CH1_CTL_MASK_SFT (0x7 << 9) +#define C_FREQ_DIV_CH1_CTL_SFT 4 +#define C_FREQ_DIV_CH1_CTL_MASK 0x1f +#define C_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 4) +#define C_SINE_MODE_CH1_CTL_SFT 0 +#define C_SINE_MODE_CH1_CTL_MASK 0xf +#define C_SINE_MODE_CH1_CTL_MASK_SFT (0xf << 0) + +/* AFE_ADDA_TOP_CON0 */ +#define C_LOOP_BACK_MODE_CTL_SFT 12 +#define C_LOOP_BACK_MODE_CTL_MASK 0xf +#define C_LOOP_BACK_MODE_CTL_MASK_SFT (0xf << 12) +#define C_EXT_ADC_CTL_SFT 0 +#define C_EXT_ADC_CTL_MASK 0x1 +#define C_EXT_ADC_CTL_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_UL_DL_CON0 */ +#define AFE_UL_DL_CON0_RESERVED_SFT 1 +#define AFE_UL_DL_CON0_RESERVED_MASK 0x3fff +#define AFE_UL_DL_CON0_RESERVED_MASK_SFT (0x3fff << 1) +#define ADDA_AFE_ON_SFT 0 +#define ADDA_AFE_ON_MASK 0x1 +#define ADDA_AFE_ON_MASK_SFT (0x1 << 0) + +/* AFE_IRQ_MCU_CON */ +#define IRQ7_MCU_MODE_SFT 24 +#define IRQ7_MCU_MODE_MASK 0xf +#define IRQ7_MCU_MODE_MASK_SFT (0xf << 24) +#define IRQ4_MCU_MODE_SFT 20 +#define IRQ4_MCU_MODE_MASK 0xf +#define IRQ4_MCU_MODE_MASK_SFT (0xf << 20) +#define IRQ3_MCU_MODE_SFT 16 +#define IRQ3_MCU_MODE_MASK 0xf +#define IRQ3_MCU_MODE_MASK_SFT (0xf << 16) +#define IRQ7_MCU_ON_SFT 14 +#define IRQ7_MCU_ON_MASK 0x1 +#define IRQ7_MCU_ON_MASK_SFT (0x1 << 14) +#define IRQ5_MCU_ON_SFT 12 +#define IRQ5_MCU_ON_MASK 0x1 +#define IRQ5_MCU_ON_MASK_SFT (0x1 << 12) +#define IRQ2_MCU_MODE_SFT 8 +#define IRQ2_MCU_MODE_MASK 0xf +#define IRQ2_MCU_MODE_MASK_SFT (0xf << 8) +#define IRQ1_MCU_MODE_SFT 4 +#define IRQ1_MCU_MODE_MASK 0xf +#define IRQ1_MCU_MODE_MASK_SFT (0xf << 4) +#define IRQ4_MCU_ON_SFT 3 +#define IRQ4_MCU_ON_MASK 0x1 +#define IRQ4_MCU_ON_MASK_SFT (0x1 << 3) +#define IRQ3_MCU_ON_SFT 2 +#define IRQ3_MCU_ON_MASK 0x1 +#define IRQ3_MCU_ON_MASK_SFT (0x1 << 2) +#define IRQ2_MCU_ON_SFT 1 +#define IRQ2_MCU_ON_MASK 0x1 +#define IRQ2_MCU_ON_MASK_SFT (0x1 << 1) +#define IRQ1_MCU_ON_SFT 0 +#define IRQ1_MCU_ON_MASK 0x1 +#define IRQ1_MCU_ON_MASK_SFT (0x1 << 0) + +/* AFE_IRQ_MCU_EN */ +#define AFE_IRQ_CM4_EN_SFT 16 +#define AFE_IRQ_CM4_EN_MASK 0x7f +#define AFE_IRQ_CM4_EN_MASK_SFT (0x7f << 16) +#define AFE_IRQ_MD32_EN_SFT 8 +#define AFE_IRQ_MD32_EN_MASK 0x7f +#define AFE_IRQ_MD32_EN_MASK_SFT (0x7f << 8) +#define AFE_IRQ_MCU_EN_SFT 0 +#define AFE_IRQ_MCU_EN_MASK 0x7f +#define AFE_IRQ_MCU_EN_MASK_SFT (0x7f << 0) + +/* AFE_IRQ_MCU_CLR */ +#define IRQ7_MCU_CLR_SFT 6 +#define IRQ7_MCU_CLR_MASK 0x1 +#define IRQ7_MCU_CLR_MASK_SFT (0x1 << 6) +#define IRQ5_MCU_CLR_SFT 4 +#define IRQ5_MCU_CLR_MASK 0x1 +#define IRQ5_MCU_CLR_MASK_SFT (0x1 << 4) +#define IRQ4_MCU_CLR_SFT 3 +#define IRQ4_MCU_CLR_MASK 0x1 +#define IRQ4_MCU_CLR_MASK_SFT (0x1 << 3) +#define IRQ3_MCU_CLR_SFT 2 +#define IRQ3_MCU_CLR_MASK 0x1 +#define IRQ3_MCU_CLR_MASK_SFT (0x1 << 2) +#define IRQ2_MCU_CLR_SFT 1 +#define IRQ2_MCU_CLR_MASK 0x1 +#define IRQ2_MCU_CLR_MASK_SFT (0x1 << 1) +#define IRQ1_MCU_CLR_SFT 0 +#define IRQ1_MCU_CLR_MASK 0x1 +#define IRQ1_MCU_CLR_MASK_SFT (0x1 << 0) + +/* AFE_IRQ_MCU_CNT1 */ +#define AFE_IRQ_MCU_CNT1_SFT 0 +#define AFE_IRQ_MCU_CNT1_MASK 0x3ffff +#define AFE_IRQ_MCU_CNT1_MASK_SFT (0x3ffff << 0) + +/* AFE_IRQ_MCU_CNT2 */ +#define AFE_IRQ_MCU_CNT2_SFT 0 +#define AFE_IRQ_MCU_CNT2_MASK 0x3ffff +#define AFE_IRQ_MCU_CNT2_MASK_SFT (0x3ffff << 0) + +/* AFE_IRQ_MCU_CNT3 */ +#define AFE_IRQ_MCU_CNT3_SFT 0 +#define AFE_IRQ_MCU_CNT3_MASK 0x3ffff +#define AFE_IRQ_MCU_CNT3_MASK_SFT (0x3ffff << 0) + +/* AFE_IRQ_MCU_CNT4 */ +#define AFE_IRQ_MCU_CNT4_SFT 0 +#define AFE_IRQ_MCU_CNT4_MASK 0x3ffff +#define AFE_IRQ_MCU_CNT4_MASK_SFT (0x3ffff << 0) + +/* AFE_IRQ_MCU_CNT5 */ +#define AFE_IRQ_MCU_CNT5_SFT 0 +#define AFE_IRQ_MCU_CNT5_MASK 0x3ffff +#define AFE_IRQ_MCU_CNT5_MASK_SFT (0x3ffff << 0) + +/* AFE_IRQ_MCU_CNT7 */ +#define AFE_IRQ_MCU_CNT7_SFT 0 +#define AFE_IRQ_MCU_CNT7_MASK 0x3ffff +#define AFE_IRQ_MCU_CNT7_MASK_SFT (0x3ffff << 0) + +/* AFE_MEMIF_MSB */ +#define CPU_COMPACT_MODE_SFT 23 +#define CPU_COMPACT_MODE_MASK 0x1 +#define CPU_COMPACT_MODE_MASK_SFT (0x1 << 23) +#define CPU_HD_ALIGN_SFT 22 +#define CPU_HD_ALIGN_MASK 0x1 +#define CPU_HD_ALIGN_MASK_SFT (0x1 << 22) + +/* AFE_MEMIF_HD_MODE */ +#define HDMI_HD_SFT 20 +#define HDMI_HD_MASK 0x3 +#define HDMI_HD_MASK_SFT (0x3 << 20) +#define MOD_DAI_HD_SFT 18 +#define MOD_DAI_HD_MASK 0x3 +#define MOD_DAI_HD_MASK_SFT (0x3 << 18) +#define DAI_HD_SFT 16 +#define DAI_HD_MASK 0x3 +#define DAI_HD_MASK_SFT (0x3 << 16) +#define VUL_DATA2_HD_SFT 12 +#define VUL_DATA2_HD_MASK 0x3 +#define VUL_DATA2_HD_MASK_SFT (0x3 << 12) +#define VUL_HD_SFT 10 +#define VUL_HD_MASK 0x3 +#define VUL_HD_MASK_SFT (0x3 << 10) +#define AWB_HD_SFT 8 +#define AWB_HD_MASK 0x3 +#define AWB_HD_MASK_SFT (0x3 << 8) +#define DL3_HD_SFT 6 +#define DL3_HD_MASK 0x3 +#define DL3_HD_MASK_SFT (0x3 << 6) +#define DL2_HD_SFT 4 +#define DL2_HD_MASK 0x3 +#define DL2_HD_MASK_SFT (0x3 << 4) +#define DL1_DATA2_HD_SFT 2 +#define DL1_DATA2_HD_MASK 0x3 +#define DL1_DATA2_HD_MASK_SFT (0x3 << 2) +#define DL1_HD_SFT 0 +#define DL1_HD_MASK 0x3 +#define DL1_HD_MASK_SFT (0x3 << 0) + +/* AFE_MEMIF_HDALIGN */ +#define HDMI_NORMAL_MODE_SFT 26 +#define HDMI_NORMAL_MODE_MASK 0x1 +#define HDMI_NORMAL_MODE_MASK_SFT (0x1 << 26) +#define MOD_DAI_NORMAL_MODE_SFT 25 +#define MOD_DAI_NORMAL_MODE_MASK 0x1 +#define MOD_DAI_NORMAL_MODE_MASK_SFT (0x1 << 25) +#define DAI_NORMAL_MODE_SFT 24 +#define DAI_NORMAL_MODE_MASK 0x1 +#define DAI_NORMAL_MODE_MASK_SFT (0x1 << 24) +#define VUL_DATA2_NORMAL_MODE_SFT 22 +#define VUL_DATA2_NORMAL_MODE_MASK 0x1 +#define VUL_DATA2_NORMAL_MODE_MASK_SFT (0x1 << 22) +#define VUL_NORMAL_MODE_SFT 21 +#define VUL_NORMAL_MODE_MASK 0x1 +#define VUL_NORMAL_MODE_MASK_SFT (0x1 << 21) +#define AWB_NORMAL_MODE_SFT 20 +#define AWB_NORMAL_MODE_MASK 0x1 +#define AWB_NORMAL_MODE_MASK_SFT (0x1 << 20) +#define DL3_NORMAL_MODE_SFT 19 +#define DL3_NORMAL_MODE_MASK 0x1 +#define DL3_NORMAL_MODE_MASK_SFT (0x1 << 19) +#define DL2_NORMAL_MODE_SFT 18 +#define DL2_NORMAL_MODE_MASK 0x1 +#define DL2_NORMAL_MODE_MASK_SFT (0x1 << 18) +#define DL1_DATA2_NORMAL_MODE_SFT 17 +#define DL1_DATA2_NORMAL_MODE_MASK 0x1 +#define DL1_DATA2_NORMAL_MODE_MASK_SFT (0x1 << 17) +#define DL1_NORMAL_MODE_SFT 16 +#define DL1_NORMAL_MODE_MASK 0x1 +#define DL1_NORMAL_MODE_MASK_SFT (0x1 << 16) +#define HDMI_HD_ALIGN_SFT 10 +#define HDMI_HD_ALIGN_MASK 0x1 +#define HDMI_HD_ALIGN_MASK_SFT (0x1 << 10) +#define MOD_DAI_HD_ALIGN_SFT 9 +#define MOD_DAI_HD_ALIGN_MASK 0x1 +#define MOD_DAI_HD_ALIGN_MASK_SFT (0x1 << 9) +#define DAI_ALIGN_SFT 8 +#define DAI_ALIGN_MASK 0x1 +#define DAI_ALIGN_MASK_SFT (0x1 << 8) +#define VUL2_HD_ALIGN_SFT 7 +#define VUL2_HD_ALIGN_MASK 0x1 +#define VUL2_HD_ALIGN_MASK_SFT (0x1 << 7) +#define VUL_DATA2_HD_ALIGN_SFT 6 +#define VUL_DATA2_HD_ALIGN_MASK 0x1 +#define VUL_DATA2_HD_ALIGN_MASK_SFT (0x1 << 6) +#define VUL_HD_ALIGN_SFT 5 +#define VUL_HD_ALIGN_MASK 0x1 +#define VUL_HD_ALIGN_MASK_SFT (0x1 << 5) +#define AWB_HD_ALIGN_SFT 4 +#define AWB_HD_ALIGN_MASK 0x1 +#define AWB_HD_ALIGN_MASK_SFT (0x1 << 4) +#define DL3_HD_ALIGN_SFT 3 +#define DL3_HD_ALIGN_MASK 0x1 +#define DL3_HD_ALIGN_MASK_SFT (0x1 << 3) +#define DL2_HD_ALIGN_SFT 2 +#define DL2_HD_ALIGN_MASK 0x1 +#define DL2_HD_ALIGN_MASK_SFT (0x1 << 2) +#define DL1_DATA2_HD_ALIGN_SFT 1 +#define DL1_DATA2_HD_ALIGN_MASK 0x1 +#define DL1_DATA2_HD_ALIGN_MASK_SFT (0x1 << 1) +#define DL1_HD_ALIGN_SFT 0 +#define DL1_HD_ALIGN_MASK 0x1 +#define DL1_HD_ALIGN_MASK_SFT (0x1 << 0) + +/* PCM_INTF_CON1 */ +#define PCM_FIX_VALUE_SEL_SFT 31 +#define PCM_FIX_VALUE_SEL_MASK 0x1 +#define PCM_FIX_VALUE_SEL_MASK_SFT (0x1 << 31) +#define PCM_BUFFER_LOOPBACK_SFT 30 +#define PCM_BUFFER_LOOPBACK_MASK 0x1 +#define PCM_BUFFER_LOOPBACK_MASK_SFT (0x1 << 30) +#define PCM_PARALLEL_LOOPBACK_SFT 29 +#define PCM_PARALLEL_LOOPBACK_MASK 0x1 +#define PCM_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 29) +#define PCM_SERIAL_LOOPBACK_SFT 28 +#define PCM_SERIAL_LOOPBACK_MASK 0x1 +#define PCM_SERIAL_LOOPBACK_MASK_SFT (0x1 << 28) +#define PCM_DAI_PCM_LOOPBACK_SFT 27 +#define PCM_DAI_PCM_LOOPBACK_MASK 0x1 +#define PCM_DAI_PCM_LOOPBACK_MASK_SFT (0x1 << 27) +#define PCM_I2S_PCM_LOOPBACK_SFT 26 +#define PCM_I2S_PCM_LOOPBACK_MASK 0x1 +#define PCM_I2S_PCM_LOOPBACK_MASK_SFT (0x1 << 26) +#define PCM_SYNC_DELSEL_SFT 25 +#define PCM_SYNC_DELSEL_MASK 0x1 +#define PCM_SYNC_DELSEL_MASK_SFT (0x1 << 25) +#define PCM_TX_LR_SWAP_SFT 24 +#define PCM_TX_LR_SWAP_MASK 0x1 +#define PCM_TX_LR_SWAP_MASK_SFT (0x1 << 24) +#define PCM_SYNC_OUT_INV_SFT 23 +#define PCM_SYNC_OUT_INV_MASK 0x1 +#define PCM_SYNC_OUT_INV_MASK_SFT (0x1 << 23) +#define PCM_BCLK_OUT_INV_SFT 22 +#define PCM_BCLK_OUT_INV_MASK 0x1 +#define PCM_BCLK_OUT_INV_MASK_SFT (0x1 << 22) +#define PCM_SYNC_IN_INV_SFT 21 +#define PCM_SYNC_IN_INV_MASK 0x1 +#define PCM_SYNC_IN_INV_MASK_SFT (0x1 << 21) +#define PCM_BCLK_IN_INV_SFT 20 +#define PCM_BCLK_IN_INV_MASK 0x1 +#define PCM_BCLK_IN_INV_MASK_SFT (0x1 << 20) +#define PCM_TX_LCH_RPT_SFT 19 +#define PCM_TX_LCH_RPT_MASK 0x1 +#define PCM_TX_LCH_RPT_MASK_SFT (0x1 << 19) +#define PCM_VBT_16K_MODE_SFT 18 +#define PCM_VBT_16K_MODE_MASK 0x1 +#define PCM_VBT_16K_MODE_MASK_SFT (0x1 << 18) +#define PCM_EXT_MODEM_SFT 17 +#define PCM_EXT_MODEM_MASK 0x1 +#define PCM_EXT_MODEM_MASK_SFT (0x1 << 17) +#define PCM_24BIT_SFT 16 +#define PCM_24BIT_MASK 0x1 +#define PCM_24BIT_MASK_SFT (0x1 << 16) +#define PCM_WLEN_SFT 14 +#define PCM_WLEN_MASK 0x3 +#define PCM_WLEN_MASK_SFT (0x3 << 14) +#define PCM_SYNC_LENGTH_SFT 9 +#define PCM_SYNC_LENGTH_MASK 0x1f +#define PCM_SYNC_LENGTH_MASK_SFT (0x1f << 9) +#define PCM_SYNC_TYPE_SFT 8 +#define PCM_SYNC_TYPE_MASK 0x1 +#define PCM_SYNC_TYPE_MASK_SFT (0x1 << 8) +#define PCM_BT_MODE_SFT 7 +#define PCM_BT_MODE_MASK 0x1 +#define PCM_BT_MODE_MASK_SFT (0x1 << 7) +#define PCM_BYP_ASRC_SFT 6 +#define PCM_BYP_ASRC_MASK 0x1 +#define PCM_BYP_ASRC_MASK_SFT (0x1 << 6) +#define PCM_SLAVE_SFT 5 +#define PCM_SLAVE_MASK 0x1 +#define PCM_SLAVE_MASK_SFT (0x1 << 5) +#define PCM_MODE_SFT 3 +#define PCM_MODE_MASK 0x3 +#define PCM_MODE_MASK_SFT (0x3 << 3) +#define PCM_FMT_SFT 1 +#define PCM_FMT_MASK 0x3 +#define PCM_FMT_MASK_SFT (0x3 << 1) +#define PCM_EN_SFT 0 +#define PCM_EN_MASK 0x1 +#define PCM_EN_MASK_SFT (0x1 << 0) + +/* PCM_INTF_CON2 */ +#define PCM1_TX_FIFO_OV_SFT 31 +#define PCM1_TX_FIFO_OV_MASK 0x1 +#define PCM1_TX_FIFO_OV_MASK_SFT (0x1 << 31) +#define PCM1_RX_FIFO_OV_SFT 30 +#define PCM1_RX_FIFO_OV_MASK 0x1 +#define PCM1_RX_FIFO_OV_MASK_SFT (0x1 << 30) +#define PCM2_TX_FIFO_OV_SFT 29 +#define PCM2_TX_FIFO_OV_MASK 0x1 +#define PCM2_TX_FIFO_OV_MASK_SFT (0x1 << 29) +#define PCM2_RX_FIFO_OV_SFT 28 +#define PCM2_RX_FIFO_OV_MASK 0x1 +#define PCM2_RX_FIFO_OV_MASK_SFT (0x1 << 28) +#define PCM1_SYNC_GLITCH_SFT 27 +#define PCM1_SYNC_GLITCH_MASK 0x1 +#define PCM1_SYNC_GLITCH_MASK_SFT (0x1 << 27) +#define PCM2_SYNC_GLITCH_SFT 26 +#define PCM2_SYNC_GLITCH_MASK 0x1 +#define PCM2_SYNC_GLITCH_MASK_SFT (0x1 << 26) +#define PCM1_PCM2_LOOPBACK_SFT 15 +#define PCM1_PCM2_LOOPBACK_MASK 0x1 +#define PCM1_PCM2_LOOPBACK_MASK_SFT (0x1 << 15) +#define DAI_PCM_LOOPBACK_CH_SFT 13 +#define DAI_PCM_LOOPBACK_CH_MASK 0x1 +#define DAI_PCM_LOOPBACK_CH_MASK_SFT (0x1 << 13) +#define I2S_PCM_LOOPBACK_CH_SFT 12 +#define I2S_PCM_LOOPBACK_CH_MASK 0x1 +#define I2S_PCM_LOOPBACK_CH_MASK_SFT (0x1 << 12) +#define PCM_USE_MD3_SFT 8 +#define PCM_USE_MD3_MASK 0x1 +#define PCM_USE_MD3_MASK_SFT (0x1 << 8) +#define TX_FIX_VALUE_SFT 0 +#define TX_FIX_VALUE_MASK 0xff +#define TX_FIX_VALUE_MASK_SFT (0xff << 0) + +/* PCM2_INTF_CON */ +#define PCM2_TX_FIX_VALUE_SFT 24 +#define PCM2_TX_FIX_VALUE_MASK 0xff +#define PCM2_TX_FIX_VALUE_MASK_SFT (0xff << 24) +#define PCM2_FIX_VALUE_SEL_SFT 23 +#define PCM2_FIX_VALUE_SEL_MASK 0x1 +#define PCM2_FIX_VALUE_SEL_MASK_SFT (0x1 << 23) +#define PCM2_BUFFER_LOOPBACK_SFT 22 +#define PCM2_BUFFER_LOOPBACK_MASK 0x1 +#define PCM2_BUFFER_LOOPBACK_MASK_SFT (0x1 << 22) +#define PCM2_PARALLEL_LOOPBACK_SFT 21 +#define PCM2_PARALLEL_LOOPBACK_MASK 0x1 +#define PCM2_PARALLEL_LOOPBACK_MASK_SFT (0x1 << 21) +#define PCM2_SERIAL_LOOPBACK_SFT 20 +#define PCM2_SERIAL_LOOPBACK_MASK 0x1 +#define PCM2_SERIAL_LOOPBACK_MASK_SFT (0x1 << 20) +#define PCM2_DAI_PCM_LOOPBACK_SFT 19 +#define PCM2_DAI_PCM_LOOPBACK_MASK 0x1 +#define PCM2_DAI_PCM_LOOPBACK_MASK_SFT (0x1 << 19) +#define PCM2_I2S_PCM_LOOPBACK_SFT 18 +#define PCM2_I2S_PCM_LOOPBACK_MASK 0x1 +#define PCM2_I2S_PCM_LOOPBACK_MASK_SFT (0x1 << 18) +#define PCM2_SYNC_DELSEL_SFT 17 +#define PCM2_SYNC_DELSEL_MASK 0x1 +#define PCM2_SYNC_DELSEL_MASK_SFT (0x1 << 17) +#define PCM2_TX_LR_SWAP_SFT 16 +#define PCM2_TX_LR_SWAP_MASK 0x1 +#define PCM2_TX_LR_SWAP_MASK_SFT (0x1 << 16) +#define PCM2_SYNC_IN_INV_SFT 15 +#define PCM2_SYNC_IN_INV_MASK 0x1 +#define PCM2_SYNC_IN_INV_MASK_SFT (0x1 << 15) +#define PCM2_BCLK_IN_INV_SFT 14 +#define PCM2_BCLK_IN_INV_MASK 0x1 +#define PCM2_BCLK_IN_INV_MASK_SFT (0x1 << 14) +#define PCM2_TX_LCH_RPT_SFT 13 +#define PCM2_TX_LCH_RPT_MASK 0x1 +#define PCM2_TX_LCH_RPT_MASK_SFT (0x1 << 13) +#define PCM2_VBT_16K_MODE_SFT 12 +#define PCM2_VBT_16K_MODE_MASK 0x1 +#define PCM2_VBT_16K_MODE_MASK_SFT (0x1 << 12) +#define PCM2_LOOPBACK_CH_SEL_SFT 10 +#define PCM2_LOOPBACK_CH_SEL_MASK 0x3 +#define PCM2_LOOPBACK_CH_SEL_MASK_SFT (0x3 << 10) +#define PCM2_TX2_BT_MODE_SFT 8 +#define PCM2_TX2_BT_MODE_MASK 0x1 +#define PCM2_TX2_BT_MODE_MASK_SFT (0x1 << 8) +#define PCM2_BT_MODE_SFT 7 +#define PCM2_BT_MODE_MASK 0x1 +#define PCM2_BT_MODE_MASK_SFT (0x1 << 7) +#define PCM2_AFIFO_SFT 6 +#define PCM2_AFIFO_MASK 0x1 +#define PCM2_AFIFO_MASK_SFT (0x1 << 6) +#define PCM2_WLEN_SFT 5 +#define PCM2_WLEN_MASK 0x1 +#define PCM2_WLEN_MASK_SFT (0x1 << 5) +#define PCM2_MODE_SFT 3 +#define PCM2_MODE_MASK 0x3 +#define PCM2_MODE_MASK_SFT (0x3 << 3) +#define PCM2_FMT_SFT 1 +#define PCM2_FMT_MASK 0x3 +#define PCM2_FMT_MASK_SFT (0x3 << 1) +#define PCM2_EN_SFT 0 +#define PCM2_EN_MASK 0x1 +#define PCM2_EN_MASK_SFT (0x1 << 0) +#endif diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h index 9a4837cc181a..396fe2355eea 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-common.h +++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * mt8173_afe_common.h -- Mediatek 8173 audio driver common definitions * @@ -6,15 +7,6 @@ * Sascha Hauer <s.hauer@pengutronix.de> * Hidalgo Huang <hidalgo.huang@mediatek.com> * Ir Lian <ir.lian@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _MT8173_AFE_COMMON_H_ diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index 65d1433a0944..c0b6697503fd 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Mediatek 8173 ALSA SoC AFE platform driver * @@ -6,15 +7,6 @@ * Sascha Hauer <s.hauer@pengutronix.de> * Hidalgo Huang <hidalgo.huang@mediatek.com> * Ir Lian <ir.lian@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/delay.h> @@ -303,9 +295,7 @@ static void mt8173_afe_dais_disable_clks(struct mtk_base_afe *afe, static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); if (dai->active) return 0; @@ -318,9 +308,7 @@ static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream, static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); if (dai->active) return; @@ -334,10 +322,8 @@ static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream, static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime * const runtime = substream->runtime; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt8173_afe_private *afe_priv = afe->platform_priv; int ret; @@ -358,9 +344,7 @@ static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream, static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt8173_afe_private *afe_priv = afe->platform_priv; if (dai->active) @@ -374,9 +358,7 @@ static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream, static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - 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); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt8173_afe_private *afe_priv = afe->platform_priv; if (dai->active) @@ -389,10 +371,8 @@ static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream, static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime * const runtime = substream->runtime; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt8173_afe_private *afe_priv = afe->platform_priv; unsigned int val; @@ -454,9 +434,7 @@ static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream, static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); - struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name); diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index b49b527a7cf9..902d111016d6 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mt8173-max98090.c -- MT8173 MAX98090 ALSA SoC machine driver * * Copyright (c) 2015 MediaTek Inc. * Author: Koro Chen <koro.chen@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index 904f3ee6b0eb..582174d98c6c 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mt8173-rt5650-rt5514.c -- MT8173 machine driver with RT5650/5514 codecs * * Copyright (c) 2016 MediaTek Inc. * Author: Koro Chen <koro.chen@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 9c61b8c099c5..b3670c8a5b8d 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mt8173-rt5650-rt5676.c -- MT8173 machine driver with RT5650/5676 codecs * * Copyright (c) 2015 MediaTek Inc. * Author: Koro Chen <koro.chen@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 84aa09d3dd98..7a89b4aad182 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mt8173-rt5650.c -- MT8173 machine driver with RT5650 codecs * * Copyright (c) 2016 MediaTek Inc. * Author: Koro Chen <koro.chen@mediatek.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index f5451c78ede5..6dccea6fdaeb 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -1,7 +1,12 @@ config SND_OMAP_SOC - tristate "SoC Audio for the Texas Instruments OMAP chips" + tristate "SoC Audio for Texas Instruments OMAP chips (deprecated)" depends on (ARCH_OMAP && DMA_OMAP) || (ARM && COMPILE_TEST) - select SND_DMAENGINE_PCM + select SND_SDMA_SOC + +config SND_SDMA_SOC + tristate "SoC Audio for Texas Instruments chips using sDMA" + depends on DMA_OMAP || COMPILE_TEST + select SND_SOC_GENERIC_DMAENGINE_PCM config SND_OMAP_SOC_DMIC tristate @@ -14,7 +19,7 @@ config SND_OMAP_SOC_MCPDM config SND_OMAP_SOC_HDMI_AUDIO tristate "HDMI audio support for OMAP4+ based SoCs" - depends on SND_OMAP_SOC + depends on SND_SDMA_SOC help For HDMI audio to work OMAPDSS HDMI support should be enabled. @@ -29,8 +34,7 @@ config SND_OMAP_SOC_HDMI_AUDIO config SND_OMAP_SOC_N810 tristate "SoC Audio support for Nokia N810" - depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C - depends on OMAP_MUX + depends on SND_SDMA_SOC && MACH_NOKIA_N810 && I2C select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC3X help @@ -38,7 +42,7 @@ config SND_OMAP_SOC_N810 config SND_OMAP_SOC_RX51 tristate "SoC Audio support for Nokia N900 (RX-51)" - depends on SND_OMAP_SOC && ARM && I2C + depends on SND_SDMA_SOC && ARM && I2C select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC3X select SND_SOC_TPA6130A2 @@ -49,7 +53,7 @@ config SND_OMAP_SOC_RX51 config SND_OMAP_SOC_AMS_DELTA tristate "SoC Audio support for Amstrad E3 (Delta) videophone" - depends on SND_OMAP_SOC && MACH_AMS_DELTA && TTY + depends on SND_SDMA_SOC && MACH_AMS_DELTA && TTY select SND_OMAP_SOC_MCBSP select SND_SOC_CX20442 help @@ -68,7 +72,7 @@ config SND_OMAP_SOC_AMS_DELTA config SND_OMAP_SOC_OSK5912 tristate "SoC Audio support for omap osk5912" - depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C + depends on SND_SDMA_SOC && MACH_OMAP_OSK && I2C select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC23_I2C help @@ -76,7 +80,7 @@ config SND_OMAP_SOC_OSK5912 config SND_OMAP_SOC_AM3517EVM tristate "SoC Audio support for OMAP3517 / AM3517 EVM" - depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C + depends on SND_SDMA_SOC && MACH_OMAP3517EVM && I2C select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC23_I2C help @@ -85,7 +89,7 @@ config SND_OMAP_SOC_AM3517EVM config SND_OMAP_SOC_OMAP_TWL4030 tristate "SoC Audio support for TI SoC based boards with twl4030 codec" - depends on TWL4030_CORE && SND_OMAP_SOC + depends on TWL4030_CORE && SND_SDMA_SOC select SND_OMAP_SOC_MCBSP select SND_SOC_TWL4030 help @@ -100,7 +104,7 @@ config SND_OMAP_SOC_OMAP_TWL4030 config SND_OMAP_SOC_OMAP_ABE_TWL6040 tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" - depends on TWL6040_CORE && SND_OMAP_SOC && COMMON_CLK + depends on TWL6040_CORE && SND_SDMA_SOC && COMMON_CLK depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST select SND_OMAP_SOC_DMIC select SND_OMAP_SOC_MCPDM @@ -118,7 +122,7 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040 config SND_OMAP_SOC_OMAP3_PANDORA tristate "SoC Audio support for OMAP3 Pandora" - depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_PANDORA + depends on TWL4030_CORE && SND_SDMA_SOC && MACH_OMAP3_PANDORA select SND_OMAP_SOC_MCBSP select SND_SOC_TWL4030 help diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index a6785dc4fc90..53eba3413485 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -1,12 +1,12 @@ # SPDX-License-Identifier: GPL-2.0 # OMAP Platform Support -snd-soc-omap-objs := omap-pcm.o +snd-soc-sdma-objs := sdma-pcm.o snd-soc-omap-dmic-objs := omap-dmic.o snd-soc-omap-mcbsp-objs := omap-mcbsp.o mcbsp.o snd-soc-omap-mcpdm-objs := omap-mcpdm.o snd-soc-omap-hdmi-audio-objs := omap-hdmi-audio.o -obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o +obj-$(CONFIG_SND_SDMA_SOC) += snd-soc-sdma.o obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 71e5f31fa306..9cfefe44a75f 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -80,9 +80,9 @@ static void n810_ext_control(struct snd_soc_dapm_context *dapm) else snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); if (line1l) - snd_soc_dapm_enable_pin_unlocked(dapm, "LINE1L"); + snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic"); else - snd_soc_dapm_disable_pin_unlocked(dapm, "LINE1L"); + snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic"); if (n810_dmic_func) snd_soc_dapm_enable_pin_unlocked(dapm, "DMic"); @@ -222,6 +222,7 @@ static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = { SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event), SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event), SND_SOC_DAPM_MIC("DMic", NULL), + SND_SOC_DAPM_MIC("HS Mic", NULL), }; static const struct snd_soc_dapm_route audio_map[] = { @@ -231,8 +232,14 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Ext Spk", NULL, "LLOUT"}, {"Ext Spk", NULL, "RLOUT"}, - {"DMic Rate 64", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "DMic"}, + {"DMic Rate 64", NULL, "DMic"}, + {"DMic", NULL, "Mic Bias"}, + + /* + * Note that the mic bias is coming from Retu/Vilma and we don't have + * control over it atm. The analog HS mic is not working. <- TODO + */ + {"LINE1L", NULL, "HS Mic"}, }; static const char *spk_function[] = {"Off", "On"}; @@ -257,9 +264,9 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = { static struct snd_soc_dai_link n810_dai = { .name = "TLV320AIC33", .stream_name = "AIC33", - .cpu_dai_name = "omap-mcbsp.2", - .platform_name = "omap-mcbsp.2", - .codec_name = "tlv320aic3x-codec.2-0018", + .cpu_dai_name = "48076000.mcbsp", + .platform_name = "48076000.mcbsp", + .codec_name = "tlv320aic3x-codec.1-0018", .codec_dai_name = "tlv320aic3x-hifi", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index b2f5d2fa354d..51dd7c65096b 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -40,9 +40,9 @@ #include <sound/initval.h> #include <sound/soc.h> #include <sound/dmaengine_pcm.h> -#include <sound/omap-pcm.h> #include "omap-dmic.h" +#include "sdma-pcm.h" struct omap_dmic { struct device *dev; @@ -501,7 +501,7 @@ static int asoc_dmic_probe(struct platform_device *pdev) if (ret) return ret; - ret = omap_pcm_platform_register(&pdev->dev); + ret = sdma_pcm_platform_register(&pdev->dev, NULL, "up_link"); if (ret) return ret; diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index 8eeac7cab1c1..8a99a8837dc9 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -26,9 +26,10 @@ #include <sound/dmaengine_pcm.h> #include <uapi/sound/asound.h> #include <sound/asoundef.h> -#include <sound/omap-pcm.h> #include <sound/omap-hdmi-audio.h> +#include "sdma-pcm.h" + #define DRV_NAME "omap-hdmi-audio" struct hdmi_audio_data { @@ -352,7 +353,7 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) if (ret) return ret; - ret = omap_pcm_platform_register(ad->dssdev); + ret = sdma_pcm_platform_register(ad->dssdev, "audio_tx", NULL); if (ret) return ret; diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 6b40bdbef336..d0ebb6b9bfac 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -34,11 +34,11 @@ #include <sound/initval.h> #include <sound/soc.h> #include <sound/dmaengine_pcm.h> -#include <sound/omap-pcm.h> #include <linux/platform_data/asoc-ti-mcbsp.h> #include "mcbsp.h" #include "omap-mcbsp.h" +#include "sdma-pcm.h" #define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) @@ -868,7 +868,7 @@ static int asoc_mcbsp_probe(struct platform_device *pdev) if (ret) return ret; - return omap_pcm_platform_register(&pdev->dev); + return sdma_pcm_platform_register(&pdev->dev, NULL, NULL); } static int asoc_mcbsp_remove(struct platform_device *pdev) diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index 64609c77a79d..0e97360f9890 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -40,9 +40,9 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/dmaengine_pcm.h> -#include <sound/omap-pcm.h> #include "omap-mcpdm.h" +#include "sdma-pcm.h" struct mcpdm_link_config { u32 link_mask; /* channel mask for the direction */ @@ -548,7 +548,7 @@ static int asoc_mcpdm_probe(struct platform_device *pdev) if (ret) return ret; - return omap_pcm_platform_register(&pdev->dev); + return sdma_pcm_platform_register(&pdev->dev, "dn_link", "up_link"); } static const struct of_device_id omap_mcpdm_of_match[] = { diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c deleted file mode 100644 index 778cc8f75b6a..000000000000 --- a/sound/soc/omap/omap-pcm.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * omap-pcm.c -- ALSA PCM interface for the OMAP SoC - * - * Copyright (C) 2008 Nokia Corporation - * - * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> - * Peter Ujfalusi <peter.ujfalusi@ti.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <linux/dma-mapping.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/omap-dma.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/dmaengine_pcm.h> -#include <sound/soc.h> -#include <sound/omap-pcm.h> - -#ifdef CONFIG_ARCH_OMAP1 -#define pcm_omap1510() cpu_is_omap1510() -#else -#define pcm_omap1510() 0 -#endif - -static struct snd_pcm_hardware omap_pcm_hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - .period_bytes_min = 32, - .period_bytes_max = 64 * 1024, - .periods_min = 2, - .periods_max = 255, - .buffer_bytes_max = 128 * 1024, -}; - -/* sDMA supports only 1, 2, and 4 byte transfer elements. */ -static void omap_pcm_limit_supported_formats(void) -{ - int i; - - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { - switch (snd_pcm_format_physical_width(i)) { - case 8: - case 16: - case 32: - omap_pcm_hardware.formats |= (1LL << i); - break; - default: - break; - } - } -} - -/* this may get called several times by oss emulation */ -static int omap_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct omap_pcm_dma_data *dma_data; - struct dma_slave_config config; - struct dma_chan *chan; - int err = 0; - - memset(&config, 0x00, sizeof(config)); - - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - /* return if this is a bufferless transfer e.g. - * codec <--> BT codec or GSM modem -- lg FIXME */ - if (!dma_data) - return 0; - - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - runtime->dma_bytes = params_buffer_bytes(params); - - chan = snd_dmaengine_pcm_get_chan(substream); - if (!chan) - return -EINVAL; - - /* fills in addr_width and direction */ - err = snd_hwparams_to_dma_slave_config(substream, params, &config); - if (err) - return err; - - snd_dmaengine_pcm_set_config_from_dai_data(substream, - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream), - &config); - - return dmaengine_slave_config(chan, &config); -} - -static int omap_pcm_hw_free(struct snd_pcm_substream *substream) -{ - snd_pcm_set_runtime_buffer(substream, NULL); - return 0; -} - -static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) -{ - snd_pcm_uframes_t offset; - - if (pcm_omap1510()) - offset = snd_dmaengine_pcm_pointer_no_residue(substream); - else - offset = snd_dmaengine_pcm_pointer(substream); - - return offset; -} - -static int omap_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_dmaengine_dai_dma_data *dma_data; - int ret; - - snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware); - - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - /* DT boot: filter_data is the DMA name */ - if (rtd->cpu_dai->dev->of_node) { - struct dma_chan *chan; - - chan = dma_request_slave_channel(rtd->cpu_dai->dev, - dma_data->filter_data); - ret = snd_dmaengine_pcm_open(substream, chan); - } else { - ret = snd_dmaengine_pcm_open_request_chan(substream, - omap_dma_filter_fn, - dma_data->filter_data); - } - return ret; -} - -static int omap_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, - runtime->dma_addr, runtime->dma_bytes); -} - -static const struct snd_pcm_ops omap_pcm_ops = { - .open = omap_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = omap_pcm_hw_params, - .hw_free = omap_pcm_hw_free, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = omap_pcm_pointer, - .mmap = omap_pcm_mmap, -}; - -static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, - int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = omap_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - - buf->bytes = size; - return 0; -} - -static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr); - buf->area = NULL; - } -} - -static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret; - - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = omap_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = omap_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - -out: - /* free preallocated buffers in case of error */ - if (ret) - omap_pcm_free_dma_buffers(pcm); - - return ret; -} - -static const struct snd_soc_component_driver omap_soc_component = { - .ops = &omap_pcm_ops, - .pcm_new = omap_pcm_new, - .pcm_free = omap_pcm_free_dma_buffers, -}; - -int omap_pcm_platform_register(struct device *dev) -{ - omap_pcm_limit_supported_formats(); - return devm_snd_soc_register_component(dev, &omap_soc_component, - NULL, 0); -} -EXPORT_SYMBOL_GPL(omap_pcm_platform_register); - -MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>"); -MODULE_DESCRIPTION("OMAP PCM DMA module"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/omap/sdma-pcm.c b/sound/soc/omap/sdma-pcm.c new file mode 100644 index 000000000000..21a9c2499d48 --- /dev/null +++ b/sound/soc/omap/sdma-pcm.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> +#include <linux/omap-dmaengine.h> + +#include "sdma-pcm.h" + +static const struct snd_pcm_hardware sdma_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_INTERLEAVED, + .period_bytes_min = 32, + .period_bytes_max = 64 * 1024, + .buffer_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 255, +}; + +static const struct snd_dmaengine_pcm_config sdma_dmaengine_pcm_config = { + .pcm_hardware = &sdma_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = omap_dma_filter_fn, + .prealloc_buffer_size = 128 * 1024, +}; + +int sdma_pcm_platform_register(struct device *dev, + char *txdmachan, char *rxdmachan) +{ + struct snd_dmaengine_pcm_config *config; + unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT; + + /* Standard names for the directions: 'tx' and 'rx' */ + if (!txdmachan && !rxdmachan) + return devm_snd_dmaengine_pcm_register(dev, + &sdma_dmaengine_pcm_config, + flags); + + config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + *config = sdma_dmaengine_pcm_config; + + if (!txdmachan || !rxdmachan) { + /* One direction only PCM */ + flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX; + if (!txdmachan) { + txdmachan = rxdmachan; + rxdmachan = NULL; + } + } + + config->chan_names[0] = txdmachan; + config->chan_names[1] = rxdmachan; + + return devm_snd_dmaengine_pcm_register(dev, config, flags); +} +EXPORT_SYMBOL_GPL(sdma_pcm_platform_register); + +MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); +MODULE_DESCRIPTION("sDMA PCM ASoC platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/omap/sdma-pcm.h b/sound/soc/omap/sdma-pcm.h new file mode 100644 index 000000000000..34a7f90b2587 --- /dev/null +++ b/sound/soc/omap/sdma-pcm.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> + */ + +#ifndef __SDMA_PCM_H__ +#define __SDMA_PCM_H__ + +#if IS_ENABLED(CONFIG_SND_SDMA_SOC) +int sdma_pcm_platform_register(struct device *dev, + char *txdmachan, char *rxdmachan); +#else +static inline int sdma_pcm_platform_register(struct device *dev, + char *txdmachan, char *rxdmachan) +{ + return -ENODEV; +} +#endif /* CONFIG_SND_SDMA_SOC */ + +#endif /* __SDMA_PCM_H__ */ diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 484ab3c2ad67..960744e46edc 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -1,7 +1,6 @@ config SND_PXA2XX_SOC tristate "SoC Audio for the Intel PXA2xx chip" depends on ARCH_PXA || COMPILE_TEST - depends on HAS_DMA select SND_PXA2XX_LIB help Say Y or M if you want to add support for codecs attached to diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 0291c7cb64eb..6fc986080130 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -43,7 +43,8 @@ struct ssp_priv { struct ssp_device *ssp; unsigned int sysclk; - int dai_fmt; + unsigned int dai_fmt; + unsigned int configured_dai_fmt; #ifdef CONFIG_PM uint32_t cr0; uint32_t cr1; @@ -216,10 +217,9 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, { struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; - int val; u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & - ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); + ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); dev_dbg(&ssp->pdev->dev, "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n", @@ -257,8 +257,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, * on PXA2xx. On PXA3xx it must be enabled when doing so. */ if (ssp->type != PXA3xx_SSP) clk_disable_unprepare(ssp->clk); - val = pxa_ssp_read_reg(ssp, SSCR0) | sscr0; - pxa_ssp_write_reg(ssp, SSCR0, val); + pxa_ssp_write_reg(ssp, SSCR0, sscr0); if (ssp->type != PXA3xx_SSP) clk_prepare_enable(ssp->clk); @@ -433,36 +432,72 @@ static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, return 0; } +static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + case SND_SOC_DAIFMT_IB_NF: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + break; + + default: + return -EINVAL; + } + + /* Settings will be applied in hw_params() */ + priv->dai_fmt = fmt; + + return 0; +} + /* * Set up the SSP DAI format. * The SSP Port must be inactive before calling this function as the * physical interface format is changed. */ -static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, - unsigned int fmt) +static int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv) { - struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; u32 sscr0, sscr1, sspsp, scfr; /* check if we need to change anything at all */ - if (priv->dai_fmt == fmt) + if (priv->configured_dai_fmt == priv->dai_fmt) return 0; - /* we can only change the settings if the port is not in use */ - if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { - dev_err(&ssp->pdev->dev, - "can't change hardware dai format: stream is in use"); - return -EINVAL; - } - /* reset port settings */ sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & - ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); - sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7); - sspsp = 0; + ~(SSCR0_PSP | SSCR0_MOD); + sscr1 = pxa_ssp_read_reg(ssp, SSCR1) & + ~(SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR | + SSCR1_RWOT | SSCR1_TRAIL | SSCR1_TFT | SSCR1_RFT); + sspsp = pxa_ssp_read_reg(ssp, SSPSP) & + ~(SSPSP_SFRMP | SSPSP_SCMODE(3)); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + sscr1 |= SSCR1_RxTresh(8) | SSCR1_TxTresh(7); + + switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR; break; @@ -475,7 +510,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + switch (priv->dai_fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: sspsp |= SSPSP_SFRMP; break; @@ -491,7 +526,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: sscr0 |= SSCR0_PSP; sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; @@ -513,7 +548,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, pxa_ssp_write_reg(ssp, SSCR1, sscr1); pxa_ssp_write_reg(ssp, SSPSP, sspsp); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFS: scfr = pxa_ssp_read_reg(ssp, SSCR1) | SSCR1_SCFR; @@ -530,7 +565,7 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, * we have to defer some things until hw_params() where we * know parameters like the sample size. */ - priv->dai_fmt = fmt; + priv->configured_dai_fmt = priv->dai_fmt; return 0; } @@ -551,6 +586,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, int width = snd_pcm_format_physical_width(params_format(params)); int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; struct snd_dmaengine_dai_dma_data *dma_data; + int ret; dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); @@ -566,6 +602,10 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) return 0; + ret = pxa_ssp_configure_dai_fmt(priv); + if (ret < 0) + return ret; + /* clear selected SSP bits */ sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 8ec9a074b38b..87838fa27997 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -11,24 +11,21 @@ config SND_SOC_LPASS_CPU config SND_SOC_LPASS_PLATFORM tristate - depends on HAS_DMA select REGMAP_MMIO config SND_SOC_LPASS_IPQ806X tristate - depends on HAS_DMA select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM config SND_SOC_LPASS_APQ8016 tristate - depends on HAS_DMA select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM config SND_SOC_STORM tristate "ASoC I2S support for Storm boards" - depends on SND_SOC_QCOM && HAS_DMA + depends on SND_SOC_QCOM select SND_SOC_LPASS_IPQ806X select SND_SOC_MAX98357A help @@ -37,9 +34,59 @@ config SND_SOC_STORM config SND_SOC_APQ8016_SBC tristate "SoC Audio support for APQ8016 SBC platforms" - depends on SND_SOC_QCOM && HAS_DMA + depends on SND_SOC_QCOM select SND_SOC_LPASS_APQ8016 help Support for Qualcomm Technologies LPASS audio block in APQ8016 SOC-based systems. Say Y if you want to use audio devices on MI2S. + +config SND_SOC_QDSP6_COMMON + tristate + +config SND_SOC_QDSP6_CORE + tristate + +config SND_SOC_QDSP6_AFE + tristate + +config SND_SOC_QDSP6_AFE_DAI + tristate + +config SND_SOC_QDSP6_ADM + tristate + +config SND_SOC_QDSP6_ROUTING + tristate + +config SND_SOC_QDSP6_ASM + tristate + +config SND_SOC_QDSP6_ASM_DAI + tristate + +config SND_SOC_QDSP6 + tristate "SoC ALSA audio driver for QDSP6" + depends on QCOM_APR && HAS_DMA + select SND_SOC_QDSP6_COMMON + select SND_SOC_QDSP6_CORE + select SND_SOC_QDSP6_AFE + select SND_SOC_QDSP6_AFE_DAI + select SND_SOC_QDSP6_ADM + select SND_SOC_QDSP6_ROUTING + select SND_SOC_QDSP6_ASM + select SND_SOC_QDSP6_ASM_DAI + help + To add support for MSM QDSP6 Soc Audio. + This will enable sound soc platform specific + audio drivers. This includes q6asm, q6adm, + q6afe interfaces to DSP using apr. + +config SND_SOC_MSM8996 + tristate "SoC Machine driver for MSM8996 and APQ8096 boards" + depends on QCOM_APR + select SND_SOC_QDSP6 + help + Support for Qualcomm Technologies LPASS audio block in + APQ8096 SoC-based systems. + Say Y if you want to use audio device on this SoCs diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index d5280355c24f..206945bb9ba1 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -13,6 +13,11 @@ obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o # Machine snd-soc-storm-objs := storm.o snd-soc-apq8016-sbc-objs := apq8016_sbc.o +snd-soc-apq8096-objs := apq8096.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o +obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o + +#DSP lib +obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/ diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c new file mode 100644 index 000000000000..561cd429e6f2 --- /dev/null +++ b/sound/soc/qcom/apq8096.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, Linaro Limited + +#include <linux/soc/qcom/apr.h> +#include <linux/module.h> +#include <linux/component.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm.h> + +static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + return 0; +} + +static int apq8096_sbc_parse_of(struct snd_soc_card *card) +{ + struct device_node *np; + struct device_node *codec = NULL; + struct device_node *platform = NULL; + struct device_node *cpu = NULL; + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + int ret, num_links; + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(dev, "Error parsing card name: %d\n", ret); + return ret; + } + + /* DAPM routes */ + if (of_property_read_bool(dev->of_node, "qcom,audio-routing")) { + ret = snd_soc_of_parse_audio_routing(card, + "qcom,audio-routing"); + if (ret) + return ret; + } + + /* Populate links */ + num_links = of_get_child_count(dev->of_node); + + /* Allocate the DAI link array */ + card->dai_link = kcalloc(num_links, sizeof(*link), GFP_KERNEL); + if (!card->dai_link) + return -ENOMEM; + + card->num_links = num_links; + link = card->dai_link; + + for_each_child_of_node(dev->of_node, np) { + cpu = of_get_child_by_name(np, "cpu"); + if (!cpu) { + dev_err(dev, "Can't find cpu DT node\n"); + ret = -EINVAL; + goto err; + } + + link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); + if (!link->cpu_of_node) { + dev_err(card->dev, "error getting cpu phandle\n"); + ret = -EINVAL; + goto err; + } + + ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name); + if (ret) { + dev_err(card->dev, "error getting cpu dai name\n"); + goto err; + } + + platform = of_get_child_by_name(np, "platform"); + codec = of_get_child_by_name(np, "codec"); + if (codec && platform) { + link->platform_of_node = of_parse_phandle(platform, + "sound-dai", + 0); + if (!link->platform_of_node) { + dev_err(card->dev, "platform dai not found\n"); + ret = -EINVAL; + goto err; + } + + ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); + if (ret < 0) { + dev_err(card->dev, "codec dai not found\n"); + goto err; + } + link->no_pcm = 1; + link->ignore_pmdown_time = 1; + link->be_hw_params_fixup = apq8096_be_hw_params_fixup; + } else { + link->platform_of_node = link->cpu_of_node; + link->codec_dai_name = "snd-soc-dummy-dai"; + link->codec_name = "snd-soc-dummy"; + link->dynamic = 1; + } + + link->ignore_suspend = 1; + ret = of_property_read_string(np, "link-name", &link->name); + if (ret) { + dev_err(card->dev, "error getting codec dai_link name\n"); + goto err; + } + + link->dpcm_playback = 1; + link->dpcm_capture = 1; + link->stream_name = link->name; + link++; + } + + return 0; +err: + of_node_put(cpu); + of_node_put(codec); + of_node_put(platform); + kfree(card->dai_link); + return ret; +} + +static int apq8096_bind(struct device *dev) +{ + struct snd_soc_card *card; + int ret; + + card = kzalloc(sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + component_bind_all(dev, card); + card->dev = dev; + ret = apq8096_sbc_parse_of(card); + if (ret) { + dev_err(dev, "Error parsing OF data\n"); + goto err; + } + + ret = snd_soc_register_card(card); + if (ret) + goto err; + + return 0; + +err: + component_unbind_all(dev, card); + kfree(card); + return ret; +} + +static void apq8096_unbind(struct device *dev) +{ + struct snd_soc_card *card = dev_get_drvdata(dev); + + snd_soc_unregister_card(card); + component_unbind_all(dev, card); + kfree(card->dai_link); + kfree(card); +} + +static const struct component_master_ops apq8096_ops = { + .bind = apq8096_bind, + .unbind = apq8096_unbind, +}; + +static int apq8016_compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static void apq8016_release_of(struct device *dev, void *data) +{ + of_node_put(data); +} + +static int add_audio_components(struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np, *platform, *cpu, *node, *dai_node; + + node = dev->of_node; + + for_each_child_of_node(node, np) { + cpu = of_get_child_by_name(np, "cpu"); + if (cpu) { + dai_node = of_parse_phandle(cpu, "sound-dai", 0); + of_node_get(dai_node); + component_match_add_release(dev, matchptr, + apq8016_release_of, + apq8016_compare_of, + dai_node); + } + + platform = of_get_child_by_name(np, "platform"); + if (platform) { + dai_node = of_parse_phandle(platform, "sound-dai", 0); + component_match_add_release(dev, matchptr, + apq8016_release_of, + apq8016_compare_of, + dai_node); + } + } + + return 0; +} + +static int apq8096_platform_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + int ret; + + ret = add_audio_components(&pdev->dev, &match); + if (ret) + return ret; + + return component_master_add_with_match(&pdev->dev, &apq8096_ops, match); +} + +static int apq8096_platform_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &apq8096_ops); + + return 0; +} + +static const struct of_device_id msm_snd_apq8096_dt_match[] = { + {.compatible = "qcom,apq8096-sndcard"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_snd_apq8096_dt_match); + +static struct platform_driver msm_snd_apq8096_driver = { + .probe = apq8096_platform_probe, + .remove = apq8096_platform_remove, + .driver = { + .name = "msm-snd-apq8096", + .owner = THIS_MODULE, + .of_match_table = msm_snd_apq8096_dt_match, + }, +}; +module_platform_driver(msm_snd_apq8096_driver); +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); +MODULE_DESCRIPTION("APQ8096 ASoC Machine Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile new file mode 100644 index 000000000000..c33b3cacbea1 --- /dev/null +++ b/sound/soc/qcom/qdsp6/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += q6dsp-common.o +obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o +obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o +obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o +obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o +obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o +obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o +obj-$(CONFIG_SND_SOC_QDSP6_ASM_DAI) += q6asm-dai.o diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c new file mode 100644 index 000000000000..9983c665a941 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6adm.c @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2018, Linaro Limited + +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/jiffies.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/kref.h> +#include <linux/wait.h> +#include <linux/soc/qcom/apr.h> +#include <linux/platform_device.h> +#include <sound/asound.h> +#include "q6adm.h" +#include "q6afe.h" +#include "q6core.h" +#include "q6dsp-errno.h" +#include "q6dsp-common.h" + +#define ADM_CMD_DEVICE_OPEN_V5 0x00010326 +#define ADM_CMDRSP_DEVICE_OPEN_V5 0x00010329 +#define ADM_CMD_DEVICE_CLOSE_V5 0x00010327 +#define ADM_CMD_MATRIX_MAP_ROUTINGS_V5 0x00010325 + +#define TIMEOUT_MS 1000 +#define RESET_COPP_ID 99 +#define INVALID_COPP_ID 0xFF +/* Definition for a legacy device session. */ +#define ADM_LEGACY_DEVICE_SESSION 0 +#define ADM_MATRIX_ID_AUDIO_RX 0 +#define ADM_MATRIX_ID_AUDIO_TX 1 + +struct q6copp { + int afe_port; + int copp_idx; + int id; + int topology; + int mode; + int rate; + int bit_width; + int channels; + int app_type; + int acdb_id; + + struct aprv2_ibasic_rsp_result_t result; + struct kref refcount; + wait_queue_head_t wait; + struct list_head node; + struct q6adm *adm; +}; + +struct q6adm { + struct apr_device *apr; + struct device *dev; + struct q6core_svc_api_info ainfo; + unsigned long copp_bitmap[AFE_MAX_PORTS]; + struct list_head copps_list; + spinlock_t copps_list_lock; + struct aprv2_ibasic_rsp_result_t result; + struct mutex lock; + wait_queue_head_t matrix_map_wait; + struct platform_device *pdev_routing; +}; + +struct q6adm_cmd_device_open_v5 { + u16 flags; + u16 mode_of_operation; + u16 endpoint_id_1; + u16 endpoint_id_2; + u32 topology_id; + u16 dev_num_channel; + u16 bit_width; + u32 sample_rate; + u8 dev_channel_mapping[8]; +} __packed; + +struct q6adm_cmd_matrix_map_routings_v5 { + u32 matrix_id; + u32 num_sessions; +} __packed; + +struct q6adm_session_map_node_v5 { + u16 session_id; + u16 num_copps; +} __packed; + +static struct q6copp *q6adm_find_copp(struct q6adm *adm, int port_idx, + int copp_idx) +{ + struct q6copp *c = NULL; + struct q6copp *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&adm->copps_list_lock, flags); + list_for_each_entry(c, &adm->copps_list, node) { + if ((port_idx == c->afe_port) && (copp_idx == c->copp_idx)) { + ret = c; + kref_get(&c->refcount); + break; + } + } + + spin_unlock_irqrestore(&adm->copps_list_lock, flags); + + return ret; + +} + +static void q6adm_free_copp(struct kref *ref) +{ + struct q6copp *c = container_of(ref, struct q6copp, refcount); + struct q6adm *adm = c->adm; + unsigned long flags; + + spin_lock_irqsave(&adm->copps_list_lock, flags); + clear_bit(c->copp_idx, &adm->copp_bitmap[c->afe_port]); + list_del(&c->node); + spin_unlock_irqrestore(&adm->copps_list_lock, flags); + kfree(c); +} + +static int q6adm_callback(struct apr_device *adev, struct apr_resp_pkt *data) +{ + struct aprv2_ibasic_rsp_result_t *result = data->payload; + int port_idx, copp_idx; + struct apr_hdr *hdr = &data->hdr; + struct q6copp *copp; + struct q6adm *adm = dev_get_drvdata(&adev->dev); + + if (!data->payload_size) + return 0; + + copp_idx = (hdr->token) & 0XFF; + port_idx = ((hdr->token) >> 16) & 0xFF; + if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) { + dev_err(&adev->dev, "Invalid port idx %d token %d\n", + port_idx, hdr->token); + return 0; + } + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + dev_err(&adev->dev, "Invalid copp idx %d token %d\n", + copp_idx, hdr->token); + return 0; + } + + switch (hdr->opcode) { + case APR_BASIC_RSP_RESULT: { + if (result->status != 0) { + dev_err(&adev->dev, "cmd = 0x%x return error = 0x%x\n", + result->opcode, result->status); + } + switch (result->opcode) { + case ADM_CMD_DEVICE_OPEN_V5: + case ADM_CMD_DEVICE_CLOSE_V5: + copp = q6adm_find_copp(adm, port_idx, copp_idx); + if (!copp) + return 0; + + copp->result = *result; + wake_up(&copp->wait); + kref_put(&copp->refcount, q6adm_free_copp); + break; + case ADM_CMD_MATRIX_MAP_ROUTINGS_V5: + adm->result = *result; + wake_up(&adm->matrix_map_wait); + break; + + default: + dev_err(&adev->dev, "Unknown Cmd: 0x%x\n", + result->opcode); + break; + } + return 0; + } + case ADM_CMDRSP_DEVICE_OPEN_V5: { + struct adm_cmd_rsp_device_open_v5 { + u32 status; + u16 copp_id; + u16 reserved; + } __packed * open = data->payload; + + copp = q6adm_find_copp(adm, port_idx, copp_idx); + if (!copp) + return 0; + + if (open->copp_id == INVALID_COPP_ID) { + dev_err(&adev->dev, "Invalid coppid rxed %d\n", + open->copp_id); + copp->result.status = ADSP_EBADPARAM; + wake_up(&copp->wait); + kref_put(&copp->refcount, q6adm_free_copp); + break; + } + copp->result.opcode = hdr->opcode; + copp->id = open->copp_id; + wake_up(&copp->wait); + kref_put(&copp->refcount, q6adm_free_copp); + } + break; + default: + dev_err(&adev->dev, "Unknown cmd:0x%x\n", + hdr->opcode); + break; + } + + return 0; +} + +static struct q6copp *q6adm_alloc_copp(struct q6adm *adm, int port_idx) +{ + struct q6copp *c; + int idx; + + idx = find_first_zero_bit(&adm->copp_bitmap[port_idx], + MAX_COPPS_PER_PORT); + + if (idx > MAX_COPPS_PER_PORT) + return ERR_PTR(-EBUSY); + + c = kzalloc(sizeof(*c), GFP_ATOMIC); + if (!c) + return ERR_PTR(-ENOMEM); + + set_bit(idx, &adm->copp_bitmap[port_idx]); + c->copp_idx = idx; + c->afe_port = port_idx; + c->adm = adm; + + init_waitqueue_head(&c->wait); + + return c; +} + +static int q6adm_apr_send_copp_pkt(struct q6adm *adm, struct q6copp *copp, + struct apr_pkt *pkt, uint32_t rsp_opcode) +{ + struct device *dev = adm->dev; + uint32_t opcode = pkt->hdr.opcode; + int ret; + + mutex_lock(&adm->lock); + copp->result.opcode = 0; + copp->result.status = 0; + ret = apr_send_pkt(adm->apr, pkt); + if (ret < 0) { + dev_err(dev, "Failed to send APR packet\n"); + ret = -EINVAL; + goto err; + } + + /* Wait for the callback with copp id */ + if (rsp_opcode) + ret = wait_event_timeout(copp->wait, + (copp->result.opcode == opcode) || + (copp->result.opcode == rsp_opcode), + msecs_to_jiffies(TIMEOUT_MS)); + else + ret = wait_event_timeout(copp->wait, + (copp->result.opcode == opcode), + msecs_to_jiffies(TIMEOUT_MS)); + + if (!ret) { + dev_err(dev, "ADM copp cmd timedout\n"); + ret = -ETIMEDOUT; + } else if (copp->result.status > 0) { + dev_err(dev, "DSP returned error[%d]\n", + copp->result.status); + ret = -EINVAL; + } + +err: + mutex_unlock(&adm->lock); + return ret; +} + +static int q6adm_device_close(struct q6adm *adm, struct q6copp *copp, + int port_id, int copp_idx) +{ + struct apr_pkt close; + + close.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + close.hdr.pkt_size = sizeof(close); + close.hdr.src_port = port_id; + close.hdr.dest_port = copp->id; + close.hdr.token = port_id << 16 | copp_idx; + close.hdr.opcode = ADM_CMD_DEVICE_CLOSE_V5; + + return q6adm_apr_send_copp_pkt(adm, copp, &close, 0); +} + +static struct q6copp *q6adm_find_matching_copp(struct q6adm *adm, + int port_id, int topology, + int mode, int rate, + int channel_mode, int bit_width, + int app_type) +{ + struct q6copp *c = NULL; + struct q6copp *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&adm->copps_list_lock, flags); + + list_for_each_entry(c, &adm->copps_list, node) { + if ((port_id == c->afe_port) && (topology == c->topology) && + (mode == c->mode) && (rate == c->rate) && + (bit_width == c->bit_width) && (app_type == c->app_type)) { + ret = c; + kref_get(&c->refcount); + } + } + spin_unlock_irqrestore(&adm->copps_list_lock, flags); + + return ret; +} + +static int q6adm_device_open(struct q6adm *adm, struct q6copp *copp, + int port_id, int path, int topology, + int channel_mode, int bit_width, int rate) +{ + struct q6adm_cmd_device_open_v5 *open; + int afe_port = q6afe_get_port_id(port_id); + struct apr_pkt *pkt; + void *p; + int ret, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(*open); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + open = p + APR_HDR_SIZE; + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = afe_port; + pkt->hdr.dest_port = afe_port; + pkt->hdr.token = port_id << 16 | copp->copp_idx; + pkt->hdr.opcode = ADM_CMD_DEVICE_OPEN_V5; + open->flags = ADM_LEGACY_DEVICE_SESSION; + open->mode_of_operation = path; + open->endpoint_id_1 = afe_port; + open->topology_id = topology; + open->dev_num_channel = channel_mode & 0x00FF; + open->bit_width = bit_width; + open->sample_rate = rate; + + ret = q6dsp_map_channels(&open->dev_channel_mapping[0], + channel_mode); + if (ret) + goto err; + + ret = q6adm_apr_send_copp_pkt(adm, copp, pkt, + ADM_CMDRSP_DEVICE_OPEN_V5); + +err: + kfree(pkt); + return ret; +} + +/** + * q6adm_open() - open adm and grab a free copp + * + * @dev: Pointer to adm child device. + * @port_id: port id + * @path: playback or capture path. + * @rate: rate at which copp is required. + * @channel_mode: channel mode + * @topology: adm topology id + * @perf_mode: performace mode. + * @bit_width: audio sample bit width + * @app_type: Application type. + * @acdb_id: ACDB id + * + * Return: Will be an negative on error or a valid copp pointer on success. + */ +struct q6copp *q6adm_open(struct device *dev, int port_id, int path, int rate, + int channel_mode, int topology, int perf_mode, + uint16_t bit_width, int app_type, int acdb_id) +{ + struct q6adm *adm = dev_get_drvdata(dev->parent); + struct q6copp *copp; + unsigned long flags; + int ret = 0; + + if (port_id < 0) { + dev_err(dev, "Invalid port_id 0x%x\n", port_id); + return ERR_PTR(-EINVAL); + } + + copp = q6adm_find_matching_copp(adm, port_id, topology, perf_mode, + rate, channel_mode, bit_width, app_type); + if (copp) { + dev_err(dev, "Found Matching Copp 0x%x\n", copp->copp_idx); + return copp; + } + + spin_lock_irqsave(&adm->copps_list_lock, flags); + copp = q6adm_alloc_copp(adm, port_id); + if (IS_ERR_OR_NULL(copp)) { + spin_unlock_irqrestore(&adm->copps_list_lock, flags); + return ERR_CAST(copp); + } + + list_add_tail(&copp->node, &adm->copps_list); + spin_unlock_irqrestore(&adm->copps_list_lock, flags); + + kref_init(&copp->refcount); + copp->topology = topology; + copp->mode = perf_mode; + copp->rate = rate; + copp->channels = channel_mode; + copp->bit_width = bit_width; + copp->app_type = app_type; + + + ret = q6adm_device_open(adm, copp, port_id, path, topology, + channel_mode, bit_width, rate); + if (ret < 0) { + kref_put(&copp->refcount, q6adm_free_copp); + return ERR_PTR(ret); + } + + return copp; +} +EXPORT_SYMBOL_GPL(q6adm_open); + +/** + * q6adm_get_copp_id() - get copp index + * + * @copp: Pointer to valid copp + * + * Return: Will be an negative on error or a valid copp index on success. + **/ +int q6adm_get_copp_id(struct q6copp *copp) +{ + if (!copp) + return -EINVAL; + + return copp->copp_idx; +} +EXPORT_SYMBOL_GPL(q6adm_get_copp_id); + +/** + * q6adm_matrix_map() - Map asm streams and afe ports using payload + * + * @dev: Pointer to adm child device. + * @path: playback or capture path. + * @payload_map: map between session id and afe ports. + * @perf_mode: Performace mode. + * + * Return: Will be an negative on error or a zero on success. + */ +int q6adm_matrix_map(struct device *dev, int path, + struct route_payload payload_map, int perf_mode) +{ + struct q6adm *adm = dev_get_drvdata(dev->parent); + struct q6adm_cmd_matrix_map_routings_v5 *route; + struct q6adm_session_map_node_v5 *node; + struct apr_pkt *pkt; + uint16_t *copps_list; + int pkt_size, ret, i, copp_idx; + void *matrix_map = NULL; + struct q6copp *copp; + + /* Assumes port_ids have already been validated during adm_open */ + pkt_size = (APR_HDR_SIZE + sizeof(*route) + sizeof(*node) + + (sizeof(uint32_t) * payload_map.num_copps)); + + matrix_map = kzalloc(pkt_size, GFP_KERNEL); + if (!matrix_map) + return -ENOMEM; + + pkt = matrix_map; + route = matrix_map + APR_HDR_SIZE; + node = matrix_map + APR_HDR_SIZE + sizeof(*route); + copps_list = matrix_map + APR_HDR_SIZE + sizeof(*route) + sizeof(*node); + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.token = 0; + pkt->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5; + route->num_sessions = 1; + + switch (path) { + case ADM_PATH_PLAYBACK: + route->matrix_id = ADM_MATRIX_ID_AUDIO_RX; + break; + case ADM_PATH_LIVE_REC: + route->matrix_id = ADM_MATRIX_ID_AUDIO_TX; + break; + default: + dev_err(dev, "Wrong path set[%d]\n", path); + break; + } + + node->session_id = payload_map.session_id; + node->num_copps = payload_map.num_copps; + + for (i = 0; i < payload_map.num_copps; i++) { + int port_idx = payload_map.port_id[i]; + + if (port_idx < 0) { + dev_err(dev, "Invalid port_id 0x%x\n", + payload_map.port_id[i]); + kfree(pkt); + return -EINVAL; + } + copp_idx = payload_map.copp_idx[i]; + + copp = q6adm_find_copp(adm, port_idx, copp_idx); + if (!copp) { + kfree(pkt); + return -EINVAL; + } + + copps_list[i] = copp->id; + kref_put(&copp->refcount, q6adm_free_copp); + } + + mutex_lock(&adm->lock); + adm->result.status = 0; + adm->result.opcode = 0; + + ret = apr_send_pkt(adm->apr, pkt); + if (ret < 0) { + dev_err(dev, "routing for stream %d failed ret %d\n", + payload_map.session_id, ret); + goto fail_cmd; + } + ret = wait_event_timeout(adm->matrix_map_wait, + adm->result.opcode == pkt->hdr.opcode, + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + dev_err(dev, "routing for stream %d failed\n", + payload_map.session_id); + ret = -ETIMEDOUT; + goto fail_cmd; + } else if (adm->result.status > 0) { + dev_err(dev, "DSP returned error[%d]\n", + adm->result.status); + ret = -EINVAL; + goto fail_cmd; + } + +fail_cmd: + mutex_unlock(&adm->lock); + kfree(pkt); + return ret; +} +EXPORT_SYMBOL_GPL(q6adm_matrix_map); + +/** + * q6adm_close() - Close adm copp + * + * @dev: Pointer to adm child device. + * @copp: pointer to previously opened copp + * + * Return: Will be an negative on error or a zero on success. + */ +int q6adm_close(struct device *dev, struct q6copp *copp) +{ + struct q6adm *adm = dev_get_drvdata(dev->parent); + int ret = 0; + + ret = q6adm_device_close(adm, copp, copp->afe_port, copp->copp_idx); + if (ret < 0) { + dev_err(adm->dev, "Failed to close copp %d\n", ret); + return ret; + } + + kref_put(&copp->refcount, q6adm_free_copp); + + return 0; +} +EXPORT_SYMBOL_GPL(q6adm_close); + +static int q6adm_probe(struct apr_device *adev) +{ + struct device *dev = &adev->dev; + struct device_node *dais_np; + struct q6adm *adm; + + adm = devm_kzalloc(&adev->dev, sizeof(*adm), GFP_KERNEL); + if (!adm) + return -ENOMEM; + + adm->apr = adev; + dev_set_drvdata(&adev->dev, adm); + adm->dev = dev; + q6core_get_svc_api_info(adev->svc_id, &adm->ainfo); + mutex_init(&adm->lock); + init_waitqueue_head(&adm->matrix_map_wait); + + INIT_LIST_HEAD(&adm->copps_list); + spin_lock_init(&adm->copps_list_lock); + + dais_np = of_get_child_by_name(dev->of_node, "routing"); + if (dais_np) { + adm->pdev_routing = of_platform_device_create(dais_np, + "q6routing", dev); + of_node_put(dais_np); + } + + return 0; +} + +static int q6adm_remove(struct apr_device *adev) +{ + struct q6adm *adm = dev_get_drvdata(&adev->dev); + + if (adm->pdev_routing) + of_platform_device_destroy(&adm->pdev_routing->dev, NULL); + + return 0; +} + +static const struct of_device_id q6adm_device_id[] = { + { .compatible = "qcom,q6adm" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6adm_device_id); + +static struct apr_driver qcom_q6adm_driver = { + .probe = q6adm_probe, + .remove = q6adm_remove, + .callback = q6adm_callback, + .driver = { + .name = "qcom-q6adm", + .of_match_table = of_match_ptr(q6adm_device_id), + }, +}; + +module_apr_driver(qcom_q6adm_driver); +MODULE_DESCRIPTION("Q6 Audio Device Manager"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6adm.h b/sound/soc/qcom/qdsp6/q6adm.h new file mode 100644 index 000000000000..4f56999b7fab --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6adm.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __Q6_ADM_V2_H__ +#define __Q6_ADM_V2_H__ + +#define ADM_PATH_PLAYBACK 0x1 +#define ADM_PATH_LIVE_REC 0x2 +#define MAX_COPPS_PER_PORT 8 +#define NULL_COPP_TOPOLOGY 0x00010312 + +/* multiple copp per stream. */ +struct route_payload { + int num_copps; + int session_id; + int copp_idx[MAX_COPPS_PER_PORT]; + int port_id[MAX_COPPS_PER_PORT]; +}; + +struct q6copp; +struct q6copp *q6adm_open(struct device *dev, int port_id, int path, int rate, + int channel_mode, int topology, int perf_mode, + uint16_t bit_width, int app_type, int acdb_id); +int q6adm_close(struct device *dev, struct q6copp *copp); +int q6adm_get_copp_id(struct q6copp *copp); +int q6adm_matrix_map(struct device *dev, int path, + struct route_payload payload_map, int perf_mode); + +#endif /* __Q6_ADM_V2_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c new file mode 100644 index 000000000000..5002dd05bf27 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -0,0 +1,1303 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2018, Linaro Limited + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/component.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> +#include "q6afe.h" + +#define Q6AFE_TDM_PB_DAI(pre, num, did) { \ + .playback = { \ + .stream_name = pre" TDM"#num" Playback", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .ops = &q6tdm_ops, \ + .id = did, \ + .probe = msm_dai_q6_dai_probe, \ + .remove = msm_dai_q6_dai_remove, \ + } + +#define Q6AFE_TDM_CAP_DAI(pre, num, did) { \ + .capture = { \ + .stream_name = pre" TDM"#num" Capture", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .ops = &q6tdm_ops, \ + .id = did, \ + .probe = msm_dai_q6_dai_probe, \ + .remove = msm_dai_q6_dai_remove, \ + } + +struct q6afe_dai_priv_data { + uint32_t sd_line_mask; + uint32_t sync_mode; + uint32_t sync_src; + uint32_t data_out_enable; + uint32_t invert_sync; + uint32_t data_delay; + uint32_t data_align; +}; + +struct q6afe_dai_data { + struct q6afe_port *port[AFE_PORT_MAX]; + struct q6afe_port_config port_config[AFE_PORT_MAX]; + bool is_port_started[AFE_PORT_MAX]; + struct q6afe_dai_priv_data priv[AFE_PORT_MAX]; +}; + +static int q6slim_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_slim_cfg *slim = &dai_data->port_config[dai->id].slim; + + slim->num_channels = params_channels(params); + slim->sample_rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_SPECIAL: + slim->bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + slim->bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + slim->bit_width = 32; + break; + default: + pr_err("%s: format %d\n", + __func__, params_format(params)); + return -EINVAL; + } + + return 0; +} + +static int q6hdmi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + int channels = params_channels(params); + struct q6afe_hdmi_cfg *hdmi = &dai_data->port_config[dai->id].hdmi; + + hdmi->sample_rate = params_rate(params); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + hdmi->bit_width = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + hdmi->bit_width = 24; + break; + } + + /* HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4 */ + switch (channels) { + case 2: + hdmi->channel_allocation = 0; + break; + case 3: + hdmi->channel_allocation = 0x02; + break; + case 4: + hdmi->channel_allocation = 0x06; + break; + case 5: + hdmi->channel_allocation = 0x0A; + break; + case 6: + hdmi->channel_allocation = 0x0B; + break; + case 7: + hdmi->channel_allocation = 0x12; + break; + case 8: + hdmi->channel_allocation = 0x13; + break; + default: + dev_err(dai->dev, "invalid Channels = %u\n", channels); + return -EINVAL; + } + + return 0; +} + +static int q6i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_i2s_cfg *i2s = &dai_data->port_config[dai->id].i2s_cfg; + + i2s->sample_rate = params_rate(params); + i2s->bit_width = params_width(params); + i2s->num_channels = params_channels(params); + i2s->sd_line_mask = dai_data->priv[dai->id].sd_line_mask; + + return 0; +} + +static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_i2s_cfg *i2s = &dai_data->port_config[dai->id].i2s_cfg; + + i2s->fmt = fmt; + + return 0; +} + +static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_tdm_cfg *tdm = &dai_data->port_config[dai->id].tdm; + unsigned int cap_mask; + int rc = 0; + + /* HW only supports 16 and 32 bit slot width configuration */ + if ((slot_width != 16) && (slot_width != 32)) { + dev_err(dai->dev, "%s: invalid slot_width %d\n", + __func__, slot_width); + return -EINVAL; + } + + /* HW supports 1-32 slots configuration. Typical: 1, 2, 4, 8, 16, 32 */ + switch (slots) { + case 2: + cap_mask = 0x03; + break; + case 4: + cap_mask = 0x0F; + break; + case 8: + cap_mask = 0xFF; + break; + case 16: + cap_mask = 0xFFFF; + break; + default: + dev_err(dai->dev, "%s: invalid slots %d\n", + __func__, slots); + return -EINVAL; + } + + switch (dai->id) { + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + tdm->nslots_per_frame = slots; + tdm->slot_width = slot_width; + /* TDM RX dais ids are even and tx are odd */ + tdm->slot_mask = (dai->id & 0x1 ? tx_mask : rx_mask) & cap_mask; + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; + } + + return rc; +} + +static int q6tdm_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_tdm_cfg *tdm = &dai_data->port_config[dai->id].tdm; + int rc = 0; + int i = 0; + + switch (dai->id) { + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + if (dai->id & 0x1) { + if (!tx_slot) { + dev_err(dai->dev, "tx slot not found\n"); + return -EINVAL; + } + if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "invalid tx num %d\n", + tx_num); + return -EINVAL; + } + + for (i = 0; i < tx_num; i++) + tdm->ch_mapping[i] = tx_slot[i]; + + for (i = tx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++) + tdm->ch_mapping[i] = Q6AFE_CMAP_INVALID; + + tdm->num_channels = tx_num; + } else { + /* rx */ + if (!rx_slot) { + dev_err(dai->dev, "rx slot not found\n"); + return -EINVAL; + } + if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "invalid rx num %d\n", + rx_num); + return -EINVAL; + } + + for (i = 0; i < rx_num; i++) + tdm->ch_mapping[i] = rx_slot[i]; + + for (i = rx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++) + tdm->ch_mapping[i] = Q6AFE_CMAP_INVALID; + + tdm->num_channels = rx_num; + } + + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; + } + + return rc; +} + +static int q6tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_tdm_cfg *tdm = &dai_data->port_config[dai->id].tdm; + + tdm->bit_width = params_width(params); + tdm->sample_rate = params_rate(params); + tdm->num_channels = params_channels(params); + tdm->data_align_type = dai_data->priv[dai->id].data_align; + tdm->sync_src = dai_data->priv[dai->id].sync_src; + tdm->sync_mode = dai_data->priv[dai->id].sync_mode; + + return 0; +} +static void q6afe_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc; + + rc = q6afe_port_stop(dai_data->port[dai->id]); + if (rc < 0) + dev_err(dai->dev, "fail to close AFE port (%d)\n", rc); + + dai_data->is_port_started[dai->id] = false; + +} + +static int q6afe_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + int rc; + + if (dai_data->is_port_started[dai->id]) { + /* stop the port and restart with new port config */ + rc = q6afe_port_stop(dai_data->port[dai->id]); + if (rc < 0) { + dev_err(dai->dev, "fail to close AFE port (%d)\n", rc); + return rc; + } + } + + switch (dai->id) { + case HDMI_RX: + q6afe_hdmi_port_prepare(dai_data->port[dai->id], + &dai_data->port_config[dai->id].hdmi); + break; + case SLIMBUS_0_RX ... SLIMBUS_6_TX: + q6afe_slim_port_prepare(dai_data->port[dai->id], + &dai_data->port_config[dai->id].slim); + break; + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + rc = q6afe_i2s_port_prepare(dai_data->port[dai->id], + &dai_data->port_config[dai->id].i2s_cfg); + if (rc < 0) { + dev_err(dai->dev, "fail to prepare AFE port %x\n", + dai->id); + return rc; + } + break; + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + q6afe_tdm_port_prepare(dai_data->port[dai->id], + &dai_data->port_config[dai->id].tdm); + break; + default: + return -EINVAL; + } + + rc = q6afe_port_start(dai_data->port[dai->id]); + if (rc < 0) { + dev_err(dai->dev, "fail to start AFE port %x\n", dai->id); + return rc; + } + dai_data->is_port_started[dai->id] = true; + + return 0; +} + +static int q6slim_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_port_config *pcfg = &dai_data->port_config[dai->id]; + int i; + + if (!rx_slot) { + pr_err("%s: rx slot not found\n", __func__); + return -EINVAL; + } + + for (i = 0; i < rx_num; i++) { + pcfg->slim.ch_mapping[i] = rx_slot[i]; + pr_debug("%s: find number of channels[%d] ch[%d]\n", + __func__, i, rx_slot[i]); + } + + pcfg->slim.num_channels = rx_num; + + pr_debug("%s: SLIMBUS_%d_RX cnt[%d] ch[%d %d]\n", __func__, + (dai->id - SLIMBUS_0_RX) / 2, rx_num, + pcfg->slim.ch_mapping[0], + pcfg->slim.ch_mapping[1]); + + return 0; +} + +static int q6afe_mi2s_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_port *port = dai_data->port[dai->id]; + + switch (clk_id) { + case LPAIF_DIG_CLK: + return q6afe_port_set_sysclk(port, clk_id, 0, 5, freq, dir); + case LPAIF_BIT_CLK: + case LPAIF_OSR_CLK: + return q6afe_port_set_sysclk(port, clk_id, + Q6AFE_LPASS_CLK_SRC_INTERNAL, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + freq, dir); + case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR: + case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1: + return q6afe_port_set_sysclk(port, clk_id, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + freq, dir); + case Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT ... Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT: + return q6afe_port_set_sysclk(port, clk_id, + Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + freq, dir); + } + + return 0; +} + +static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { + {"HDMI Playback", NULL, "HDMI_RX"}, + {"Slimbus1 Playback", NULL, "SLIMBUS_1_RX"}, + {"Slimbus2 Playback", NULL, "SLIMBUS_2_RX"}, + {"Slimbus3 Playback", NULL, "SLIMBUS_3_RX"}, + {"Slimbus4 Playback", NULL, "SLIMBUS_4_RX"}, + {"Slimbus5 Playback", NULL, "SLIMBUS_5_RX"}, + {"Slimbus6 Playback", NULL, "SLIMBUS_6_RX"}, + + {"Primary MI2S Playback", NULL, "PRI_MI2S_RX"}, + {"Secondary MI2S Playback", NULL, "SEC_MI2S_RX"}, + {"Tertiary MI2S Playback", NULL, "TERT_MI2S_RX"}, + {"Quaternary MI2S Playback", NULL, "QUAT_MI2S_RX"}, + + {"Primary TDM0 Playback", NULL, "PRIMARY_TDM_RX_0"}, + {"Primary TDM1 Playback", NULL, "PRIMARY_TDM_RX_1"}, + {"Primary TDM2 Playback", NULL, "PRIMARY_TDM_RX_2"}, + {"Primary TDM3 Playback", NULL, "PRIMARY_TDM_RX_3"}, + {"Primary TDM4 Playback", NULL, "PRIMARY_TDM_RX_4"}, + {"Primary TDM5 Playback", NULL, "PRIMARY_TDM_RX_5"}, + {"Primary TDM6 Playback", NULL, "PRIMARY_TDM_RX_6"}, + {"Primary TDM7 Playback", NULL, "PRIMARY_TDM_RX_7"}, + + {"Secondary TDM0 Playback", NULL, "SEC_TDM_RX_0"}, + {"Secondary TDM1 Playback", NULL, "SEC_TDM_RX_1"}, + {"Secondary TDM2 Playback", NULL, "SEC_TDM_RX_2"}, + {"Secondary TDM3 Playback", NULL, "SEC_TDM_RX_3"}, + {"Secondary TDM4 Playback", NULL, "SEC_TDM_RX_4"}, + {"Secondary TDM5 Playback", NULL, "SEC_TDM_RX_5"}, + {"Secondary TDM6 Playback", NULL, "SEC_TDM_RX_6"}, + {"Secondary TDM7 Playback", NULL, "SEC_TDM_RX_7"}, + + {"Tertiary TDM0 Playback", NULL, "TERT_TDM_RX_0"}, + {"Tertiary TDM1 Playback", NULL, "TERT_TDM_RX_1"}, + {"Tertiary TDM2 Playback", NULL, "TERT_TDM_RX_2"}, + {"Tertiary TDM3 Playback", NULL, "TERT_TDM_RX_3"}, + {"Tertiary TDM4 Playback", NULL, "TERT_TDM_RX_4"}, + {"Tertiary TDM5 Playback", NULL, "TERT_TDM_RX_5"}, + {"Tertiary TDM6 Playback", NULL, "TERT_TDM_RX_6"}, + {"Tertiary TDM7 Playback", NULL, "TERT_TDM_RX_7"}, + + {"Quaternary TDM0 Playback", NULL, "QUAT_TDM_RX_0"}, + {"Quaternary TDM1 Playback", NULL, "QUAT_TDM_RX_1"}, + {"Quaternary TDM2 Playback", NULL, "QUAT_TDM_RX_2"}, + {"Quaternary TDM3 Playback", NULL, "QUAT_TDM_RX_3"}, + {"Quaternary TDM4 Playback", NULL, "QUAT_TDM_RX_4"}, + {"Quaternary TDM5 Playback", NULL, "QUAT_TDM_RX_5"}, + {"Quaternary TDM6 Playback", NULL, "QUAT_TDM_RX_6"}, + {"Quaternary TDM7 Playback", NULL, "QUAT_TDM_RX_7"}, + + {"Quinary TDM0 Playback", NULL, "QUIN_TDM_RX_0"}, + {"Quinary TDM1 Playback", NULL, "QUIN_TDM_RX_1"}, + {"Quinary TDM2 Playback", NULL, "QUIN_TDM_RX_2"}, + {"Quinary TDM3 Playback", NULL, "QUIN_TDM_RX_3"}, + {"Quinary TDM4 Playback", NULL, "QUIN_TDM_RX_4"}, + {"Quinary TDM5 Playback", NULL, "QUIN_TDM_RX_5"}, + {"Quinary TDM6 Playback", NULL, "QUIN_TDM_RX_6"}, + {"Quinary TDM7 Playback", NULL, "QUIN_TDM_RX_7"}, + + {"PRIMARY_TDM_TX_0", NULL, "Primary TDM0 Capture"}, + {"PRIMARY_TDM_TX_1", NULL, "Primary TDM1 Capture"}, + {"PRIMARY_TDM_TX_2", NULL, "Primary TDM2 Capture"}, + {"PRIMARY_TDM_TX_3", NULL, "Primary TDM3 Capture"}, + {"PRIMARY_TDM_TX_4", NULL, "Primary TDM4 Capture"}, + {"PRIMARY_TDM_TX_5", NULL, "Primary TDM5 Capture"}, + {"PRIMARY_TDM_TX_6", NULL, "Primary TDM6 Capture"}, + {"PRIMARY_TDM_TX_7", NULL, "Primary TDM7 Capture"}, + + {"SEC_TDM_TX_0", NULL, "Secondary TDM0 Capture"}, + {"SEC_TDM_TX_1", NULL, "Secondary TDM1 Capture"}, + {"SEC_TDM_TX_2", NULL, "Secondary TDM2 Capture"}, + {"SEC_TDM_TX_3", NULL, "Secondary TDM3 Capture"}, + {"SEC_TDM_TX_4", NULL, "Secondary TDM4 Capture"}, + {"SEC_TDM_TX_5", NULL, "Secondary TDM5 Capture"}, + {"SEC_TDM_TX_6", NULL, "Secondary TDM6 Capture"}, + {"SEC_TDM_TX_7", NULL, "Secondary TDM7 Capture"}, + + {"TERT_TDM_TX_0", NULL, "Tertiary TDM0 Capture"}, + {"TERT_TDM_TX_1", NULL, "Tertiary TDM1 Capture"}, + {"TERT_TDM_TX_2", NULL, "Tertiary TDM2 Capture"}, + {"TERT_TDM_TX_3", NULL, "Tertiary TDM3 Capture"}, + {"TERT_TDM_TX_4", NULL, "Tertiary TDM4 Capture"}, + {"TERT_TDM_TX_5", NULL, "Tertiary TDM5 Capture"}, + {"TERT_TDM_TX_6", NULL, "Tertiary TDM6 Capture"}, + {"TERT_TDM_TX_7", NULL, "Tertiary TDM7 Capture"}, + + {"QUAT_TDM_TX_0", NULL, "Quaternary TDM0 Capture"}, + {"QUAT_TDM_TX_1", NULL, "Quaternary TDM1 Capture"}, + {"QUAT_TDM_TX_2", NULL, "Quaternary TDM2 Capture"}, + {"QUAT_TDM_TX_3", NULL, "Quaternary TDM3 Capture"}, + {"QUAT_TDM_TX_4", NULL, "Quaternary TDM4 Capture"}, + {"QUAT_TDM_TX_5", NULL, "Quaternary TDM5 Capture"}, + {"QUAT_TDM_TX_6", NULL, "Quaternary TDM6 Capture"}, + {"QUAT_TDM_TX_7", NULL, "Quaternary TDM7 Capture"}, + + {"QUIN_TDM_TX_0", NULL, "Quinary TDM0 Capture"}, + {"QUIN_TDM_TX_1", NULL, "Quinary TDM1 Capture"}, + {"QUIN_TDM_TX_2", NULL, "Quinary TDM2 Capture"}, + {"QUIN_TDM_TX_3", NULL, "Quinary TDM3 Capture"}, + {"QUIN_TDM_TX_4", NULL, "Quinary TDM4 Capture"}, + {"QUIN_TDM_TX_5", NULL, "Quinary TDM5 Capture"}, + {"QUIN_TDM_TX_6", NULL, "Quinary TDM6 Capture"}, + {"QUIN_TDM_TX_7", NULL, "Quinary TDM7 Capture"}, + + {"TERT_MI2S_TX", NULL, "Tertiary MI2S Capture"}, + {"PRI_MI2S_TX", NULL, "Primary MI2S Capture"}, + {"SEC_MI2S_TX", NULL, "Secondary MI2S Capture"}, + {"QUAT_MI2S_TX", NULL, "Quaternary MI2S Capture"}, +}; + +static struct snd_soc_dai_ops q6hdmi_ops = { + .prepare = q6afe_dai_prepare, + .hw_params = q6hdmi_hw_params, + .shutdown = q6afe_dai_shutdown, +}; + +static 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 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 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 int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_port *port; + + port = q6afe_port_get_from_id(dai->dev, dai->id); + if (IS_ERR(port)) { + dev_err(dai->dev, "Unable to get afe port\n"); + return -EINVAL; + } + dai_data->port[dai->id] = port; + + return 0; +} + +static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + + q6afe_port_put(dai_data->port[dai->id]); + dai_data->port[dai->id] = NULL; + + return 0; +} + +static struct snd_soc_dai_driver q6afe_dais[] = { + { + .playback = { + .stream_name = "HDMI Playback", + .rates = SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 2, + .channels_max = 8, + .rate_max = 192000, + .rate_min = 48000, + }, + .ops = &q6hdmi_ops, + .id = HDMI_RX, + .name = "HDMI", + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .name = "SLIMBUS_0_RX", + .ops = &q6slim_ops, + .id = SLIMBUS_0_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .playback = { + .stream_name = "Slimbus Playback", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + }, { + .playback = { + .stream_name = "Slimbus1 Playback", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .name = "SLIMBUS_1_RX", + .ops = &q6slim_ops, + .id = SLIMBUS_1_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .playback = { + .stream_name = "Slimbus2 Playback", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 192000, + }, + .name = "SLIMBUS_2_RX", + .ops = &q6slim_ops, + .id = SLIMBUS_2_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .playback = { + .stream_name = "Slimbus3 Playback", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .name = "SLIMBUS_3_RX", + .ops = &q6slim_ops, + .id = SLIMBUS_3_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .playback = { + .stream_name = "Slimbus4 Playback", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .name = "SLIMBUS_4_RX", + .ops = &q6slim_ops, + .id = SLIMBUS_4_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .playback = { + .stream_name = "Slimbus5 Playback", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .name = "SLIMBUS_5_RX", + .ops = &q6slim_ops, + .id = SLIMBUS_5_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .playback = { + .stream_name = "Slimbus6 Playback", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &q6slim_ops, + .name = "SLIMBUS_6_RX", + .id = SLIMBUS_6_RX, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .playback = { + .stream_name = "Primary MI2S Playback", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .id = PRIMARY_MI2S_RX, + .name = "PRI_MI2S_RX", + .ops = &q6i2s_ops, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .capture = { + .stream_name = "Primary MI2S Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .id = PRIMARY_MI2S_TX, + .name = "PRI_MI2S_TX", + .ops = &q6i2s_ops, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .playback = { + .stream_name = "Secondary MI2S Playback", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .name = "SEC_MI2S_RX", + .id = SECONDARY_MI2S_RX, + .ops = &q6i2s_ops, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .capture = { + .stream_name = "Secondary MI2S Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .id = SECONDARY_MI2S_TX, + .name = "SEC_MI2S_TX", + .ops = &q6i2s_ops, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .playback = { + .stream_name = "Tertiary MI2S Playback", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .name = "TERT_MI2S_RX", + .id = TERTIARY_MI2S_RX, + .ops = &q6i2s_ops, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .capture = { + .stream_name = "Tertiary MI2S Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .id = TERTIARY_MI2S_TX, + .name = "TERT_MI2S_TX", + .ops = &q6i2s_ops, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .playback = { + .stream_name = "Quaternary MI2S Playback", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .name = "QUAT_MI2S_RX", + .id = QUATERNARY_MI2S_RX, + .ops = &q6i2s_ops, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, { + .capture = { + .stream_name = "Quaternary MI2S Capture", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .id = QUATERNARY_MI2S_TX, + .name = "QUAT_MI2S_TX", + .ops = &q6i2s_ops, + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, + Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0), + Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1), + Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2), + Q6AFE_TDM_PB_DAI("Primary", 3, PRIMARY_TDM_RX_3), + Q6AFE_TDM_PB_DAI("Primary", 4, PRIMARY_TDM_RX_4), + Q6AFE_TDM_PB_DAI("Primary", 5, PRIMARY_TDM_RX_5), + Q6AFE_TDM_PB_DAI("Primary", 6, PRIMARY_TDM_RX_6), + Q6AFE_TDM_PB_DAI("Primary", 7, PRIMARY_TDM_RX_7), + Q6AFE_TDM_CAP_DAI("Primary", 0, PRIMARY_TDM_TX_0), + Q6AFE_TDM_CAP_DAI("Primary", 1, PRIMARY_TDM_TX_1), + Q6AFE_TDM_CAP_DAI("Primary", 2, PRIMARY_TDM_TX_2), + Q6AFE_TDM_CAP_DAI("Primary", 3, PRIMARY_TDM_TX_3), + Q6AFE_TDM_CAP_DAI("Primary", 4, PRIMARY_TDM_TX_4), + Q6AFE_TDM_CAP_DAI("Primary", 5, PRIMARY_TDM_TX_5), + Q6AFE_TDM_CAP_DAI("Primary", 6, PRIMARY_TDM_TX_6), + Q6AFE_TDM_CAP_DAI("Primary", 7, PRIMARY_TDM_TX_7), + Q6AFE_TDM_PB_DAI("Secondary", 0, SECONDARY_TDM_RX_0), + Q6AFE_TDM_PB_DAI("Secondary", 1, SECONDARY_TDM_RX_1), + Q6AFE_TDM_PB_DAI("Secondary", 2, SECONDARY_TDM_RX_2), + Q6AFE_TDM_PB_DAI("Secondary", 3, SECONDARY_TDM_RX_3), + Q6AFE_TDM_PB_DAI("Secondary", 4, SECONDARY_TDM_RX_4), + Q6AFE_TDM_PB_DAI("Secondary", 5, SECONDARY_TDM_RX_5), + Q6AFE_TDM_PB_DAI("Secondary", 6, SECONDARY_TDM_RX_6), + Q6AFE_TDM_PB_DAI("Secondary", 7, SECONDARY_TDM_RX_7), + Q6AFE_TDM_CAP_DAI("Secondary", 0, SECONDARY_TDM_TX_0), + Q6AFE_TDM_CAP_DAI("Secondary", 1, SECONDARY_TDM_TX_1), + Q6AFE_TDM_CAP_DAI("Secondary", 2, SECONDARY_TDM_TX_2), + Q6AFE_TDM_CAP_DAI("Secondary", 3, SECONDARY_TDM_TX_3), + Q6AFE_TDM_CAP_DAI("Secondary", 4, SECONDARY_TDM_TX_4), + Q6AFE_TDM_CAP_DAI("Secondary", 5, SECONDARY_TDM_TX_5), + Q6AFE_TDM_CAP_DAI("Secondary", 6, SECONDARY_TDM_TX_6), + Q6AFE_TDM_CAP_DAI("Secondary", 7, SECONDARY_TDM_TX_7), + Q6AFE_TDM_PB_DAI("Tertiary", 0, TERTIARY_TDM_RX_0), + Q6AFE_TDM_PB_DAI("Tertiary", 1, TERTIARY_TDM_RX_1), + Q6AFE_TDM_PB_DAI("Tertiary", 2, TERTIARY_TDM_RX_2), + Q6AFE_TDM_PB_DAI("Tertiary", 3, TERTIARY_TDM_RX_3), + Q6AFE_TDM_PB_DAI("Tertiary", 4, TERTIARY_TDM_RX_4), + Q6AFE_TDM_PB_DAI("Tertiary", 5, TERTIARY_TDM_RX_5), + Q6AFE_TDM_PB_DAI("Tertiary", 6, TERTIARY_TDM_RX_6), + Q6AFE_TDM_PB_DAI("Tertiary", 7, TERTIARY_TDM_RX_7), + Q6AFE_TDM_CAP_DAI("Tertiary", 0, TERTIARY_TDM_TX_0), + Q6AFE_TDM_CAP_DAI("Tertiary", 1, TERTIARY_TDM_TX_1), + Q6AFE_TDM_CAP_DAI("Tertiary", 2, TERTIARY_TDM_TX_2), + Q6AFE_TDM_CAP_DAI("Tertiary", 3, TERTIARY_TDM_TX_3), + Q6AFE_TDM_CAP_DAI("Tertiary", 4, TERTIARY_TDM_TX_4), + Q6AFE_TDM_CAP_DAI("Tertiary", 5, TERTIARY_TDM_TX_5), + Q6AFE_TDM_CAP_DAI("Tertiary", 6, TERTIARY_TDM_TX_6), + Q6AFE_TDM_CAP_DAI("Tertiary", 7, TERTIARY_TDM_TX_7), + Q6AFE_TDM_PB_DAI("Quaternary", 0, QUATERNARY_TDM_RX_0), + Q6AFE_TDM_PB_DAI("Quaternary", 1, QUATERNARY_TDM_RX_1), + Q6AFE_TDM_PB_DAI("Quaternary", 2, QUATERNARY_TDM_RX_2), + Q6AFE_TDM_PB_DAI("Quaternary", 3, QUATERNARY_TDM_RX_3), + Q6AFE_TDM_PB_DAI("Quaternary", 4, QUATERNARY_TDM_RX_4), + Q6AFE_TDM_PB_DAI("Quaternary", 5, QUATERNARY_TDM_RX_5), + Q6AFE_TDM_PB_DAI("Quaternary", 6, QUATERNARY_TDM_RX_6), + Q6AFE_TDM_PB_DAI("Quaternary", 7, QUATERNARY_TDM_RX_7), + Q6AFE_TDM_CAP_DAI("Quaternary", 0, QUATERNARY_TDM_TX_0), + Q6AFE_TDM_CAP_DAI("Quaternary", 1, QUATERNARY_TDM_TX_1), + Q6AFE_TDM_CAP_DAI("Quaternary", 2, QUATERNARY_TDM_TX_2), + Q6AFE_TDM_CAP_DAI("Quaternary", 3, QUATERNARY_TDM_TX_3), + Q6AFE_TDM_CAP_DAI("Quaternary", 4, QUATERNARY_TDM_TX_4), + Q6AFE_TDM_CAP_DAI("Quaternary", 5, QUATERNARY_TDM_TX_5), + Q6AFE_TDM_CAP_DAI("Quaternary", 6, QUATERNARY_TDM_TX_6), + Q6AFE_TDM_CAP_DAI("Quaternary", 7, QUATERNARY_TDM_TX_7), + Q6AFE_TDM_PB_DAI("Quinary", 0, QUINARY_TDM_RX_0), + Q6AFE_TDM_PB_DAI("Quinary", 1, QUINARY_TDM_RX_1), + Q6AFE_TDM_PB_DAI("Quinary", 2, QUINARY_TDM_RX_2), + Q6AFE_TDM_PB_DAI("Quinary", 3, QUINARY_TDM_RX_3), + Q6AFE_TDM_PB_DAI("Quinary", 4, QUINARY_TDM_RX_4), + Q6AFE_TDM_PB_DAI("Quinary", 5, QUINARY_TDM_RX_5), + Q6AFE_TDM_PB_DAI("Quinary", 6, QUINARY_TDM_RX_6), + Q6AFE_TDM_PB_DAI("Quinary", 7, QUINARY_TDM_RX_7), + Q6AFE_TDM_CAP_DAI("Quinary", 0, QUINARY_TDM_TX_0), + Q6AFE_TDM_CAP_DAI("Quinary", 1, QUINARY_TDM_TX_1), + Q6AFE_TDM_CAP_DAI("Quinary", 2, QUINARY_TDM_TX_2), + Q6AFE_TDM_CAP_DAI("Quinary", 3, QUINARY_TDM_TX_3), + Q6AFE_TDM_CAP_DAI("Quinary", 4, QUINARY_TDM_TX_4), + Q6AFE_TDM_CAP_DAI("Quinary", 5, QUINARY_TDM_TX_5), + Q6AFE_TDM_CAP_DAI("Quinary", 6, QUINARY_TDM_TX_6), + Q6AFE_TDM_CAP_DAI("Quinary", 7, QUINARY_TDM_TX_7), +}; + +static int q6afe_of_xlate_dai_name(struct snd_soc_component *component, + struct of_phandle_args *args, + const char **dai_name) +{ + int id = args->args[0]; + int ret = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(q6afe_dais); i++) { + if (q6afe_dais[i].id == id) { + *dai_name = q6afe_dais[i].name; + ret = 0; + break; + } + } + + return ret; +} + +static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { + SND_SOC_DAPM_AIF_OUT("HDMI_RX", "HDMI Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_1_RX", "Slimbus1 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_2_RX", "Slimbus2 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_3_RX", "Slimbus3 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_RX", "Slimbus6 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX", "Tertiary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX", "Tertiary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX", "Secondary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX", "Secondary MI2S Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX_SD1", + "Secondary MI2S Playback SD1", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRI_MI2S_RX", "Primary MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRI_MI2S_TX", "Primary MI2S Capture", + 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_0", "Primary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_1", "Primary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_2", "Primary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_3", "Primary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_4", "Primary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_5", "Primary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_6", "Primary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("PRIMARY_TDM_RX_7", "Primary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_0", "Primary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_1", "Primary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_2", "Primary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_3", "Primary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_4", "Primary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_5", "Primary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_6", "Primary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("PRIMARY_TDM_TX_7", "Primary TDM7 Capture", + 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_0", "Secondary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_1", "Secondary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_2", "Secondary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_3", "Secondary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_4", "Secondary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_5", "Secondary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_6", "Secondary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_7", "Secondary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_0", "Secondary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_1", "Secondary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_2", "Secondary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_3", "Secondary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_4", "Secondary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_5", "Secondary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_6", "Secondary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_7", "Secondary TDM7 Capture", + 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_0", "Tertiary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_1", "Tertiary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_2", "Tertiary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_3", "Tertiary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_4", "Tertiary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_5", "Tertiary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_6", "Tertiary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_7", "Tertiary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_0", "Tertiary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_1", "Tertiary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_2", "Tertiary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_3", "Tertiary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_4", "Tertiary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_5", "Tertiary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_6", "Tertiary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_7", "Tertiary TDM7 Capture", + 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_0", "Quaternary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_1", "Quaternary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_2", "Quaternary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_3", "Quaternary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_4", "Quaternary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_5", "Quaternary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_6", "Quaternary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_7", "Quaternary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_0", "Quaternary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_1", "Quaternary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_2", "Quaternary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_3", "Quaternary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_4", "Quaternary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_5", "Quaternary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_6", "Quaternary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_7", "Quaternary TDM7 Capture", + 0, 0, 0, 0), + + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_0", "Quinary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_1", "Quinary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_2", "Quinary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_3", "Quinary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_4", "Quinary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_5", "Quinary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_6", "Quinary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_7", "Quinary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_0", "Quinary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_1", "Quinary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_2", "Quinary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_3", "Quinary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_4", "Quinary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_5", "Quinary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_6", "Quinary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_7", "Quinary TDM7 Capture", + 0, 0, 0, 0), +}; + +static const struct snd_soc_component_driver q6afe_dai_component = { + .name = "q6afe-dai-component", + .dapm_widgets = q6afe_dai_widgets, + .num_dapm_widgets = ARRAY_SIZE(q6afe_dai_widgets), + .dapm_routes = q6afe_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(q6afe_dapm_routes), + .of_xlate_dai_name = q6afe_of_xlate_dai_name, + +}; + +static void of_q6afe_parse_dai_data(struct device *dev, + struct q6afe_dai_data *data) +{ + struct device_node *node; + int ret; + + for_each_child_of_node(dev->of_node, node) { + unsigned int lines[Q6AFE_MAX_MI2S_LINES]; + struct q6afe_dai_priv_data *priv; + int id, i, num_lines; + + ret = of_property_read_u32(node, "reg", &id); + if (ret || id > AFE_PORT_MAX) { + dev_err(dev, "valid dai id not found:%d\n", ret); + continue; + } + + switch (id) { + /* MI2S specific properties */ + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + priv = &data->priv[id]; + ret = of_property_read_variable_u32_array(node, + "qcom,sd-lines", + lines, 0, + Q6AFE_MAX_MI2S_LINES); + if (ret < 0) + num_lines = 0; + else + num_lines = ret; + + priv->sd_line_mask = 0; + + for (i = 0; i < num_lines; i++) + priv->sd_line_mask |= BIT(lines[i]); + + break; + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + priv = &data->priv[id]; + ret = of_property_read_u32(node, "qcom,tdm-sync-mode", + &priv->sync_mode); + if (ret) { + dev_err(dev, "No Sync mode from DT\n"); + break; + } + ret = of_property_read_u32(node, "qcom,tdm-sync-src", + &priv->sync_src); + if (ret) { + dev_err(dev, "No Sync Src from DT\n"); + break; + } + ret = of_property_read_u32(node, "qcom,tdm-data-out", + &priv->data_out_enable); + if (ret) { + dev_err(dev, "No Data out enable from DT\n"); + break; + } + ret = of_property_read_u32(node, "qcom,tdm-invert-sync", + &priv->invert_sync); + if (ret) { + dev_err(dev, "No Invert sync from DT\n"); + break; + } + ret = of_property_read_u32(node, "qcom,tdm-data-delay", + &priv->data_delay); + if (ret) { + dev_err(dev, "No Data Delay from DT\n"); + break; + } + ret = of_property_read_u32(node, "qcom,tdm-data-align", + &priv->data_align); + if (ret) { + dev_err(dev, "No Data align from DT\n"); + break; + } + break; + default: + break; + } + } +} + +static int q6afe_dai_bind(struct device *dev, struct device *master, void *data) +{ + struct q6afe_dai_data *dai_data; + + dai_data = kzalloc(sizeof(*dai_data), GFP_KERNEL); + if (!dai_data) + return -ENOMEM; + + dev_set_drvdata(dev, dai_data); + + of_q6afe_parse_dai_data(dev, dai_data); + + return snd_soc_register_component(dev, &q6afe_dai_component, + q6afe_dais, ARRAY_SIZE(q6afe_dais)); +} + +static void q6afe_dai_unbind(struct device *dev, struct device *master, + void *data) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dev); + + snd_soc_unregister_component(dev); + kfree(dai_data); +} + +static const struct component_ops q6afe_dai_comp_ops = { + .bind = q6afe_dai_bind, + .unbind = q6afe_dai_unbind, +}; + +static int q6afe_dai_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &q6afe_dai_comp_ops); +} + +static int q6afe_dai_dev_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &q6afe_dai_comp_ops); + return 0; +} + +static struct platform_driver q6afe_dai_platform_driver = { + .driver = { + .name = "q6afe-dai", + }, + .probe = q6afe_dai_dev_probe, + .remove = q6afe_dai_dev_remove, +}; +module_platform_driver(q6afe_dai_platform_driver); + +MODULE_DESCRIPTION("Q6 Audio Fronend dai driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c new file mode 100644 index 000000000000..01f43218984b --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -0,0 +1,1495 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2018, Linaro Limited + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/uaccess.h> +#include <linux/wait.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/soc/qcom/apr.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include "q6dsp-errno.h" +#include "q6core.h" +#include "q6afe.h" + +/* AFE CMDs */ +#define AFE_PORT_CMD_DEVICE_START 0x000100E5 +#define AFE_PORT_CMD_DEVICE_STOP 0x000100E6 +#define AFE_PORT_CMD_SET_PARAM_V2 0x000100EF +#define AFE_SVC_CMD_SET_PARAM 0x000100f3 +#define AFE_PORT_CMDRSP_GET_PARAM_V2 0x00010106 +#define AFE_PARAM_ID_HDMI_CONFIG 0x00010210 +#define AFE_MODULE_AUDIO_DEV_INTERFACE 0x0001020C +#define AFE_MODULE_TDM 0x0001028A + +#define AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG 0x00010235 + +#define AFE_PARAM_ID_LPAIF_CLK_CONFIG 0x00010238 +#define AFE_PARAM_ID_INT_DIGITAL_CDC_CLK_CONFIG 0x00010239 + +#define AFE_PARAM_ID_SLIMBUS_CONFIG 0x00010212 +#define AFE_PARAM_ID_I2S_CONFIG 0x0001020D +#define AFE_PARAM_ID_TDM_CONFIG 0x0001029D +#define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG 0x00010297 + +/* I2S config specific */ +#define AFE_API_VERSION_I2S_CONFIG 0x1 +#define AFE_PORT_I2S_SD0 0x1 +#define AFE_PORT_I2S_SD1 0x2 +#define AFE_PORT_I2S_SD2 0x3 +#define AFE_PORT_I2S_SD3 0x4 +#define AFE_PORT_I2S_SD0_MASK BIT(0x1) +#define AFE_PORT_I2S_SD1_MASK BIT(0x2) +#define AFE_PORT_I2S_SD2_MASK BIT(0x3) +#define AFE_PORT_I2S_SD3_MASK BIT(0x4) +#define AFE_PORT_I2S_SD0_1_MASK GENMASK(2, 1) +#define AFE_PORT_I2S_SD2_3_MASK GENMASK(4, 3) +#define AFE_PORT_I2S_SD0_1_2_MASK GENMASK(3, 1) +#define AFE_PORT_I2S_SD0_1_2_3_MASK GENMASK(4, 1) +#define AFE_PORT_I2S_QUAD01 0x5 +#define AFE_PORT_I2S_QUAD23 0x6 +#define AFE_PORT_I2S_6CHS 0x7 +#define AFE_PORT_I2S_8CHS 0x8 +#define AFE_PORT_I2S_MONO 0x0 +#define AFE_PORT_I2S_STEREO 0x1 +#define AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL 0x0 +#define AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL 0x1 +#define AFE_LINEAR_PCM_DATA 0x0 + + +/* Port IDs */ +#define AFE_API_VERSION_HDMI_CONFIG 0x1 +#define AFE_PORT_ID_MULTICHAN_HDMI_RX 0x100E + +#define AFE_API_VERSION_SLIMBUS_CONFIG 0x1 +/* Clock set API version */ +#define AFE_API_VERSION_CLOCK_SET 1 +#define Q6AFE_LPASS_CLK_CONFIG_API_VERSION 0x1 +#define AFE_MODULE_CLOCK_SET 0x0001028F +#define AFE_PARAM_ID_CLOCK_SET 0x00010290 + +/* SLIMbus Rx port on channel 0. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX 0x4000 +/* SLIMbus Tx port on channel 0. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX 0x4001 +/* SLIMbus Rx port on channel 1. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX 0x4002 +/* SLIMbus Tx port on channel 1. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX 0x4003 +/* SLIMbus Rx port on channel 2. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX 0x4004 +/* SLIMbus Tx port on channel 2. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX 0x4005 +/* SLIMbus Rx port on channel 3. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX 0x4006 +/* SLIMbus Tx port on channel 3. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX 0x4007 +/* SLIMbus Rx port on channel 4. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX 0x4008 +/* SLIMbus Tx port on channel 4. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX 0x4009 +/* SLIMbus Rx port on channel 5. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX 0x400a +/* SLIMbus Tx port on channel 5. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX 0x400b +/* SLIMbus Rx port on channel 6. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX 0x400c +/* SLIMbus Tx port on channel 6. */ +#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX 0x400d +#define AFE_PORT_ID_PRIMARY_MI2S_RX 0x1000 +#define AFE_PORT_ID_PRIMARY_MI2S_TX 0x1001 +#define AFE_PORT_ID_SECONDARY_MI2S_RX 0x1002 +#define AFE_PORT_ID_SECONDARY_MI2S_TX 0x1003 +#define AFE_PORT_ID_TERTIARY_MI2S_RX 0x1004 +#define AFE_PORT_ID_TERTIARY_MI2S_TX 0x1005 +#define AFE_PORT_ID_QUATERNARY_MI2S_RX 0x1006 +#define AFE_PORT_ID_QUATERNARY_MI2S_TX 0x1007 + +/* Start of the range of port IDs for TDM devices. */ +#define AFE_PORT_ID_TDM_PORT_RANGE_START 0x9000 + +/* End of the range of port IDs for TDM devices. */ +#define AFE_PORT_ID_TDM_PORT_RANGE_END \ + (AFE_PORT_ID_TDM_PORT_RANGE_START+0x50-1) + +/* Size of the range of port IDs for TDM ports. */ +#define AFE_PORT_ID_TDM_PORT_RANGE_SIZE \ + (AFE_PORT_ID_TDM_PORT_RANGE_END - \ + AFE_PORT_ID_TDM_PORT_RANGE_START+1) + +#define AFE_PORT_ID_PRIMARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x00) +#define AFE_PORT_ID_PRIMARY_TDM_RX_1 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x02) +#define AFE_PORT_ID_PRIMARY_TDM_RX_2 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x04) +#define AFE_PORT_ID_PRIMARY_TDM_RX_3 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x06) +#define AFE_PORT_ID_PRIMARY_TDM_RX_4 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x08) +#define AFE_PORT_ID_PRIMARY_TDM_RX_5 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_PRIMARY_TDM_RX_6 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_PRIMARY_TDM_RX_7 \ + (AFE_PORT_ID_PRIMARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_PRIMARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x01) +#define AFE_PORT_ID_PRIMARY_TDM_TX_1 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x02) +#define AFE_PORT_ID_PRIMARY_TDM_TX_2 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x04) +#define AFE_PORT_ID_PRIMARY_TDM_TX_3 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x06) +#define AFE_PORT_ID_PRIMARY_TDM_TX_4 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x08) +#define AFE_PORT_ID_PRIMARY_TDM_TX_5 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_PRIMARY_TDM_TX_6 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_PRIMARY_TDM_TX_7 \ + (AFE_PORT_ID_PRIMARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_SECONDARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x10) +#define AFE_PORT_ID_SECONDARY_TDM_RX_1 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x02) +#define AFE_PORT_ID_SECONDARY_TDM_RX_2 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x04) +#define AFE_PORT_ID_SECONDARY_TDM_RX_3 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x06) +#define AFE_PORT_ID_SECONDARY_TDM_RX_4 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x08) +#define AFE_PORT_ID_SECONDARY_TDM_RX_5 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_SECONDARY_TDM_RX_6 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_SECONDARY_TDM_RX_7 \ + (AFE_PORT_ID_SECONDARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_SECONDARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x11) +#define AFE_PORT_ID_SECONDARY_TDM_TX_1 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x02) +#define AFE_PORT_ID_SECONDARY_TDM_TX_2 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x04) +#define AFE_PORT_ID_SECONDARY_TDM_TX_3 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x06) +#define AFE_PORT_ID_SECONDARY_TDM_TX_4 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x08) +#define AFE_PORT_ID_SECONDARY_TDM_TX_5 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_SECONDARY_TDM_TX_6 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_SECONDARY_TDM_TX_7 \ + (AFE_PORT_ID_SECONDARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_TERTIARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x20) +#define AFE_PORT_ID_TERTIARY_TDM_RX_1 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x02) +#define AFE_PORT_ID_TERTIARY_TDM_RX_2 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x04) +#define AFE_PORT_ID_TERTIARY_TDM_RX_3 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x06) +#define AFE_PORT_ID_TERTIARY_TDM_RX_4 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x08) +#define AFE_PORT_ID_TERTIARY_TDM_RX_5 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_TERTIARY_TDM_RX_6 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_TERTIARY_TDM_RX_7 \ + (AFE_PORT_ID_TERTIARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_TERTIARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x21) +#define AFE_PORT_ID_TERTIARY_TDM_TX_1 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x02) +#define AFE_PORT_ID_TERTIARY_TDM_TX_2 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x04) +#define AFE_PORT_ID_TERTIARY_TDM_TX_3 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x06) +#define AFE_PORT_ID_TERTIARY_TDM_TX_4 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x08) +#define AFE_PORT_ID_TERTIARY_TDM_TX_5 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_TERTIARY_TDM_TX_6 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_TERTIARY_TDM_TX_7 \ + (AFE_PORT_ID_TERTIARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_QUATERNARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x30) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_1 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x02) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_2 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x04) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_3 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x06) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_4 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x08) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_5 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_6 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_QUATERNARY_TDM_RX_7 \ + (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_QUATERNARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x31) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_1 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x02) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_2 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x04) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_3 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x06) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_4 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x08) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_5 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_6 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_QUATERNARY_TDM_TX_7 \ + (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0E) + +#define AFE_PORT_ID_QUINARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x40) +#define AFE_PORT_ID_QUINARY_TDM_RX_1 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x02) +#define AFE_PORT_ID_QUINARY_TDM_RX_2 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x04) +#define AFE_PORT_ID_QUINARY_TDM_RX_3 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x06) +#define AFE_PORT_ID_QUINARY_TDM_RX_4 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x08) +#define AFE_PORT_ID_QUINARY_TDM_RX_5 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_QUINARY_TDM_RX_6 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_QUINARY_TDM_RX_7 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_QUINARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x41) +#define AFE_PORT_ID_QUINARY_TDM_TX_1 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x02) +#define AFE_PORT_ID_QUINARY_TDM_TX_2 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x04) +#define AFE_PORT_ID_QUINARY_TDM_TX_3 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x06) +#define AFE_PORT_ID_QUINARY_TDM_TX_4 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x08) +#define AFE_PORT_ID_QUINARY_TDM_TX_5 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_QUINARY_TDM_TX_6 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_QUINARY_TDM_TX_7 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x0E) + +#define Q6AFE_LPASS_MODE_CLK1_VALID 1 +#define Q6AFE_LPASS_MODE_CLK2_VALID 2 +#define Q6AFE_LPASS_CLK_SRC_INTERNAL 1 +#define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0 +#define AFE_API_VERSION_TDM_CONFIG 1 +#define AFE_API_VERSION_SLOT_MAPPING_CONFIG 1 + +#define TIMEOUT_MS 1000 +#define AFE_CMD_RESP_AVAIL 0 +#define AFE_CMD_RESP_NONE 1 + +struct q6afe { + struct apr_device *apr; + struct device *dev; + struct q6core_svc_api_info ainfo; + struct mutex lock; + struct list_head port_list; + spinlock_t port_list_lock; + struct platform_device *pdev_dais; +}; + +struct afe_port_cmd_device_start { + u16 port_id; + u16 reserved; +} __packed; + +struct afe_port_cmd_device_stop { + u16 port_id; + u16 reserved; +/* Reserved for 32-bit alignment. This field must be set to 0.*/ +} __packed; + +struct afe_port_param_data_v2 { + u32 module_id; + u32 param_id; + u16 param_size; + u16 reserved; +} __packed; + +struct afe_svc_cmd_set_param { + uint32_t payload_size; + uint32_t payload_address_lsw; + uint32_t payload_address_msw; + uint32_t mem_map_handle; +} __packed; + +struct afe_port_cmd_set_param_v2 { + u16 port_id; + u16 payload_size; + u32 payload_address_lsw; + u32 payload_address_msw; + u32 mem_map_handle; +} __packed; + +struct afe_param_id_hdmi_multi_chan_audio_cfg { + u32 hdmi_cfg_minor_version; + u16 datatype; + u16 channel_allocation; + u32 sample_rate; + u16 bit_width; + u16 reserved; +} __packed; + +struct afe_param_id_slimbus_cfg { + u32 sb_cfg_minor_version; +/* Minor version used for tracking the version of the SLIMBUS + * configuration interface. + * Supported values: #AFE_API_VERSION_SLIMBUS_CONFIG + */ + + u16 slimbus_dev_id; +/* SLIMbus hardware device ID, which is required to handle + * multiple SLIMbus hardware blocks. + * Supported values: - #AFE_SLIMBUS_DEVICE_1 - #AFE_SLIMBUS_DEVICE_2 + */ + u16 bit_width; +/* Bit width of the sample. + * Supported values: 16, 24 + */ + u16 data_format; +/* Data format supported by the SLIMbus hardware. The default is + * 0 (#AFE_SB_DATA_FORMAT_NOT_INDICATED), which indicates the + * hardware does not perform any format conversions before the data + * transfer. + */ + u16 num_channels; +/* Number of channels. + * Supported values: 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT + */ + u8 shared_ch_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT]; +/* Mapping of shared channel IDs (128 to 255) to which the + * master port is to be connected. + * Shared_channel_mapping[i] represents the shared channel assigned + * for audio channel i in multichannel audio data. + */ + u32 sample_rate; +/* Sampling rate of the port. + * Supported values: + * - #AFE_PORT_SAMPLE_RATE_8K + * - #AFE_PORT_SAMPLE_RATE_16K + * - #AFE_PORT_SAMPLE_RATE_48K + * - #AFE_PORT_SAMPLE_RATE_96K + * - #AFE_PORT_SAMPLE_RATE_192K + */ +} __packed; + +struct afe_clk_cfg { + u32 i2s_cfg_minor_version; + u32 clk_val1; + u32 clk_val2; + u16 clk_src; + u16 clk_root; + u16 clk_set_mode; + u16 reserved; +} __packed; + +struct afe_digital_clk_cfg { + u32 i2s_cfg_minor_version; + u32 clk_val; + u16 clk_root; + u16 reserved; +} __packed; + +struct afe_param_id_i2s_cfg { + u32 i2s_cfg_minor_version; + u16 bit_width; + u16 channel_mode; + u16 mono_stereo; + u16 ws_src; + u32 sample_rate; + u16 data_format; + u16 reserved; +} __packed; + +struct afe_param_id_tdm_cfg { + u32 tdm_cfg_minor_version; + u32 num_channels; + u32 sample_rate; + u32 bit_width; + u16 data_format; + u16 sync_mode; + u16 sync_src; + u16 nslots_per_frame; + u16 ctrl_data_out_enable; + u16 ctrl_invert_sync_pulse; + u16 ctrl_sync_data_delay; + u16 slot_width; + u32 slot_mask; +} __packed; + +union afe_port_config { + struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch; + struct afe_param_id_slimbus_cfg slim_cfg; + struct afe_param_id_i2s_cfg i2s_cfg; + struct afe_param_id_tdm_cfg tdm_cfg; +} __packed; + + +struct afe_clk_set { + uint32_t clk_set_minor_version; + uint32_t clk_id; + uint32_t clk_freq_in_hz; + uint16_t clk_attri; + uint16_t clk_root; + uint32_t enable; +}; + +struct afe_param_id_slot_mapping_cfg { + u32 minor_version; + u16 num_channels; + u16 bitwidth; + u32 data_align_type; + u16 ch_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT]; +} __packed; + +struct q6afe_port { + wait_queue_head_t wait; + union afe_port_config port_cfg; + struct afe_param_id_slot_mapping_cfg *scfg; + struct aprv2_ibasic_rsp_result_t result; + int token; + int id; + int cfg_type; + struct q6afe *afe; + struct kref refcount; + struct list_head node; +}; + +struct afe_port_map { + int port_id; + int token; + int is_rx; + int is_dig_pcm; +}; + +/* + * Mapping between Virtual Port IDs to DSP AFE Port ID + * On B Family SoCs DSP Port IDs are consistent across multiple SoCs + * on A Family SoCs DSP port IDs are same as virtual Port IDs. + */ + +static struct afe_port_map port_maps[AFE_PORT_MAX] = { + [HDMI_RX] = { AFE_PORT_ID_MULTICHAN_HDMI_RX, HDMI_RX, 1, 1}, + [SLIMBUS_0_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX, + SLIMBUS_0_RX, 1, 1}, + [SLIMBUS_1_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX, + SLIMBUS_1_RX, 1, 1}, + [SLIMBUS_2_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX, + SLIMBUS_2_RX, 1, 1}, + [SLIMBUS_3_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX, + SLIMBUS_3_RX, 1, 1}, + [SLIMBUS_4_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX, + SLIMBUS_4_RX, 1, 1}, + [SLIMBUS_5_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX, + SLIMBUS_5_RX, 1, 1}, + [SLIMBUS_6_RX] = { AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX, + SLIMBUS_6_RX, 1, 1}, + [PRIMARY_MI2S_RX] = { AFE_PORT_ID_PRIMARY_MI2S_RX, + PRIMARY_MI2S_RX, 1, 1}, + [PRIMARY_MI2S_TX] = { AFE_PORT_ID_PRIMARY_MI2S_TX, + PRIMARY_MI2S_RX, 0, 1}, + [SECONDARY_MI2S_RX] = { AFE_PORT_ID_SECONDARY_MI2S_RX, + SECONDARY_MI2S_RX, 1, 1}, + [SECONDARY_MI2S_TX] = { AFE_PORT_ID_SECONDARY_MI2S_TX, + SECONDARY_MI2S_TX, 0, 1}, + [TERTIARY_MI2S_RX] = { AFE_PORT_ID_TERTIARY_MI2S_RX, + TERTIARY_MI2S_RX, 1, 1}, + [TERTIARY_MI2S_TX] = { AFE_PORT_ID_TERTIARY_MI2S_TX, + TERTIARY_MI2S_TX, 0, 1}, + [QUATERNARY_MI2S_RX] = { AFE_PORT_ID_QUATERNARY_MI2S_RX, + QUATERNARY_MI2S_RX, 1, 1}, + [QUATERNARY_MI2S_TX] = { AFE_PORT_ID_QUATERNARY_MI2S_TX, + QUATERNARY_MI2S_TX, 0, 1}, + [PRIMARY_TDM_RX_0] = { AFE_PORT_ID_PRIMARY_TDM_RX, + PRIMARY_TDM_RX_0, 1, 1}, + [PRIMARY_TDM_TX_0] = { AFE_PORT_ID_PRIMARY_TDM_TX, + PRIMARY_TDM_TX_0, 0, 1}, + [PRIMARY_TDM_RX_1] = { AFE_PORT_ID_PRIMARY_TDM_RX_1, + PRIMARY_TDM_RX_1, 1, 1}, + [PRIMARY_TDM_TX_1] = { AFE_PORT_ID_PRIMARY_TDM_TX_1, + PRIMARY_TDM_TX_1, 0, 1}, + [PRIMARY_TDM_RX_2] = { AFE_PORT_ID_PRIMARY_TDM_RX_2, + PRIMARY_TDM_RX_2, 1, 1}, + [PRIMARY_TDM_TX_2] = { AFE_PORT_ID_PRIMARY_TDM_TX_2, + PRIMARY_TDM_TX_2, 0, 1}, + [PRIMARY_TDM_RX_3] = { AFE_PORT_ID_PRIMARY_TDM_RX_3, + PRIMARY_TDM_RX_3, 1, 1}, + [PRIMARY_TDM_TX_3] = { AFE_PORT_ID_PRIMARY_TDM_TX_3, + PRIMARY_TDM_TX_3, 0, 1}, + [PRIMARY_TDM_RX_4] = { AFE_PORT_ID_PRIMARY_TDM_RX_4, + PRIMARY_TDM_RX_4, 1, 1}, + [PRIMARY_TDM_TX_4] = { AFE_PORT_ID_PRIMARY_TDM_TX_4, + PRIMARY_TDM_TX_4, 0, 1}, + [PRIMARY_TDM_RX_5] = { AFE_PORT_ID_PRIMARY_TDM_RX_5, + PRIMARY_TDM_RX_5, 1, 1}, + [PRIMARY_TDM_TX_5] = { AFE_PORT_ID_PRIMARY_TDM_TX_5, + PRIMARY_TDM_TX_5, 0, 1}, + [PRIMARY_TDM_RX_6] = { AFE_PORT_ID_PRIMARY_TDM_RX_6, + PRIMARY_TDM_RX_6, 1, 1}, + [PRIMARY_TDM_TX_6] = { AFE_PORT_ID_PRIMARY_TDM_TX_6, + PRIMARY_TDM_TX_6, 0, 1}, + [PRIMARY_TDM_RX_7] = { AFE_PORT_ID_PRIMARY_TDM_RX_7, + PRIMARY_TDM_RX_7, 1, 1}, + [PRIMARY_TDM_TX_7] = { AFE_PORT_ID_PRIMARY_TDM_TX_7, + PRIMARY_TDM_TX_7, 0, 1}, + [SECONDARY_TDM_RX_0] = { AFE_PORT_ID_SECONDARY_TDM_RX, + SECONDARY_TDM_RX_0, 1, 1}, + [SECONDARY_TDM_TX_0] = { AFE_PORT_ID_SECONDARY_TDM_TX, + SECONDARY_TDM_TX_0, 0, 1}, + [SECONDARY_TDM_RX_1] = { AFE_PORT_ID_SECONDARY_TDM_RX_1, + SECONDARY_TDM_RX_1, 1, 1}, + [SECONDARY_TDM_TX_1] = { AFE_PORT_ID_SECONDARY_TDM_TX_1, + SECONDARY_TDM_TX_1, 0, 1}, + [SECONDARY_TDM_RX_2] = { AFE_PORT_ID_SECONDARY_TDM_RX_2, + SECONDARY_TDM_RX_2, 1, 1}, + [SECONDARY_TDM_TX_2] = { AFE_PORT_ID_SECONDARY_TDM_TX_2, + SECONDARY_TDM_TX_2, 0, 1}, + [SECONDARY_TDM_RX_3] = { AFE_PORT_ID_SECONDARY_TDM_RX_3, + SECONDARY_TDM_RX_3, 1, 1}, + [SECONDARY_TDM_TX_3] = { AFE_PORT_ID_SECONDARY_TDM_TX_3, + SECONDARY_TDM_TX_3, 0, 1}, + [SECONDARY_TDM_RX_4] = { AFE_PORT_ID_SECONDARY_TDM_RX_4, + SECONDARY_TDM_RX_4, 1, 1}, + [SECONDARY_TDM_TX_4] = { AFE_PORT_ID_SECONDARY_TDM_TX_4, + SECONDARY_TDM_TX_4, 0, 1}, + [SECONDARY_TDM_RX_5] = { AFE_PORT_ID_SECONDARY_TDM_RX_5, + SECONDARY_TDM_RX_5, 1, 1}, + [SECONDARY_TDM_TX_5] = { AFE_PORT_ID_SECONDARY_TDM_TX_5, + SECONDARY_TDM_TX_5, 0, 1}, + [SECONDARY_TDM_RX_6] = { AFE_PORT_ID_SECONDARY_TDM_RX_6, + SECONDARY_TDM_RX_6, 1, 1}, + [SECONDARY_TDM_TX_6] = { AFE_PORT_ID_SECONDARY_TDM_TX_6, + SECONDARY_TDM_TX_6, 0, 1}, + [SECONDARY_TDM_RX_7] = { AFE_PORT_ID_SECONDARY_TDM_RX_7, + SECONDARY_TDM_RX_7, 1, 1}, + [SECONDARY_TDM_TX_7] = { AFE_PORT_ID_SECONDARY_TDM_TX_7, + SECONDARY_TDM_TX_7, 0, 1}, + [TERTIARY_TDM_RX_0] = { AFE_PORT_ID_TERTIARY_TDM_RX, + TERTIARY_TDM_RX_0, 1, 1}, + [TERTIARY_TDM_TX_0] = { AFE_PORT_ID_TERTIARY_TDM_TX, + TERTIARY_TDM_TX_0, 0, 1}, + [TERTIARY_TDM_RX_1] = { AFE_PORT_ID_TERTIARY_TDM_RX_1, + TERTIARY_TDM_RX_1, 1, 1}, + [TERTIARY_TDM_TX_1] = { AFE_PORT_ID_TERTIARY_TDM_TX_1, + TERTIARY_TDM_TX_1, 0, 1}, + [TERTIARY_TDM_RX_2] = { AFE_PORT_ID_TERTIARY_TDM_RX_2, + TERTIARY_TDM_RX_2, 1, 1}, + [TERTIARY_TDM_TX_2] = { AFE_PORT_ID_TERTIARY_TDM_TX_2, + TERTIARY_TDM_TX_2, 0, 1}, + [TERTIARY_TDM_RX_3] = { AFE_PORT_ID_TERTIARY_TDM_RX_3, + TERTIARY_TDM_RX_3, 1, 1}, + [TERTIARY_TDM_TX_3] = { AFE_PORT_ID_TERTIARY_TDM_TX_3, + TERTIARY_TDM_TX_3, 0, 1}, + [TERTIARY_TDM_RX_4] = { AFE_PORT_ID_TERTIARY_TDM_RX_4, + TERTIARY_TDM_RX_4, 1, 1}, + [TERTIARY_TDM_TX_4] = { AFE_PORT_ID_TERTIARY_TDM_TX_4, + TERTIARY_TDM_TX_4, 0, 1}, + [TERTIARY_TDM_RX_5] = { AFE_PORT_ID_TERTIARY_TDM_RX_5, + TERTIARY_TDM_RX_5, 1, 1}, + [TERTIARY_TDM_TX_5] = { AFE_PORT_ID_TERTIARY_TDM_TX_5, + TERTIARY_TDM_TX_5, 0, 1}, + [TERTIARY_TDM_RX_6] = { AFE_PORT_ID_TERTIARY_TDM_RX_6, + TERTIARY_TDM_RX_6, 1, 1}, + [TERTIARY_TDM_TX_6] = { AFE_PORT_ID_TERTIARY_TDM_TX_6, + TERTIARY_TDM_TX_6, 0, 1}, + [TERTIARY_TDM_RX_7] = { AFE_PORT_ID_TERTIARY_TDM_RX_7, + TERTIARY_TDM_RX_7, 1, 1}, + [TERTIARY_TDM_TX_7] = { AFE_PORT_ID_TERTIARY_TDM_TX_7, + TERTIARY_TDM_TX_7, 0, 1}, + [QUATERNARY_TDM_RX_0] = { AFE_PORT_ID_QUATERNARY_TDM_RX, + QUATERNARY_TDM_RX_0, 1, 1}, + [QUATERNARY_TDM_TX_0] = { AFE_PORT_ID_QUATERNARY_TDM_TX, + QUATERNARY_TDM_TX_0, 0, 1}, + [QUATERNARY_TDM_RX_1] = { AFE_PORT_ID_QUATERNARY_TDM_RX_1, + QUATERNARY_TDM_RX_1, 1, 1}, + [QUATERNARY_TDM_TX_1] = { AFE_PORT_ID_QUATERNARY_TDM_TX_1, + QUATERNARY_TDM_TX_1, 0, 1}, + [QUATERNARY_TDM_RX_2] = { AFE_PORT_ID_QUATERNARY_TDM_RX_2, + QUATERNARY_TDM_RX_2, 1, 1}, + [QUATERNARY_TDM_TX_2] = { AFE_PORT_ID_QUATERNARY_TDM_TX_2, + QUATERNARY_TDM_TX_2, 0, 1}, + [QUATERNARY_TDM_RX_3] = { AFE_PORT_ID_QUATERNARY_TDM_RX_3, + QUATERNARY_TDM_RX_3, 1, 1}, + [QUATERNARY_TDM_TX_3] = { AFE_PORT_ID_QUATERNARY_TDM_TX_3, + QUATERNARY_TDM_TX_3, 0, 1}, + [QUATERNARY_TDM_RX_4] = { AFE_PORT_ID_QUATERNARY_TDM_RX_4, + QUATERNARY_TDM_RX_4, 1, 1}, + [QUATERNARY_TDM_TX_4] = { AFE_PORT_ID_QUATERNARY_TDM_TX_4, + QUATERNARY_TDM_TX_4, 0, 1}, + [QUATERNARY_TDM_RX_5] = { AFE_PORT_ID_QUATERNARY_TDM_RX_5, + QUATERNARY_TDM_RX_5, 1, 1}, + [QUATERNARY_TDM_TX_5] = { AFE_PORT_ID_QUATERNARY_TDM_TX_5, + QUATERNARY_TDM_TX_5, 0, 1}, + [QUATERNARY_TDM_RX_6] = { AFE_PORT_ID_QUATERNARY_TDM_RX_6, + QUATERNARY_TDM_RX_6, 1, 1}, + [QUATERNARY_TDM_TX_6] = { AFE_PORT_ID_QUATERNARY_TDM_TX_6, + QUATERNARY_TDM_TX_6, 0, 1}, + [QUATERNARY_TDM_RX_7] = { AFE_PORT_ID_QUATERNARY_TDM_RX_7, + QUATERNARY_TDM_RX_7, 1, 1}, + [QUATERNARY_TDM_TX_7] = { AFE_PORT_ID_QUATERNARY_TDM_TX_7, + QUATERNARY_TDM_TX_7, 0, 1}, + [QUINARY_TDM_RX_0] = { AFE_PORT_ID_QUINARY_TDM_RX, + QUINARY_TDM_RX_0, 1, 1}, + [QUINARY_TDM_TX_0] = { AFE_PORT_ID_QUINARY_TDM_TX, + QUINARY_TDM_TX_0, 0, 1}, + [QUINARY_TDM_RX_1] = { AFE_PORT_ID_QUINARY_TDM_RX_1, + QUINARY_TDM_RX_1, 1, 1}, + [QUINARY_TDM_TX_1] = { AFE_PORT_ID_QUINARY_TDM_TX_1, + QUINARY_TDM_TX_1, 0, 1}, + [QUINARY_TDM_RX_2] = { AFE_PORT_ID_QUINARY_TDM_RX_2, + QUINARY_TDM_RX_2, 1, 1}, + [QUINARY_TDM_TX_2] = { AFE_PORT_ID_QUINARY_TDM_TX_2, + QUINARY_TDM_TX_2, 0, 1}, + [QUINARY_TDM_RX_3] = { AFE_PORT_ID_QUINARY_TDM_RX_3, + QUINARY_TDM_RX_3, 1, 1}, + [QUINARY_TDM_TX_3] = { AFE_PORT_ID_QUINARY_TDM_TX_3, + QUINARY_TDM_TX_3, 0, 1}, + [QUINARY_TDM_RX_4] = { AFE_PORT_ID_QUINARY_TDM_RX_4, + QUINARY_TDM_RX_4, 1, 1}, + [QUINARY_TDM_TX_4] = { AFE_PORT_ID_QUINARY_TDM_TX_4, + QUINARY_TDM_TX_4, 0, 1}, + [QUINARY_TDM_RX_5] = { AFE_PORT_ID_QUINARY_TDM_RX_5, + QUINARY_TDM_RX_5, 1, 1}, + [QUINARY_TDM_TX_5] = { AFE_PORT_ID_QUINARY_TDM_TX_5, + QUINARY_TDM_TX_5, 0, 1}, + [QUINARY_TDM_RX_6] = { AFE_PORT_ID_QUINARY_TDM_RX_6, + QUINARY_TDM_RX_6, 1, 1}, + [QUINARY_TDM_TX_6] = { AFE_PORT_ID_QUINARY_TDM_TX_6, + QUINARY_TDM_TX_6, 0, 1}, + [QUINARY_TDM_RX_7] = { AFE_PORT_ID_QUINARY_TDM_RX_7, + QUINARY_TDM_RX_7, 1, 1}, + [QUINARY_TDM_TX_7] = { AFE_PORT_ID_QUINARY_TDM_TX_7, + QUINARY_TDM_TX_7, 0, 1}, +}; + +static void q6afe_port_free(struct kref *ref) +{ + struct q6afe_port *port; + struct q6afe *afe; + unsigned long flags; + + port = container_of(ref, struct q6afe_port, refcount); + afe = port->afe; + spin_lock_irqsave(&afe->port_list_lock, flags); + list_del(&port->node); + spin_unlock_irqrestore(&afe->port_list_lock, flags); + kfree(port->scfg); + kfree(port); +} + +static struct q6afe_port *q6afe_find_port(struct q6afe *afe, int token) +{ + struct q6afe_port *p = NULL; + struct q6afe_port *ret = NULL; + unsigned long flags; + + spin_lock_irqsave(&afe->port_list_lock, flags); + list_for_each_entry(p, &afe->port_list, node) + if (p->token == token) { + ret = p; + kref_get(&p->refcount); + break; + } + + spin_unlock_irqrestore(&afe->port_list_lock, flags); + return ret; +} + +static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data) +{ + struct q6afe *afe = dev_get_drvdata(&adev->dev); + struct aprv2_ibasic_rsp_result_t *res; + struct apr_hdr *hdr = &data->hdr; + struct q6afe_port *port; + + if (!data->payload_size) + return 0; + + res = data->payload; + switch (hdr->opcode) { + case APR_BASIC_RSP_RESULT: { + if (res->status) { + dev_err(afe->dev, "cmd = 0x%x returned error = 0x%x\n", + res->opcode, res->status); + } + switch (res->opcode) { + case AFE_PORT_CMD_SET_PARAM_V2: + case AFE_PORT_CMD_DEVICE_STOP: + case AFE_PORT_CMD_DEVICE_START: + case AFE_SVC_CMD_SET_PARAM: + port = q6afe_find_port(afe, hdr->token); + if (port) { + port->result = *res; + wake_up(&port->wait); + kref_put(&port->refcount, q6afe_port_free); + } + break; + default: + dev_err(afe->dev, "Unknown cmd 0x%x\n", res->opcode); + break; + } + } + break; + default: + break; + } + + return 0; +} + +/** + * q6afe_get_port_id() - Get port id from a given port index + * + * @index: port index + * + * Return: Will be an negative on error or valid port_id on success + */ +int q6afe_get_port_id(int index) +{ + if (index < 0 || index > AFE_PORT_MAX) + return -EINVAL; + + return port_maps[index].port_id; +} +EXPORT_SYMBOL_GPL(q6afe_get_port_id); + +static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt, + struct q6afe_port *port) +{ + wait_queue_head_t *wait = &port->wait; + struct apr_hdr *hdr = &pkt->hdr; + int ret; + + mutex_lock(&afe->lock); + port->result.opcode = 0; + port->result.status = 0; + + ret = apr_send_pkt(afe->apr, pkt); + if (ret < 0) { + dev_err(afe->dev, "packet not transmitted (%d)\n", ret); + ret = -EINVAL; + goto err; + } + + ret = wait_event_timeout(*wait, (port->result.opcode == hdr->opcode), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + ret = -ETIMEDOUT; + } else if (port->result.status > 0) { + dev_err(afe->dev, "DSP returned error[%x]\n", + port->result.status); + ret = -EINVAL; + } else { + ret = 0; + } + +err: + mutex_unlock(&afe->lock); + + return ret; +} + +static int q6afe_port_set_param(struct q6afe_port *port, void *data, + int param_id, int module_id, int psize) +{ + struct afe_svc_cmd_set_param *param; + struct afe_port_param_data_v2 *pdata; + struct q6afe *afe = port->afe; + struct apr_pkt *pkt; + u16 port_id = port->id; + int ret, pkt_size; + void *p, *pl; + + pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize; + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + param = p + APR_HDR_SIZE; + pdata = p + APR_HDR_SIZE + sizeof(*param); + pl = p + APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata); + memcpy(pl, data, psize); + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = port->token; + pkt->hdr.opcode = AFE_SVC_CMD_SET_PARAM; + + param->payload_size = sizeof(*pdata) + psize; + param->payload_address_lsw = 0x00; + param->payload_address_msw = 0x00; + param->mem_map_handle = 0x00; + pdata->module_id = module_id; + pdata->param_id = param_id; + pdata->param_size = psize; + + ret = afe_apr_send_pkt(afe, pkt, port); + if (ret) + dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", + port_id, ret); + + kfree(pkt); + return ret; +} + +static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, + int param_id, int module_id, int psize) +{ + struct afe_port_cmd_set_param_v2 *param; + struct afe_port_param_data_v2 *pdata; + struct q6afe *afe = port->afe; + struct apr_pkt *pkt; + u16 port_id = port->id; + int ret, pkt_size; + void *p, *pl; + + pkt_size = APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata) + psize; + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + param = p + APR_HDR_SIZE; + pdata = p + APR_HDR_SIZE + sizeof(*param); + pl = p + APR_HDR_SIZE + sizeof(*param) + sizeof(*pdata); + memcpy(pl, data, psize); + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = port->token; + pkt->hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + + param->port_id = port_id; + param->payload_size = sizeof(*pdata) + psize; + param->payload_address_lsw = 0x00; + param->payload_address_msw = 0x00; + param->mem_map_handle = 0x00; + pdata->module_id = module_id; + pdata->param_id = param_id; + pdata->param_size = psize; + + ret = afe_apr_send_pkt(afe, pkt, port); + if (ret) + dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", + port_id, ret); + + kfree(pkt); + return ret; +} + +static int q6afe_set_lpass_clock(struct q6afe_port *port, + struct afe_clk_cfg *cfg) +{ + return q6afe_port_set_param_v2(port, cfg, + AFE_PARAM_ID_LPAIF_CLK_CONFIG, + AFE_MODULE_AUDIO_DEV_INTERFACE, + sizeof(*cfg)); +} + +static int q6afe_set_lpass_clock_v2(struct q6afe_port *port, + struct afe_clk_set *cfg) +{ + return q6afe_port_set_param(port, cfg, AFE_PARAM_ID_CLOCK_SET, + AFE_MODULE_CLOCK_SET, sizeof(*cfg)); +} + +static int q6afe_set_digital_codec_core_clock(struct q6afe_port *port, + struct afe_digital_clk_cfg *cfg) +{ + return q6afe_port_set_param_v2(port, cfg, + AFE_PARAM_ID_INT_DIGITAL_CDC_CLK_CONFIG, + AFE_MODULE_AUDIO_DEV_INTERFACE, + sizeof(*cfg)); +} + +int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, + int clk_src, int clk_root, + unsigned int freq, int dir) +{ + struct afe_clk_cfg ccfg = {0,}; + struct afe_clk_set cset = {0,}; + struct afe_digital_clk_cfg dcfg = {0,}; + int ret; + + switch (clk_id) { + case LPAIF_DIG_CLK: + dcfg.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG; + dcfg.clk_val = freq; + dcfg.clk_root = clk_root; + ret = q6afe_set_digital_codec_core_clock(port, &dcfg); + break; + case LPAIF_BIT_CLK: + ccfg.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG; + ccfg.clk_val1 = freq; + ccfg.clk_src = clk_src; + ccfg.clk_root = clk_root; + ccfg.clk_set_mode = Q6AFE_LPASS_MODE_CLK1_VALID; + ret = q6afe_set_lpass_clock(port, &ccfg); + break; + + case LPAIF_OSR_CLK: + ccfg.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG; + ccfg.clk_val2 = freq; + ccfg.clk_src = clk_src; + ccfg.clk_root = clk_root; + ccfg.clk_set_mode = Q6AFE_LPASS_MODE_CLK2_VALID; + ret = q6afe_set_lpass_clock(port, &ccfg); + break; + case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR: + case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1: + case Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT ... Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT: + cset.clk_set_minor_version = AFE_API_VERSION_CLOCK_SET; + cset.clk_id = clk_id; + cset.clk_freq_in_hz = freq; + cset.clk_attri = clk_src; + cset.clk_root = clk_root; + cset.enable = !!freq; + ret = q6afe_set_lpass_clock_v2(port, &cset); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(q6afe_port_set_sysclk); + +/** + * q6afe_port_stop() - Stop a afe port + * + * @port: Instance of port to stop + * + * Return: Will be an negative on packet size on success. + */ +int q6afe_port_stop(struct q6afe_port *port) +{ + struct afe_port_cmd_device_stop *stop; + struct q6afe *afe = port->afe; + struct apr_pkt *pkt; + int port_id = port->id; + int ret = 0; + int index, pkt_size; + void *p; + + port_id = port->id; + index = port->token; + if (index < 0 || index > AFE_PORT_MAX) { + dev_err(afe->dev, "AFE port index[%d] invalid!\n", index); + return -EINVAL; + } + + pkt_size = APR_HDR_SIZE + sizeof(*stop); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + stop = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = index; + pkt->hdr.opcode = AFE_PORT_CMD_DEVICE_STOP; + stop->port_id = port_id; + stop->reserved = 0; + + ret = afe_apr_send_pkt(afe, pkt, port); + if (ret) + dev_err(afe->dev, "AFE close failed %d\n", ret); + + kfree(pkt); + return ret; +} +EXPORT_SYMBOL_GPL(q6afe_port_stop); + +/** + * q6afe_slim_port_prepare() - Prepare slim afe port. + * + * @port: Instance of afe port + * @cfg: SLIM configuration for the afe port + * + */ +void q6afe_slim_port_prepare(struct q6afe_port *port, + struct q6afe_slim_cfg *cfg) +{ + union afe_port_config *pcfg = &port->port_cfg; + + pcfg->slim_cfg.sb_cfg_minor_version = AFE_API_VERSION_SLIMBUS_CONFIG; + pcfg->slim_cfg.sample_rate = cfg->sample_rate; + pcfg->slim_cfg.bit_width = cfg->bit_width; + pcfg->slim_cfg.num_channels = cfg->num_channels; + pcfg->slim_cfg.data_format = cfg->data_format; + pcfg->slim_cfg.shared_ch_mapping[0] = cfg->ch_mapping[0]; + pcfg->slim_cfg.shared_ch_mapping[1] = cfg->ch_mapping[1]; + pcfg->slim_cfg.shared_ch_mapping[2] = cfg->ch_mapping[2]; + pcfg->slim_cfg.shared_ch_mapping[3] = cfg->ch_mapping[3]; + +} +EXPORT_SYMBOL_GPL(q6afe_slim_port_prepare); + +/** + * q6afe_tdm_port_prepare() - Prepare tdm afe port. + * + * @port: Instance of afe port + * @cfg: TDM configuration for the afe port + * + */ +void q6afe_tdm_port_prepare(struct q6afe_port *port, + struct q6afe_tdm_cfg *cfg) +{ + union afe_port_config *pcfg = &port->port_cfg; + + pcfg->tdm_cfg.tdm_cfg_minor_version = AFE_API_VERSION_TDM_CONFIG; + pcfg->tdm_cfg.num_channels = cfg->num_channels; + pcfg->tdm_cfg.sample_rate = cfg->sample_rate; + pcfg->tdm_cfg.bit_width = cfg->bit_width; + pcfg->tdm_cfg.data_format = cfg->data_format; + pcfg->tdm_cfg.sync_mode = cfg->sync_mode; + pcfg->tdm_cfg.sync_src = cfg->sync_src; + pcfg->tdm_cfg.nslots_per_frame = cfg->nslots_per_frame; + + pcfg->tdm_cfg.slot_width = cfg->slot_width; + pcfg->tdm_cfg.slot_mask = cfg->slot_mask; + port->scfg = kzalloc(sizeof(*port->scfg), GFP_KERNEL); + if (!port->scfg) + return; + + port->scfg->minor_version = AFE_API_VERSION_SLOT_MAPPING_CONFIG; + port->scfg->num_channels = cfg->num_channels; + port->scfg->bitwidth = cfg->bit_width; + port->scfg->data_align_type = cfg->data_align_type; + memcpy(port->scfg->ch_mapping, cfg->ch_mapping, + sizeof(u16) * AFE_PORT_MAX_AUDIO_CHAN_CNT); +} +EXPORT_SYMBOL_GPL(q6afe_tdm_port_prepare); + +/** + * q6afe_hdmi_port_prepare() - Prepare hdmi afe port. + * + * @port: Instance of afe port + * @cfg: HDMI configuration for the afe port + * + */ +void q6afe_hdmi_port_prepare(struct q6afe_port *port, + struct q6afe_hdmi_cfg *cfg) +{ + union afe_port_config *pcfg = &port->port_cfg; + + pcfg->hdmi_multi_ch.hdmi_cfg_minor_version = + AFE_API_VERSION_HDMI_CONFIG; + pcfg->hdmi_multi_ch.datatype = cfg->datatype; + pcfg->hdmi_multi_ch.channel_allocation = cfg->channel_allocation; + pcfg->hdmi_multi_ch.sample_rate = cfg->sample_rate; + pcfg->hdmi_multi_ch.bit_width = cfg->bit_width; +} +EXPORT_SYMBOL_GPL(q6afe_hdmi_port_prepare); + +/** + * q6afe_i2s_port_prepare() - Prepare i2s afe port. + * + * @port: Instance of afe port + * @cfg: I2S configuration for the afe port + * Return: Will be an negative on error and zero on success. + */ +int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg) +{ + union afe_port_config *pcfg = &port->port_cfg; + struct device *dev = port->afe->dev; + int num_sd_lines; + + pcfg->i2s_cfg.i2s_cfg_minor_version = AFE_API_VERSION_I2S_CONFIG; + pcfg->i2s_cfg.sample_rate = cfg->sample_rate; + pcfg->i2s_cfg.bit_width = cfg->bit_width; + pcfg->i2s_cfg.data_format = AFE_LINEAR_PCM_DATA; + + switch (cfg->fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + pcfg->i2s_cfg.ws_src = AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL; + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* CPU is slave */ + pcfg->i2s_cfg.ws_src = AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL; + break; + default: + break; + } + + num_sd_lines = hweight_long(cfg->sd_line_mask); + + switch (num_sd_lines) { + case 0: + dev_err(dev, "no line is assigned\n"); + return -EINVAL; + case 1: + switch (cfg->sd_line_mask) { + case AFE_PORT_I2S_SD0_MASK: + pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD0; + break; + case AFE_PORT_I2S_SD1_MASK: + pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD1; + break; + case AFE_PORT_I2S_SD2_MASK: + pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD2; + break; + case AFE_PORT_I2S_SD3_MASK: + pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD3; + break; + default: + dev_err(dev, "Invalid SD lines\n"); + return -EINVAL; + } + break; + case 2: + switch (cfg->sd_line_mask) { + case AFE_PORT_I2S_SD0_1_MASK: + pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_QUAD01; + break; + case AFE_PORT_I2S_SD2_3_MASK: + pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_QUAD23; + break; + default: + dev_err(dev, "Invalid SD lines\n"); + return -EINVAL; + } + break; + case 3: + switch (cfg->sd_line_mask) { + case AFE_PORT_I2S_SD0_1_2_MASK: + pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_6CHS; + break; + default: + dev_err(dev, "Invalid SD lines\n"); + return -EINVAL; + } + break; + case 4: + switch (cfg->sd_line_mask) { + case AFE_PORT_I2S_SD0_1_2_3_MASK: + pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_8CHS; + + break; + default: + dev_err(dev, "Invalid SD lines\n"); + return -EINVAL; + } + break; + default: + dev_err(dev, "Invalid SD lines\n"); + return -EINVAL; + } + + switch (cfg->num_channels) { + case 1: + case 2: + switch (pcfg->i2s_cfg.channel_mode) { + case AFE_PORT_I2S_QUAD01: + case AFE_PORT_I2S_6CHS: + case AFE_PORT_I2S_8CHS: + pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD0; + break; + case AFE_PORT_I2S_QUAD23: + pcfg->i2s_cfg.channel_mode = AFE_PORT_I2S_SD2; + break; + } + + if (cfg->num_channels == 2) + pcfg->i2s_cfg.mono_stereo = AFE_PORT_I2S_STEREO; + else + pcfg->i2s_cfg.mono_stereo = AFE_PORT_I2S_MONO; + + break; + case 3: + case 4: + if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_QUAD01) { + dev_err(dev, "Invalid Channel mode\n"); + return -EINVAL; + } + break; + case 5: + case 6: + if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_6CHS) { + dev_err(dev, "Invalid Channel mode\n"); + return -EINVAL; + } + break; + case 7: + case 8: + if (pcfg->i2s_cfg.channel_mode < AFE_PORT_I2S_8CHS) { + dev_err(dev, "Invalid Channel mode\n"); + return -EINVAL; + } + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(q6afe_i2s_port_prepare); + +/** + * q6afe_port_start() - Start a afe port + * + * @port: Instance of port to start + * + * Return: Will be an negative on packet size on success. + */ +int q6afe_port_start(struct q6afe_port *port) +{ + struct afe_port_cmd_device_start *start; + struct q6afe *afe = port->afe; + int port_id = port->id; + int ret, param_id = port->cfg_type; + struct apr_pkt *pkt; + int pkt_size; + void *p; + + ret = q6afe_port_set_param_v2(port, &port->port_cfg, param_id, + AFE_MODULE_AUDIO_DEV_INTERFACE, + sizeof(port->port_cfg)); + if (ret) { + dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", + port_id, ret); + return ret; + } + + if (port->scfg) { + ret = q6afe_port_set_param_v2(port, port->scfg, + AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG, + AFE_MODULE_TDM, sizeof(*port->scfg)); + if (ret) { + dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", + port_id, ret); + return ret; + } + } + + pkt_size = APR_HDR_SIZE + sizeof(*start); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + start = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = port->token; + pkt->hdr.opcode = AFE_PORT_CMD_DEVICE_START; + + start->port_id = port_id; + + ret = afe_apr_send_pkt(afe, pkt, port); + if (ret) + dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", + port_id, ret); + + kfree(pkt); + return ret; +} +EXPORT_SYMBOL_GPL(q6afe_port_start); + +/** + * q6afe_port_get_from_id() - Get port instance from a port id + * + * @dev: Pointer to afe child device. + * @id: port id + * + * Return: Will be an error pointer on error or a valid afe port + * on success. + */ +struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) +{ + int port_id; + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct q6afe_port *port; + unsigned long flags; + int cfg_type; + + if (id < 0 || id > AFE_PORT_MAX) { + dev_err(dev, "AFE port token[%d] invalid!\n", id); + return ERR_PTR(-EINVAL); + } + + /* if port is multiple times bind/unbind before callback finishes */ + port = q6afe_find_port(afe, id); + if (port) { + dev_err(dev, "AFE Port already open\n"); + return port; + } + + port_id = port_maps[id].port_id; + + switch (port_id) { + case AFE_PORT_ID_MULTICHAN_HDMI_RX: + cfg_type = AFE_PARAM_ID_HDMI_CONFIG; + break; + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX: + case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX: + cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG; + break; + + case AFE_PORT_ID_PRIMARY_MI2S_RX: + case AFE_PORT_ID_PRIMARY_MI2S_TX: + case AFE_PORT_ID_SECONDARY_MI2S_RX: + case AFE_PORT_ID_SECONDARY_MI2S_TX: + case AFE_PORT_ID_TERTIARY_MI2S_RX: + case AFE_PORT_ID_TERTIARY_MI2S_TX: + case AFE_PORT_ID_QUATERNARY_MI2S_RX: + case AFE_PORT_ID_QUATERNARY_MI2S_TX: + cfg_type = AFE_PARAM_ID_I2S_CONFIG; + break; + case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: + cfg_type = AFE_PARAM_ID_TDM_CONFIG; + break; + + default: + dev_err(dev, "Invalid port id 0x%x\n", port_id); + return ERR_PTR(-EINVAL); + } + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return ERR_PTR(-ENOMEM); + + init_waitqueue_head(&port->wait); + + port->token = id; + port->id = port_id; + port->afe = afe; + port->cfg_type = cfg_type; + kref_init(&port->refcount); + + spin_lock_irqsave(&afe->port_list_lock, flags); + list_add_tail(&port->node, &afe->port_list); + spin_unlock_irqrestore(&afe->port_list_lock, flags); + + return port; + +} +EXPORT_SYMBOL_GPL(q6afe_port_get_from_id); + +/** + * q6afe_port_put() - Release port reference + * + * @port: Instance of port to put + */ +void q6afe_port_put(struct q6afe_port *port) +{ + kref_put(&port->refcount, q6afe_port_free); +} +EXPORT_SYMBOL_GPL(q6afe_port_put); + +static int q6afe_probe(struct apr_device *adev) +{ + struct q6afe *afe; + struct device *dev = &adev->dev; + struct device_node *dais_np; + + afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + + q6core_get_svc_api_info(adev->svc_id, &afe->ainfo); + afe->apr = adev; + mutex_init(&afe->lock); + afe->dev = dev; + INIT_LIST_HEAD(&afe->port_list); + spin_lock_init(&afe->port_list_lock); + + dev_set_drvdata(dev, afe); + + dais_np = of_get_child_by_name(dev->of_node, "dais"); + if (dais_np) { + afe->pdev_dais = of_platform_device_create(dais_np, + "q6afe-dai", dev); + of_node_put(dais_np); + } + + return 0; +} + +static int q6afe_remove(struct apr_device *adev) +{ + struct q6afe *afe = dev_get_drvdata(&adev->dev); + + if (afe->pdev_dais) + of_platform_device_destroy(&afe->pdev_dais->dev, NULL); + + return 0; +} + +static const struct of_device_id q6afe_device_id[] = { + { .compatible = "qcom,q6afe" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6afe_device_id); + +static struct apr_driver qcom_q6afe_driver = { + .probe = q6afe_probe, + .remove = q6afe_remove, + .callback = q6afe_callback, + .driver = { + .name = "qcom-q6afe", + .of_match_table = of_match_ptr(q6afe_device_id), + + }, +}; + +module_apr_driver(qcom_q6afe_driver); +MODULE_DESCRIPTION("Q6 Audio Front End"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h new file mode 100644 index 000000000000..c7ed5422baff --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6afe.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __Q6AFE_H__ +#define __Q6AFE_H__ + +#include <dt-bindings/sound/qcom,q6afe.h> + +#define AFE_PORT_MAX 105 + +#define MSM_AFE_PORT_TYPE_RX 0 +#define MSM_AFE_PORT_TYPE_TX 1 +#define AFE_MAX_PORTS AFE_PORT_MAX + +#define Q6AFE_MAX_MI2S_LINES 4 + +#define AFE_MAX_CHAN_COUNT 8 +#define AFE_PORT_MAX_AUDIO_CHAN_CNT 0x8 + +#define Q6AFE_LPASS_CLK_SRC_INTERNAL 1 +#define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0 + +#define LPAIF_DIG_CLK 1 +#define LPAIF_BIT_CLK 2 +#define LPAIF_OSR_CLK 3 + +/* Clock ID for Primary I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 +/* Clock ID for Primary I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT 0x101 +/* Clock ID for Secondary I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT 0x102 +/* Clock ID for Secondary I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT 0x103 +/* Clock ID for Tertiary I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT 0x104 +/* Clock ID for Tertiary I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT 0x105 +/* Clock ID for Quartnery I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT 0x106 +/* Clock ID for Quartnery I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT 0x107 +/* Clock ID for Speaker I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_IBIT 0x108 +/* Clock ID for Speaker I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_EBIT 0x109 +/* Clock ID for Speaker I2S OSR */ +#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR 0x10A + +/* Clock ID for QUINARY I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT 0x10B +/* Clock ID for QUINARY I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT 0x10C +/* Clock ID for SENARY I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_SEN_MI2S_IBIT 0x10D +/* Clock ID for SENARY I2S EBIT */ +#define Q6AFE_LPASS_CLK_ID_SEN_MI2S_EBIT 0x10E +/* Clock ID for INT0 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT 0x10F +/* Clock ID for INT1 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT1_MI2S_IBIT 0x110 +/* Clock ID for INT2 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT2_MI2S_IBIT 0x111 +/* Clock ID for INT3 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT 0x112 +/* Clock ID for INT4 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT4_MI2S_IBIT 0x113 +/* Clock ID for INT5 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT5_MI2S_IBIT 0x114 +/* Clock ID for INT6 I2S IBIT */ +#define Q6AFE_LPASS_CLK_ID_INT6_MI2S_IBIT 0x115 + +/* Clock ID for QUINARY MI2S OSR CLK */ +#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR 0x116 + +/* Clock ID for Primary PCM IBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT 0x200 +/* Clock ID for Primary PCM EBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_PCM_EBIT 0x201 +/* Clock ID for Secondary PCM IBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT 0x202 +/* Clock ID for Secondary PCM EBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT 0x203 +/* Clock ID for Tertiary PCM IBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT 0x204 +/* Clock ID for Tertiary PCM EBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT 0x205 +/* Clock ID for Quartery PCM IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT 0x206 +/* Clock ID for Quartery PCM EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT 0x207 +/* Clock ID for Quinary PCM IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUIN_PCM_IBIT 0x208 +/* Clock ID for Quinary PCM EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUIN_PCM_EBIT 0x209 +/* Clock ID for QUINARY PCM OSR */ +#define Q6AFE_LPASS_CLK_ID_QUI_PCM_OSR 0x20A + +/** Clock ID for Primary TDM IBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT 0x200 +/** Clock ID for Primary TDM EBIT */ +#define Q6AFE_LPASS_CLK_ID_PRI_TDM_EBIT 0x201 +/** Clock ID for Secondary TDM IBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT 0x202 +/** Clock ID for Secondary TDM EBIT */ +#define Q6AFE_LPASS_CLK_ID_SEC_TDM_EBIT 0x203 +/** Clock ID for Tertiary TDM IBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT 0x204 +/** Clock ID for Tertiary TDM EBIT */ +#define Q6AFE_LPASS_CLK_ID_TER_TDM_EBIT 0x205 +/** Clock ID for Quartery TDM IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT 0x206 +/** Clock ID for Quartery TDM EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT 0x207 +/** Clock ID for Quinary TDM IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUIN_TDM_IBIT 0x208 +/** Clock ID for Quinary TDM EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT 0x209 +/** Clock ID for Quinary TDM OSR */ +#define Q6AFE_LPASS_CLK_ID_QUIN_TDM_OSR 0x20A + +/* Clock ID for MCLK1 */ +#define Q6AFE_LPASS_CLK_ID_MCLK_1 0x300 +/* Clock ID for MCLK2 */ +#define Q6AFE_LPASS_CLK_ID_MCLK_2 0x301 +/* Clock ID for MCLK3 */ +#define Q6AFE_LPASS_CLK_ID_MCLK_3 0x302 +/* Clock ID for MCLK4 */ +#define Q6AFE_LPASS_CLK_ID_MCLK_4 0x304 +/* Clock ID for Internal Digital Codec Core */ +#define Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE 0x303 +/* Clock ID for INT MCLK0 */ +#define Q6AFE_LPASS_CLK_ID_INT_MCLK_0 0x305 +/* Clock ID for INT MCLK1 */ +#define Q6AFE_LPASS_CLK_ID_INT_MCLK_1 0x306 + +/* Clock attribute for invalid use (reserved for internal usage) */ +#define Q6AFE_LPASS_CLK_ATTRIBUTE_INVALID 0x0 +/* Clock attribute for no couple case */ +#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 +/* Clock attribute for dividend couple case */ +#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND 0x2 +/* Clock attribute for divisor couple case */ +#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR 0x3 +/* Clock attribute for invert and no couple case */ +#define Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO 0x4 + +#define Q6AFE_CMAP_INVALID 0xFFFF + +struct q6afe_hdmi_cfg { + u16 datatype; + u16 channel_allocation; + u32 sample_rate; + u16 bit_width; +}; + +struct q6afe_slim_cfg { + u32 sample_rate; + u16 bit_width; + u16 data_format; + u16 num_channels; + u8 ch_mapping[AFE_MAX_CHAN_COUNT]; +}; + +struct q6afe_i2s_cfg { + u32 sample_rate; + u16 bit_width; + u16 data_format; + u16 num_channels; + u32 sd_line_mask; + int fmt; +}; + +struct q6afe_tdm_cfg { + u16 num_channels; + u32 sample_rate; + u16 bit_width; + u16 data_format; + u16 sync_mode; + u16 sync_src; + u16 nslots_per_frame; + u16 slot_width; + u16 slot_mask; + u32 data_align_type; + u16 ch_mapping[AFE_MAX_CHAN_COUNT]; +}; + +struct q6afe_port_config { + struct q6afe_hdmi_cfg hdmi; + struct q6afe_slim_cfg slim; + struct q6afe_i2s_cfg i2s_cfg; + struct q6afe_tdm_cfg tdm; +}; + +struct q6afe_port; + +struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id); +int q6afe_port_start(struct q6afe_port *port); +int q6afe_port_stop(struct q6afe_port *port); +void q6afe_port_put(struct q6afe_port *port); +int q6afe_get_port_id(int index); +void q6afe_hdmi_port_prepare(struct q6afe_port *port, + struct q6afe_hdmi_cfg *cfg); +void q6afe_slim_port_prepare(struct q6afe_port *port, + struct q6afe_slim_cfg *cfg); +int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg); +void q6afe_tdm_port_prepare(struct q6afe_port *port, struct q6afe_tdm_cfg *cfg); + +int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, + int clk_src, int clk_root, + unsigned int freq, int dir); +#endif /* __Q6AFE_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c new file mode 100644 index 000000000000..349c6a883c63 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -0,0 +1,624 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2018, Linaro Limited + +#include <linux/init.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/component.h> +#include <sound/soc.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm.h> +#include <asm/dma.h> +#include <linux/dma-mapping.h> +#include <linux/of_device.h> +#include <sound/pcm_params.h> +#include "q6asm.h" +#include "q6routing.h" +#include "q6dsp-errno.h" + +#define DRV_NAME "q6asm-fe-dai" + +#define PLAYBACK_MIN_NUM_PERIODS 2 +#define PLAYBACK_MAX_NUM_PERIODS 8 +#define PLAYBACK_MAX_PERIOD_SIZE 65536 +#define PLAYBACK_MIN_PERIOD_SIZE 128 +#define CAPTURE_MIN_NUM_PERIODS 2 +#define CAPTURE_MAX_NUM_PERIODS 8 +#define CAPTURE_MAX_PERIOD_SIZE 4096 +#define CAPTURE_MIN_PERIOD_SIZE 320 +#define SID_MASK_DEFAULT 0xF + +enum stream_state { + Q6ASM_STREAM_IDLE = 0, + Q6ASM_STREAM_STOPPED, + Q6ASM_STREAM_RUNNING, +}; + +struct q6asm_dai_rtd { + struct snd_pcm_substream *substream; + phys_addr_t phys; + unsigned int pcm_size; + unsigned int pcm_count; + unsigned int pcm_irq_pos; /* IRQ position */ + unsigned int periods; + uint16_t bits_per_sample; + uint16_t source; /* Encoding source bit mask */ + struct audio_client *audio_client; + uint16_t session_id; + enum stream_state state; +}; + +struct q6asm_dai_data { + long long int sid; +}; + +static struct snd_pcm_hardware q6asm_dai_hardware_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 4, + .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * + CAPTURE_MAX_PERIOD_SIZE, + .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, + .periods_min = CAPTURE_MIN_NUM_PERIODS, + .periods_max = CAPTURE_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware q6asm_dai_hardware_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = (PLAYBACK_MAX_NUM_PERIODS * + PLAYBACK_MAX_PERIOD_SIZE), + .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = PLAYBACK_MIN_NUM_PERIODS, + .periods_max = PLAYBACK_MAX_NUM_PERIODS, + .fifo_size = 0, +}; + +#define Q6ASM_FEDAI_DRIVER(num) { \ + .playback = { \ + .stream_name = "MultiMedia"#num" Playback", \ + .rates = (SNDRV_PCM_RATE_8000_192000| \ + SNDRV_PCM_RATE_KNOT), \ + .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE), \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 192000, \ + }, \ + .capture = { \ + .stream_name = "MultiMedia"#num" Capture", \ + .rates = (SNDRV_PCM_RATE_8000_48000| \ + SNDRV_PCM_RATE_KNOT), \ + .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE), \ + .channels_min = 1, \ + .channels_max = 4, \ + .rate_min = 8000, \ + .rate_max = 48000, \ + }, \ + .name = "MultiMedia"#num, \ + .probe = fe_dai_probe, \ + .id = MSM_FRONTEND_DAI_MULTIMEDIA##num, \ + } + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000, 176400, 192000 +}; + +static struct snd_pcm_hw_constraint_list constraints_sample_rates = { + .count = ARRAY_SIZE(supported_sample_rates), + .list = supported_sample_rates, + .mask = 0, +}; + +static void event_handler(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6asm_dai_rtd *prtd = priv; + struct snd_pcm_substream *substream = prtd->substream; + + switch (opcode) { + case ASM_CLIENT_EVENT_CMD_RUN_DONE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + q6asm_write_async(prtd->audio_client, + prtd->pcm_count, 0, 0, NO_TIMESTAMP); + break; + case ASM_CLIENT_EVENT_CMD_EOS_DONE: + prtd->state = Q6ASM_STREAM_STOPPED; + break; + case ASM_CLIENT_EVENT_DATA_WRITE_DONE: { + prtd->pcm_irq_pos += prtd->pcm_count; + snd_pcm_period_elapsed(substream); + if (prtd->state == Q6ASM_STREAM_RUNNING) + q6asm_write_async(prtd->audio_client, + prtd->pcm_count, 0, 0, NO_TIMESTAMP); + + break; + } + case ASM_CLIENT_EVENT_DATA_READ_DONE: + prtd->pcm_irq_pos += prtd->pcm_count; + snd_pcm_period_elapsed(substream); + if (prtd->state == Q6ASM_STREAM_RUNNING) + q6asm_read(prtd->audio_client); + + break; + default: + break; + } +} + +static int q6asm_dai_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct q6asm_dai_rtd *prtd = runtime->private_data; + struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); + struct q6asm_dai_data *pdata; + int ret, i; + + pdata = snd_soc_component_get_drvdata(c); + if (!pdata) + return -EINVAL; + + if (!prtd || !prtd->audio_client) { + pr_err("%s: private data null or audio client freed\n", + __func__); + return -EINVAL; + } + + prtd->pcm_count = snd_pcm_lib_period_bytes(substream); + prtd->pcm_irq_pos = 0; + /* rate and channels are sent to audio driver */ + if (prtd->state) { + /* clear the previous setup if any */ + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_unmap_memory_regions(substream->stream, + prtd->audio_client); + q6routing_stream_close(soc_prtd->dai_link->id, + substream->stream); + } + + ret = q6asm_map_memory_regions(substream->stream, prtd->audio_client, + prtd->phys, + (prtd->pcm_size / prtd->periods), + prtd->periods); + + if (ret < 0) { + pr_err("Audio Start: Buffer Allocation failed rc = %d\n", + ret); + return -ENOMEM; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM, + prtd->bits_per_sample); + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM, + prtd->bits_per_sample); + } + + if (ret < 0) { + pr_err("%s: q6asm_open_write failed\n", __func__); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return -ENOMEM; + } + + prtd->session_id = q6asm_get_session_id(prtd->audio_client); + ret = q6routing_stream_open(soc_prtd->dai_link->id, LEGACY_PCM_MODE, + prtd->session_id, substream->stream); + if (ret) { + pr_err("%s: stream reg failed ret:%d\n", __func__, ret); + return ret; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = q6asm_media_format_block_multi_ch_pcm( + prtd->audio_client, runtime->rate, + runtime->channels, NULL, + prtd->bits_per_sample); + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client, + runtime->rate, runtime->channels, + prtd->bits_per_sample); + + /* Queue the buffers */ + for (i = 0; i < runtime->periods; i++) + q6asm_read(prtd->audio_client); + + } + if (ret < 0) + pr_info("%s: CMD Format block failed\n", __func__); + + prtd->state = Q6ASM_STREAM_RUNNING; + + return 0; +} + +static int q6asm_dai_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + break; + case SNDRV_PCM_TRIGGER_STOP: + prtd->state = Q6ASM_STREAM_STOPPED; + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int q6asm_dai_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct snd_soc_dai *cpu_dai = soc_prtd->cpu_dai; + struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); + struct q6asm_dai_rtd *prtd; + struct q6asm_dai_data *pdata; + struct device *dev = c->dev; + int ret = 0; + int stream_id; + + stream_id = cpu_dai->driver->id; + + pdata = snd_soc_component_get_drvdata(c); + if (!pdata) { + pr_err("Drv data not found ..\n"); + return -EINVAL; + } + + prtd = kzalloc(sizeof(struct q6asm_dai_rtd), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + prtd->substream = substream; + prtd->audio_client = q6asm_audio_client_alloc(dev, + (q6asm_cb)event_handler, prtd, stream_id, + LEGACY_PCM_MODE); + if (!prtd->audio_client) { + pr_info("%s: Could not allocate memory\n", __func__); + kfree(prtd); + return -ENOMEM; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = q6asm_dai_hardware_playback; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + runtime->hw = q6asm_dai_hardware_capture; + + ret = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_sample_rates); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_list failed\n"); + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + pr_info("snd_pcm_hw_constraint_integer failed\n"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE, + PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE); + if (ret < 0) { + pr_err("constraint for buffer bytes min max ret = %d\n", + ret); + } + } + + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (ret < 0) { + pr_err("constraint for period bytes step ret = %d\n", + ret); + } + ret = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (ret < 0) { + pr_err("constraint for buffer bytes step ret = %d\n", + ret); + } + + runtime->private_data = prtd; + + snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_playback); + + runtime->dma_bytes = q6asm_dai_hardware_playback.buffer_bytes_max; + + + if (pdata->sid < 0) + prtd->phys = substream->dma_buffer.addr; + else + prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int q6asm_dai_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct q6asm_dai_rtd *prtd = runtime->private_data; + + if (prtd->audio_client) { + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_unmap_memory_regions(substream->stream, + prtd->audio_client); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + } + q6routing_stream_close(soc_prtd->dai_link->id, + substream->stream); + kfree(prtd); + return 0; +} + +static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_pcm_substream *substream) +{ + + struct snd_pcm_runtime *runtime = substream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + + if (prtd->pcm_irq_pos >= prtd->pcm_size) + prtd->pcm_irq_pos = 0; + + return bytes_to_frames(runtime, (prtd->pcm_irq_pos)); +} + +static int q6asm_dai_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; + struct snd_soc_component *c = snd_soc_rtdcom_lookup(soc_prtd, DRV_NAME); + struct device *dev = c->dev; + + return dma_mmap_coherent(dev, vma, + runtime->dma_area, runtime->dma_addr, + runtime->dma_bytes); +} + +static int q6asm_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + + prtd->pcm_size = params_buffer_bytes(params); + prtd->periods = params_periods(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + prtd->bits_per_sample = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + prtd->bits_per_sample = 24; + break; + } + + return 0; +} + +static struct snd_pcm_ops q6asm_dai_ops = { + .open = q6asm_dai_open, + .hw_params = q6asm_dai_hw_params, + .close = q6asm_dai_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = q6asm_dai_prepare, + .trigger = q6asm_dai_trigger, + .pointer = q6asm_dai_pointer, + .mmap = q6asm_dai_mmap, +}; + +static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm_substream *psubstream, *csubstream; + struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_pcm *pcm = rtd->pcm; + struct device *dev; + int size, ret; + + dev = c->dev; + size = q6asm_dai_hardware_playback.buffer_bytes_max; + psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (psubstream) { + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, + &psubstream->dma_buffer); + if (ret) { + dev_err(dev, "Cannot allocate buffer(s)\n"); + return ret; + } + } + + csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (csubstream) { + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, + &csubstream->dma_buffer); + if (ret) { + dev_err(dev, "Cannot allocate buffer(s)\n"); + if (psubstream) + snd_dma_free_pages(&psubstream->dma_buffer); + return ret; + } + } + + return ret; +} + +static void q6asm_dai_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + int i; + + for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { + substream = pcm->streams[i].substream; + if (substream) { + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } + } +} + +static const struct snd_soc_dapm_route afe_pcm_routes[] = { + {"MM_DL1", NULL, "MultiMedia1 Playback" }, + {"MM_DL2", NULL, "MultiMedia2 Playback" }, + {"MM_DL3", NULL, "MultiMedia3 Playback" }, + {"MM_DL4", NULL, "MultiMedia4 Playback" }, + {"MM_DL5", NULL, "MultiMedia5 Playback" }, + {"MM_DL6", NULL, "MultiMedia6 Playback" }, + {"MM_DL7", NULL, "MultiMedia7 Playback" }, + {"MM_DL7", NULL, "MultiMedia8 Playback" }, + {"MultiMedia1 Capture", NULL, "MM_UL1"}, + {"MultiMedia2 Capture", NULL, "MM_UL2"}, + {"MultiMedia3 Capture", NULL, "MM_UL3"}, + {"MultiMedia4 Capture", NULL, "MM_UL4"}, + {"MultiMedia5 Capture", NULL, "MM_UL5"}, + {"MultiMedia6 Capture", NULL, "MM_UL6"}, + {"MultiMedia7 Capture", NULL, "MM_UL7"}, + {"MultiMedia8 Capture", NULL, "MM_UL8"}, + +}; + +static int fe_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_context *dapm; + + dapm = snd_soc_component_get_dapm(dai->component); + snd_soc_dapm_add_routes(dapm, afe_pcm_routes, + ARRAY_SIZE(afe_pcm_routes)); + + return 0; +} + + +static const struct snd_soc_component_driver q6asm_fe_dai_component = { + .name = DRV_NAME, + .ops = &q6asm_dai_ops, + .pcm_new = q6asm_dai_pcm_new, + .pcm_free = q6asm_dai_pcm_free, + +}; + +static struct snd_soc_dai_driver q6asm_fe_dais[] = { + Q6ASM_FEDAI_DRIVER(1), + Q6ASM_FEDAI_DRIVER(2), + Q6ASM_FEDAI_DRIVER(3), + Q6ASM_FEDAI_DRIVER(4), + Q6ASM_FEDAI_DRIVER(5), + Q6ASM_FEDAI_DRIVER(6), + Q6ASM_FEDAI_DRIVER(7), + Q6ASM_FEDAI_DRIVER(8), +}; + +static int q6asm_dai_bind(struct device *dev, struct device *master, void *data) +{ + struct device_node *node = dev->of_node; + struct of_phandle_args args; + struct q6asm_dai_data *pdata; + int rc; + + pdata = kzalloc(sizeof(struct q6asm_dai_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args); + if (rc < 0) + pdata->sid = -1; + else + pdata->sid = args.args[0] & SID_MASK_DEFAULT; + + dev_set_drvdata(dev, pdata); + + return snd_soc_register_component(dev, &q6asm_fe_dai_component, + q6asm_fe_dais, + ARRAY_SIZE(q6asm_fe_dais)); +} +static void q6asm_dai_unbind(struct device *dev, struct device *master, + void *data) +{ + struct q6asm_dai_data *pdata = dev_get_drvdata(dev); + + snd_soc_unregister_component(dev); + + kfree(pdata); + +} + +static const struct component_ops q6asm_dai_comp_ops = { + .bind = q6asm_dai_bind, + .unbind = q6asm_dai_unbind, +}; + +static int q6asm_dai_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &q6asm_dai_comp_ops); +} + +static int q6asm_dai_dev_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &q6asm_dai_comp_ops); + return 0; +} + +static struct platform_driver q6asm_dai_platform_driver = { + .driver = { + .name = "q6asm-dai", + }, + .probe = q6asm_dai_probe, + .remove = q6asm_dai_dev_remove, +}; +module_platform_driver(q6asm_dai_platform_driver); + +MODULE_DESCRIPTION("Q6ASM dai driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c new file mode 100644 index 000000000000..530852385cad --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -0,0 +1,1399 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2018, Linaro Limited + +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/module.h> +#include <linux/soc/qcom/apr.h> +#include <linux/device.h> +#include <linux/of_platform.h> +#include <linux/spinlock.h> +#include <linux/kref.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <uapi/sound/asound.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include "q6asm.h" +#include "q6core.h" +#include "q6dsp-errno.h" +#include "q6dsp-common.h" + +#define ASM_STREAM_CMD_CLOSE 0x00010BCD +#define ASM_STREAM_CMD_FLUSH 0x00010BCE +#define ASM_SESSION_CMD_PAUSE 0x00010BD3 +#define ASM_DATA_CMD_EOS 0x00010BDB +#define ASM_NULL_POPP_TOPOLOGY 0x00010C68 +#define ASM_STREAM_CMD_FLUSH_READBUFS 0x00010C09 +#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10 +#define ASM_STREAM_POSTPROC_TOPO_ID_NONE 0x00010C68 +#define ASM_CMD_SHARED_MEM_MAP_REGIONS 0x00010D92 +#define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010D93 +#define ASM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010D94 +#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2 0x00010D98 +#define ASM_DATA_EVENT_WRITE_DONE_V2 0x00010D99 +#define ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 0x00010DA3 +#define ASM_SESSION_CMD_RUN_V2 0x00010DAA +#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5 +#define ASM_DATA_CMD_WRITE_V2 0x00010DAB +#define ASM_DATA_CMD_READ_V2 0x00010DAC +#define ASM_SESSION_CMD_SUSPEND 0x00010DEC +#define ASM_STREAM_CMD_OPEN_WRITE_V3 0x00010DB3 +#define ASM_STREAM_CMD_OPEN_READ_V3 0x00010DB4 +#define ASM_DATA_EVENT_READ_DONE_V2 0x00010D9A +#define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D + + +#define ASM_LEGACY_STREAM_SESSION 0 +/* Bit shift for the stream_perf_mode subfield. */ +#define ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ 29 +#define ASM_END_POINT_DEVICE_MATRIX 0 +#define ASM_DEFAULT_APP_TYPE 0 +#define ASM_SYNC_IO_MODE 0x0001 +#define ASM_ASYNC_IO_MODE 0x0002 +#define ASM_TUN_READ_IO_MODE 0x0004 /* tunnel read write mode */ +#define ASM_TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */ +#define ASM_SHIFT_GAPLESS_MODE_FLAG 31 +#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3 + +struct avs_cmd_shared_mem_map_regions { + u16 mem_pool_id; + u16 num_regions; + u32 property_flag; +} __packed; + +struct avs_shared_map_region_payload { + u32 shm_addr_lsw; + u32 shm_addr_msw; + u32 mem_size_bytes; +} __packed; + +struct avs_cmd_shared_mem_unmap_regions { + u32 mem_map_handle; +} __packed; + +struct asm_data_cmd_media_fmt_update_v2 { + u32 fmt_blk_size; +} __packed; + +struct asm_multi_channel_pcm_fmt_blk_v2 { + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + u16 num_channels; + u16 bits_per_sample; + u32 sample_rate; + u16 is_signed; + u16 reserved; + u8 channel_mapping[PCM_MAX_NUM_CHANNEL]; +} __packed; + +struct asm_stream_cmd_set_encdec_param { + u32 param_id; + u32 param_size; +} __packed; + +struct asm_enc_cfg_blk_param_v2 { + u32 frames_per_buf; + u32 enc_cfg_blk_size; +} __packed; + +struct asm_multi_channel_pcm_enc_cfg_v2 { + struct asm_stream_cmd_set_encdec_param encdec; + struct asm_enc_cfg_blk_param_v2 encblk; + uint16_t num_channels; + uint16_t bits_per_sample; + uint32_t sample_rate; + uint16_t is_signed; + uint16_t reserved; + uint8_t channel_mapping[8]; +} __packed; + +struct asm_data_cmd_read_v2 { + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 seq_id; +} __packed; + +struct asm_data_cmd_read_v2_done { + u32 status; + u32 buf_addr_lsw; + u32 buf_addr_msw; +}; + +struct asm_stream_cmd_open_read_v3 { + u32 mode_flags; + u32 src_endpointype; + u32 preprocopo_id; + u32 enc_cfg_id; + u16 bits_per_sample; + u16 reserved; +} __packed; + +struct asm_data_cmd_write_v2 { + u32 buf_addr_lsw; + u32 buf_addr_msw; + u32 mem_map_handle; + u32 buf_size; + u32 seq_id; + u32 timestamp_lsw; + u32 timestamp_msw; + u32 flags; +} __packed; + +struct asm_stream_cmd_open_write_v3 { + uint32_t mode_flags; + uint16_t sink_endpointype; + uint16_t bits_per_sample; + uint32_t postprocopo_id; + uint32_t dec_fmt_id; +} __packed; + +struct asm_session_cmd_run_v2 { + u32 flags; + u32 time_lsw; + u32 time_msw; +} __packed; + +struct audio_buffer { + phys_addr_t phys; + uint32_t size; /* size of buffer */ +}; + +struct audio_port_data { + struct audio_buffer *buf; + uint32_t num_periods; + uint32_t dsp_buf; + uint32_t mem_map_handle; +}; + +struct q6asm { + struct apr_device *adev; + struct device *dev; + struct q6core_svc_api_info ainfo; + wait_queue_head_t mem_wait; + struct platform_device *pcmdev; + spinlock_t slock; + struct audio_client *session[MAX_SESSIONS + 1]; + struct platform_device *pdev_dais; +}; + +struct audio_client { + int session; + q6asm_cb cb; + void *priv; + uint32_t io_mode; + struct apr_device *adev; + struct mutex cmd_lock; + spinlock_t lock; + struct kref refcount; + /* idx:1 out port, 0: in port */ + struct audio_port_data port[2]; + wait_queue_head_t cmd_wait; + struct aprv2_ibasic_rsp_result_t result; + int perf_mode; + int stream_id; + struct q6asm *q6asm; + struct device *dev; +}; + +static inline void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, + uint32_t pkt_size, bool cmd_flg, + uint32_t stream_id) +{ + hdr->hdr_field = APR_SEQ_CMD_HDR_FIELD; + hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id); + hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id); + hdr->pkt_size = pkt_size; + if (cmd_flg) + hdr->token = ac->session; +} + +static int q6asm_apr_send_session_pkt(struct q6asm *a, struct audio_client *ac, + struct apr_pkt *pkt, uint32_t rsp_opcode) +{ + struct apr_hdr *hdr = &pkt->hdr; + int rc; + + mutex_lock(&ac->cmd_lock); + ac->result.opcode = 0; + ac->result.status = 0; + rc = apr_send_pkt(a->adev, pkt); + if (rc < 0) + goto err; + + if (rsp_opcode) + rc = wait_event_timeout(a->mem_wait, + (ac->result.opcode == hdr->opcode) || + (ac->result.opcode == rsp_opcode), + 5 * HZ); + else + rc = wait_event_timeout(a->mem_wait, + (ac->result.opcode == hdr->opcode), + 5 * HZ); + + if (!rc) { + dev_err(a->dev, "CMD timeout\n"); + rc = -ETIMEDOUT; + } else if (ac->result.status > 0) { + dev_err(a->dev, "DSP returned error[%x]\n", + ac->result.status); + rc = -EINVAL; + } + +err: + mutex_unlock(&ac->cmd_lock); + return rc; +} + +static int __q6asm_memory_unmap(struct audio_client *ac, + phys_addr_t buf_add, int dir) +{ + struct avs_cmd_shared_mem_unmap_regions *mem_unmap; + struct q6asm *a = dev_get_drvdata(ac->dev->parent); + struct apr_pkt *pkt; + int rc, pkt_size; + void *p; + + if (ac->port[dir].mem_map_handle == 0) { + dev_err(ac->dev, "invalid mem handle\n"); + return -EINVAL; + } + + pkt_size = APR_HDR_SIZE + sizeof(*mem_unmap); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + mem_unmap = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_SEQ_CMD_HDR_FIELD; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.token = ((ac->session << 8) | dir); + + pkt->hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS; + mem_unmap->mem_map_handle = ac->port[dir].mem_map_handle; + + rc = q6asm_apr_send_session_pkt(a, ac, pkt, 0); + if (rc < 0) { + kfree(pkt); + return rc; + } + + ac->port[dir].mem_map_handle = 0; + + kfree(pkt); + return 0; +} + + +static void q6asm_audio_client_free_buf(struct audio_client *ac, + struct audio_port_data *port) +{ + unsigned long flags; + + spin_lock_irqsave(&ac->lock, flags); + port->num_periods = 0; + kfree(port->buf); + port->buf = NULL; + spin_unlock_irqrestore(&ac->lock, flags); +} + +/** + * q6asm_unmap_memory_regions() - unmap memory regions in the dsp. + * + * @dir: direction of audio stream + * @ac: audio client instanace + * + * Return: Will be an negative value on failure or zero on success + */ +int q6asm_unmap_memory_regions(unsigned int dir, struct audio_client *ac) +{ + struct audio_port_data *port; + int cnt = 0; + int rc = 0; + + port = &ac->port[dir]; + if (!port->buf) { + rc = -EINVAL; + goto err; + } + + cnt = port->num_periods - 1; + if (cnt >= 0) { + rc = __q6asm_memory_unmap(ac, port->buf[dir].phys, dir); + if (rc < 0) { + dev_err(ac->dev, "%s: Memory_unmap_regions failed %d\n", + __func__, rc); + goto err; + } + } + + q6asm_audio_client_free_buf(ac, port); + +err: + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_unmap_memory_regions); + +static int __q6asm_memory_map_regions(struct audio_client *ac, int dir, + size_t period_sz, unsigned int periods, + bool is_contiguous) +{ + struct avs_cmd_shared_mem_map_regions *cmd = NULL; + struct avs_shared_map_region_payload *mregions = NULL; + struct q6asm *a = dev_get_drvdata(ac->dev->parent); + struct audio_port_data *port = NULL; + struct audio_buffer *ab = NULL; + struct apr_pkt *pkt; + void *p; + unsigned long flags; + uint32_t num_regions, buf_sz; + int rc, i, pkt_size; + + if (is_contiguous) { + num_regions = 1; + buf_sz = period_sz * periods; + } else { + buf_sz = period_sz; + num_regions = periods; + } + + /* DSP expects size should be aligned to 4K */ + buf_sz = ALIGN(buf_sz, 4096); + + pkt_size = APR_HDR_SIZE + sizeof(*cmd) + + (sizeof(*mregions) * num_regions); + + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + cmd = p + APR_HDR_SIZE; + mregions = p + APR_HDR_SIZE + sizeof(*cmd); + + pkt->hdr.hdr_field = APR_SEQ_CMD_HDR_FIELD; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.token = ((ac->session << 8) | dir); + pkt->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS; + + cmd->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + cmd->num_regions = num_regions; + cmd->property_flag = 0x00; + + spin_lock_irqsave(&ac->lock, flags); + port = &ac->port[dir]; + + for (i = 0; i < num_regions; i++) { + ab = &port->buf[i]; + mregions->shm_addr_lsw = lower_32_bits(ab->phys); + mregions->shm_addr_msw = upper_32_bits(ab->phys); + mregions->mem_size_bytes = buf_sz; + ++mregions; + } + spin_unlock_irqrestore(&ac->lock, flags); + + rc = q6asm_apr_send_session_pkt(a, ac, pkt, + ASM_CMDRSP_SHARED_MEM_MAP_REGIONS); + + kfree(pkt); + + return rc; +} + +/** + * q6asm_map_memory_regions() - map memory regions in the dsp. + * + * @dir: direction of audio stream + * @ac: audio client instanace + * @phys: physcial address that needs mapping. + * @period_sz: audio period size + * @periods: number of periods + * + * Return: Will be an negative value on failure or zero on success + */ +int q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac, + phys_addr_t phys, + size_t period_sz, unsigned int periods) +{ + struct audio_buffer *buf; + unsigned long flags; + int cnt; + int rc; + + spin_lock_irqsave(&ac->lock, flags); + if (ac->port[dir].buf) { + dev_err(ac->dev, "Buffer already allocated\n"); + spin_unlock_irqrestore(&ac->lock, flags); + return 0; + } + + buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_ATOMIC); + if (!buf) { + spin_unlock_irqrestore(&ac->lock, flags); + return -ENOMEM; + } + + + ac->port[dir].buf = buf; + + buf[0].phys = phys; + buf[0].size = period_sz; + + for (cnt = 1; cnt < periods; cnt++) { + if (period_sz > 0) { + buf[cnt].phys = buf[0].phys + (cnt * period_sz); + buf[cnt].size = period_sz; + } + } + ac->port[dir].num_periods = periods; + + spin_unlock_irqrestore(&ac->lock, flags); + + rc = __q6asm_memory_map_regions(ac, dir, period_sz, periods, 1); + if (rc < 0) { + dev_err(ac->dev, "Memory_map_regions failed\n"); + q6asm_audio_client_free_buf(ac, &ac->port[dir]); + } + + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_map_memory_regions); + +static void q6asm_audio_client_release(struct kref *ref) +{ + struct audio_client *ac; + struct q6asm *a; + unsigned long flags; + + ac = container_of(ref, struct audio_client, refcount); + a = ac->q6asm; + + spin_lock_irqsave(&a->slock, flags); + a->session[ac->session] = NULL; + spin_unlock_irqrestore(&a->slock, flags); + + kfree(ac); +} + +/** + * q6asm_audio_client_free() - Freee allocated audio client + * + * @ac: audio client to free + */ +void q6asm_audio_client_free(struct audio_client *ac) +{ + kref_put(&ac->refcount, q6asm_audio_client_release); +} +EXPORT_SYMBOL_GPL(q6asm_audio_client_free); + +static struct audio_client *q6asm_get_audio_client(struct q6asm *a, + int session_id) +{ + struct audio_client *ac = NULL; + unsigned long flags; + + spin_lock_irqsave(&a->slock, flags); + if ((session_id <= 0) || (session_id > MAX_SESSIONS)) { + dev_err(a->dev, "invalid session: %d\n", session_id); + goto err; + } + + /* check for valid session */ + if (!a->session[session_id]) + goto err; + else if (a->session[session_id]->session != session_id) + goto err; + + ac = a->session[session_id]; + kref_get(&ac->refcount); +err: + spin_unlock_irqrestore(&a->slock, flags); + return ac; +} + +static int32_t q6asm_stream_callback(struct apr_device *adev, + struct apr_resp_pkt *data, + int session_id) +{ + struct q6asm *q6asm = dev_get_drvdata(&adev->dev); + struct aprv2_ibasic_rsp_result_t *result; + struct apr_hdr *hdr = &data->hdr; + struct audio_port_data *port; + struct audio_client *ac; + uint32_t client_event = 0; + int ret = 0; + + ac = q6asm_get_audio_client(q6asm, session_id); + if (!ac)/* Audio client might already be freed by now */ + return 0; + + result = data->payload; + + switch (hdr->opcode) { + case APR_BASIC_RSP_RESULT: + switch (result->opcode) { + case ASM_SESSION_CMD_PAUSE: + client_event = ASM_CLIENT_EVENT_CMD_PAUSE_DONE; + break; + case ASM_SESSION_CMD_SUSPEND: + client_event = ASM_CLIENT_EVENT_CMD_SUSPEND_DONE; + break; + case ASM_DATA_CMD_EOS: + client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE; + break; + case ASM_STREAM_CMD_FLUSH: + client_event = ASM_CLIENT_EVENT_CMD_FLUSH_DONE; + break; + case ASM_SESSION_CMD_RUN_V2: + client_event = ASM_CLIENT_EVENT_CMD_RUN_DONE; + break; + case ASM_STREAM_CMD_CLOSE: + client_event = ASM_CLIENT_EVENT_CMD_CLOSE_DONE; + break; + case ASM_STREAM_CMD_FLUSH_READBUFS: + client_event = ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE; + break; + case ASM_STREAM_CMD_OPEN_WRITE_V3: + case ASM_STREAM_CMD_OPEN_READ_V3: + case ASM_STREAM_CMD_OPEN_READWRITE_V2: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + if (result->status != 0) { + dev_err(ac->dev, + "cmd = 0x%x returned error = 0x%x\n", + result->opcode, result->status); + ac->result = *result; + wake_up(&ac->cmd_wait); + ret = 0; + goto done; + } + break; + default: + dev_err(ac->dev, "command[0x%x] not expecting rsp\n", + result->opcode); + break; + } + + ac->result = *result; + wake_up(&ac->cmd_wait); + + if (ac->cb) + ac->cb(client_event, hdr->token, + data->payload, ac->priv); + + ret = 0; + goto done; + + case ASM_DATA_EVENT_WRITE_DONE_V2: + client_event = ASM_CLIENT_EVENT_DATA_WRITE_DONE; + if (ac->io_mode & ASM_SYNC_IO_MODE) { + phys_addr_t phys; + unsigned long flags; + + spin_lock_irqsave(&ac->lock, flags); + + port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; + + if (!port->buf) { + spin_unlock_irqrestore(&ac->lock, flags); + ret = 0; + goto done; + } + + phys = port->buf[hdr->token].phys; + + if (lower_32_bits(phys) != result->opcode || + upper_32_bits(phys) != result->status) { + dev_err(ac->dev, "Expected addr %pa\n", + &port->buf[hdr->token].phys); + spin_unlock_irqrestore(&ac->lock, flags); + ret = -EINVAL; + goto done; + } + spin_unlock_irqrestore(&ac->lock, flags); + } + break; + case ASM_DATA_EVENT_READ_DONE_V2: + client_event = ASM_CLIENT_EVENT_DATA_READ_DONE; + if (ac->io_mode & ASM_SYNC_IO_MODE) { + struct asm_data_cmd_read_v2_done *done = data->payload; + unsigned long flags; + phys_addr_t phys; + + spin_lock_irqsave(&ac->lock, flags); + port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; + if (!port->buf) { + spin_unlock_irqrestore(&ac->lock, flags); + ret = 0; + goto done; + } + + phys = port->buf[hdr->token].phys; + + if (upper_32_bits(phys) != done->buf_addr_msw || + lower_32_bits(phys) != done->buf_addr_lsw) { + dev_err(ac->dev, "Expected addr %pa %08x-%08x\n", + &port->buf[hdr->token].phys, + done->buf_addr_lsw, + done->buf_addr_msw); + spin_unlock_irqrestore(&ac->lock, flags); + ret = -EINVAL; + goto done; + } + spin_unlock_irqrestore(&ac->lock, flags); + } + + break; + } + + if (ac->cb) + ac->cb(client_event, hdr->token, data->payload, ac->priv); + +done: + kref_put(&ac->refcount, q6asm_audio_client_release); + return ret; +} + +static int q6asm_srvc_callback(struct apr_device *adev, + struct apr_resp_pkt *data) +{ + struct q6asm *q6asm = dev_get_drvdata(&adev->dev); + struct aprv2_ibasic_rsp_result_t *result; + struct audio_port_data *port; + struct audio_client *ac = NULL; + struct apr_hdr *hdr = &data->hdr; + struct q6asm *a; + uint32_t sid = 0; + uint32_t dir = 0; + int session_id; + + session_id = (hdr->dest_port >> 8) & 0xFF; + if (session_id) + return q6asm_stream_callback(adev, data, session_id); + + sid = (hdr->token >> 8) & 0x0F; + ac = q6asm_get_audio_client(q6asm, sid); + if (!ac) { + dev_err(&adev->dev, "Audio Client not active\n"); + return 0; + } + + a = dev_get_drvdata(ac->dev->parent); + dir = (hdr->token & 0x0F); + port = &ac->port[dir]; + result = data->payload; + + switch (hdr->opcode) { + case APR_BASIC_RSP_RESULT: + switch (result->opcode) { + case ASM_CMD_SHARED_MEM_MAP_REGIONS: + case ASM_CMD_SHARED_MEM_UNMAP_REGIONS: + ac->result = *result; + wake_up(&a->mem_wait); + break; + default: + dev_err(&adev->dev, "command[0x%x] not expecting rsp\n", + result->opcode); + break; + } + goto done; + case ASM_CMDRSP_SHARED_MEM_MAP_REGIONS: + ac->result.status = 0; + ac->result.opcode = hdr->opcode; + port->mem_map_handle = result->opcode; + wake_up(&a->mem_wait); + break; + case ASM_CMD_SHARED_MEM_UNMAP_REGIONS: + ac->result.opcode = hdr->opcode; + ac->result.status = 0; + port->mem_map_handle = 0; + wake_up(&a->mem_wait); + break; + default: + dev_dbg(&adev->dev, "command[0x%x]success [0x%x]\n", + result->opcode, result->status); + break; + } + + if (ac->cb) + ac->cb(hdr->opcode, hdr->token, data->payload, ac->priv); + +done: + kref_put(&ac->refcount, q6asm_audio_client_release); + + return 0; +} + +/** + * q6asm_get_session_id() - get session id for audio client + * + * @c: audio client pointer + * + * Return: Will be an session id of the audio client. + */ +int q6asm_get_session_id(struct audio_client *c) +{ + return c->session; +} +EXPORT_SYMBOL_GPL(q6asm_get_session_id); + +/** + * q6asm_audio_client_alloc() - Allocate a new audio client + * + * @dev: Pointer to asm child device. + * @cb: event callback. + * @priv: private data associated with this client. + * @stream_id: stream id + * @perf_mode: performace mode for this client + * + * Return: Will be an error pointer on error or a valid audio client + * on success. + */ +struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, + void *priv, int stream_id, + int perf_mode) +{ + struct q6asm *a = dev_get_drvdata(dev->parent); + struct audio_client *ac; + unsigned long flags; + + ac = q6asm_get_audio_client(a, stream_id + 1); + if (ac) { + dev_err(dev, "Audio Client already active\n"); + return ac; + } + + ac = kzalloc(sizeof(*ac), GFP_KERNEL); + if (!ac) + return ERR_PTR(-ENOMEM); + + spin_lock_irqsave(&a->slock, flags); + a->session[stream_id + 1] = ac; + spin_unlock_irqrestore(&a->slock, flags); + ac->session = stream_id + 1; + ac->cb = cb; + ac->dev = dev; + ac->q6asm = a; + ac->priv = priv; + ac->io_mode = ASM_SYNC_IO_MODE; + ac->perf_mode = perf_mode; + /* DSP expects stream id from 1 */ + ac->stream_id = 1; + ac->adev = a->adev; + kref_init(&ac->refcount); + + init_waitqueue_head(&ac->cmd_wait); + mutex_init(&ac->cmd_lock); + spin_lock_init(&ac->lock); + + return ac; +} +EXPORT_SYMBOL_GPL(q6asm_audio_client_alloc); + +static int q6asm_ac_send_cmd_sync(struct audio_client *ac, struct apr_pkt *pkt) +{ + struct apr_hdr *hdr = &pkt->hdr; + int rc; + + mutex_lock(&ac->cmd_lock); + ac->result.opcode = 0; + ac->result.status = 0; + + rc = apr_send_pkt(ac->adev, pkt); + if (rc < 0) + goto err; + + rc = wait_event_timeout(ac->cmd_wait, + (ac->result.opcode == hdr->opcode), 5 * HZ); + if (!rc) { + dev_err(ac->dev, "CMD timeout\n"); + rc = -ETIMEDOUT; + goto err; + } + + if (ac->result.status > 0) { + dev_err(ac->dev, "DSP returned error[%x]\n", + ac->result.status); + rc = -EINVAL; + } else { + rc = 0; + } + + +err: + mutex_unlock(&ac->cmd_lock); + return rc; +} + +/** + * q6asm_open_write() - Open audio client for writing + * + * @ac: audio client pointer + * @format: audio sample format + * @bits_per_sample: bits per sample + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_open_write(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + struct asm_stream_cmd_open_write_v3 *open; + struct apr_pkt *pkt; + void *p; + int rc, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(*open); + + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + open = p + APR_HDR_SIZE; + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + + pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3; + open->mode_flags = 0x00; + open->mode_flags |= ASM_LEGACY_STREAM_SESSION; + + /* source endpoint : matrix */ + open->sink_endpointype = ASM_END_POINT_DEVICE_MATRIX; + open->bits_per_sample = bits_per_sample; + open->postprocopo_id = ASM_NULL_POPP_TOPOLOGY; + + switch (format) { + case FORMAT_LINEAR_PCM: + open->dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + default: + dev_err(ac->dev, "Invalid format 0x%x\n", format); + rc = -EINVAL; + goto err; + } + + rc = q6asm_ac_send_cmd_sync(ac, pkt); + if (rc < 0) + goto err; + + ac->io_mode |= ASM_TUN_WRITE_IO_MODE; + +err: + kfree(pkt); + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_open_write); + +static int __q6asm_run(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts, bool wait) +{ + struct asm_session_cmd_run_v2 *run; + struct apr_pkt *pkt; + int pkt_size, rc; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*run); + p = kzalloc(pkt_size, GFP_ATOMIC); + if (!p) + return -ENOMEM; + + pkt = p; + run = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + + pkt->hdr.opcode = ASM_SESSION_CMD_RUN_V2; + run->flags = flags; + run->time_lsw = lsw_ts; + run->time_msw = msw_ts; + if (wait) { + rc = q6asm_ac_send_cmd_sync(ac, pkt); + } else { + rc = apr_send_pkt(ac->adev, pkt); + if (rc == pkt_size) + rc = 0; + } + + kfree(pkt); + return rc; +} + +/** + * q6asm_run() - start the audio client + * + * @ac: audio client pointer + * @flags: flags associated with write + * @msw_ts: timestamp msw + * @lsw_ts: timestamp lsw + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_run(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + return __q6asm_run(ac, flags, msw_ts, lsw_ts, true); +} +EXPORT_SYMBOL_GPL(q6asm_run); + +/** + * q6asm_run_nowait() - start the audio client withou blocking + * + * @ac: audio client pointer + * @flags: flags associated with write + * @msw_ts: timestamp msw + * @lsw_ts: timestamp lsw + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts) +{ + return __q6asm_run(ac, flags, msw_ts, lsw_ts, false); +} +EXPORT_SYMBOL_GPL(q6asm_run_nowait); + +/** + * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration + * + * @ac: audio client pointer + * @rate: audio sample rate + * @channels: number of audio channels. + * @channel_map: channel map pointer + * @bits_per_sample: bits per sample + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + u8 channel_map[PCM_MAX_NUM_CHANNEL], + uint16_t bits_per_sample) +{ + struct asm_multi_channel_pcm_fmt_blk_v2 *fmt; + struct apr_pkt *pkt; + u8 *channel_mapping; + void *p; + int rc, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(*fmt); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + fmt = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + + pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); + fmt->num_channels = channels; + fmt->bits_per_sample = bits_per_sample; + fmt->sample_rate = rate; + fmt->is_signed = 1; + + channel_mapping = fmt->channel_mapping; + + if (channel_map) { + memcpy(channel_mapping, channel_map, PCM_MAX_NUM_CHANNEL); + } else { + if (q6dsp_map_channels(channel_mapping, channels)) { + dev_err(ac->dev, " map channels failed %d\n", channels); + rc = -EINVAL; + goto err; + } + } + + rc = q6asm_ac_send_cmd_sync(ac, pkt); + +err: + kfree(pkt); + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm); + +/** + * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture + * + * @ac: audio client pointer + * @rate: audio sample rate + * @channels: number of audio channels. + * @bits_per_sample: bits per sample + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, + uint32_t rate, uint32_t channels, uint16_t bits_per_sample) +{ + struct asm_multi_channel_pcm_enc_cfg_v2 *enc_cfg; + struct apr_pkt *pkt; + u8 *channel_mapping; + u32 frames_per_buf = 0; + int pkt_size, rc; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*enc_cfg); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + enc_cfg = p + APR_HDR_SIZE; + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + + pkt->hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + enc_cfg->encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; + enc_cfg->encdec.param_size = sizeof(*enc_cfg) - sizeof(enc_cfg->encdec); + enc_cfg->encblk.frames_per_buf = frames_per_buf; + enc_cfg->encblk.enc_cfg_blk_size = enc_cfg->encdec.param_size - + sizeof(struct asm_enc_cfg_blk_param_v2); + + enc_cfg->num_channels = channels; + enc_cfg->bits_per_sample = bits_per_sample; + enc_cfg->sample_rate = rate; + enc_cfg->is_signed = 1; + channel_mapping = enc_cfg->channel_mapping; + + if (q6dsp_map_channels(channel_mapping, channels)) { + rc = -EINVAL; + goto err; + } + + rc = q6asm_ac_send_cmd_sync(ac, pkt); +err: + kfree(pkt); + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support); + +/** + * q6asm_read() - read data of period size from audio client + * + * @ac: audio client pointer + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_read(struct audio_client *ac) +{ + struct asm_data_cmd_read_v2 *read; + struct audio_port_data *port; + struct audio_buffer *ab; + struct apr_pkt *pkt; + unsigned long flags; + int pkt_size; + int rc = 0; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*read); + p = kzalloc(pkt_size, GFP_ATOMIC); + if (!p) + return -ENOMEM; + + pkt = p; + read = p + APR_HDR_SIZE; + + spin_lock_irqsave(&ac->lock, flags); + port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id); + ab = &port->buf[port->dsp_buf]; + pkt->hdr.opcode = ASM_DATA_CMD_READ_V2; + read->buf_addr_lsw = lower_32_bits(ab->phys); + read->buf_addr_msw = upper_32_bits(ab->phys); + read->mem_map_handle = port->mem_map_handle; + + read->buf_size = ab->size; + read->seq_id = port->dsp_buf; + pkt->hdr.token = port->dsp_buf; + + port->dsp_buf++; + + if (port->dsp_buf >= port->num_periods) + port->dsp_buf = 0; + + spin_unlock_irqrestore(&ac->lock, flags); + rc = apr_send_pkt(ac->adev, pkt); + if (rc == pkt_size) + rc = 0; + else + pr_err("read op[0x%x]rc[%d]\n", pkt->hdr.opcode, rc); + + kfree(pkt); + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_read); + +static int __q6asm_open_read(struct audio_client *ac, + uint32_t format, uint16_t bits_per_sample) +{ + struct asm_stream_cmd_open_read_v3 *open; + struct apr_pkt *pkt; + int pkt_size, rc; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*open); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + open = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3; + /* Stream prio : High, provide meta info with encoded frames */ + open->src_endpointype = ASM_END_POINT_DEVICE_MATRIX; + + open->preprocopo_id = ASM_STREAM_POSTPROC_TOPO_ID_NONE; + open->bits_per_sample = bits_per_sample; + open->mode_flags = 0x0; + + open->mode_flags |= ASM_LEGACY_STREAM_SESSION << + ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ; + + switch (format) { + case FORMAT_LINEAR_PCM: + open->mode_flags |= 0x00; + open->enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + break; + default: + pr_err("Invalid format[%d]\n", format); + } + + rc = q6asm_ac_send_cmd_sync(ac, pkt); + + kfree(pkt); + return rc; +} + +/** + * q6asm_open_read() - Open audio client for reading + * + * @ac: audio client pointer + * @format: audio sample format + * @bits_per_sample: bits per sample + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_open_read(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample) +{ + return __q6asm_open_read(ac, format, bits_per_sample); +} +EXPORT_SYMBOL_GPL(q6asm_open_read); + +/** + * q6asm_write_async() - non blocking write + * + * @ac: audio client pointer + * @len: lenght in bytes + * @msw_ts: timestamp msw + * @lsw_ts: timestamp lsw + * @wflags: flags associated with write + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t wflags) +{ + struct asm_data_cmd_write_v2 *write; + struct audio_port_data *port; + struct audio_buffer *ab; + unsigned long flags; + struct apr_pkt *pkt; + int pkt_size; + int rc = 0; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*write); + p = kzalloc(pkt_size, GFP_ATOMIC); + if (!p) + return -ENOMEM; + + pkt = p; + write = p + APR_HDR_SIZE; + + spin_lock_irqsave(&ac->lock, flags); + port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id); + + ab = &port->buf[port->dsp_buf]; + pkt->hdr.token = port->dsp_buf; + pkt->hdr.opcode = ASM_DATA_CMD_WRITE_V2; + write->buf_addr_lsw = lower_32_bits(ab->phys); + write->buf_addr_msw = upper_32_bits(ab->phys); + write->buf_size = len; + write->seq_id = port->dsp_buf; + write->timestamp_lsw = lsw_ts; + write->timestamp_msw = msw_ts; + write->mem_map_handle = + ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle; + + if (wflags == NO_TIMESTAMP) + write->flags = (wflags & 0x800000FF); + else + write->flags = (0x80000000 | wflags); + + port->dsp_buf++; + + if (port->dsp_buf >= port->num_periods) + port->dsp_buf = 0; + + spin_unlock_irqrestore(&ac->lock, flags); + rc = apr_send_pkt(ac->adev, pkt); + if (rc == pkt_size) + rc = 0; + + kfree(pkt); + return rc; +} +EXPORT_SYMBOL_GPL(q6asm_write_async); + +static void q6asm_reset_buf_state(struct audio_client *ac) +{ + struct audio_port_data *port = NULL; + unsigned long flags; + + spin_lock_irqsave(&ac->lock, flags); + port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; + port->dsp_buf = 0; + port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; + port->dsp_buf = 0; + spin_unlock_irqrestore(&ac->lock, flags); +} + +static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait) +{ + int stream_id = ac->stream_id; + struct apr_pkt pkt; + int rc; + + q6asm_add_hdr(ac, &pkt.hdr, APR_HDR_SIZE, true, stream_id); + + switch (cmd) { + case CMD_PAUSE: + pkt.hdr.opcode = ASM_SESSION_CMD_PAUSE; + break; + case CMD_SUSPEND: + pkt.hdr.opcode = ASM_SESSION_CMD_SUSPEND; + break; + case CMD_FLUSH: + pkt.hdr.opcode = ASM_STREAM_CMD_FLUSH; + break; + case CMD_OUT_FLUSH: + pkt.hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS; + break; + case CMD_EOS: + pkt.hdr.opcode = ASM_DATA_CMD_EOS; + break; + case CMD_CLOSE: + pkt.hdr.opcode = ASM_STREAM_CMD_CLOSE; + break; + default: + return -EINVAL; + } + + if (wait) + rc = q6asm_ac_send_cmd_sync(ac, &pkt); + else + return apr_send_pkt(ac->adev, &pkt); + + if (rc < 0) + return rc; + + if (cmd == CMD_FLUSH) + q6asm_reset_buf_state(ac); + + return 0; +} + +/** + * q6asm_cmd() - run cmd on audio client + * + * @ac: audio client pointer + * @cmd: command to run on audio client. + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_cmd(struct audio_client *ac, int cmd) +{ + return __q6asm_cmd(ac, cmd, true); +} +EXPORT_SYMBOL_GPL(q6asm_cmd); + +/** + * q6asm_cmd_nowait() - non blocking, run cmd on audio client + * + * @ac: audio client pointer + * @cmd: command to run on audio client. + * + * Return: Will be an negative value on error or zero on success + */ +int q6asm_cmd_nowait(struct audio_client *ac, int cmd) +{ + return __q6asm_cmd(ac, cmd, false); +} +EXPORT_SYMBOL_GPL(q6asm_cmd_nowait); + +static int q6asm_probe(struct apr_device *adev) +{ + struct device *dev = &adev->dev; + struct device_node *dais_np; + struct q6asm *q6asm; + + q6asm = devm_kzalloc(dev, sizeof(*q6asm), GFP_KERNEL); + if (!q6asm) + return -ENOMEM; + + q6core_get_svc_api_info(adev->svc_id, &q6asm->ainfo); + + q6asm->dev = dev; + q6asm->adev = adev; + init_waitqueue_head(&q6asm->mem_wait); + spin_lock_init(&q6asm->slock); + dev_set_drvdata(dev, q6asm); + + dais_np = of_get_child_by_name(dev->of_node, "dais"); + if (dais_np) { + q6asm->pdev_dais = of_platform_device_create(dais_np, + "q6asm-dai", dev); + of_node_put(dais_np); + } + + return 0; +} + +static int q6asm_remove(struct apr_device *adev) +{ + struct q6asm *q6asm = dev_get_drvdata(&adev->dev); + + if (q6asm->pdev_dais) + of_platform_device_destroy(&q6asm->pdev_dais->dev, NULL); + + return 0; +} +static const struct of_device_id q6asm_device_id[] = { + { .compatible = "qcom,q6asm" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6asm_device_id); + +static struct apr_driver qcom_q6asm_driver = { + .probe = q6asm_probe, + .remove = q6asm_remove, + .callback = q6asm_srvc_callback, + .driver = { + .name = "qcom-q6asm", + .of_match_table = of_match_ptr(q6asm_device_id), + }, +}; + +module_apr_driver(qcom_q6asm_driver); +MODULE_DESCRIPTION("Q6 Audio Stream Manager driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h new file mode 100644 index 000000000000..9f5fb573e4a0 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __Q6_ASM_H__ +#define __Q6_ASM_H__ +#include "q6dsp-common.h" +#include <dt-bindings/sound/qcom,q6asm.h> + +/* ASM client callback events */ +#define CMD_PAUSE 0x0001 +#define ASM_CLIENT_EVENT_CMD_PAUSE_DONE 0x1001 +#define CMD_FLUSH 0x0002 +#define ASM_CLIENT_EVENT_CMD_FLUSH_DONE 0x1002 +#define CMD_EOS 0x0003 +#define ASM_CLIENT_EVENT_CMD_EOS_DONE 0x1003 +#define CMD_CLOSE 0x0004 +#define ASM_CLIENT_EVENT_CMD_CLOSE_DONE 0x1004 +#define CMD_OUT_FLUSH 0x0005 +#define ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE 0x1005 +#define CMD_SUSPEND 0x0006 +#define ASM_CLIENT_EVENT_CMD_SUSPEND_DONE 0x1006 +#define ASM_CLIENT_EVENT_CMD_RUN_DONE 0x1008 +#define ASM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009 +#define ASM_CLIENT_EVENT_DATA_READ_DONE 0x100a + +enum { + LEGACY_PCM_MODE = 0, + LOW_LATENCY_PCM_MODE, + ULTRA_LOW_LATENCY_PCM_MODE, + ULL_POST_PROCESSING_PCM_MODE, +}; + +#define MAX_SESSIONS 8 +#define NO_TIMESTAMP 0xFF00 +#define FORMAT_LINEAR_PCM 0x0000 + +typedef void (*q6asm_cb) (uint32_t opcode, uint32_t token, + void *payload, void *priv); +struct audio_client; +struct audio_client *q6asm_audio_client_alloc(struct device *dev, + q6asm_cb cb, void *priv, + int session_id, int perf_mode); +void q6asm_audio_client_free(struct audio_client *ac); +int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, + uint32_t lsw_ts, uint32_t flags); +int q6asm_open_write(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); + +int q6asm_open_read(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample); +int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, + uint32_t rate, uint32_t channels, uint16_t bits_per_sample); +int q6asm_read(struct audio_client *ac); + +int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t rate, uint32_t channels, + u8 channel_map[PCM_MAX_NUM_CHANNEL], + uint16_t bits_per_sample); +int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, + uint32_t lsw_ts); +int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, + uint32_t lsw_ts); +int q6asm_cmd(struct audio_client *ac, int cmd); +int q6asm_cmd_nowait(struct audio_client *ac, int cmd); +int q6asm_get_session_id(struct audio_client *ac); +int q6asm_map_memory_regions(unsigned int dir, + struct audio_client *ac, + phys_addr_t phys, + size_t bufsz, unsigned int bufcnt); +int q6asm_unmap_memory_regions(unsigned int dir, struct audio_client *ac); +#endif /* __Q6_ASM_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c new file mode 100644 index 000000000000..06f03a5fe9bd --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6core.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2018, Linaro Limited + +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/jiffies.h> +#include <linux/wait.h> +#include <linux/soc/qcom/apr.h> +#include "q6core.h" +#include "q6dsp-errno.h" + +#define ADSP_STATE_READY_TIMEOUT_MS 3000 +#define Q6_READY_TIMEOUT_MS 100 +#define AVCS_CMD_ADSP_EVENT_GET_STATE 0x0001290C +#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D +#define AVCS_GET_VERSIONS 0x00012905 +#define AVCS_GET_VERSIONS_RSP 0x00012906 +#define AVCS_CMD_GET_FWK_VERSION 0x001292c +#define AVCS_CMDRSP_GET_FWK_VERSION 0x001292d + +struct avcs_svc_info { + uint32_t service_id; + uint32_t version; +} __packed; + +struct avcs_cmdrsp_get_version { + uint32_t build_id; + uint32_t num_services; + struct avcs_svc_info svc_api_info[]; +} __packed; + +/* for ADSP2.8 and above */ +struct avcs_svc_api_info { + uint32_t service_id; + uint32_t api_version; + uint32_t api_branch_version; +} __packed; + +struct avcs_cmdrsp_get_fwk_version { + uint32_t build_major_version; + uint32_t build_minor_version; + uint32_t build_branch_version; + uint32_t build_subbranch_version; + uint32_t num_services; + struct avcs_svc_api_info svc_api_info[]; +} __packed; + +struct q6core { + struct apr_device *adev; + wait_queue_head_t wait; + uint32_t avcs_state; + struct mutex lock; + bool resp_received; + uint32_t num_services; + struct avcs_cmdrsp_get_fwk_version *fwk_version; + struct avcs_cmdrsp_get_version *svc_version; + bool fwk_version_supported; + bool get_state_supported; + bool get_version_supported; + bool is_version_requested; +}; + +static struct q6core *g_core; + +static int q6core_callback(struct apr_device *adev, struct apr_resp_pkt *data) +{ + struct q6core *core = dev_get_drvdata(&adev->dev); + struct aprv2_ibasic_rsp_result_t *result; + struct apr_hdr *hdr = &data->hdr; + + result = data->payload; + switch (hdr->opcode) { + case APR_BASIC_RSP_RESULT:{ + result = data->payload; + switch (result->opcode) { + case AVCS_GET_VERSIONS: + if (result->status == ADSP_EUNSUPPORTED) + core->get_version_supported = false; + core->resp_received = true; + break; + case AVCS_CMD_GET_FWK_VERSION: + if (result->status == ADSP_EUNSUPPORTED) + core->fwk_version_supported = false; + core->resp_received = true; + break; + case AVCS_CMD_ADSP_EVENT_GET_STATE: + if (result->status == ADSP_EUNSUPPORTED) + core->get_state_supported = false; + core->resp_received = true; + break; + } + break; + } + case AVCS_CMDRSP_GET_FWK_VERSION: { + struct avcs_cmdrsp_get_fwk_version *fwk; + int bytes; + + fwk = data->payload; + bytes = sizeof(*fwk) + fwk->num_services * + sizeof(fwk->svc_api_info[0]); + + core->fwk_version = kzalloc(bytes, GFP_ATOMIC); + if (!core->fwk_version) + return -ENOMEM; + + memcpy(core->fwk_version, data->payload, bytes); + + core->fwk_version_supported = true; + core->resp_received = true; + + break; + } + case AVCS_GET_VERSIONS_RSP: { + struct avcs_cmdrsp_get_version *v; + int len; + + v = data->payload; + + len = sizeof(*v) + v->num_services * sizeof(v->svc_api_info[0]); + + core->svc_version = kzalloc(len, GFP_ATOMIC); + if (!core->svc_version) + return -ENOMEM; + + memcpy(core->svc_version, data->payload, len); + + core->get_version_supported = true; + core->resp_received = true; + + break; + } + case AVCS_CMDRSP_ADSP_EVENT_GET_STATE: + core->get_state_supported = true; + core->avcs_state = result->opcode; + + core->resp_received = true; + break; + default: + dev_err(&adev->dev, "Message id from adsp core svc: 0x%x\n", + hdr->opcode); + break; + } + + if (core->resp_received) + wake_up(&core->wait); + + return 0; +} + +static int q6core_get_fwk_versions(struct q6core *core) +{ + struct apr_device *adev = core->adev; + struct apr_pkt pkt; + int rc; + + pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + pkt.hdr.pkt_size = APR_HDR_SIZE; + pkt.hdr.opcode = AVCS_CMD_GET_FWK_VERSION; + + rc = apr_send_pkt(adev, &pkt); + if (rc < 0) + return rc; + + rc = wait_event_timeout(core->wait, (core->resp_received), + msecs_to_jiffies(Q6_READY_TIMEOUT_MS)); + if (rc > 0 && core->resp_received) { + core->resp_received = false; + + if (!core->fwk_version_supported) + return -ENOTSUPP; + else + return 0; + } + + + return rc; +} + +static int q6core_get_svc_versions(struct q6core *core) +{ + struct apr_device *adev = core->adev; + struct apr_pkt pkt; + int rc; + + pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + pkt.hdr.pkt_size = APR_HDR_SIZE; + pkt.hdr.opcode = AVCS_GET_VERSIONS; + + rc = apr_send_pkt(adev, &pkt); + if (rc < 0) + return rc; + + rc = wait_event_timeout(core->wait, (core->resp_received), + msecs_to_jiffies(Q6_READY_TIMEOUT_MS)); + if (rc > 0 && core->resp_received) { + core->resp_received = false; + return 0; + } + + return rc; +} + +static bool __q6core_is_adsp_ready(struct q6core *core) +{ + struct apr_device *adev = core->adev; + struct apr_pkt pkt; + int rc; + + core->get_state_supported = false; + + pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + pkt.hdr.pkt_size = APR_HDR_SIZE; + pkt.hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE; + + rc = apr_send_pkt(adev, &pkt); + if (rc < 0) + return false; + + rc = wait_event_timeout(core->wait, (core->resp_received), + msecs_to_jiffies(Q6_READY_TIMEOUT_MS)); + if (rc > 0 && core->resp_received) { + core->resp_received = false; + + if (core->avcs_state) + return true; + } + + /* assume that the adsp is up if we not support this command */ + if (!core->get_state_supported) + return true; + + return false; +} + +/** + * q6core_get_svc_api_info() - Get version number of a service. + * + * @svc_id: service id of the service. + * @ainfo: Valid struct pointer to fill svc api information. + * + * Return: zero on success and error code on failure or unsupported + */ +int q6core_get_svc_api_info(int svc_id, struct q6core_svc_api_info *ainfo) +{ + int i; + int ret = -ENOTSUPP; + + if (!g_core || !ainfo) + return 0; + + mutex_lock(&g_core->lock); + if (!g_core->is_version_requested) { + if (q6core_get_fwk_versions(g_core) == -ENOTSUPP) + q6core_get_svc_versions(g_core); + g_core->is_version_requested = true; + } + + if (g_core->fwk_version_supported) { + for (i = 0; i < g_core->fwk_version->num_services; i++) { + struct avcs_svc_api_info *info; + + info = &g_core->fwk_version->svc_api_info[i]; + if (svc_id != info->service_id) + continue; + + ainfo->api_version = info->api_version; + ainfo->api_branch_version = info->api_branch_version; + ret = 0; + break; + } + } else if (g_core->get_version_supported) { + for (i = 0; i < g_core->svc_version->num_services; i++) { + struct avcs_svc_info *info; + + info = &g_core->svc_version->svc_api_info[i]; + if (svc_id != info->service_id) + continue; + + ainfo->api_version = info->version; + ainfo->api_branch_version = 0; + ret = 0; + break; + } + } + + mutex_unlock(&g_core->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(q6core_get_svc_api_info); + +/** + * q6core_is_adsp_ready() - Get status of adsp + * + * Return: Will be an true if adsp is ready and false if not. + */ +bool q6core_is_adsp_ready(void) +{ + unsigned long timeout; + bool ret = false; + + if (!g_core) + return false; + + mutex_lock(&g_core->lock); + timeout = jiffies + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + for (;;) { + if (__q6core_is_adsp_ready(g_core)) { + ret = true; + break; + } + + if (!time_after(timeout, jiffies)) { + ret = false; + break; + } + } + + mutex_unlock(&g_core->lock); + return ret; +} +EXPORT_SYMBOL_GPL(q6core_is_adsp_ready); + +static int q6core_probe(struct apr_device *adev) +{ + g_core = kzalloc(sizeof(*g_core), GFP_KERNEL); + if (!g_core) + return -ENOMEM; + + dev_set_drvdata(&adev->dev, g_core); + + mutex_init(&g_core->lock); + g_core->adev = adev; + init_waitqueue_head(&g_core->wait); + return 0; +} + +static int q6core_exit(struct apr_device *adev) +{ + struct q6core *core = dev_get_drvdata(&adev->dev); + + if (core->fwk_version_supported) + kfree(core->fwk_version); + if (core->get_version_supported) + kfree(core->svc_version); + + g_core = NULL; + kfree(core); + + return 0; +} + +static const struct of_device_id q6core_device_id[] = { + { .compatible = "qcom,q6core" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6core_device_id); + +static struct apr_driver qcom_q6core_driver = { + .probe = q6core_probe, + .remove = q6core_exit, + .callback = q6core_callback, + .driver = { + .name = "qcom-q6core", + .of_match_table = of_match_ptr(q6core_device_id), + }, +}; + +module_apr_driver(qcom_q6core_driver); +MODULE_DESCRIPTION("q6 core"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6core.h b/sound/soc/qcom/qdsp6/q6core.h new file mode 100644 index 000000000000..4105b1d730be --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6core.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __Q6CORE_H__ +#define __Q6CORE_H__ + +struct q6core_svc_api_info { + uint32_t service_id; + uint32_t api_version; + uint32_t api_branch_version; +}; + +bool q6core_is_adsp_ready(void); +int q6core_get_svc_api_info(int svc_id, struct q6core_svc_api_info *ainfo); + +#endif /* __Q6CORE_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6dsp-common.c b/sound/soc/qcom/qdsp6/q6dsp-common.c new file mode 100644 index 000000000000..d393003492c7 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6dsp-common.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2018, Linaro Limited + +#include "q6dsp-common.h" +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/errno.h> + +int q6dsp_map_channels(u8 ch_map[PCM_MAX_NUM_CHANNEL], int ch) +{ + memset(ch_map, 0, PCM_MAX_NUM_CHANNEL); + + switch (ch) { + case 1: + ch_map[0] = PCM_CHANNEL_FC; + break; + case 2: + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + break; + case 3: + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_FC; + break; + case 4: + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_LS; + ch_map[3] = PCM_CHANNEL_RS; + break; + case 5: + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_FC; + ch_map[3] = PCM_CHANNEL_LS; + ch_map[4] = PCM_CHANNEL_RS; + break; + case 6: + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_LFE; + ch_map[3] = PCM_CHANNEL_FC; + ch_map[4] = PCM_CHANNEL_LS; + ch_map[5] = PCM_CHANNEL_RS; + break; + case 8: + ch_map[0] = PCM_CHANNEL_FL; + ch_map[1] = PCM_CHANNEL_FR; + ch_map[2] = PCM_CHANNEL_LFE; + ch_map[3] = PCM_CHANNEL_FC; + ch_map[4] = PCM_CHANNEL_LS; + ch_map[5] = PCM_CHANNEL_RS; + ch_map[6] = PCM_CHANNEL_LB; + ch_map[7] = PCM_CHANNEL_RB; + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(q6dsp_map_channels); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6dsp-common.h b/sound/soc/qcom/qdsp6/q6dsp-common.h new file mode 100644 index 000000000000..01094d108b8a --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6dsp-common.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __Q6DSP_COMMON_H__ +#define __Q6DSP_COMMON_H__ + +#include <linux/kernel.h> + +#define PCM_MAX_NUM_CHANNEL 8 +#define PCM_CHANNEL_NULL 0 + +#define PCM_CHANNEL_FL 1 /* Front left channel. */ +#define PCM_CHANNEL_FR 2 /* Front right channel. */ +#define PCM_CHANNEL_FC 3 /* Front center channel. */ +#define PCM_CHANNEL_LS 4 /* Left surround channel. */ +#define PCM_CHANNEL_RS 5 /* Right surround channel. */ +#define PCM_CHANNEL_LFE 6 /* Low frequency effect channel. */ +#define PCM_CHANNEL_CS 7 /* Center surround channel; Rear center ch */ +#define PCM_CHANNEL_LB 8 /* Left back channel; Rear left channel. */ +#define PCM_CHANNEL_RB 9 /* Right back channel; Rear right channel. */ +#define PCM_CHANNELS 10 /* Top surround channel. */ + +int q6dsp_map_channels(u8 ch_map[PCM_MAX_NUM_CHANNEL], int ch); + +#endif /* __Q6DSP_COMMON_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6dsp-errno.h b/sound/soc/qcom/qdsp6/q6dsp-errno.h new file mode 100644 index 000000000000..1ec00ff8c1d2 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6dsp-errno.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __Q6DSP_ERR_NO_H__ +#define __Q6DSP_ERR_NO_H__ +#include <linux/kernel.h> + +/* Success. The operation completed with no errors. */ +#define ADSP_EOK 0x00000000 +/* General failure. */ +#define ADSP_EFAILED 0x00000001 +/* Bad operation parameter. */ +#define ADSP_EBADPARAM 0x00000002 +/* Unsupported routine or operation. */ +#define ADSP_EUNSUPPORTED 0x00000003 +/* Unsupported version. */ +#define ADSP_EVERSION 0x00000004 +/* Unexpected problem encountered. */ +#define ADSP_EUNEXPECTED 0x00000005 +/* Unhandled problem occurred. */ +#define ADSP_EPANIC 0x00000006 +/* Unable to allocate resource. */ +#define ADSP_ENORESOURCE 0x00000007 +/* Invalid handle. */ +#define ADSP_EHANDLE 0x00000008 +/* Operation is already processed. */ +#define ADSP_EALREADY 0x00000009 +/* Operation is not ready to be processed. */ +#define ADSP_ENOTREADY 0x0000000A +/* Operation is pending completion. */ +#define ADSP_EPENDING 0x0000000B +/* Operation could not be accepted or processed. */ +#define ADSP_EBUSY 0x0000000C +/* Operation aborted due to an error. */ +#define ADSP_EABORTED 0x0000000D +/* Operation preempted by a higher priority. */ +#define ADSP_EPREEMPTED 0x0000000E +/* Operation requests intervention to complete. */ +#define ADSP_ECONTINUE 0x0000000F +/* Operation requests immediate intervention to complete. */ +#define ADSP_EIMMEDIATE 0x00000010 +/* Operation is not implemented. */ +#define ADSP_ENOTIMPL 0x00000011 +/* Operation needs more data or resources. */ +#define ADSP_ENEEDMORE 0x00000012 +/* Operation does not have memory. */ +#define ADSP_ENOMEMORY 0x00000014 +/* Item does not exist. */ +#define ADSP_ENOTEXIST 0x00000015 +/* Max count for adsp error code sent to HLOS*/ + +#endif /*__Q6DSP_ERR_NO_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c new file mode 100644 index 000000000000..593f66b8622f --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -0,0 +1,1006 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +// Copyright (c) 2018, Linaro Limited + +#include <linux/init.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/bitops.h> +#include <linux/component.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm.h> +#include <sound/control.h> +#include <sound/asound.h> +#include <sound/pcm_params.h> +#include "q6afe.h" +#include "q6asm.h" +#include "q6adm.h" +#include "q6routing.h" + +#define DRV_NAME "q6routing-component" + +#define Q6ROUTING_RX_MIXERS(id) \ + SOC_SINGLE_EXT("MultiMedia1", id, \ + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,\ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("MultiMedia2", id, \ + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,\ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("MultiMedia3", id, \ + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,\ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("MultiMedia4", id, \ + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,\ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("MultiMedia5", id, \ + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,\ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("MultiMedia6", id, \ + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,\ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("MultiMedia7", id, \ + MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,\ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("MultiMedia8", id, \ + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,\ + msm_routing_put_audio_mixer), + +#define Q6ROUTING_RX_DAPM_ROUTE(mix_name, s) \ + { mix_name, "MultiMedia1", "MM_DL1" }, \ + { mix_name, "MultiMedia2", "MM_DL2" }, \ + { mix_name, "MultiMedia3", "MM_DL3" }, \ + { mix_name, "MultiMedia4", "MM_DL4" }, \ + { mix_name, "MultiMedia5", "MM_DL5" }, \ + { mix_name, "MultiMedia6", "MM_DL6" }, \ + { mix_name, "MultiMedia7", "MM_DL7" }, \ + { mix_name, "MultiMedia8", "MM_DL8" }, \ + { s, NULL, mix_name } + +#define Q6ROUTING_TX_DAPM_ROUTE(mix_name) \ + { mix_name, "PRI_MI2S_TX", "PRI_MI2S_TX" }, \ + { mix_name, "SEC_MI2S_TX", "SEC_MI2S_TX" }, \ + { mix_name, "QUAT_MI2S_TX", "QUAT_MI2S_TX" }, \ + { mix_name, "TERT_MI2S_TX", "TERT_MI2S_TX" }, \ + { mix_name, "PRIMARY_TDM_TX_0", "PRIMARY_TDM_TX_0"}, \ + { mix_name, "PRIMARY_TDM_TX_1", "PRIMARY_TDM_TX_1"}, \ + { mix_name, "PRIMARY_TDM_TX_2", "PRIMARY_TDM_TX_2"}, \ + { mix_name, "PRIMARY_TDM_TX_3", "PRIMARY_TDM_TX_3"}, \ + { mix_name, "PRIMARY_TDM_TX_4", "PRIMARY_TDM_TX_4"}, \ + { mix_name, "PRIMARY_TDM_TX_5", "PRIMARY_TDM_TX_5"}, \ + { mix_name, "PRIMARY_TDM_TX_6", "PRIMARY_TDM_TX_6"}, \ + { mix_name, "PRIMARY_TDM_TX_7", "PRIMARY_TDM_TX_7"}, \ + { mix_name, "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, \ + { mix_name, "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, \ + { mix_name, "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, \ + { mix_name, "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, \ + { mix_name, "SEC_TDM_TX_4", "SEC_TDM_TX_4"}, \ + { mix_name, "SEC_TDM_TX_5", "SEC_TDM_TX_5"}, \ + { mix_name, "SEC_TDM_TX_6", "SEC_TDM_TX_6"}, \ + { mix_name, "SEC_TDM_TX_7", "SEC_TDM_TX_7"}, \ + { mix_name, "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, \ + { mix_name, "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, \ + { mix_name, "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, \ + { mix_name, "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, \ + { mix_name, "TERT_TDM_TX_4", "TERT_TDM_TX_4"}, \ + { mix_name, "TERT_TDM_TX_5", "TERT_TDM_TX_5"}, \ + { mix_name, "TERT_TDM_TX_6", "TERT_TDM_TX_6"}, \ + { mix_name, "TERT_TDM_TX_7", "TERT_TDM_TX_7"}, \ + { mix_name, "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, \ + { mix_name, "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, \ + { mix_name, "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, \ + { mix_name, "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, \ + { mix_name, "QUAT_TDM_TX_4", "QUAT_TDM_TX_4"}, \ + { mix_name, "QUAT_TDM_TX_5", "QUAT_TDM_TX_5"}, \ + { mix_name, "QUAT_TDM_TX_6", "QUAT_TDM_TX_6"}, \ + { mix_name, "QUAT_TDM_TX_7", "QUAT_TDM_TX_7"}, \ + { mix_name, "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, \ + { mix_name, "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, \ + { mix_name, "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, \ + { mix_name, "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, \ + { mix_name, "QUIN_TDM_TX_4", "QUIN_TDM_TX_4"}, \ + { mix_name, "QUIN_TDM_TX_5", "QUIN_TDM_TX_5"}, \ + { mix_name, "QUIN_TDM_TX_6", "QUIN_TDM_TX_6"}, \ + { mix_name, "QUIN_TDM_TX_7", "QUIN_TDM_TX_7"} + +#define Q6ROUTING_TX_MIXERS(id) \ + SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SEC_MI2S_TX", SECONDARY_MI2S_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TERT_MI2S_TX", TERTIARY_MI2S_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUAT_MI2S_TX", QUATERNARY_MI2S_TX, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("PRIMARY_TDM_TX_0", PRIMARY_TDM_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("PRIMARY_TDM_TX_1", PRIMARY_TDM_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("PRIMARY_TDM_TX_2", PRIMARY_TDM_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("PRIMARY_TDM_TX_3", PRIMARY_TDM_TX_3, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("PRIMARY_TDM_TX_4", PRIMARY_TDM_TX_4, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("PRIMARY_TDM_TX_5", PRIMARY_TDM_TX_5, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("PRIMARY_TDM_TX_6", PRIMARY_TDM_TX_6, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("PRIMARY_TDM_TX_7", PRIMARY_TDM_TX_7, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SEC_TDM_TX_0", SECONDARY_TDM_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SEC_TDM_TX_1", SECONDARY_TDM_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SEC_TDM_TX_2", SECONDARY_TDM_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SEC_TDM_TX_3", SECONDARY_TDM_TX_3, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SEC_TDM_TX_4", SECONDARY_TDM_TX_4, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SEC_TDM_TX_5", SECONDARY_TDM_TX_5, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SEC_TDM_TX_6", SECONDARY_TDM_TX_6, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("SEC_TDM_TX_7", SECONDARY_TDM_TX_7, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TERT_TDM_TX_0", TERTIARY_TDM_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TERT_TDM_TX_1", TERTIARY_TDM_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TERT_TDM_TX_2", TERTIARY_TDM_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TERT_TDM_TX_3", TERTIARY_TDM_TX_3, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TERT_TDM_TX_4", TERTIARY_TDM_TX_4, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TERT_TDM_TX_5", TERTIARY_TDM_TX_5, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TERT_TDM_TX_6", TERTIARY_TDM_TX_6, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TERT_TDM_TX_7", TERTIARY_TDM_TX_7, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUAT_TDM_TX_0", QUATERNARY_TDM_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUAT_TDM_TX_1", QUATERNARY_TDM_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUAT_TDM_TX_2", QUATERNARY_TDM_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUAT_TDM_TX_3", QUATERNARY_TDM_TX_3, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUAT_TDM_TX_4", QUATERNARY_TDM_TX_4, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUAT_TDM_TX_5", QUATERNARY_TDM_TX_5, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUAT_TDM_TX_6", QUATERNARY_TDM_TX_6, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUAT_TDM_TX_7", QUATERNARY_TDM_TX_7, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUIN_TDM_TX_0", QUINARY_TDM_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUIN_TDM_TX_1", QUINARY_TDM_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUIN_TDM_TX_2", QUINARY_TDM_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUIN_TDM_TX_3", QUINARY_TDM_TX_3, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUIN_TDM_TX_4", QUINARY_TDM_TX_4, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUIN_TDM_TX_5", QUINARY_TDM_TX_5, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUIN_TDM_TX_6", QUINARY_TDM_TX_6, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("QUIN_TDM_TX_7", QUINARY_TDM_TX_7, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), + +struct session_data { + int state; + int port_id; + int path_type; + int app_type; + int acdb_id; + int sample_rate; + int bits_per_sample; + int channels; + int perf_mode; + int numcopps; + int fedai_id; + unsigned long copp_map; + struct q6copp *copps[MAX_COPPS_PER_PORT]; +}; + +struct msm_routing_data { + struct session_data sessions[MAX_SESSIONS]; + struct session_data port_data[AFE_MAX_PORTS]; + struct device *dev; + struct mutex lock; +}; + +static struct msm_routing_data *routing_data; + +/** + * q6routing_stream_open() - Register a new stream for route setup + * + * @fedai_id: Frontend dai id. + * @perf_mode: Performance mode. + * @stream_id: ASM stream id to map. + * @stream_type: Direction of stream + * + * Return: Will be an negative on error or a zero on success. + */ +int q6routing_stream_open(int fedai_id, int perf_mode, + int stream_id, int stream_type) +{ + int j, topology, num_copps = 0; + struct route_payload payload; + struct q6copp *copp; + int copp_idx; + struct session_data *session, *pdata; + + if (!routing_data) { + pr_err("Routing driver not yet ready\n"); + return -EINVAL; + } + + session = &routing_data->sessions[stream_id - 1]; + pdata = &routing_data->port_data[session->port_id]; + + mutex_lock(&routing_data->lock); + session->fedai_id = fedai_id; + + session->path_type = pdata->path_type; + session->sample_rate = pdata->sample_rate; + session->channels = pdata->channels; + session->bits_per_sample = pdata->bits_per_sample; + + payload.num_copps = 0; /* only RX needs to use payload */ + topology = NULL_COPP_TOPOLOGY; + copp = q6adm_open(routing_data->dev, session->port_id, + session->path_type, session->sample_rate, + session->channels, topology, perf_mode, + session->bits_per_sample, 0, 0); + + if (!copp) { + mutex_unlock(&routing_data->lock); + return -EINVAL; + } + + copp_idx = q6adm_get_copp_id(copp); + set_bit(copp_idx, &session->copp_map); + session->copps[copp_idx] = copp; + + for_each_set_bit(j, &session->copp_map, MAX_COPPS_PER_PORT) { + payload.port_id[num_copps] = session->port_id; + payload.copp_idx[num_copps] = j; + num_copps++; + } + + if (num_copps) { + payload.num_copps = num_copps; + payload.session_id = stream_id; + q6adm_matrix_map(routing_data->dev, session->path_type, + payload, perf_mode); + } + mutex_unlock(&routing_data->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(q6routing_stream_open); + +static struct session_data *get_session_from_id(struct msm_routing_data *data, + int fedai_id) +{ + int i; + + for (i = 0; i < MAX_SESSIONS; i++) { + if (fedai_id == data->sessions[i].fedai_id) + return &data->sessions[i]; + } + + return NULL; +} +/** + * q6routing_stream_close() - Deregister a stream + * + * @fedai_id: Frontend dai id. + * @stream_type: Direction of stream + * + * Return: Will be an negative on error or a zero on success. + */ +void q6routing_stream_close(int fedai_id, int stream_type) +{ + struct session_data *session; + int idx; + + session = get_session_from_id(routing_data, fedai_id); + if (!session) + return; + + for_each_set_bit(idx, &session->copp_map, MAX_COPPS_PER_PORT) { + if (session->copps[idx]) { + q6adm_close(routing_data->dev, session->copps[idx]); + session->copps[idx] = NULL; + } + } + + session->fedai_id = -1; + session->copp_map = 0; +} +EXPORT_SYMBOL_GPL(q6routing_stream_close); + +static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int session_id = mc->shift; + struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); + struct msm_routing_data *priv = dev_get_drvdata(c->dev); + struct session_data *session = &priv->sessions[session_id]; + + if (session->port_id == mc->reg) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); + struct msm_routing_data *data = dev_get_drvdata(c->dev); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + int be_id = mc->reg; + int session_id = mc->shift; + struct session_data *session = &data->sessions[session_id]; + + if (ucontrol->value.integer.value[0]) { + session->port_id = be_id; + snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, update); + } else { + session->port_id = -1; + snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, update); + } + + return 1; +} + +static const struct snd_kcontrol_new hdmi_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(HDMI_RX) }; + +static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(PRIMARY_MI2S_RX) }; + +static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SECONDARY_MI2S_RX) }; + +static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUATERNARY_MI2S_RX) }; + +static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(TERTIARY_MI2S_RX) }; + +static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SLIMBUS_0_RX) }; + +static const struct snd_kcontrol_new slimbus_1_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SLIMBUS_1_RX) }; + +static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SLIMBUS_2_RX) }; + +static const struct snd_kcontrol_new slimbus_3_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SLIMBUS_3_RX) }; + +static const struct snd_kcontrol_new slimbus_4_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SLIMBUS_4_RX) }; + +static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SLIMBUS_5_RX) }; + +static const struct snd_kcontrol_new slimbus_6_rx_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SLIMBUS_6_RX) }; + +static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_0) }; + +static const struct snd_kcontrol_new pri_tdm_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_1) }; + +static const struct snd_kcontrol_new pri_tdm_rx_2_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_2) }; + +static const struct snd_kcontrol_new pri_tdm_rx_3_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_3) }; + +static const struct snd_kcontrol_new pri_tdm_rx_4_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_4) }; + +static const struct snd_kcontrol_new pri_tdm_rx_5_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_5) }; + +static const struct snd_kcontrol_new pri_tdm_rx_6_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_6) }; + +static const struct snd_kcontrol_new pri_tdm_rx_7_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(PRIMARY_TDM_RX_7) }; + +static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_0) }; + +static const struct snd_kcontrol_new sec_tdm_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_1) }; + +static const struct snd_kcontrol_new sec_tdm_rx_2_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_2) }; + +static const struct snd_kcontrol_new sec_tdm_rx_3_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_3) }; + +static const struct snd_kcontrol_new sec_tdm_rx_4_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_4) }; + +static const struct snd_kcontrol_new sec_tdm_rx_5_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_5) }; + +static const struct snd_kcontrol_new sec_tdm_rx_6_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_6) }; + +static const struct snd_kcontrol_new sec_tdm_rx_7_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(SECONDARY_TDM_RX_7) }; + +static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_0) }; + +static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_1) }; + +static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_2) }; + +static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_3) }; + +static const struct snd_kcontrol_new tert_tdm_rx_4_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_4) }; + +static const struct snd_kcontrol_new tert_tdm_rx_5_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_5) }; + +static const struct snd_kcontrol_new tert_tdm_rx_6_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_6) }; + +static const struct snd_kcontrol_new tert_tdm_rx_7_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(TERTIARY_TDM_RX_7) }; + +static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_0) }; + +static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_1) }; + +static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_2) }; + +static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_3) }; + +static const struct snd_kcontrol_new quat_tdm_rx_4_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_4) }; + +static const struct snd_kcontrol_new quat_tdm_rx_5_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_5) }; + +static const struct snd_kcontrol_new quat_tdm_rx_6_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_6) }; + +static const struct snd_kcontrol_new quat_tdm_rx_7_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUATERNARY_TDM_RX_7) }; + +static const struct snd_kcontrol_new quin_tdm_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_0) }; + +static const struct snd_kcontrol_new quin_tdm_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_1) }; + +static const struct snd_kcontrol_new quin_tdm_rx_2_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_2) }; + +static const struct snd_kcontrol_new quin_tdm_rx_3_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_3) }; + +static const struct snd_kcontrol_new quin_tdm_rx_4_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_4) }; + +static const struct snd_kcontrol_new quin_tdm_rx_5_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_5) }; + +static const struct snd_kcontrol_new quin_tdm_rx_6_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_6) }; + +static const struct snd_kcontrol_new quin_tdm_rx_7_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_7) }; + + +static const struct snd_kcontrol_new mmul1_mixer_controls[] = { + Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) }; + +static const struct snd_kcontrol_new mmul2_mixer_controls[] = { + Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA2) }; + +static const struct snd_kcontrol_new mmul3_mixer_controls[] = { + Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA3) }; + +static const struct snd_kcontrol_new mmul4_mixer_controls[] = { + Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA4) }; + +static const struct snd_kcontrol_new mmul5_mixer_controls[] = { + Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA5) }; + +static const struct snd_kcontrol_new mmul6_mixer_controls[] = { + Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA6) }; + +static const struct snd_kcontrol_new mmul7_mixer_controls[] = { + Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA7) }; + +static const struct snd_kcontrol_new mmul8_mixer_controls[] = { + Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA8) }; + +static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { + /* Frontend AIF */ + SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL3", "MultiMedia3 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL7", "MultiMedia7 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, 0, 0, 0), + + /* Mixer definitions */ + SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0, + hdmi_mixer_controls, + ARRAY_SIZE(hdmi_mixer_controls)), + + SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_rx_mixer_controls, + ARRAY_SIZE(slimbus_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_1_rx_mixer_controls, + ARRAY_SIZE(slimbus_1_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_2_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_2_rx_mixer_controls, + ARRAY_SIZE(slimbus_2_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_3_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_3_rx_mixer_controls, + ARRAY_SIZE(slimbus_3_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_4_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_4_rx_mixer_controls, + ARRAY_SIZE(slimbus_4_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_5_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_5_rx_mixer_controls, + ARRAY_SIZE(slimbus_5_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_6_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + slimbus_6_rx_mixer_controls, + ARRAY_SIZE(slimbus_6_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + primary_mi2s_rx_mixer_controls, + ARRAY_SIZE(primary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + secondary_mi2s_rx_mixer_controls, + ARRAY_SIZE(secondary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + quaternary_mi2s_rx_mixer_controls, + ARRAY_SIZE(quaternary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + tertiary_mi2s_rx_mixer_controls, + ARRAY_SIZE(tertiary_mi2s_rx_mixer_controls)), + SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_0_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_1_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_2_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_3_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_4_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_5_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_6_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("PRIMARY_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_7_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_7_mixer_controls)), + + SND_SOC_DAPM_MIXER("SEC_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_0_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_1_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_2_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_3_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_4_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_5_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_6_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_7_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_7_mixer_controls)), + + SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_0_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_1_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_2_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_3_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_4_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_5_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_6_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("TERT_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_7_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_7_mixer_controls)), + + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_0_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_1_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_2_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_3_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_4_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_5_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_6_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("QUAT_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, + quat_tdm_rx_7_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_7_mixer_controls)), + + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_0_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_1_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_2_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_3_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_4_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_5_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_6_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_7_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_7_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, + mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, + mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia3 Mixer", SND_SOC_NOPM, 0, 0, + mmul3_mixer_controls, ARRAY_SIZE(mmul3_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia4 Mixer", SND_SOC_NOPM, 0, 0, + mmul4_mixer_controls, ARRAY_SIZE(mmul4_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia5 Mixer", SND_SOC_NOPM, 0, 0, + mmul5_mixer_controls, ARRAY_SIZE(mmul5_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia6 Mixer", SND_SOC_NOPM, 0, 0, + mmul6_mixer_controls, ARRAY_SIZE(mmul6_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia7 Mixer", SND_SOC_NOPM, 0, 0, + mmul7_mixer_controls, ARRAY_SIZE(mmul7_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia8 Mixer", SND_SOC_NOPM, 0, 0, + mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)), + +}; + +static const struct snd_soc_dapm_route intercon[] = { + Q6ROUTING_RX_DAPM_ROUTE("HDMI Mixer", "HDMI_RX"), + Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_0_RX Audio Mixer", "SLIMBUS_0_RX"), + Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_1_RX Audio Mixer", "SLIMBUS_1_RX"), + Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_2_RX Audio Mixer", "SLIMBUS_2_RX"), + Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_3_RX Audio Mixer", "SLIMBUS_3_RX"), + Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_4_RX Audio Mixer", "SLIMBUS_4_RX"), + Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_5_RX Audio Mixer", "SLIMBUS_5_RX"), + Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_6_RX Audio Mixer", "SLIMBUS_6_RX"), + Q6ROUTING_RX_DAPM_ROUTE("QUAT_MI2S_RX Audio Mixer", "QUAT_MI2S_RX"), + Q6ROUTING_RX_DAPM_ROUTE("TERT_MI2S_RX Audio Mixer", "TERT_MI2S_RX"), + Q6ROUTING_RX_DAPM_ROUTE("SEC_MI2S_RX Audio Mixer", "SEC_MI2S_RX"), + Q6ROUTING_RX_DAPM_ROUTE("PRI_MI2S_RX Audio Mixer", "PRI_MI2S_RX"), + Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_0 Audio Mixer", + "PRIMARY_TDM_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_1 Audio Mixer", + "PRIMARY_TDM_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_2 Audio Mixer", + "PRIMARY_TDM_RX_2"), + Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_3 Audio Mixer", + "PRIMARY_TDM_RX_3"), + Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_4 Audio Mixer", + "PRIMARY_TDM_RX_4"), + Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_5 Audio Mixer", + "PRIMARY_TDM_RX_5"), + Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_6 Audio Mixer", + "PRIMARY_TDM_RX_6"), + Q6ROUTING_RX_DAPM_ROUTE("PRIMARY_TDM_RX_7 Audio Mixer", + "PRIMARY_TDM_RX_7"), + Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_0 Audio Mixer", "SEC_TDM_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_1 Audio Mixer", "SEC_TDM_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_2 Audio Mixer", "SEC_TDM_RX_2"), + Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_3 Audio Mixer", "SEC_TDM_RX_3"), + Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_4 Audio Mixer", "SEC_TDM_RX_4"), + Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_5 Audio Mixer", "SEC_TDM_RX_5"), + Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_6 Audio Mixer", "SEC_TDM_RX_6"), + Q6ROUTING_RX_DAPM_ROUTE("SEC_TDM_RX_7 Audio Mixer", "SEC_TDM_RX_7"), + Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_0 Audio Mixer", "TERT_TDM_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_1 Audio Mixer", "TERT_TDM_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_2 Audio Mixer", "TERT_TDM_RX_2"), + Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_3 Audio Mixer", "TERT_TDM_RX_3"), + Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_4 Audio Mixer", "TERT_TDM_RX_4"), + Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_5 Audio Mixer", "TERT_TDM_RX_5"), + Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_6 Audio Mixer", "TERT_TDM_RX_6"), + Q6ROUTING_RX_DAPM_ROUTE("TERT_TDM_RX_7 Audio Mixer", "TERT_TDM_RX_7"), + Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_0 Audio Mixer", "QUAT_TDM_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_1 Audio Mixer", "QUAT_TDM_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_2 Audio Mixer", "QUAT_TDM_RX_2"), + Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_3 Audio Mixer", "QUAT_TDM_RX_3"), + Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_4 Audio Mixer", "QUAT_TDM_RX_4"), + Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_5 Audio Mixer", "QUAT_TDM_RX_5"), + Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_6 Audio Mixer", "QUAT_TDM_RX_6"), + Q6ROUTING_RX_DAPM_ROUTE("QUAT_TDM_RX_7 Audio Mixer", "QUAT_TDM_RX_7"), + Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_0 Audio Mixer", "QUIN_TDM_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_1 Audio Mixer", "QUIN_TDM_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_2 Audio Mixer", "QUIN_TDM_RX_2"), + Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_3 Audio Mixer", "QUIN_TDM_RX_3"), + Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_4 Audio Mixer", "QUIN_TDM_RX_4"), + Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_5 Audio Mixer", "QUIN_TDM_RX_5"), + Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_6 Audio Mixer", "QUIN_TDM_RX_6"), + Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_7 Audio Mixer", "QUIN_TDM_RX_7"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia4 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia5 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia6 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia7 Mixer"), + Q6ROUTING_TX_DAPM_ROUTE("MultiMedia8 Mixer"), + + {"MM_UL1", NULL, "MultiMedia1 Mixer"}, + {"MM_UL2", NULL, "MultiMedia2 Mixer"}, + {"MM_UL3", NULL, "MultiMedia3 Mixer"}, + {"MM_UL4", NULL, "MultiMedia4 Mixer"}, + {"MM_UL5", NULL, "MultiMedia5 Mixer"}, + {"MM_UL6", NULL, "MultiMedia6 Mixer"}, + {"MM_UL7", NULL, "MultiMedia7 Mixer"}, + {"MM_UL8", NULL, "MultiMedia8 Mixer"}, +}; + +static int routing_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_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct msm_routing_data *data = dev_get_drvdata(c->dev); + unsigned int be_id = rtd->cpu_dai->id; + struct session_data *session; + int path_type; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + path_type = ADM_PATH_PLAYBACK; + else + path_type = ADM_PATH_LIVE_REC; + + if (be_id > AFE_MAX_PORTS) + return -EINVAL; + + session = &data->port_data[be_id]; + + mutex_lock(&data->lock); + + session->path_type = path_type; + session->sample_rate = params_rate(params); + session->channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + session->bits_per_sample = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + session->bits_per_sample = 24; + break; + default: + break; + } + + mutex_unlock(&data->lock); + return 0; +} + +static struct snd_pcm_ops q6pcm_routing_ops = { + .hw_params = routing_hw_params, +}; + +static int msm_routing_probe(struct snd_soc_component *c) +{ + int i; + + for (i = 0; i < MAX_SESSIONS; i++) + routing_data->sessions[i].port_id = -1; + + return 0; +} + +static const struct snd_soc_component_driver msm_soc_routing_component = { + .ops = &q6pcm_routing_ops, + .probe = msm_routing_probe, + .name = DRV_NAME, + .dapm_widgets = msm_qdsp6_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_qdsp6_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), +}; + +static int q6routing_dai_bind(struct device *dev, struct device *master, + void *data) +{ + routing_data = kzalloc(sizeof(*routing_data), GFP_KERNEL); + if (!routing_data) + return -ENOMEM; + + routing_data->dev = dev; + + mutex_init(&routing_data->lock); + dev_set_drvdata(dev, routing_data); + + return snd_soc_register_component(dev, &msm_soc_routing_component, + NULL, 0); +} + +static void q6routing_dai_unbind(struct device *dev, struct device *master, + void *d) +{ + struct msm_routing_data *data = dev_get_drvdata(dev); + + snd_soc_unregister_component(dev); + + kfree(data); + + routing_data = NULL; +} + +static const struct component_ops q6routing_dai_comp_ops = { + .bind = q6routing_dai_bind, + .unbind = q6routing_dai_unbind, +}; + +static int q6pcm_routing_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &q6routing_dai_comp_ops); +} + +static int q6pcm_routing_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &q6routing_dai_comp_ops); + return 0; +} + +static struct platform_driver q6pcm_routing_platform_driver = { + .driver = { + .name = "q6routing", + }, + .probe = q6pcm_routing_probe, + .remove = q6pcm_routing_remove, +}; +module_platform_driver(q6pcm_routing_platform_driver); + +MODULE_DESCRIPTION("Q6 Routing platform"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6routing.h b/sound/soc/qcom/qdsp6/q6routing.h new file mode 100644 index 000000000000..35514e651130 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6routing.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _Q6_PCM_ROUTING_H +#define _Q6_PCM_ROUTING_H + +int q6routing_stream_open(int fedai_id, int perf_mode, + int stream_id, int stream_type); +void q6routing_stream_close(int fedai_id, int stream_type); + +#endif /*_Q6_PCM_ROUTING_H */ diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index 9a10181a0811..f184168f9a41 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -220,45 +220,6 @@ static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int rockchip_sound_cdndp_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int mclk, ret; - - /* in bypass mode, the mclk has to be one of the frequencies below */ - switch (params_rate(params)) { - case 8000: - case 16000: - case 24000: - case 32000: - case 48000: - case 64000: - case 96000: - mclk = 12288000; - break; - case 11025: - case 22050: - case 44100: - case 88200: - mclk = 11289600; - break; - default: - return -EINVAL; - } - - ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, - SND_SOC_CLOCK_OUT); - if (ret < 0) { - dev_err(codec_dai->dev, "Can't set cpu clock out %d\n", ret); - return ret; - } - - return 0; -} - static int rockchip_sound_dmic_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -293,10 +254,6 @@ static const struct snd_soc_ops rockchip_sound_da7219_ops = { .hw_params = rockchip_sound_da7219_hw_params, }; -static const struct snd_soc_ops rockchip_sound_cdndp_ops = { - .hw_params = rockchip_sound_cdndp_hw_params, -}; - static const struct snd_soc_ops rockchip_sound_dmic_ops = { .hw_params = rockchip_sound_dmic_hw_params, }; @@ -323,8 +280,7 @@ static const struct snd_soc_dai_link rockchip_dais[] = { [DAILINK_CDNDP] = { .name = "DP", .stream_name = "DP PCM", - .codec_dai_name = "i2s-hifi", - .ops = &rockchip_sound_cdndp_ops, + .codec_dai_name = "spdif-hifi", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, }, diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 1aa5cd77ca24..0ae0800bf3a8 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -1,5 +1,5 @@ -menu "SoC Audio support for SuperH" - depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST +menu "SoC Audio support for Renesas SoCs" + depends on SUPERH || ARCH_RENESAS || COMPILE_TEST config SND_SOC_PCM_SH7760 tristate "SoC Audio support for Renesas SH7760" @@ -28,7 +28,7 @@ config SND_SOC_SH4_FSI config SND_SOC_SH4_SIU tristate - depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK + depends on ARCH_SHMOBILE && HAVE_CLK select DMA_ENGINE select DMADEVICES select SH_DMAE diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index f1d4fb566892..4221937ae79b 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -125,6 +125,13 @@ static struct rsnd_mod_ops rsnd_cmd_ops = { .stop = rsnd_cmd_stop, }; +static struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id) +{ + if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv))) + id = 0; + + return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id); +} int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id) { struct rsnd_priv *priv = rsnd_io_to_priv(io); @@ -133,14 +140,6 @@ int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id) return rsnd_dai_connect(mod, io, mod->type); } -struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id) -{ - if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv))) - id = 0; - - return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id); -} - int rsnd_cmd_probe(struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 94f081b93258..af04d41a4274 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -111,7 +111,7 @@ static const struct of_device_id rsnd_of_match[] = { { .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 }, { .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 }, - { .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN2 }, /* gen2 compatible */ + { .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN3 }, {}, }; MODULE_DEVICE_TABLE(of, rsnd_of_match); @@ -1352,6 +1352,37 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, #define PREALLOC_BUFFER (32 * 1024) #define PREALLOC_BUFFER_MAX (32 * 1024) +static int rsnd_preallocate_pages(struct snd_soc_pcm_runtime *rtd, + struct rsnd_dai_stream *io, + int stream) +{ + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_pcm_substream *substream; + int err; + + /* + * use Audio-DMAC dev if we can use IPMMU + * see + * rsnd_dmaen_attach() + */ + if (io->dmac_dev) + dev = io->dmac_dev; + + for (substream = rtd->pcm->streams[stream].substream; + substream; + substream = substream->next) { + err = snd_pcm_lib_preallocate_pages(substream, + SNDRV_DMA_TYPE_DEV, + dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); + if (err < 0) + return err; + } + + return 0; +} + static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai = rtd->cpu_dai; @@ -1366,11 +1397,17 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) if (ret) return ret; - return snd_pcm_lib_preallocate_pages_for_all( - rtd->pcm, - SNDRV_DMA_TYPE_DEV, - rtd->card->snd_card->dev, - PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); + ret = rsnd_preallocate_pages(rtd, &rdai->playback, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + return ret; + + ret = rsnd_preallocate_pages(rtd, &rdai->capture, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + return ret; + + return 0; } static const struct snd_soc_component_driver rsnd_soc_component = { diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 41de23417c4a..ef82b94d038b 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -253,6 +253,13 @@ static int rsnd_dmaen_attach(struct rsnd_dai_stream *io, return -EAGAIN; } + /* + * use it for IPMMU if needed + * see + * rsnd_preallocate_pages() + */ + io->dmac_dev = chan->device->dev; + dma_release_channel(chan); dmac->dmaen_num++; @@ -695,7 +702,7 @@ static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod, rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to); - /* for Gen2 */ + /* for Gen2 or later */ if (mod_from && mod_to) { ops = &rsnd_dmapp_ops; attach = rsnd_dmapp_attach; @@ -773,7 +780,7 @@ int rsnd_dma_probe(struct rsnd_priv *priv) return 0; /* - * for Gen2 + * for Gen2 or later */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp"); dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index f04c4100043a..25642e92dae0 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -414,7 +414,8 @@ int rsnd_gen_probe(struct rsnd_priv *priv) ret = -ENODEV; if (rsnd_is_gen1(priv)) ret = rsnd_gen1_probe(priv); - else if (rsnd_is_gen2(priv)) + else if (rsnd_is_gen2(priv) || + rsnd_is_gen3(priv)) ret = rsnd_gen2_probe(priv); if (ret < 0) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 172c8d612890..6d7280d2d9be 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -435,6 +435,7 @@ struct rsnd_dai_stream { struct snd_pcm_substream *substream; struct rsnd_mod *mod[RSND_MOD_MAX]; struct rsnd_dai *rdai; + struct device *dmac_dev; /* for IPMMU */ u32 parent_ssi_status; }; #define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) @@ -538,6 +539,7 @@ struct rsnd_priv { #define RSND_GEN_MASK (0xF << 0) #define RSND_GEN1 (1 << 0) #define RSND_GEN2 (2 << 0) +#define RSND_GEN3 (3 << 0) /* * below value will be filled on rsnd_gen_probe() @@ -609,6 +611,7 @@ struct rsnd_priv { #define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1) #define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2) +#define rsnd_is_gen3(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN3) #define rsnd_flags_has(p, f) ((p)->flags & (f)) #define rsnd_flags_set(p, f) ((p)->flags |= (f)) @@ -775,7 +778,6 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); int rsnd_cmd_probe(struct rsnd_priv *priv); void rsnd_cmd_remove(struct rsnd_priv *priv); int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id); -struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id); void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type); #ifdef DEBUG diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 333b802681ad..9538f76f8e20 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -171,7 +171,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, if (status & bit) return; - udelay(50); + udelay(5); } dev_warn(dev, "%s[%d] status check failed\n", @@ -1004,19 +1004,26 @@ static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); struct rsnd_ssi *ssi; + struct device_node *remote_node = of_graph_get_port_parent(remote_ep); + + /* support Gen3 only */ + if (!rsnd_is_gen3(priv)) + return; if (!mod) return; ssi = rsnd_mod_to_ssi(mod); - if (strstr(remote_ep->full_name, "hdmi0")) { + /* HDMI0 */ + if (strstr(remote_node->full_name, "hdmi@fead0000")) { rsnd_flags_set(ssi, RSND_SSI_HDMI0); dev_dbg(dev, "%s[%d] connected to HDMI0\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); } - if (strstr(remote_ep->full_name, "hdmi1")) { + /* HDMI1 */ + if (strstr(remote_node->full_name, "hdmi@feae0000")) { rsnd_flags_set(ssi, RSND_SSI_HDMI1); dev_dbg(dev, "%s[%d] connected to HDMI1\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c deleted file mode 100644 index 07f43356f963..000000000000 --- a/sound/soc/soc-cache.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * soc-cache.c -- ASoC register cache helpers - * - * Copyright 2009 Wolfson Microelectronics PLC. - * - * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include <sound/soc.h> -#include <linux/export.h> -#include <linux/slab.h> - -int snd_soc_cache_init(struct snd_soc_codec *codec) -{ - const struct snd_soc_codec_driver *codec_drv = codec->driver; - size_t reg_size; - - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - - if (!reg_size) - return 0; - - dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n", - codec->component.name); - - if (codec_drv->reg_cache_default) - codec->reg_cache = kmemdup(codec_drv->reg_cache_default, - reg_size, GFP_KERNEL); - else - codec->reg_cache = kzalloc(reg_size, GFP_KERNEL); - if (!codec->reg_cache) - return -ENOMEM; - - return 0; -} - -/* - * NOTE: keep in mind that this function might be called - * multiple times. - */ -int snd_soc_cache_exit(struct snd_soc_codec *codec) -{ - dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n", - codec->component.name); - kfree(codec->reg_cache); - codec->reg_cache = NULL; - return 0; -} diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 82402688bd8e..e095115fa9f9 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -26,56 +26,79 @@ #include <sound/initval.h> #include <sound/soc-dpcm.h> -static int soc_compr_open(struct snd_compr_stream *cstream) +static int soc_compr_components_open(struct snd_compr_stream *cstream, + struct snd_soc_component **last) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0, __ret; + int ret; - mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; - if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) { - ret = cpu_dai->driver->cops->startup(cstream, cpu_dai); - if (ret < 0) { - dev_err(cpu_dai->dev, - "Compress ASoC: can't open interface %s: %d\n", - cpu_dai->name, ret); - goto out; - } - } + if (!component->driver->compr_ops || + !component->driver->compr_ops->open) + continue; - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { - ret = platform->driver->compr_ops->open(cstream); + ret = component->driver->compr_ops->open(cstream); if (ret < 0) { - dev_err(platform->dev, + dev_err(component->dev, "Compress ASoC: can't open platform %s: %d\n", - platform->component.name, ret); - goto plat_err; + component->name, ret); + + *last = component; + return ret; } } + *last = NULL; + return 0; +} + +static int soc_compr_components_free(struct snd_compr_stream *cstream, + struct snd_soc_component *last) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; + if (component == last) + break; if (!component->driver->compr_ops || - !component->driver->compr_ops->open) + !component->driver->compr_ops->free) continue; - __ret = component->driver->compr_ops->open(cstream); - if (__ret < 0) { - dev_err(component->dev, - "Compress ASoC: can't open platform %s: %d\n", - component->name, __ret); - ret = __ret; + component->driver->compr_ops->free(cstream); + } + + return 0; +} + +static int soc_compr_open(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + if (cpu_dai->driver->cops && cpu_dai->driver->cops->startup) { + ret = cpu_dai->driver->cops->startup(cstream, cpu_dai); + if (ret < 0) { + dev_err(cpu_dai->dev, + "Compress ASoC: can't open interface %s: %d\n", + cpu_dai->name, ret); + goto out; } } + + ret = soc_compr_components_open(cstream, &component); if (ret < 0) goto machine_err; @@ -96,23 +119,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream) return 0; machine_err: - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - - if (!component->driver->compr_ops || - !component->driver->compr_ops->free) - continue; + soc_compr_components_free(cstream, component); - component->driver->compr_ops->free(cstream); - } - - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) - platform->driver->compr_ops->free(cstream); -plat_err: if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) cpu_dai->driver->cops->shutdown(cstream, cpu_dai); out: @@ -125,14 +133,12 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_pcm_substream *fe_substream = fe->pcm->streams[cstream->direction].substream; - struct snd_soc_platform *platform = fe->platform; struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; struct snd_soc_dapm_widget_list *list; int stream; - int ret = 0, __ret; + int ret; if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -151,35 +157,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) } } - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { - ret = platform->driver->compr_ops->open(cstream); - if (ret < 0) { - dev_err(platform->dev, - "Compress ASoC: can't open platform %s: %d\n", - platform->component.name, ret); - goto plat_err; - } - } - - for_each_rtdcom(fe, rtdcom) { - component = rtdcom->component; - - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - - if (!component->driver->compr_ops || - !component->driver->compr_ops->open) - continue; - - __ret = component->driver->compr_ops->open(cstream); - if (__ret < 0) { - dev_err(component->dev, - "Compress ASoC: can't open platform %s: %d\n", - component->name, __ret); - ret = __ret; - } - } + ret = soc_compr_components_open(cstream, &component); if (ret < 0) goto machine_err; @@ -235,23 +213,8 @@ fe_err: if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) fe->dai_link->compr_ops->shutdown(cstream); machine_err: - for_each_rtdcom(fe, rtdcom) { - component = rtdcom->component; - - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; + soc_compr_components_free(cstream, component); - if (!component->driver->compr_ops || - !component->driver->compr_ops->free) - continue; - - component->driver->compr_ops->free(cstream); - } - - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) - platform->driver->compr_ops->free(cstream); -plat_err: if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) cpu_dai->driver->cops->shutdown(cstream, cpu_dai); out: @@ -292,9 +255,6 @@ static void close_delayed_work(struct work_struct *work) static int soc_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; int stream; @@ -319,22 +279,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown) rtd->dai_link->compr_ops->shutdown(cstream); - for_each_rtdcom(rtd, rtdcom) { - component = rtdcom->component; - - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - - if (!component->driver->compr_ops || - !component->driver->compr_ops->free) - continue; - - component->driver->compr_ops->free(cstream); - } - - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) - platform->driver->compr_ops->free(cstream); + soc_compr_components_free(cstream, NULL); if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) cpu_dai->driver->cops->shutdown(cstream, cpu_dai); @@ -342,8 +287,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (cstream->direction == SND_COMPRESS_PLAYBACK) { if (snd_soc_runtime_ignore_pmdown_time(rtd)) { snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_STOP); + SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); } else { rtd->pop_wait = 1; queue_delayed_work(system_power_efficient_wq, @@ -353,8 +298,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) } else { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(rtd, - SNDRV_PCM_STREAM_CAPTURE, - SND_SOC_DAPM_STREAM_STOP); + SNDRV_PCM_STREAM_CAPTURE, + SND_SOC_DAPM_STREAM_STOP); } mutex_unlock(&rtd->pcm_mutex); @@ -364,9 +309,6 @@ static int soc_compr_free(struct snd_compr_stream *cstream) static int soc_compr_free_fe(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *fe = cstream->private_data; - struct snd_soc_platform *platform = fe->platform; - struct snd_soc_component *component; - struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; int stream, ret; @@ -404,22 +346,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) fe->dai_link->compr_ops->shutdown(cstream); - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) - platform->driver->compr_ops->free(cstream); - - for_each_rtdcom(fe, rtdcom) { - component = rtdcom->component; - - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - - if (!component->driver->compr_ops || - !component->driver->compr_ops->free) - continue; - - component->driver->compr_ops->free(cstream); - } + soc_compr_components_free(cstream, NULL); if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) cpu_dai->driver->cops->shutdown(cstream, cpu_dai); @@ -432,7 +359,6 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *codec_dai = rtd->codec_dai; @@ -441,19 +367,9 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->trigger) { - ret = platform->driver->compr_ops->trigger(cstream, cmd); - if (ret < 0) - goto out; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->trigger) continue; @@ -485,7 +401,6 @@ out: static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) { struct snd_soc_pcm_runtime *fe = cstream->private_data; - struct snd_soc_platform *platform = fe->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; @@ -494,19 +409,9 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || cmd == SND_COMPR_TRIGGER_DRAIN) { - if (platform && - platform->driver->compr_ops && - platform->driver->compr_ops->trigger) - return platform->driver->compr_ops->trigger(cstream, - cmd); - for_each_rtdcom(fe, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->trigger) continue; @@ -523,7 +428,6 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) else stream = SNDRV_PCM_STREAM_CAPTURE; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); if (cpu_dai->driver->cops && cpu_dai->driver->cops->trigger) { @@ -532,19 +436,9 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) goto out; } - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->trigger) { - ret = platform->driver->compr_ops->trigger(cstream, cmd); - if (ret < 0) - goto out; - } - for_each_rtdcom(fe, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->trigger) continue; @@ -585,7 +479,6 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, struct snd_compr_params *params) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -593,11 +486,12 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - /* first we call set_params for the platform driver - * this should configure the soc side - * if the machine has compressed ops then we call that as well - * expectation is that platform and machine will configure everything - * for this compress path, like configuring pcm port for codec + /* + * First we call set_params for the CPU DAI, then the component + * driver this should configure the SoC side. If the machine has + * compressed ops then we call that as well. The expectation is + * that these callbacks will configure everything for this compress + * path, like configuring a PCM port for a CODEC. */ if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_params) { ret = cpu_dai->driver->cops->set_params(cstream, params, cpu_dai); @@ -605,19 +499,9 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, goto err; } - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_params) { - ret = platform->driver->compr_ops->set_params(cstream, params); - if (ret < 0) - goto err; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->set_params) continue; @@ -637,10 +521,10 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, if (cstream->direction == SND_COMPRESS_PLAYBACK) snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_START); + SND_SOC_DAPM_STREAM_START); else snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, - SND_SOC_DAPM_STREAM_START); + SND_SOC_DAPM_STREAM_START); /* cancel any delayed stream shutdown that is pending */ rtd->pop_wait = 0; @@ -656,12 +540,11 @@ err: } static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, - struct snd_compr_params *params) + struct snd_compr_params *params) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_pcm_substream *fe_substream = fe->pcm->streams[cstream->direction].substream; - struct snd_soc_platform *platform = fe->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; @@ -680,19 +563,9 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, goto out; } - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_params) { - ret = platform->driver->compr_ops->set_params(cstream, params); - if (ret < 0) - goto out; - } - for_each_rtdcom(fe, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->set_params) continue; @@ -738,10 +611,9 @@ out: } static int soc_compr_get_params(struct snd_compr_stream *cstream, - struct snd_codec *params) + struct snd_codec *params) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -755,19 +627,9 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, goto err; } - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_params) { - ret = platform->driver->compr_ops->get_params(cstream, params); - if (ret < 0) - goto err; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->get_params) continue; @@ -783,29 +645,18 @@ err: } static int soc_compr_get_caps(struct snd_compr_stream *cstream, - struct snd_compr_caps *caps) + struct snd_compr_caps *caps) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_caps) { - ret = platform->driver->compr_ops->get_caps(cstream, caps); - if (ret < 0) - goto err; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->get_caps) continue; @@ -815,35 +666,23 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, ret = __ret; } -err: mutex_unlock(&rtd->pcm_mutex); return ret; } static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, - struct snd_compr_codec_caps *codec) + struct snd_compr_codec_caps *codec) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) { - ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); - if (ret < 0) - goto err; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->get_codec_caps) continue; @@ -853,7 +692,6 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, ret = __ret; } -err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -861,7 +699,6 @@ err: static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -875,19 +712,9 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) goto err; } - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->ack) { - ret = platform->driver->compr_ops->ack(cstream, bytes); - if (ret < 0) - goto err; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->ack) continue; @@ -903,10 +730,9 @@ err: } static int soc_compr_pointer(struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp) + struct snd_compr_tstamp *tstamp) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; int ret = 0, __ret; @@ -917,19 +743,9 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, if (cpu_dai->driver->cops && cpu_dai->driver->cops->pointer) cpu_dai->driver->cops->pointer(cstream, tstamp, cpu_dai); - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->pointer) { - ret = platform->driver->compr_ops->pointer(cstream, tstamp); - if (ret < 0) - goto err; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->pointer) continue; @@ -939,7 +755,6 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, ret = __ret; } -err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -948,26 +763,15 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, char __user *buf, size_t count) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; int ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->copy) { - ret = platform->driver->compr_ops->copy(cstream, buf, count); - if (ret < 0) - goto err; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->copy) continue; @@ -976,16 +780,14 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, break; } -err: mutex_unlock(&rtd->pcm_mutex); return ret; } static int soc_compr_set_metadata(struct snd_compr_stream *cstream, - struct snd_compr_metadata *metadata) + struct snd_compr_metadata *metadata) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -997,19 +799,9 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, return ret; } - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_metadata) { - ret = platform->driver->compr_ops->set_metadata(cstream, metadata); - if (ret < 0) - return ret; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->set_metadata) continue; @@ -1023,10 +815,9 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, } static int soc_compr_get_metadata(struct snd_compr_stream *cstream, - struct snd_compr_metadata *metadata) + struct snd_compr_metadata *metadata) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -1038,19 +829,9 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, return ret; } - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_metadata) { - ret = platform->driver->compr_ops->get_metadata(cstream, metadata); - if (ret < 0) - return ret; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->get_metadata) continue; @@ -1103,7 +884,6 @@ static struct snd_compr_ops soc_compr_dyn_ops = { */ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *codec_dai = rtd->codec_dai; @@ -1184,26 +964,17 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); } - - /* Add copy callback for not memory mapped DSPs */ - if (platform && platform->driver->compr_ops && platform->driver->compr_ops->copy) - compr->ops->copy = soc_compr_copy; - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->compr_ops || !component->driver->compr_ops->copy) continue; compr->ops->copy = soc_compr_copy; + break; } - mutex_init(&compr->lock); ret = snd_compress_new(rtd->card->snd_card, num, direction, new_name, compr); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index bf7ca32ab31f..3d56f1fe5914 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -56,8 +56,6 @@ EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); #endif static DEFINE_MUTEX(client_mutex); -static LIST_HEAD(platform_list); -static LIST_HEAD(codec_list); static LIST_HEAD(component_list); /* @@ -83,98 +81,6 @@ static const char * const dmi_blacklist[] = { NULL, /* terminator */ }; -/* returns the minimum number of bytes needed to represent - * a particular given value */ -static int min_bytes_needed(unsigned long val) -{ - int c = 0; - int i; - - for (i = (sizeof val * 8) - 1; i >= 0; --i, ++c) - if (val & (1UL << i)) - break; - c = (sizeof val * 8) - c; - if (!c || (c % 8)) - c = (c + 8) / 8; - else - c /= 8; - return c; -} - -/* fill buf which is 'len' bytes with a formatted - * string of the form 'reg: value\n' */ -static int format_register_str(struct snd_soc_codec *codec, - unsigned int reg, char *buf, size_t len) -{ - int wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; - int regsize = codec->driver->reg_word_size * 2; - int ret; - - /* +2 for ': ' and + 1 for '\n' */ - if (wordsize + regsize + 2 + 1 != len) - return -EINVAL; - - sprintf(buf, "%.*x: ", wordsize, reg); - buf += wordsize + 2; - - ret = snd_soc_read(codec, reg); - if (ret < 0) - memset(buf, 'X', regsize); - else - sprintf(buf, "%.*x", regsize, ret); - buf[regsize] = '\n'; - /* no NUL-termination needed */ - return 0; -} - -/* codec register dump */ -static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, - size_t count, loff_t pos) -{ - int i, step = 1; - int wordsize, regsize; - int len; - size_t total = 0; - loff_t p = 0; - - wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; - regsize = codec->driver->reg_word_size * 2; - - len = wordsize + regsize + 2 + 1; - - if (!codec->driver->reg_cache_size) - return 0; - - if (codec->driver->reg_cache_step) - step = codec->driver->reg_cache_step; - - for (i = 0; i < codec->driver->reg_cache_size; i += step) { - /* only support larger than PAGE_SIZE bytes debugfs - * entries for the default case */ - if (p >= pos) { - if (total + len >= count - 1) - break; - format_register_str(codec, i, buf + total, len); - total += len; - } - p += len; - } - - total = min(total, count - 1); - - return total; -} - -static ssize_t codec_reg_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); - - return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0); -} - -static DEVICE_ATTR_RO(codec_reg); - static ssize_t pmdown_time_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -200,7 +106,6 @@ static ssize_t pmdown_time_set(struct device *dev, static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set); static struct attribute *soc_dev_attrs[] = { - &dev_attr_codec_reg.attr, &dev_attr_pmdown_time.attr, NULL }; @@ -233,71 +138,6 @@ static const struct attribute_group *soc_dev_attr_groups[] = { }; #ifdef CONFIG_DEBUG_FS -static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - ssize_t ret; - struct snd_soc_codec *codec = file->private_data; - char *buf; - - if (*ppos < 0 || !count) - return -EINVAL; - - buf = kmalloc(count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = soc_codec_reg_show(codec, buf, count, *ppos); - if (ret >= 0) { - if (copy_to_user(user_buf, buf, ret)) { - kfree(buf); - return -EFAULT; - } - *ppos += ret; - } - - kfree(buf); - return ret; -} - -static ssize_t codec_reg_write_file(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - char buf[32]; - size_t buf_size; - char *start = buf; - unsigned long reg, value; - struct snd_soc_codec *codec = file->private_data; - int ret; - - buf_size = min(count, (sizeof(buf)-1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - while (*start == ' ') - start++; - reg = simple_strtoul(start, &start, 16); - while (*start == ' ') - start++; - ret = kstrtoul(start, 16, &value); - if (ret) - return ret; - - /* Userspace has been fiddling around behind the kernel's back */ - add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); - - snd_soc_write(codec, reg, value); - return buf_size; -} - -static const struct file_operations codec_reg_fops = { - .open = simple_open, - .read = codec_reg_read_file, - .write = codec_reg_write_file, - .llseek = default_llseek, -}; - static void soc_init_component_debugfs(struct snd_soc_component *component) { if (!component->card->debugfs_card_root) @@ -326,9 +166,6 @@ static void soc_init_component_debugfs(struct snd_soc_component *component) snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component), component->debugfs_root); - - if (component->init_debugfs) - component->init_debugfs(component); } static void soc_cleanup_component_debugfs(struct snd_soc_component *component) @@ -336,34 +173,6 @@ static void soc_cleanup_component_debugfs(struct snd_soc_component *component) debugfs_remove_recursive(component->debugfs_root); } -static void soc_init_codec_debugfs(struct snd_soc_component *component) -{ - struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - struct dentry *debugfs_reg; - - debugfs_reg = debugfs_create_file("codec_reg", 0644, - codec->component.debugfs_root, - codec, &codec_reg_fops); - if (!debugfs_reg) - dev_warn(codec->dev, - "ASoC: Failed to create codec register debugfs file\n"); -} - -static int codec_list_show(struct seq_file *m, void *v) -{ - struct snd_soc_codec *codec; - - mutex_lock(&client_mutex); - - list_for_each_entry(codec, &codec_list, list) - seq_printf(m, "%s\n", codec->component.name); - - mutex_unlock(&client_mutex); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(codec_list); - static int dai_list_show(struct seq_file *m, void *v) { struct snd_soc_component *component; @@ -381,20 +190,20 @@ static int dai_list_show(struct seq_file *m, void *v) } DEFINE_SHOW_ATTRIBUTE(dai_list); -static int platform_list_show(struct seq_file *m, void *v) +static int component_list_show(struct seq_file *m, void *v) { - struct snd_soc_platform *platform; + struct snd_soc_component *component; mutex_lock(&client_mutex); - list_for_each_entry(platform, &platform_list, list) - seq_printf(m, "%s\n", platform->component.name); + list_for_each_entry(component, &component_list, list) + seq_printf(m, "%s\n", component->name); mutex_unlock(&client_mutex); return 0; } -DEFINE_SHOW_ATTRIBUTE(platform_list); +DEFINE_SHOW_ATTRIBUTE(component_list); static void soc_init_card_debugfs(struct snd_soc_card *card) { @@ -431,17 +240,13 @@ static void snd_soc_debugfs_init(void) return; } - if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, - &codec_list_fops)) - pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); - if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, &dai_list_fops)) pr_warn("ASoC: Failed to create DAI list debugfs file\n"); - if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, - &platform_list_fops)) - pr_warn("ASoC: Failed to create platform list debugfs file\n"); + if (!debugfs_create_file("components", 0444, snd_soc_debugfs_root, NULL, + &component_list_fops)) + pr_warn("ASoC: Failed to create component list debugfs file\n"); } static void snd_soc_debugfs_exit(void) @@ -451,8 +256,6 @@ static void snd_soc_debugfs_exit(void) #else -#define soc_init_codec_debugfs NULL - static inline void soc_init_component_debugfs( struct snd_soc_component *component) { @@ -732,8 +535,8 @@ int snd_soc_suspend(struct device *dev) } case SND_SOC_BIAS_OFF: - if (component->suspend) - component->suspend(component); + if (component->driver->suspend) + component->driver->suspend(component); component->suspended = 1; if (component->regmap) regcache_mark_dirty(component->regmap); @@ -804,8 +607,8 @@ static void soc_resume_deferred(struct work_struct *work) list_for_each_entry(component, &card->component_dev_list, card_list) { if (component->suspended) { - if (component->resume) - component->resume(component); + if (component->driver->resume) + component->driver->resume(component); component->suspended = 0; } } @@ -1045,7 +848,6 @@ static int soc_bind_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link_component cpu_dai_component; struct snd_soc_component *component; struct snd_soc_dai **codec_dais; - struct snd_soc_platform *platform; struct device_node *platform_of_node; const char *platform_name; int i; @@ -1089,7 +891,6 @@ static int soc_bind_dai_link(struct snd_soc_card *card, /* Single codec links expect codec and codec_dai in runtime data */ rtd->codec_dai = codec_dais[0]; - rtd->codec = rtd->codec_dai->codec; /* if there's no platform we match on the empty platform */ platform_name = dai_link->platform_name; @@ -1113,23 +914,6 @@ static int soc_bind_dai_link(struct snd_soc_card *card, snd_soc_rtdcom_add(rtd, component); } - /* find one from the set of registered platforms */ - list_for_each_entry(platform, &platform_list, list) { - platform_of_node = platform->dev->of_node; - if (!platform_of_node && platform->dev->parent->of_node) - platform_of_node = platform->dev->parent->of_node; - - if (dai_link->platform_of_node) { - if (platform_of_node != dai_link->platform_of_node) - continue; - } else { - if (strcmp(platform->component.name, platform_name)) - continue; - } - - rtd->platform = platform; - } - soc_add_pcm_runtime(card, rtd); return 0; @@ -1145,8 +929,8 @@ static void soc_remove_component(struct snd_soc_component *component) list_del(&component->card_list); - if (component->remove) - component->remove(component); + if (component->driver->remove) + component->driver->remove(component); snd_soc_dapm_free(snd_soc_component_get_dapm(component)); @@ -1421,7 +1205,12 @@ static void soc_set_name_prefix(struct snd_soc_card *card, for (i = 0; i < card->num_configs; i++) { struct snd_soc_codec_conf *map = &card->codec_conf[i]; - if (map->of_node && component->dev->of_node != map->of_node) + struct device_node *component_of_node = component->dev->of_node; + + if (!component_of_node && component->dev->parent) + component_of_node = component->dev->parent->of_node; + + if (map->of_node && component_of_node != map->of_node) continue; if (map->dev_name && strcmp(component->name, map->dev_name)) continue; @@ -1480,8 +1269,8 @@ static int soc_probe_component(struct snd_soc_card *card, } } - if (component->probe) { - ret = component->probe(component); + if (component->driver->probe) { + ret = component->driver->probe(component); if (ret < 0) { dev_err(component->dev, "ASoC: failed to probe component %d\n", ret); @@ -1838,24 +1627,6 @@ static void soc_remove_aux_devices(struct snd_soc_card *card) } } -static int snd_soc_init_codec_cache(struct snd_soc_codec *codec) -{ - int ret; - - if (codec->cache_init) - return 0; - - ret = snd_soc_cache_init(codec); - if (ret < 0) { - dev_err(codec->dev, - "ASoC: Failed to set cache compression type: %d\n", - ret); - return ret; - } - codec->cache_init = 1; - return 0; -} - /** * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime * @rtd: The runtime for which the DAI link format should be changed @@ -1890,8 +1661,7 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, /* Flip the polarity for the "CPU" end of a CODEC<->CODEC link */ /* the component which has non_legacy_dai_naming is Codec */ - if (cpu_dai->codec || - cpu_dai->component->driver->non_legacy_dai_naming) { + if (cpu_dai->component->driver->non_legacy_dai_naming) { unsigned int inv_dai_fmt; inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; @@ -2078,7 +1848,6 @@ EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); static int snd_soc_instantiate_card(struct snd_soc_card *card) { - struct snd_soc_codec *codec; struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai_link *dai_link; int ret, i, order; @@ -2104,15 +1873,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) for (i = 0; i < card->num_links; i++) snd_soc_add_dai_link(card, card->dai_link+i); - /* initialize the register cache for each available codec */ - list_for_each_entry(codec, &codec_list, list) { - if (codec->cache_init) - continue; - ret = snd_soc_init_codec_cache(codec); - if (ret < 0) - goto base_error; - } - /* card bind complete so register a sound card */ ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, card->owner, 0, &card->snd_card); @@ -2494,43 +2254,6 @@ int snd_soc_add_component_controls(struct snd_soc_component *component, EXPORT_SYMBOL_GPL(snd_soc_add_component_controls); /** - * snd_soc_add_codec_controls - add an array of controls to a codec. - * Convenience function to add a list of controls. Many codecs were - * duplicating this code. - * - * @codec: codec to add controls to - * @controls: array of controls to add - * @num_controls: number of elements in the array - * - * Return 0 for success, else error. - */ -int snd_soc_add_codec_controls(struct snd_soc_codec *codec, - const struct snd_kcontrol_new *controls, unsigned int num_controls) -{ - return snd_soc_add_component_controls(&codec->component, controls, - num_controls); -} -EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls); - -/** - * snd_soc_add_platform_controls - add an array of controls to a platform. - * Convenience function to add a list of controls. - * - * @platform: platform to add controls to - * @controls: array of controls to add - * @num_controls: number of elements in the array - * - * Return 0 for success, else error. - */ -int snd_soc_add_platform_controls(struct snd_soc_platform *platform, - const struct snd_kcontrol_new *controls, unsigned int num_controls) -{ - return snd_soc_add_component_controls(&platform->component, controls, - num_controls); -} -EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls); - -/** * snd_soc_add_card_controls - add an array of controls to a SoC card. * Convenience function to add a list of controls. * @@ -2591,27 +2314,6 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); /** - * snd_soc_codec_set_sysclk - configure CODEC system or master clock. - * @codec: CODEC - * @clk_id: DAI specific clock ID - * @source: Source for the clock - * @freq: new clock frequency in Hz - * @dir: new clock direction - input/output. - * - * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. - */ -int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, - int source, unsigned int freq, int dir) -{ - if (codec->driver->set_sysclk) - return codec->driver->set_sysclk(codec, clk_id, source, - freq, dir); - else - return -ENOTSUPP; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk); - -/** * snd_soc_component_set_sysclk - configure COMPONENT system or master clock. * @component: COMPONENT * @clk_id: DAI specific clock ID @@ -2624,11 +2326,6 @@ EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk); int snd_soc_component_set_sysclk(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir) { - /* will be removed */ - if (component->set_sysclk) - return component->set_sysclk(component, clk_id, source, - freq, dir); - if (component->driver->set_sysclk) return component->driver->set_sysclk(component, clk_id, source, freq, dir); @@ -2680,27 +2377,6 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); /* - * snd_soc_codec_set_pll - configure codec PLL. - * @codec: CODEC - * @pll_id: DAI specific PLL ID - * @source: DAI specific source for the PLL - * @freq_in: PLL input clock frequency in Hz - * @freq_out: requested PLL output clock frequency in Hz - * - * Configures and enables PLL to generate output clock based on input clock. - */ -int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, - unsigned int freq_in, unsigned int freq_out) -{ - if (codec->driver->set_pll) - return codec->driver->set_pll(codec, pll_id, source, - freq_in, freq_out); - else - return -EINVAL; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); - -/* * snd_soc_component_set_pll - configure component PLL. * @component: COMPONENT * @pll_id: DAI specific PLL ID @@ -2714,11 +2390,6 @@ int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { - /* will be removed */ - if (component->set_pll) - return component->set_pll(component, pll_id, source, - freq_in, freq_out); - if (component->driver->set_pll) return component->driver->set_pll(component, pll_id, source, freq_in, freq_out); @@ -3112,8 +2783,7 @@ static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component, * parent's name. */ static int snd_soc_register_dais(struct snd_soc_component *component, - struct snd_soc_dai_driver *dai_drv, size_t count, - bool legacy_dai_naming) + struct snd_soc_dai_driver *dai_drv, size_t count) { struct device *dev = component->dev; struct snd_soc_dai *dai; @@ -3125,7 +2795,7 @@ static int snd_soc_register_dais(struct snd_soc_component *component, for (i = 0; i < count; i++) { dai = soc_add_dai(component, dai_drv + i, - count == 1 && legacy_dai_naming); + count == 1 && !component->driver->non_legacy_dai_naming); if (dai == NULL) { ret = -ENOMEM; goto err; @@ -3198,22 +2868,6 @@ static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm, return component->driver->stream_event(component, event); } -static int snd_soc_component_drv_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) -{ - if (component->driver->pcm_new) - return component->driver->pcm_new(rtd); - - return 0; -} - -static void snd_soc_component_drv_pcm_free(struct snd_soc_component *component, - struct snd_pcm *pcm) -{ - if (component->driver->pcm_free) - component->driver->pcm_free(pcm); -} - static int snd_soc_component_set_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { @@ -3235,15 +2889,6 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, component->dev = dev; component->driver = driver; - component->probe = component->driver->probe; - component->remove = component->driver->remove; - component->suspend = component->driver->suspend; - component->resume = component->driver->resume; - component->set_sysclk = component->driver->set_sysclk; - component->set_pll = component->driver->set_pll; - component->set_jack = component->driver->set_jack; - component->pcm_new = snd_soc_component_drv_pcm_new; - component->pcm_free = snd_soc_component_drv_pcm_free; dapm = snd_soc_component_get_dapm(component); dapm->dev = dev; @@ -3312,9 +2957,11 @@ EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap); #endif -static void snd_soc_component_add_unlocked(struct snd_soc_component *component) +static void snd_soc_component_add(struct snd_soc_component *component) { - if (!component->write && !component->read) { + mutex_lock(&client_mutex); + + if (!component->driver->write && !component->driver->read) { if (!component->regmap) component->regmap = dev_get_regmap(component->dev, NULL); if (component->regmap) @@ -3323,12 +2970,7 @@ static void snd_soc_component_add_unlocked(struct snd_soc_component *component) list_add(&component->list, &component_list); INIT_LIST_HEAD(&component->dobj_list); -} -static void snd_soc_component_add(struct snd_soc_component *component) -{ - mutex_lock(&client_mutex); - snd_soc_component_add_unlocked(component); mutex_unlock(&client_mutex); } @@ -3396,9 +3038,6 @@ int snd_soc_add_component(struct device *dev, if (ret) goto err_free; - component->ignore_pmdown_time = true; - component->registered_as_component = true; - if (component_driver->endianness) { for (i = 0; i < num_dai; i++) { convert_endianness_formats(&dai_drv[i].playback); @@ -3406,8 +3045,7 @@ int snd_soc_add_component(struct device *dev, } } - ret = snd_soc_register_dais(component, dai_drv, num_dai, - !component_driver->non_legacy_dai_naming); + ret = snd_soc_register_dais(component, dai_drv, num_dai); if (ret < 0) { dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); goto err_cleanup; @@ -3453,8 +3091,7 @@ static int __snd_soc_unregister_component(struct device *dev) mutex_lock(&client_mutex); list_for_each_entry(component, &component_list, list) { - if (dev != component->dev || - !component->registered_as_component) + if (dev != component->dev) continue; snd_soc_tplg_component_remove(component, SND_SOC_TPLG_INDEX_ALL); @@ -3503,375 +3140,6 @@ struct snd_soc_component *snd_soc_lookup_component(struct device *dev, } EXPORT_SYMBOL_GPL(snd_soc_lookup_component); -static int snd_soc_platform_drv_probe(struct snd_soc_component *component) -{ - struct snd_soc_platform *platform = snd_soc_component_to_platform(component); - - return platform->driver->probe(platform); -} - -static void snd_soc_platform_drv_remove(struct snd_soc_component *component) -{ - struct snd_soc_platform *platform = snd_soc_component_to_platform(component); - - platform->driver->remove(platform); -} - -static int snd_soc_platform_drv_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_platform *platform = snd_soc_component_to_platform(component); - - if (platform->driver->pcm_new) - return platform->driver->pcm_new(rtd); - - return 0; -} - -static void snd_soc_platform_drv_pcm_free(struct snd_soc_component *component, - struct snd_pcm *pcm) -{ - struct snd_soc_platform *platform = snd_soc_component_to_platform(component); - - if (platform->driver->pcm_free) - platform->driver->pcm_free(pcm); -} - -/** - * snd_soc_add_platform - Add a platform to the ASoC core - * @dev: The parent device for the platform - * @platform: The platform to add - * @platform_drv: The driver for the platform - */ -int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, - const struct snd_soc_platform_driver *platform_drv) -{ - int ret; - - ret = snd_soc_component_initialize(&platform->component, - &platform_drv->component_driver, dev); - if (ret) - return ret; - - platform->dev = dev; - platform->driver = platform_drv; - - if (platform_drv->probe) - platform->component.probe = snd_soc_platform_drv_probe; - if (platform_drv->remove) - platform->component.remove = snd_soc_platform_drv_remove; - if (platform_drv->pcm_new) - platform->component.pcm_new = snd_soc_platform_drv_pcm_new; - if (platform_drv->pcm_free) - platform->component.pcm_free = snd_soc_platform_drv_pcm_free; - -#ifdef CONFIG_DEBUG_FS - platform->component.debugfs_prefix = "platform"; -#endif - - mutex_lock(&client_mutex); - snd_soc_component_add_unlocked(&platform->component); - list_add(&platform->list, &platform_list); - mutex_unlock(&client_mutex); - - dev_dbg(dev, "ASoC: Registered platform '%s'\n", - platform->component.name); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_add_platform); - -/** - * snd_soc_register_platform - Register a platform with the ASoC core - * - * @dev: The device for the platform - * @platform_drv: The driver for the platform - */ -int snd_soc_register_platform(struct device *dev, - const struct snd_soc_platform_driver *platform_drv) -{ - struct snd_soc_platform *platform; - int ret; - - dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev)); - - platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL); - if (platform == NULL) - return -ENOMEM; - - ret = snd_soc_add_platform(dev, platform, platform_drv); - if (ret) - kfree(platform); - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_register_platform); - -/** - * snd_soc_remove_platform - Remove a platform from the ASoC core - * @platform: the platform to remove - */ -void snd_soc_remove_platform(struct snd_soc_platform *platform) -{ - - mutex_lock(&client_mutex); - list_del(&platform->list); - snd_soc_component_del_unlocked(&platform->component); - mutex_unlock(&client_mutex); - - dev_dbg(platform->dev, "ASoC: Unregistered platform '%s'\n", - platform->component.name); - - snd_soc_component_cleanup(&platform->component); -} -EXPORT_SYMBOL_GPL(snd_soc_remove_platform); - -struct snd_soc_platform *snd_soc_lookup_platform(struct device *dev) -{ - struct snd_soc_platform *platform; - - mutex_lock(&client_mutex); - list_for_each_entry(platform, &platform_list, list) { - if (dev == platform->dev) { - mutex_unlock(&client_mutex); - return platform; - } - } - mutex_unlock(&client_mutex); - - return NULL; -} -EXPORT_SYMBOL_GPL(snd_soc_lookup_platform); - -/** - * snd_soc_unregister_platform - Unregister a platform from the ASoC core - * - * @dev: platform to unregister - */ -void snd_soc_unregister_platform(struct device *dev) -{ - struct snd_soc_platform *platform; - - platform = snd_soc_lookup_platform(dev); - if (!platform) - return; - - snd_soc_remove_platform(platform); - kfree(platform); -} -EXPORT_SYMBOL_GPL(snd_soc_unregister_platform); - -static int snd_soc_codec_drv_probe(struct snd_soc_component *component) -{ - struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - - return codec->driver->probe(codec); -} - -static void snd_soc_codec_drv_remove(struct snd_soc_component *component) -{ - struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - - codec->driver->remove(codec); -} - -static int snd_soc_codec_drv_suspend(struct snd_soc_component *component) -{ - struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - - return codec->driver->suspend(codec); -} - -static int snd_soc_codec_drv_resume(struct snd_soc_component *component) -{ - struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - - return codec->driver->resume(codec); -} - -static int snd_soc_codec_drv_write(struct snd_soc_component *component, - unsigned int reg, unsigned int val) -{ - struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - - return codec->driver->write(codec, reg, val); -} - -static int snd_soc_codec_drv_read(struct snd_soc_component *component, - unsigned int reg, unsigned int *val) -{ - struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - - *val = codec->driver->read(codec, reg); - - return 0; -} - -static int snd_soc_codec_set_sysclk_(struct snd_soc_component *component, - int clk_id, int source, unsigned int freq, int dir) -{ - struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - - return snd_soc_codec_set_sysclk(codec, clk_id, source, freq, dir); -} - -static int snd_soc_codec_set_pll_(struct snd_soc_component *component, - int pll_id, int source, unsigned int freq_in, - unsigned int freq_out) -{ - struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - - return snd_soc_codec_set_pll(codec, pll_id, source, freq_in, freq_out); -} - -static int snd_soc_codec_set_jack_(struct snd_soc_component *component, - struct snd_soc_jack *jack, void *data) -{ - struct snd_soc_codec *codec = snd_soc_component_to_codec(component); - - return snd_soc_codec_set_jack(codec, jack, data); -} - -static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm, - enum snd_soc_bias_level level) -{ - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); - - return codec->driver->set_bias_level(codec, level); -} - -/** - * snd_soc_register_codec - Register a codec with the ASoC core - * - * @dev: The parent device for this codec - * @codec_drv: Codec driver - * @dai_drv: The associated DAI driver - * @num_dai: Number of DAIs - */ -int snd_soc_register_codec(struct device *dev, - const struct snd_soc_codec_driver *codec_drv, - struct snd_soc_dai_driver *dai_drv, - int num_dai) -{ - struct snd_soc_dapm_context *dapm; - struct snd_soc_codec *codec; - struct snd_soc_dai *dai; - int ret, i; - - dev_dbg(dev, "codec register %s\n", dev_name(dev)); - - codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); - if (codec == NULL) - return -ENOMEM; - - codec->component.codec = codec; - - ret = snd_soc_component_initialize(&codec->component, - &codec_drv->component_driver, dev); - if (ret) - goto err_free; - - if (codec_drv->probe) - codec->component.probe = snd_soc_codec_drv_probe; - if (codec_drv->remove) - codec->component.remove = snd_soc_codec_drv_remove; - if (codec_drv->suspend) - codec->component.suspend = snd_soc_codec_drv_suspend; - if (codec_drv->resume) - codec->component.resume = snd_soc_codec_drv_resume; - if (codec_drv->write) - codec->component.write = snd_soc_codec_drv_write; - if (codec_drv->read) - codec->component.read = snd_soc_codec_drv_read; - if (codec_drv->set_sysclk) - codec->component.set_sysclk = snd_soc_codec_set_sysclk_; - if (codec_drv->set_pll) - codec->component.set_pll = snd_soc_codec_set_pll_; - if (codec_drv->set_jack) - codec->component.set_jack = snd_soc_codec_set_jack_; - codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; - - dapm = snd_soc_codec_get_dapm(codec); - dapm->idle_bias_off = codec_drv->idle_bias_off; - dapm->suspend_bias_off = codec_drv->suspend_bias_off; - if (codec_drv->seq_notifier) - dapm->seq_notifier = codec_drv->seq_notifier; - if (codec_drv->set_bias_level) - dapm->set_bias_level = snd_soc_codec_set_bias_level; - codec->dev = dev; - codec->driver = codec_drv; - codec->component.val_bytes = codec_drv->reg_word_size; - -#ifdef CONFIG_DEBUG_FS - codec->component.init_debugfs = soc_init_codec_debugfs; - codec->component.debugfs_prefix = "codec"; -#endif - - if (codec_drv->get_regmap) - codec->component.regmap = codec_drv->get_regmap(dev); - - for (i = 0; i < num_dai; i++) { - convert_endianness_formats(&dai_drv[i].playback); - convert_endianness_formats(&dai_drv[i].capture); - } - - ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false); - if (ret < 0) { - dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); - goto err_cleanup; - } - - list_for_each_entry(dai, &codec->component.dai_list, list) - dai->codec = codec; - - mutex_lock(&client_mutex); - snd_soc_component_add_unlocked(&codec->component); - list_add(&codec->list, &codec_list); - mutex_unlock(&client_mutex); - - dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n", - codec->component.name); - return 0; - -err_cleanup: - snd_soc_component_cleanup(&codec->component); -err_free: - kfree(codec); - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_register_codec); - -/** - * snd_soc_unregister_codec - Unregister a codec from the ASoC core - * - * @dev: codec to unregister - */ -void snd_soc_unregister_codec(struct device *dev) -{ - struct snd_soc_codec *codec; - - mutex_lock(&client_mutex); - list_for_each_entry(codec, &codec_list, list) { - if (dev == codec->dev) - goto found; - } - mutex_unlock(&client_mutex); - return; - -found: - list_del(&codec->list); - snd_soc_component_del_unlocked(&codec->component); - mutex_unlock(&client_mutex); - - dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n", - codec->component.name); - - snd_soc_component_cleanup(&codec->component); - snd_soc_cache_exit(codec); - kfree(codec); -} -EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); - /* Retrieve a card's name from device tree */ int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 2d9709104ec5..36a39ba30226 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -433,6 +433,8 @@ err_data: static void dapm_kcontrol_free(struct snd_kcontrol *kctl) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); + + list_del(&data->paths); kfree(data->wlist); kfree(data); } @@ -724,18 +726,14 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, item = 0; } - for (i = 0; i < e->items; i++) { - if (!(strcmp(control_name, e->texts[i]))) { - path->name = e->texts[i]; - if (i == item) - path->connect = 1; - else - path->connect = 0; - return 0; - } - } + i = match_string(e->texts, e->items, control_name); + if (i < 0) + return -ENODEV; + + path->name = e->texts[i]; + path->connect = (i == item); + return 0; - return -ENODEV; } /* set up initial codec paths */ diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c index a57921eeee81..7ac745df1412 100644 --- a/sound/soc/soc-devres.c +++ b/sound/soc/soc-devres.c @@ -52,41 +52,6 @@ int devm_snd_soc_register_component(struct device *dev, } EXPORT_SYMBOL_GPL(devm_snd_soc_register_component); -static void devm_platform_release(struct device *dev, void *res) -{ - snd_soc_unregister_platform(*(struct device **)res); -} - -/** - * devm_snd_soc_register_platform - resource managed platform registration - * @dev: Device used to manage platform - * @platform_drv: platform to register - * - * Register a platform driver with automatic unregistration when the device is - * unregistered. - */ -int devm_snd_soc_register_platform(struct device *dev, - const struct snd_soc_platform_driver *platform_drv) -{ - struct device **ptr; - int ret; - - ptr = devres_alloc(devm_platform_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - ret = snd_soc_register_platform(dev, platform_drv); - if (ret == 0) { - *ptr = dev; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return ret; -} -EXPORT_SYMBOL_GPL(devm_snd_soc_register_platform); - static void devm_card_release(struct device *dev, void *res) { snd_soc_unregister_card(*(struct snd_soc_card **)res); diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index d36a192fbece..026cd5347e53 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -32,8 +32,6 @@ int snd_soc_component_read(struct snd_soc_component *component, if (component->regmap) ret = regmap_read(component->regmap, reg, val); - else if (component->read) - ret = component->read(component, reg, val); else if (component->driver->read) { *val = component->driver->read(component, reg); ret = 0; @@ -72,8 +70,6 @@ int snd_soc_component_write(struct snd_soc_component *component, { if (component->regmap) return regmap_write(component->regmap, reg, val); - else if (component->write) - return component->write(component, reg, val); else if (component->driver->write) return component->driver->write(component, reg, val); else @@ -209,82 +205,3 @@ int snd_soc_component_test_bits(struct snd_soc_component *component, return old != new; } EXPORT_SYMBOL_GPL(snd_soc_component_test_bits); - -unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) -{ - unsigned int val; - int ret; - - ret = snd_soc_component_read(&codec->component, reg, &val); - if (ret < 0) - return -1; - - return val; -} -EXPORT_SYMBOL_GPL(snd_soc_read); - -int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) -{ - return snd_soc_component_write(&codec->component, reg, val); -} -EXPORT_SYMBOL_GPL(snd_soc_write); - -/** - * snd_soc_update_bits - update codec register bits - * @codec: audio codec - * @reg: codec register - * @mask: register mask - * @value: new value - * - * Writes new register value. - * - * Returns 1 for change, 0 for no change, or negative error code. - */ -int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned int reg, - unsigned int mask, unsigned int value) -{ - return snd_soc_component_update_bits(&codec->component, reg, mask, - value); -} -EXPORT_SYMBOL_GPL(snd_soc_update_bits); - -/** - * snd_soc_test_bits - test register for change - * @codec: audio codec - * @reg: codec register - * @mask: register mask - * @value: new value - * - * Tests a register with a new value and checks if the new value is - * different from the old value. - * - * Returns 1 for change else 0. - */ -int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg, - unsigned int mask, unsigned int value) -{ - return snd_soc_component_test_bits(&codec->component, reg, mask, value); -} -EXPORT_SYMBOL_GPL(snd_soc_test_bits); - -int snd_soc_platform_read(struct snd_soc_platform *platform, - unsigned int reg) -{ - unsigned int val; - int ret; - - ret = snd_soc_component_read(&platform->component, reg, &val); - if (ret < 0) - return -1; - - return val; -} -EXPORT_SYMBOL_GPL(snd_soc_platform_read); - -int snd_soc_platform_write(struct snd_soc_platform *platform, - unsigned int reg, unsigned int val) -{ - return snd_soc_component_write(&platform->component, reg, val); -} -EXPORT_SYMBOL_GPL(snd_soc_platform_write); diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 99902ae1a2d9..b2b16044ae80 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -29,24 +29,6 @@ struct jack_gpio_tbl { }; /** - * snd_soc_codec_set_jack - configure codec jack. - * @codec: CODEC - * @jack: structure to use for the jack - * @data: can be used if codec driver need extra data for configuring jack - * - * Configures and enables jack detection function. - */ -int snd_soc_codec_set_jack(struct snd_soc_codec *codec, - struct snd_soc_jack *jack, void *data) -{ - if (codec->driver->set_jack) - return codec->driver->set_jack(codec, jack, data); - else - return -ENOTSUPP; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_set_jack); - -/** * snd_soc_component_set_jack - configure component jack. * @component: COMPONENTs * @jack: structure to use for the jack @@ -57,10 +39,6 @@ EXPORT_SYMBOL_GPL(snd_soc_codec_set_jack); int snd_soc_component_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *data) { - /* will be removed */ - if (component->set_jack) - return component->set_jack(component, jack, data); - if (component->driver->set_jack) return component->driver->set_jack(component, jack, data); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 68d9dc930096..5e7ae47a9658 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -135,7 +135,6 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_component *component; - int i; bool ignore = true; if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) @@ -147,10 +146,6 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) ignore &= !component->driver->use_pmdown_time; } - /* this will be removed */ - for (i = 0; i < rtd->num_codecs; i++) - ignore &= rtd->codec_dais[i]->component->ignore_pmdown_time; - return ignore; } @@ -456,13 +451,12 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) /* * Called by ALSA when a PCM substream is opened, the runtime->hw record is * then initialized and any private data can be allocated. This also calls - * startup for the cpu DAI, platform, machine and codec DAI. + * startup for the cpu DAI, component, machine and codec DAI. */ static int soc_pcm_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -492,23 +486,10 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - if (platform && platform->driver->ops && platform->driver->ops->open) { - ret = platform->driver->ops->open(substream); - if (ret < 0) { - dev_err(platform->dev, "ASoC: can't open platform" - " %s: %d\n", platform->component.name, ret); - goto platform_err; - } - } - ret = 0; for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->ops || !component->driver->ops->open) continue; @@ -517,7 +498,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) if (__ret < 0) { dev_err(component->dev, "ASoC: can't open component %s: %d\n", - component->name, ret); + component->name, __ret); ret = __ret; } } @@ -634,10 +615,6 @@ component_err: for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->ops || !component->driver->ops->close) continue; @@ -645,10 +622,6 @@ component_err: component->driver->ops->close(substream); } - if (platform && platform->driver->ops && platform->driver->ops->close) - platform->driver->ops->close(substream); - -platform_err: if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); out: @@ -701,13 +674,12 @@ static void close_delayed_work(struct work_struct *work) /* * Called by ALSA when a PCM substream is closed. Private data can be - * freed here. The cpu DAI, codec DAI, machine and platform are also + * freed here. The cpu DAI, codec DAI, machine and components are also * shutdown. */ static int soc_pcm_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -742,16 +714,9 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) if (rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); - if (platform && platform->driver->ops && platform->driver->ops->close) - platform->driver->ops->close(substream); - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->ops || !component->driver->ops->close) continue; @@ -805,7 +770,6 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) static int soc_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -823,22 +787,9 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (platform && platform->driver->ops && platform->driver->ops->prepare) { - ret = platform->driver->ops->prepare(substream); - if (ret < 0) { - dev_err(platform->dev, "ASoC: platform prepare error:" - " %d\n", ret); - goto out; - } - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->ops || !component->driver->ops->prepare) continue; @@ -932,7 +883,6 @@ static int soc_pcm_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_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -994,23 +944,10 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto interface_err; - if (platform && platform->driver->ops && platform->driver->ops->hw_params) { - ret = platform->driver->ops->hw_params(substream, params); - if (ret < 0) { - dev_err(platform->dev, "ASoC: %s hw params failed: %d\n", - platform->component.name, ret); - goto platform_err; - } - } - ret = 0; for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->ops || !component->driver->ops->hw_params) continue; @@ -1019,7 +956,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, if (__ret < 0) { dev_err(component->dev, "ASoC: %s hw params failed: %d\n", - component->name, ret); + component->name, __ret); ret = __ret; } } @@ -1043,10 +980,6 @@ component_err: for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->ops || !component->driver->ops->hw_free) continue; @@ -1054,10 +987,6 @@ component_err: component->driver->ops->hw_free(substream); } - if (platform && platform->driver->ops && platform->driver->ops->hw_free) - platform->driver->ops->hw_free(substream); - -platform_err: if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); @@ -1085,7 +1014,6 @@ codec_err: static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -1123,18 +1051,10 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) if (rtd->dai_link->ops->hw_free) rtd->dai_link->ops->hw_free(substream); - /* free any DMA resources */ - if (platform && platform->driver->ops && platform->driver->ops->hw_free) - platform->driver->ops->hw_free(substream); - /* free any component resources */ for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->ops || !component->driver->ops->hw_free) continue; @@ -1159,7 +1079,6 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -1176,19 +1095,9 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } } - if (platform && platform->driver->ops && platform->driver->ops->trigger) { - ret = platform->driver->ops->trigger(substream, cmd); - if (ret < 0) - return ret; - } - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->ops || !component->driver->ops->trigger) continue; @@ -1240,13 +1149,12 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, } /* * soc level wrapper for pointer callback - * If cpu_dai, codec_dai, platform driver has the delay callback, than + * If cpu_dai, codec_dai, component driver has the delay callback, then * the runtime->delay will be updated accordingly. */ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; @@ -1257,16 +1165,9 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) snd_pcm_sframes_t codec_delay = 0; int i; - if (platform && platform->driver->ops && platform->driver->ops->pointer) - offset = platform->driver->ops->pointer(substream); - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->ops || !component->driver->ops->pointer) continue; @@ -1878,14 +1779,15 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream, /* Symmetry only applies if we've got an active stream. */ if (rtd->cpu_dai->active) { - err = soc_pcm_apply_symmetry(be_substream, rtd->cpu_dai); + err = soc_pcm_apply_symmetry(fe_substream, + rtd->cpu_dai); if (err < 0) return err; } for (i = 0; i < rtd->num_codecs; i++) { if (rtd->codec_dais[i]->active) { - err = soc_pcm_apply_symmetry(be_substream, + err = soc_pcm_apply_symmetry(fe_substream, rtd->codec_dais[i]); if (err < 0) return err; @@ -1965,8 +1867,10 @@ int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) continue; if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) - continue; + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN)) { + soc_pcm_hw_free(be_substream); + be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; + } dev_dbg(be->dev, "ASoC: close BE %s\n", be->dai_link->name); @@ -2470,20 +2374,12 @@ static int soc_pcm_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_component *component; struct snd_soc_rtdcom_list *rtdcom; - if (platform && platform->driver->ops && platform->driver->ops->ioctl) - return platform->driver->ops->ioctl(substream, cmd, arg); - for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - /* ignore duplication for now */ - if (platform && (component == &platform->component)) - continue; - if (!component->driver->ops || !component->driver->ops->ioctl) continue; @@ -2847,8 +2743,8 @@ static void soc_pcm_private_free(struct snd_pcm *pcm) for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - if (component->pcm_free) - component->pcm_free(component, pcm); + if (component->driver->pcm_free) + component->driver->pcm_free(pcm); } } @@ -2987,7 +2883,6 @@ static int soc_rtdcom_mmap(struct snd_pcm_substream *substream, /* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { - struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_component *component; @@ -3106,16 +3001,6 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.mmap = soc_rtdcom_mmap; } - /* overwrite */ - if (platform && platform->driver->ops) { - rtd->ops.ack = platform->driver->ops->ack; - rtd->ops.copy_user = platform->driver->ops->copy_user; - rtd->ops.copy_kernel = platform->driver->ops->copy_kernel; - rtd->ops.fill_silence = platform->driver->ops->fill_silence; - rtd->ops.page = platform->driver->ops->page; - rtd->ops.mmap = platform->driver->ops->mmap; - } - if (playback) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops); @@ -3125,10 +3010,10 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) for_each_rtdcom(rtd, rtdcom) { component = rtdcom->component; - if (!component->pcm_new) + if (!component->driver->pcm_new) continue; - ret = component->pcm_new(component, rtd); + ret = component->driver->pcm_new(rtd); if (ret < 0) { dev_err(component->dev, "ASoC: pcm constructor failed: %d\n", diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 986b8b2f90fb..3fd5d9c867b9 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -54,62 +54,6 @@ #define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST #define SOC_TPLG_PASS_END SOC_TPLG_PASS_LINK -/* - * Old version of ABI structs, supported for backward compatibility. - */ - -/* Manifest v4 */ -struct snd_soc_tplg_manifest_v4 { - __le32 size; /* in bytes of this structure */ - __le32 control_elems; /* number of control elements */ - __le32 widget_elems; /* number of widget elements */ - __le32 graph_elems; /* number of graph elements */ - __le32 pcm_elems; /* number of PCM elements */ - __le32 dai_link_elems; /* number of DAI link elements */ - struct snd_soc_tplg_private priv; -} __packed; - -/* Stream Capabilities v4 */ -struct snd_soc_tplg_stream_caps_v4 { - __le32 size; /* in bytes of this structure */ - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le64 formats; /* supported formats SNDRV_PCM_FMTBIT_* */ - __le32 rates; /* supported rates SNDRV_PCM_RATE_* */ - __le32 rate_min; /* min rate */ - __le32 rate_max; /* max rate */ - __le32 channels_min; /* min channels */ - __le32 channels_max; /* max channels */ - __le32 periods_min; /* min number of periods */ - __le32 periods_max; /* max number of periods */ - __le32 period_size_min; /* min period size bytes */ - __le32 period_size_max; /* max period size bytes */ - __le32 buffer_size_min; /* min buffer size bytes */ - __le32 buffer_size_max; /* max buffer size bytes */ -} __packed; - -/* PCM v4 */ -struct snd_soc_tplg_pcm_v4 { - __le32 size; /* in bytes of this structure */ - char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - __le32 pcm_id; /* unique ID - used to match with DAI link */ - __le32 dai_id; /* unique ID - used to match */ - __le32 playback; /* supports playback mode */ - __le32 capture; /* supports capture mode */ - __le32 compress; /* 1 = compressed; 0 = PCM */ - struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */ - __le32 num_streams; /* number of streams */ - struct snd_soc_tplg_stream_caps_v4 caps[2]; /* playback and capture for DAI */ -} __packed; - -/* Physical link config v4 */ -struct snd_soc_tplg_link_config_v4 { - __le32 size; /* in bytes of this structure */ - __le32 id; /* unique ID - used to match */ - struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */ - __le32 num_streams; /* number of streams */ -} __packed; - /* topology context */ struct soc_tplg { const struct firmware *fw; @@ -1754,6 +1698,9 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, set_stream_info(stream, caps); } + if (pcm->compress) + dai_drv->compress_new = snd_soc_new_compress; + /* pass control to component driver for optional further init */ ret = soc_tplg_dai_load(tplg, dai_drv); if (ret < 0) { @@ -2006,6 +1953,21 @@ static void set_link_hw_format(struct snd_soc_dai_link *link, link->dai_fmt = hw_config->fmt & SND_SOC_DAIFMT_FORMAT_MASK; + /* clock gating */ + switch (hw_config->clock_gated) { + case SND_SOC_TPLG_DAI_CLK_GATE_GATED: + link->dai_fmt |= SND_SOC_DAIFMT_GATED; + break; + + case SND_SOC_TPLG_DAI_CLK_GATE_CONT: + link->dai_fmt |= SND_SOC_DAIFMT_CONT; + break; + + default: + /* ignore the value */ + break; + } + /* clock signal polarity */ invert_bclk = hw_config->invert_bclk; invert_fsync = hw_config->invert_fsync; @@ -2019,13 +1981,15 @@ static void set_link_hw_format(struct snd_soc_dai_link *link, link->dai_fmt |= SND_SOC_DAIFMT_IB_IF; /* clock masters */ - bclk_master = hw_config->bclk_master; - fsync_master = hw_config->fsync_master; - if (!bclk_master && !fsync_master) + bclk_master = (hw_config->bclk_master == + SND_SOC_TPLG_BCLK_CM); + fsync_master = (hw_config->fsync_master == + SND_SOC_TPLG_FSYNC_CM); + if (bclk_master && fsync_master) link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; - else if (bclk_master && !fsync_master) - link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; else if (!bclk_master && fsync_master) + link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + else if (bclk_master && !fsync_master) link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; else link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; @@ -2293,8 +2257,11 @@ static int manifest_new_ver(struct soc_tplg *tplg, *manifest = NULL; if (src->size != sizeof(*src_v4)) { - dev_err(tplg->dev, "ASoC: invalid manifest size\n"); - return -EINVAL; + dev_warn(tplg->dev, "ASoC: invalid manifest size %d\n", + src->size); + if (src->size) + return -EINVAL; + src->size = sizeof(*src_v4); } dev_warn(tplg->dev, "ASoC: old version of manifest\n"); diff --git a/sound/soc/uniphier/aio-compress.c b/sound/soc/uniphier/aio-compress.c index 4c1027aa615e..17f773ac5ca1 100644 --- a/sound/soc/uniphier/aio-compress.c +++ b/sound/soc/uniphier/aio-compress.c @@ -3,19 +3,6 @@ // Socionext UniPhier AIO Compress Audio driver. // // Copyright (c) 2017-2018 Socionext Inc. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; version 2 -// of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, see <http://www.gnu.org/licenses/>. #include <linux/bitfield.h> #include <linux/circ_buf.h> diff --git a/sound/soc/uniphier/aio-core.c b/sound/soc/uniphier/aio-core.c index 6d50042a4571..638cb3fc5f7b 100644 --- a/sound/soc/uniphier/aio-core.c +++ b/sound/soc/uniphier/aio-core.c @@ -3,19 +3,6 @@ // Socionext UniPhier AIO ALSA common driver. // // Copyright (c) 2016-2018 Socionext Inc. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; version 2 -// of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, see <http://www.gnu.org/licenses/>. #include <linux/bitfield.h> #include <linux/errno.h> @@ -673,6 +660,64 @@ void aio_port_set_enable(struct uniphier_aio_sub *sub, int enable) } /** + * aio_port_get_volume - get volume of AIO port block + * @sub: the AIO substream pointer + * + * Return: current volume, range is 0x0000 - 0xffff + */ +int aio_port_get_volume(struct uniphier_aio_sub *sub) +{ + struct regmap *r = sub->aio->chip->regmap; + u32 v; + + regmap_read(r, OPORTMXTYVOLGAINSTATUS(sub->swm->oport.map, 0), &v); + + return FIELD_GET(OPORTMXTYVOLGAINSTATUS_CUR_MASK, v); +} + +/** + * aio_port_set_volume - set volume of AIO port block + * @sub: the AIO substream pointer + * @vol: target volume, range is 0x0000 - 0xffff. + * + * Change digital volume and perfome fade-out/fade-in effect for specified + * output slot of port. Gained PCM value can calculate as the following: + * Gained = Original * vol / 0x4000 + */ +void aio_port_set_volume(struct uniphier_aio_sub *sub, int vol) +{ + struct regmap *r = sub->aio->chip->regmap; + int oport_map = sub->swm->oport.map; + int cur, diff, slope = 0, fs; + + if (sub->swm->dir == PORT_DIR_INPUT) + return; + + cur = aio_port_get_volume(sub); + diff = abs(vol - cur); + fs = params_rate(&sub->params); + if (fs) + slope = diff / AUD_VOL_FADE_TIME * 1000 / fs; + slope = max(1, slope); + + regmap_update_bits(r, OPORTMXTYVOLPARA1(oport_map, 0), + OPORTMXTYVOLPARA1_SLOPEU_MASK, slope << 16); + regmap_update_bits(r, OPORTMXTYVOLPARA2(oport_map, 0), + OPORTMXTYVOLPARA2_TARGET_MASK, vol); + + if (cur < vol) + regmap_update_bits(r, OPORTMXTYVOLPARA2(oport_map, 0), + OPORTMXTYVOLPARA2_FADE_MASK, + OPORTMXTYVOLPARA2_FADE_FADEIN); + else + regmap_update_bits(r, OPORTMXTYVOLPARA2(oport_map, 0), + OPORTMXTYVOLPARA2_FADE_MASK, + OPORTMXTYVOLPARA2_FADE_FADEOUT); + + regmap_write(r, AOUTFADECTR0, BIT(oport_map)); +} + +/** * aio_if_set_param - set parameters of AIO DMA I/F block * @sub: the AIO substream pointer * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM. diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index 1e5eb8e6f8c7..80daec17be25 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -3,19 +3,6 @@ // Socionext UniPhier AIO ALSA CPU DAI driver. // // Copyright (c) 2016-2018 Socionext Inc. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; version 2 -// of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, see <http://www.gnu.org/licenses/>. #include <linux/clk.h> #include <linux/errno.h> @@ -45,6 +32,35 @@ static bool is_valid_pll(struct uniphier_aio_chip *chip, int pll_id) return chip->plls[pll_id].enable; } +/** + * find_volume - find volume supported HW port by HW port number + * @chip: the AIO chip pointer + * @oport_hw: HW port number, one of AUD_HW_XXXX + * + * Find AIO device from device list by HW port number. Volume feature is + * available only in Output and PCM ports, this limitation comes from HW + * specifications. + * + * Return: The pointer of AIO substream if successful, otherwise NULL on error. + */ +static struct uniphier_aio_sub *find_volume(struct uniphier_aio_chip *chip, + int oport_hw) +{ + int i; + + for (i = 0; i < chip->num_aios; i++) { + struct uniphier_aio_sub *sub = &chip->aios[i].sub[0]; + + if (!sub->swm) + continue; + + if (sub->swm->oport.hw == oport_hw) + return sub; + } + + return NULL; +} + static bool match_spec(const struct uniphier_aio_spec *spec, const char *name, int dir) { @@ -300,6 +316,7 @@ static int uniphier_aio_hw_params(struct snd_pcm_substream *substream, sub->setting = 1; aio_port_reset(sub); + aio_port_set_volume(sub, sub->vol); aio_src_reset(sub); return 0; @@ -386,6 +403,8 @@ int uniphier_aio_dai_probe(struct snd_soc_dai *dai) sub->swm = &spec->swm; sub->spec = spec; + + sub->vol = AUD_VOL_INIT; } aio_iecout_set_enable(aio->chip, true); @@ -462,8 +481,116 @@ err_out_clock: } EXPORT_SYMBOL_GPL(uniphier_aio_dai_resume); +static int uniphier_aio_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.min = 0; + uinfo->value.integer.max = AUD_VOL_MAX; + + return 0; +} + +static int uniphier_aio_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct uniphier_aio_chip *chip = snd_soc_component_get_drvdata(comp); + struct uniphier_aio_sub *sub; + int oport_hw = kcontrol->private_value; + + sub = find_volume(chip, oport_hw); + if (!sub) + return 0; + + ucontrol->value.integer.value[0] = sub->vol; + + return 0; +} + +static int uniphier_aio_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct uniphier_aio_chip *chip = snd_soc_component_get_drvdata(comp); + struct uniphier_aio_sub *sub; + int oport_hw = kcontrol->private_value; + + sub = find_volume(chip, oport_hw); + if (!sub) + return 0; + + if (sub->vol == ucontrol->value.integer.value[0]) + return 0; + sub->vol = ucontrol->value.integer.value[0]; + + aio_port_set_volume(sub, sub->vol); + + return 0; +} + +static const struct snd_kcontrol_new uniphier_aio_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "HPCMOUT1 Volume", + .info = uniphier_aio_vol_info, + .get = uniphier_aio_vol_get, + .put = uniphier_aio_vol_put, + .private_value = AUD_HW_HPCMOUT1, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "PCMOUT1 Volume", + .info = uniphier_aio_vol_info, + .get = uniphier_aio_vol_get, + .put = uniphier_aio_vol_put, + .private_value = AUD_HW_PCMOUT1, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "PCMOUT2 Volume", + .info = uniphier_aio_vol_info, + .get = uniphier_aio_vol_get, + .put = uniphier_aio_vol_put, + .private_value = AUD_HW_PCMOUT2, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "PCMOUT3 Volume", + .info = uniphier_aio_vol_info, + .get = uniphier_aio_vol_get, + .put = uniphier_aio_vol_put, + .private_value = AUD_HW_PCMOUT3, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "HIECOUT1 Volume", + .info = uniphier_aio_vol_info, + .get = uniphier_aio_vol_get, + .put = uniphier_aio_vol_put, + .private_value = AUD_HW_HIECOUT1, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "IECOUT1 Volume", + .info = uniphier_aio_vol_info, + .get = uniphier_aio_vol_get, + .put = uniphier_aio_vol_put, + .private_value = AUD_HW_IECOUT1, + }, +}; + static const struct snd_soc_component_driver uniphier_aio_component = { .name = "uniphier-aio", + .controls = uniphier_aio_controls, + .num_controls = ARRAY_SIZE(uniphier_aio_controls), }; int uniphier_aio_probe(struct platform_device *pdev) diff --git a/sound/soc/uniphier/aio-dma.c b/sound/soc/uniphier/aio-dma.c index ef7bafa8e171..4ec6b65bfb44 100644 --- a/sound/soc/uniphier/aio-dma.c +++ b/sound/soc/uniphier/aio-dma.c @@ -3,19 +3,6 @@ // Socionext UniPhier AIO DMA driver. // // Copyright (c) 2016-2018 Socionext Inc. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; version 2 -// of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, see <http://www.gnu.org/licenses/>. #include <linux/dma-mapping.h> #include <linux/errno.h> diff --git a/sound/soc/uniphier/aio-ld11.c b/sound/soc/uniphier/aio-ld11.c index 4c4dd3dd4dee..ab04d3331be9 100644 --- a/sound/soc/uniphier/aio-ld11.c +++ b/sound/soc/uniphier/aio-ld11.c @@ -3,19 +3,6 @@ // Socionext UniPhier AIO ALSA driver for LD11/LD20. // // Copyright (c) 2016-2018 Socionext Inc. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; version 2 -// of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, see <http://www.gnu.org/licenses/>. #include <linux/module.h> diff --git a/sound/soc/uniphier/aio-reg.h b/sound/soc/uniphier/aio-reg.h index 136d3563cf44..45fdc6ae358a 100644 --- a/sound/soc/uniphier/aio-reg.h +++ b/sound/soc/uniphier/aio-reg.h @@ -3,19 +3,6 @@ * Socionext UniPhier AIO ALSA driver. * * Copyright (c) 2016-2018 Socionext Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef SND_UNIPHIER_AIO_REG_H__ @@ -182,6 +169,7 @@ #define PBINMXPAUSECTR1(n) (0x20208 + 0x40 * (n)) /* AOUT */ +#define AOUTFADECTR0 0x40020 #define AOUTENCTR0 0x40040 #define AOUTENCTR1 0x40044 #define AOUTENCTR2 0x40048 @@ -192,6 +180,9 @@ #define AOUTSRCRSTCTR1 0x400c4 #define AOUTSRCRSTCTR2 0x400c8 +/* AOUT PCMOUT has 5 slots, slot0-3: D0-3, slot4: DMIX */ +#define OPORT_SLOT_MAX 5 + /* AOUT(PCMOUTN) */ #define OPORTMXCTR1(n) (0x42000 + 0x400 * (n)) #define OPORTMXCTR1_I2SLRSEL_MASK (0x11 << 10) @@ -372,11 +363,30 @@ #define OPORTMXMASK_XCKMSK_ON (0x0 << 0) #define OPORTMXMASK_XCKMSK_OFF (0x7 << 0) #define OPORTMXDEBUG(n) (0x420fc + 0x400 * (n)) -#define OPORTMXT0RSTCTR(n) (0x4211c + 0x400 * (n)) -#define OPORTMXT1RSTCTR(n) (0x4213c + 0x400 * (n)) -#define OPORTMXT2RSTCTR(n) (0x4215c + 0x400 * (n)) -#define OPORTMXT3RSTCTR(n) (0x4217c + 0x400 * (n)) -#define OPORTMXT4RSTCTR(n) (0x4219c + 0x400 * (n)) +#define OPORTMXTYVOLPARA1(n, m) (0x42100 + 0x400 * (n) + 0x20 * (m)) +#define OPORTMXTYVOLPARA1_SLOPEU_MASK GENMASK(31, 16) +#define OPORTMXTYVOLPARA2(n, m) (0x42104 + 0x400 * (n) + 0x20 * (m)) +#define OPORTMXTYVOLPARA2_FADE_MASK GENMASK(17, 16) +#define OPORTMXTYVOLPARA2_FADE_NOOP (0x0 << 16) +#define OPORTMXTYVOLPARA2_FADE_FADEOUT (0x1 << 16) +#define OPORTMXTYVOLPARA2_FADE_FADEIN (0x2 << 16) +#define OPORTMXTYVOLPARA2_TARGET_MASK GENMASK(15, 0) +#define OPORTMXTYVOLGAINSTATUS(n, m) (0x42108 + 0x400 * (n) + 0x20 * (m)) +#define OPORTMXTYVOLGAINSTATUS_CUR_MASK GENMASK(15, 0) +#define OPORTMXTYSLOTCTR(n, m) (0x42114 + 0x400 * (n) + 0x20 * (m)) +#define OPORTMXTYSLOTCTR_SLOTSEL_MASK GENMASK(11, 8) +#define OPORTMXTYSLOTCTR_SLOTSEL_SLOT0 (0x8 << 8) +#define OPORTMXTYSLOTCTR_SLOTSEL_SLOT1 (0x9 << 8) +#define OPORTMXTYSLOTCTR_SLOTSEL_SLOT2 (0xa << 8) +#define OPORTMXTYSLOTCTR_SLOTSEL_SLOT3 (0xb << 8) +#define OPORTMXTYSLOTCTR_SLOTSEL_SLOT4 (0xc << 8) +#define OPORTMXT0SLOTCTR_MUTEOFF_MASK BIT(1) +#define OPORTMXT0SLOTCTR_MUTEOFF_MUTE (0x0 << 1) +#define OPORTMXT0SLOTCTR_MUTEOFF_UNMUTE (0x1 << 1) +#define OPORTMXTYRSTCTR(n, m) (0x4211c + 0x400 * (n) + 0x20 * (m)) +#define OPORTMXT0RSTCTR_RST_MASK BIT(1) +#define OPORTMXT0RSTCTR_RST_OFF (0x0 << 1) +#define OPORTMXT0RSTCTR_RST_ON (0x1 << 1) #define SBF_(frame, shift) (((frame) * 2 - 1) << shift) diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h index 8cab4a553a97..aa89c2f6fa24 100644 --- a/sound/soc/uniphier/aio.h +++ b/sound/soc/uniphier/aio.h @@ -3,19 +3,6 @@ * Socionext UniPhier AIO ALSA driver. * * Copyright (c) 2016-2018 Socionext Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef SND_UNIPHIER_AIO_H__ @@ -143,6 +130,10 @@ enum IEC61937_PC { #define AUD_PLLDIV_1_1 2 #define AUD_PLLDIV_2_3 3 +#define AUD_VOL_INIT 0x4000 /* +0dB */ +#define AUD_VOL_MAX 0xffff /* +6dB */ +#define AUD_VOL_FADE_TIME 20 /* 20ms */ + #define AUD_RING_SIZE (128 * 1024) #define AUD_MIN_FRAGMENT 4 @@ -244,6 +235,7 @@ struct uniphier_aio_sub { /* For PCM audio */ struct snd_pcm_substream *substream; struct snd_pcm_hw_params params; + int vol; /* For compress audio */ struct snd_compr_stream *cstream; @@ -336,6 +328,8 @@ int aio_port_set_clk(struct uniphier_aio_sub *sub); int aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through, const struct snd_pcm_hw_params *params); void aio_port_set_enable(struct uniphier_aio_sub *sub, int enable); +int aio_port_get_volume(struct uniphier_aio_sub *sub); +void aio_port_set_volume(struct uniphier_aio_sub *sub, int vol); int aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through); int aio_oport_set_stream_type(struct uniphier_aio_sub *sub, enum IEC61937_PC pc); diff --git a/sound/soc/uniphier/evea.c b/sound/soc/uniphier/evea.c index 73fd6730095c..f9c10165fbc1 100644 --- a/sound/soc/uniphier/evea.c +++ b/sound/soc/uniphier/evea.c @@ -54,8 +54,21 @@ struct evea_priv { int switch_hp; }; +static const char * const linsw1_sel1_text[] = { + "LIN1", "LIN2", "LIN3" +}; + +static SOC_ENUM_SINGLE_DECL(linsw1_sel1_enum, + ALINSW1, ALINSW1_SEL1_SHIFT, + linsw1_sel1_text); + +static const struct snd_kcontrol_new linesw1_mux[] = { + SOC_DAPM_ENUM("Line In 1 Source", linsw1_sel1_enum), +}; + static const struct snd_soc_dapm_widget evea_widgets[] = { - SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("Line In 1 Mux", SND_SOC_NOPM, 0, 0, linesw1_mux), SND_SOC_DAPM_INPUT("LIN1_LP"), SND_SOC_DAPM_INPUT("LIN1_RP"), SND_SOC_DAPM_INPUT("LIN2_LP"), @@ -63,7 +76,9 @@ static const struct snd_soc_dapm_widget evea_widgets[] = { SND_SOC_DAPM_INPUT("LIN3_LP"), SND_SOC_DAPM_INPUT("LIN3_RP"), - SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC HP", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC LO1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC LO2", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_OUTPUT("HP1_L"), SND_SOC_DAPM_OUTPUT("HP1_R"), SND_SOC_DAPM_OUTPUT("LO2_L"), @@ -71,17 +86,22 @@ static const struct snd_soc_dapm_widget evea_widgets[] = { }; static const struct snd_soc_dapm_route evea_routes[] = { - { "ADC", NULL, "LIN1_LP" }, - { "ADC", NULL, "LIN1_RP" }, - { "ADC", NULL, "LIN2_LP" }, - { "ADC", NULL, "LIN2_RP" }, - { "ADC", NULL, "LIN3_LP" }, - { "ADC", NULL, "LIN3_RP" }, - - { "HP1_L", NULL, "DAC" }, - { "HP1_R", NULL, "DAC" }, - { "LO2_L", NULL, "DAC" }, - { "LO2_R", NULL, "DAC" }, + { "Line In 1", NULL, "ADC" }, + { "ADC", NULL, "Line In 1 Mux" }, + { "Line In 1 Mux", "LIN1", "LIN1_LP" }, + { "Line In 1 Mux", "LIN1", "LIN1_RP" }, + { "Line In 1 Mux", "LIN2", "LIN2_LP" }, + { "Line In 1 Mux", "LIN2", "LIN2_RP" }, + { "Line In 1 Mux", "LIN3", "LIN3_LP" }, + { "Line In 1 Mux", "LIN3", "LIN3_RP" }, + + { "DAC HP", NULL, "Headphone 1" }, + { "DAC LO1", NULL, "Line Out 1" }, + { "DAC LO2", NULL, "Line Out 2" }, + { "HP1_L", NULL, "DAC HP" }, + { "HP1_R", NULL, "DAC HP" }, + { "LO2_L", NULL, "DAC LO2" }, + { "LO2_R", NULL, "DAC LO2" }, }; static void evea_set_power_state_on(struct evea_priv *evea) @@ -280,16 +300,7 @@ static int evea_set_switch_hp(struct snd_kcontrol *kcontrol, return evea_update_switch_hp(evea); } -static const char * const linsw1_sel1_text[] = { - "LIN1", "LIN2", "LIN3" -}; - -static SOC_ENUM_SINGLE_DECL(linsw1_sel1_enum, - ALINSW1, ALINSW1_SEL1_SHIFT, - linsw1_sel1_text); - static const struct snd_kcontrol_new evea_controls[] = { - SOC_ENUM("Line Capture Source", linsw1_sel1_enum), SOC_SINGLE_BOOL_EXT("Line Capture Switch", 0, evea_get_switch_lin, evea_set_switch_lin), SOC_SINGLE_BOOL_EXT("Line Playback Switch", 0, diff --git a/sound/soc/zte/zx-i2s.c b/sound/soc/zte/zx-i2s.c index 9a0565937d1f..0d36630aa1a7 100644 --- a/sound/soc/zte/zx-i2s.c +++ b/sound/soc/zte/zx-i2s.c @@ -20,9 +20,6 @@ #include <sound/core.h> #include <sound/dmaengine_pcm.h> #include <sound/initval.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> #define ZX_I2S_PROCESS_CTRL 0x04 #define ZX_I2S_TIMING_CTRL 0x08 @@ -197,7 +194,7 @@ static int zx_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) val |= (ZX_I2S_TIMING_I2S | ZX_I2S_TIMING_LSB_JUSTIF); break; default: - dev_err(cpu_dai->dev, "Unknown i2s timeing\n"); + dev_err(cpu_dai->dev, "Unknown i2s timing\n"); return -EINVAL; } diff --git a/sound/sound_core.c b/sound/sound_core.c index b4efb22db561..40ad000c2e3c 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -413,7 +413,7 @@ int register_sound_special_device(const struct file_operations *fops, int unit, break; } return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit, - name, S_IRUSR | S_IWUSR, dev); + name, 0600, dev); } EXPORT_SYMBOL(register_sound_special_device); @@ -440,7 +440,7 @@ EXPORT_SYMBOL(register_sound_special); int register_sound_mixer(const struct file_operations *fops, int dev) { return sound_insert_unit(&chains[0], fops, dev, 0, 128, - "mixer", S_IRUSR | S_IWUSR, NULL); + "mixer", 0600, NULL); } EXPORT_SYMBOL(register_sound_mixer); @@ -468,7 +468,7 @@ EXPORT_SYMBOL(register_sound_mixer); int register_sound_dsp(const struct file_operations *fops, int dev) { return sound_insert_unit(&chains[3], fops, dev, 3, 131, - "dsp", S_IWUSR | S_IRUSR, NULL); + "dsp", 0600, NULL); } EXPORT_SYMBOL(register_sound_dsp); diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index abc7bd5055eb..7609eceba1a2 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -2518,7 +2518,7 @@ static void snd_dbri_proc(struct snd_card *card) #ifdef DBRI_DEBUG if (!snd_card_proc_new(card, "debug", &entry)) { snd_info_set_text_ops(entry, dbri, dbri_debug_read); - entry->mode = S_IFREG | S_IRUGO; /* Readable only. */ + entry->mode = S_IFREG | 0444; /* Readable only. */ } #endif } @@ -2542,7 +2542,7 @@ static int snd_dbri_create(struct snd_card *card, dbri->irq = irq; dbri->dma = dma_zalloc_coherent(&op->dev, sizeof(struct dbri_dma), - &dbri->dma_dvma, GFP_ATOMIC); + &dbri->dma_dvma, GFP_KERNEL); if (!dbri->dma) return -ENOMEM; diff --git a/sound/usb/card.c b/sound/usb/card.c index 4a1c6bb3dfa0..a1ed798a1c6b 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -86,6 +86,8 @@ static bool ignore_ctl_error; static bool autoclock = true; static char *quirk_alias[SNDRV_CARDS]; +bool snd_usb_use_vmalloc = true; + module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); module_param_array(id, charp, NULL, 0444); @@ -105,6 +107,8 @@ module_param(autoclock, bool, 0444); MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes)."); module_param_array(quirk_alias, charp, NULL, 0444); MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef."); +module_param_named(use_vmalloc, snd_usb_use_vmalloc, bool, 0444); +MODULE_PARM_DESC(use_vmalloc, "Use vmalloc for PCM intermediate buffers (default: yes)."); /* * we keep the snd_usb_audio_t instances by ourselves for merging @@ -221,32 +225,13 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) struct usb_device *dev = chip->dev; struct usb_host_interface *host_iface; struct usb_interface_descriptor *altsd; - void *control_header; int i, protocol; - int rest_bytes; /* find audiocontrol interface */ host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0]; - control_header = snd_usb_find_csint_desc(host_iface->extra, - host_iface->extralen, - NULL, UAC_HEADER); altsd = get_iface_desc(host_iface); protocol = altsd->bInterfaceProtocol; - if (!control_header) { - dev_err(&dev->dev, "cannot find UAC_HEADER\n"); - return -EINVAL; - } - - rest_bytes = (void *)(host_iface->extra + host_iface->extralen) - - control_header; - - /* just to be sure -- this shouldn't hit at all */ - if (rest_bytes <= 0) { - dev_err(&dev->dev, "invalid control header\n"); - return -EINVAL; - } - switch (protocol) { default: dev_warn(&dev->dev, @@ -255,7 +240,25 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) /* fall through */ case UAC_VERSION_1: { - struct uac1_ac_header_descriptor *h1 = control_header; + struct uac1_ac_header_descriptor *h1; + int rest_bytes; + + h1 = snd_usb_find_csint_desc(host_iface->extra, + host_iface->extralen, + NULL, UAC_HEADER); + if (!h1) { + dev_err(&dev->dev, "cannot find UAC_HEADER\n"); + return -EINVAL; + } + + rest_bytes = (void *)(host_iface->extra + + host_iface->extralen) - (void *)h1; + + /* just to be sure -- this shouldn't hit at all */ + if (rest_bytes <= 0) { + dev_err(&dev->dev, "invalid control header\n"); + return -EINVAL; + } if (rest_bytes < sizeof(*h1)) { dev_err(&dev->dev, "too short v1 buffer descriptor\n"); @@ -308,6 +311,20 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) return -EINVAL; } + if (protocol == UAC_VERSION_3) { + int badd = assoc->bFunctionSubClass; + + if (badd != UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 && + (badd < UAC3_FUNCTION_SUBCLASS_GENERIC_IO || + badd > UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE)) { + dev_err(&dev->dev, + "Unsupported UAC3 BADD profile\n"); + return -EINVAL; + } + + chip->badd_profile = badd; + } + for (i = 0; i < assoc->bInterfaceCount; i++) { int intf = assoc->bFirstInterface + i; @@ -329,8 +346,9 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) * */ -static int snd_usb_audio_free(struct snd_usb_audio *chip) +static void snd_usb_audio_free(struct snd_card *card) { + struct snd_usb_audio *chip = card->private_data; struct snd_usb_endpoint *ep, *n; list_for_each_entry_safe(ep, n, &chip->ep_list, list) @@ -339,14 +357,90 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip) mutex_destroy(&chip->mutex); if (!atomic_read(&chip->shutdown)) dev_set_drvdata(&chip->dev->dev, NULL); - kfree(chip); - return 0; } -static int snd_usb_audio_dev_free(struct snd_device *device) +static void usb_audio_make_shortname(struct usb_device *dev, + struct snd_usb_audio *chip, + const struct snd_usb_audio_quirk *quirk) { - struct snd_usb_audio *chip = device->device_data; - return snd_usb_audio_free(chip); + struct snd_card *card = chip->card; + + if (quirk && quirk->product_name && *quirk->product_name) { + strlcpy(card->shortname, quirk->product_name, + sizeof(card->shortname)); + return; + } + + /* retrieve the device string as shortname */ + if (!dev->descriptor.iProduct || + usb_string(dev, dev->descriptor.iProduct, + card->shortname, sizeof(card->shortname)) <= 0) { + /* no name available from anywhere, so use ID */ + sprintf(card->shortname, "USB Device %#04x:%#04x", + USB_ID_VENDOR(chip->usb_id), + USB_ID_PRODUCT(chip->usb_id)); + } + + strim(card->shortname); +} + +static void usb_audio_make_longname(struct usb_device *dev, + struct snd_usb_audio *chip, + const struct snd_usb_audio_quirk *quirk) +{ + struct snd_card *card = chip->card; + int len; + + /* shortcut - if any pre-defined string is given, use it */ + if (quirk && quirk->profile_name && *quirk->profile_name) { + strlcpy(card->longname, quirk->profile_name, + sizeof(card->longname)); + return; + } + + if (quirk && quirk->vendor_name && *quirk->vendor_name) { + len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname)); + } else { + /* retrieve the vendor and device strings as longname */ + if (dev->descriptor.iManufacturer) + len = usb_string(dev, dev->descriptor.iManufacturer, + card->longname, sizeof(card->longname)); + else + len = 0; + /* we don't really care if there isn't any vendor string */ + } + if (len > 0) { + strim(card->longname); + if (*card->longname) + strlcat(card->longname, " ", sizeof(card->longname)); + } + + strlcat(card->longname, card->shortname, sizeof(card->longname)); + + len = strlcat(card->longname, " at ", sizeof(card->longname)); + + if (len < sizeof(card->longname)) + usb_make_path(dev, card->longname + len, sizeof(card->longname) - len); + + switch (snd_usb_get_speed(dev)) { + case USB_SPEED_LOW: + strlcat(card->longname, ", low speed", sizeof(card->longname)); + break; + case USB_SPEED_FULL: + strlcat(card->longname, ", full speed", sizeof(card->longname)); + break; + case USB_SPEED_HIGH: + strlcat(card->longname, ", high speed", sizeof(card->longname)); + break; + case USB_SPEED_SUPER: + strlcat(card->longname, ", super speed", sizeof(card->longname)); + break; + case USB_SPEED_SUPER_PLUS: + strlcat(card->longname, ", super speed plus", sizeof(card->longname)); + break; + default: + break; + } } /* @@ -360,11 +454,8 @@ static int snd_usb_audio_create(struct usb_interface *intf, { struct snd_card *card; struct snd_usb_audio *chip; - int err, len; + int err; char component[14]; - static struct snd_device_ops ops = { - .dev_free = snd_usb_audio_dev_free, - }; *rchip = NULL; @@ -382,18 +473,13 @@ static int snd_usb_audio_create(struct usb_interface *intf, } err = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE, - 0, &card); + sizeof(*chip), &card); if (err < 0) { dev_err(&dev->dev, "cannot create card instance %d\n", idx); return err; } - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (! chip) { - snd_card_free(card); - return -ENOMEM; - } - + chip = card->private_data; mutex_init(&chip->mutex); init_waitqueue_head(&chip->shutdown_wait); chip->index = idx; @@ -411,75 +497,15 @@ static int snd_usb_audio_create(struct usb_interface *intf, INIT_LIST_HEAD(&chip->midi_list); INIT_LIST_HEAD(&chip->mixer_list); - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { - snd_usb_audio_free(chip); - snd_card_free(card); - return err; - } + card->private_free = snd_usb_audio_free; strcpy(card->driver, "USB-Audio"); sprintf(component, "USB%04x:%04x", USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id)); snd_component_add(card, component); - /* retrieve the device string as shortname */ - if (quirk && quirk->product_name && *quirk->product_name) { - strlcpy(card->shortname, quirk->product_name, sizeof(card->shortname)); - } else { - if (!dev->descriptor.iProduct || - usb_string(dev, dev->descriptor.iProduct, - card->shortname, sizeof(card->shortname)) <= 0) { - /* no name available from anywhere, so use ID */ - sprintf(card->shortname, "USB Device %#04x:%#04x", - USB_ID_VENDOR(chip->usb_id), - USB_ID_PRODUCT(chip->usb_id)); - } - } - strim(card->shortname); - - /* retrieve the vendor and device strings as longname */ - if (quirk && quirk->vendor_name && *quirk->vendor_name) { - len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname)); - } else { - if (dev->descriptor.iManufacturer) - len = usb_string(dev, dev->descriptor.iManufacturer, - card->longname, sizeof(card->longname)); - else - len = 0; - /* we don't really care if there isn't any vendor string */ - } - if (len > 0) { - strim(card->longname); - if (*card->longname) - strlcat(card->longname, " ", sizeof(card->longname)); - } - - strlcat(card->longname, card->shortname, sizeof(card->longname)); - - len = strlcat(card->longname, " at ", sizeof(card->longname)); - - if (len < sizeof(card->longname)) - usb_make_path(dev, card->longname + len, sizeof(card->longname) - len); - - switch (snd_usb_get_speed(dev)) { - case USB_SPEED_LOW: - strlcat(card->longname, ", low speed", sizeof(card->longname)); - break; - case USB_SPEED_FULL: - strlcat(card->longname, ", full speed", sizeof(card->longname)); - break; - case USB_SPEED_HIGH: - strlcat(card->longname, ", high speed", sizeof(card->longname)); - break; - case USB_SPEED_SUPER: - strlcat(card->longname, ", super speed", sizeof(card->longname)); - break; - case USB_SPEED_SUPER_PLUS: - strlcat(card->longname, ", super speed plus", sizeof(card->longname)); - break; - default: - break; - } + usb_audio_make_shortname(dev, chip, quirk); + usb_audio_make_longname(dev, chip, quirk); snd_usb_audio_create_proc(chip); diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 0b030d8fe3fa..c79749613fa6 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -443,10 +443,11 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, data[0] = rate; data[1] = rate >> 8; data[2] = rate >> 16; - if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, - USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, - UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, - data, sizeof(data))) < 0) { + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, + USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, + UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, + data, sizeof(data)); + if (err < 0) { dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n", iface, fmt->altsetting, rate, ep); return err; @@ -460,10 +461,11 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, if (chip->sample_rate_read_error > 2) return 0; - if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, - USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, - UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, - data, sizeof(data))) < 0) { + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, + USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, + UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, + data, sizeof(data)); + if (err < 0) { dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n", iface, fmt->altsetting, ep); chip->sample_rate_read_error++; @@ -587,8 +589,15 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, default: return set_sample_rate_v1(chip, iface, alts, fmt, rate); - case UAC_VERSION_2: case UAC_VERSION_3: + if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { + if (rate != UAC3_BADD_SAMPLING_RATE) + return -ENXIO; + else + return 0; + } + /* fall through */ + case UAC_VERSION_2: return set_sample_rate_v2v3(chip, iface, alts, fmt, rate); } } diff --git a/sound/usb/helper.h b/sound/usb/helper.h index 4463e6d6dcb3..d338bd0e0ca6 100644 --- a/sound/usb/helper.h +++ b/sound/usb/helper.h @@ -18,16 +18,12 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, * retrieve usb_interface descriptor from the host interface * (conditional for compatibility with the older API) */ -#ifndef get_iface_desc #define get_iface_desc(iface) (&(iface)->desc) #define get_endpoint(alt,ep) (&(alt)->endpoint[ep].desc) #define get_ep_desc(ep) (&(ep)->desc) #define get_cfg_desc(cfg) (&(cfg)->desc) -#endif -#ifndef snd_usb_get_speed #define snd_usb_get_speed(dev) ((dev)->speed) -#endif static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip) { diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index bb5ab7a7dfa5..898afd3001ea 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -112,14 +112,12 @@ enum { #include "mixer_maps.c" static const struct usbmix_name_map * -find_map(struct mixer_build *state, int unitid, int control) +find_map(const struct usbmix_name_map *p, int unitid, int control) { - const struct usbmix_name_map *p = state->map; - if (!p) return NULL; - for (p = state->map; p->id; p++) { + for (; p->id; p++) { if (p->id == unitid && (!control || !p->control || control == p->control)) return p; @@ -201,10 +199,10 @@ static void *find_audio_control_unit(struct mixer_build *state, /* * copy a string with the given id */ -static int snd_usb_copy_string_desc(struct mixer_build *state, +static int snd_usb_copy_string_desc(struct snd_usb_audio *chip, int index, char *buf, int maxlen) { - int len = usb_string(state->chip->dev, index, buf, maxlen - 1); + int len = usb_string(chip->dev, index, buf, maxlen - 1); if (len < 0) return 0; @@ -600,7 +598,8 @@ int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, while (snd_ctl_find_id(mixer->chip->card, &kctl->id)) kctl->id.index++; - if ((err = snd_ctl_add(mixer->chip->card, kctl)) < 0) { + err = snd_ctl_add(mixer->chip->card, kctl); + if (err < 0) { usb_audio_dbg(mixer->chip, "cannot add control (err = %d)\n", err); return err; @@ -658,14 +657,14 @@ static struct iterm_name_combo { { 0 }, }; -static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm, +static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iterm, unsigned char *name, int maxlen, int term_only) { struct iterm_name_combo *names; int len; if (iterm->name) { - len = snd_usb_copy_string_desc(state, iterm->name, + len = snd_usb_copy_string_desc(chip, iterm->name, name, maxlen); if (len) return len; @@ -719,6 +718,66 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm } /* + * Get logical cluster information for UAC3 devices. + */ +static int get_cluster_channels_v3(struct mixer_build *state, unsigned int cluster_id) +{ + struct uac3_cluster_header_descriptor c_header; + int err; + + err = snd_usb_ctl_msg(state->chip->dev, + usb_rcvctrlpipe(state->chip->dev, 0), + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + cluster_id, + snd_usb_ctrl_intf(state->chip), + &c_header, sizeof(c_header)); + if (err < 0) + goto error; + if (err != sizeof(c_header)) { + err = -EIO; + goto error; + } + + return c_header.bNrChannels; + +error: + usb_audio_err(state->chip, "cannot request logical cluster ID: %d (err: %d)\n", cluster_id, err); + return err; +} + +/* + * Get number of channels for a Mixer Unit. + */ +static int uac_mixer_unit_get_channels(struct mixer_build *state, + struct uac_mixer_unit_descriptor *desc) +{ + int mu_channels; + + if (desc->bLength < 11) + return -EINVAL; + if (!desc->bNrInPins) + return -EINVAL; + + switch (state->mixer->protocol) { + case UAC_VERSION_1: + case UAC_VERSION_2: + default: + mu_channels = uac_mixer_unit_bNrChannels(desc); + break; + case UAC_VERSION_3: + mu_channels = get_cluster_channels_v3(state, + uac3_mixer_unit_wClusterDescrID(desc)); + break; + } + + if (!mu_channels) + return -EINVAL; + + return mu_channels; +} + +/* * parse the source unit recursively until it reaches to a terminal * or a branched unit. */ @@ -844,8 +903,12 @@ static int check_input_term(struct mixer_build *state, int id, term->id = id; term->type = le16_to_cpu(d->wTerminalType); - /* REVISIT: UAC3 IT doesn't have channels/cfg */ - term->channels = 0; + err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID)); + if (err < 0) + return err; + term->channels = err; + + /* REVISIT: UAC3 IT doesn't have channels cfg */ term->chconfig = 0; term->name = le16_to_cpu(d->wTerminalDescrStr); @@ -865,6 +928,18 @@ static int check_input_term(struct mixer_build *state, int id, term->name = le16_to_cpu(d->wClockSourceStr); return 0; } + case UAC3_MIXER_UNIT: { + struct uac_mixer_unit_descriptor *d = p1; + + err = uac_mixer_unit_get_channels(state, d); + if (err < 0) + return err; + + term->channels = err; + term->type = d->bDescriptorSubtype << 16; /* virtual type */ + + return 0; + } default: return -ENODEV; } @@ -1258,6 +1333,51 @@ static int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol, return 0; } +/* get the connectors status and report it as boolean type */ +static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *cval = kcontrol->private_data; + struct snd_usb_audio *chip = cval->head.mixer->chip; + int idx = 0, validx, ret, val; + + validx = cval->control << 8 | 0; + + ret = snd_usb_lock_shutdown(chip) ? -EIO : 0; + if (ret) + goto error; + + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); + if (cval->head.mixer->protocol == UAC_VERSION_2) { + struct uac2_connectors_ctl_blk uac2_conn; + + ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + validx, idx, &uac2_conn, sizeof(uac2_conn)); + val = !!uac2_conn.bNrChannels; + } else { /* UAC_VERSION_3 */ + struct uac3_insertion_ctl_blk uac3_conn; + + ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + validx, idx, &uac3_conn, sizeof(uac3_conn)); + val = !!uac3_conn.bmConInserted; + } + + snd_usb_unlock_shutdown(chip); + + if (ret < 0) { +error: + usb_audio_err(chip, + "cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", + UAC_GET_CUR, validx, idx, cval->val_type); + return ret; + } + + ucontrol->value.integer.value[0] = val; + return 0; +} + static struct snd_kcontrol_new usb_feature_unit_ctl = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "", /* will be filled later manually */ @@ -1288,6 +1408,15 @@ static struct snd_kcontrol_new usb_bool_master_control_ctl_ro = { .put = NULL, }; +static const struct snd_kcontrol_new usb_connector_ctl_ro = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "", /* will be filled later manually */ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_ctl_boolean_mono_info, + .get = mixer_ctl_connector_get, + .put = NULL, +}; + /* * This symbol is exported in order to allow the mixer quirks to * hook up to the standard feature unit control mechanism @@ -1341,16 +1470,16 @@ static struct usb_feature_control_info *get_feature_control_info(int control) return NULL; } -static void build_feature_ctl(struct mixer_build *state, void *raw_desc, - unsigned int ctl_mask, int control, - struct usb_audio_term *iterm, int unitid, - int readonly_mask) +static void __build_feature_ctl(struct usb_mixer_interface *mixer, + const struct usbmix_name_map *imap, + unsigned int ctl_mask, int control, + struct usb_audio_term *iterm, + struct usb_audio_term *oterm, + int unitid, int nameid, int readonly_mask) { - struct uac_feature_unit_descriptor *desc = raw_desc; struct usb_feature_control_info *ctl_info; unsigned int len = 0; int mapped_name = 0; - int nameid = uac_feature_unit_iFeature(desc); struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; const struct usbmix_name_map *map; @@ -1361,14 +1490,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, return; } - map = find_map(state, unitid, control); + map = find_map(imap, unitid, control); if (check_ignored_ctl(map)) return; cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return; - snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); + snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid); cval->control = control; cval->cmask = ctl_mask; @@ -1377,7 +1506,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, kfree(cval); return; } - if (state->mixer->protocol == UAC_VERSION_1) + if (mixer->protocol == UAC_VERSION_1) cval->val_type = ctl_info->type; else /* UAC_VERSION_2 */ cval->val_type = ctl_info->type_uac2 >= 0 ? @@ -1406,7 +1535,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (!kctl) { - usb_audio_err(state->chip, "cannot malloc kcontrol\n"); + usb_audio_err(mixer->chip, "cannot malloc kcontrol\n"); kfree(cval); return; } @@ -1415,7 +1544,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); mapped_name = len != 0; if (!len && nameid) - len = snd_usb_copy_string_desc(state, nameid, + len = snd_usb_copy_string_desc(mixer->chip, nameid, kctl->id.name, sizeof(kctl->id.name)); switch (control) { @@ -1430,10 +1559,12 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, * - otherwise, anonymous name. */ if (!len) { - len = get_term_name(state, iterm, kctl->id.name, - sizeof(kctl->id.name), 1); - if (!len) - len = get_term_name(state, &state->oterm, + if (iterm) + len = get_term_name(mixer->chip, iterm, + kctl->id.name, + sizeof(kctl->id.name), 1); + if (!len && oterm) + len = get_term_name(mixer->chip, oterm, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) @@ -1442,15 +1573,15 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, } if (!mapped_name) - check_no_speaker_on_headset(kctl, state->mixer->chip->card); + check_no_speaker_on_headset(kctl, mixer->chip->card); /* * determine the stream direction: * if the connected output is USB stream, then it's likely a * capture stream. otherwise it should be playback (hopefully :) */ - if (!mapped_name && !(state->oterm.type >> 16)) { - if ((state->oterm.type & 0xff00) == 0x0100) + if (!mapped_name && oterm && !(oterm->type >> 16)) { + if ((oterm->type & 0xff00) == 0x0100) append_ctl_name(kctl, " Capture"); else append_ctl_name(kctl, " Playback"); @@ -1478,7 +1609,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, } } - snd_usb_mixer_fu_apply_quirk(state->mixer, cval, unitid, kctl); + snd_usb_mixer_fu_apply_quirk(mixer, cval, unitid, kctl); range = (cval->max - cval->min) / cval->res; /* @@ -1487,26 +1618,46 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, * devices. It will definitively catch all buggy Logitech devices. */ if (range > 384) { - usb_audio_warn(state->chip, + usb_audio_warn(mixer->chip, "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.", range); - usb_audio_warn(state->chip, + usb_audio_warn(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d", cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); } - usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", + usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); snd_usb_mixer_add_control(&cval->head, kctl); } +static void build_feature_ctl(struct mixer_build *state, void *raw_desc, + unsigned int ctl_mask, int control, + struct usb_audio_term *iterm, int unitid, + int readonly_mask) +{ + struct uac_feature_unit_descriptor *desc = raw_desc; + int nameid = uac_feature_unit_iFeature(desc); + + __build_feature_ctl(state->mixer, state->map, ctl_mask, control, + iterm, &state->oterm, unitid, nameid, readonly_mask); +} + +static void build_feature_ctl_badd(struct usb_mixer_interface *mixer, + unsigned int ctl_mask, int control, int unitid, + const struct usbmix_name_map *badd_map) +{ + __build_feature_ctl(mixer, badd_map, ctl_mask, control, + NULL, NULL, unitid, 0, 0); +} + static void get_connector_control_name(struct mixer_build *state, struct usb_audio_term *term, bool is_input, char *name, int name_size) { - int name_len = get_term_name(state, term, name, name_size, 0); + int name_len = get_term_name(state->chip, term, name, name_size, 0); if (name_len == 0) strlcpy(name, "Unknown", name_size); @@ -1534,17 +1685,25 @@ static void build_connector_control(struct mixer_build *state, return; snd_usb_mixer_elem_init_std(&cval->head, state->mixer, term->id); /* - * The first byte from reading the UAC2_TE_CONNECTOR control returns the - * number of channels connected. This boolean ctl will simply report - * if any channels are connected or not. - * (Audio20_final.pdf Table 5-10: Connector Control CUR Parameter Block) + * UAC2: The first byte from reading the UAC2_TE_CONNECTOR control returns the + * number of channels connected. + * + * UAC3: The first byte specifies size of bitmap for the inserted controls. The + * following byte(s) specifies which connectors are inserted. + * + * This boolean ctl will simply report if any channels are connected + * or not. */ - cval->control = UAC2_TE_CONNECTOR; + if (state->mixer->protocol == UAC_VERSION_2) + cval->control = UAC2_TE_CONNECTOR; + else /* UAC_VERSION_3 */ + cval->control = UAC3_TE_INSERTION; + cval->val_type = USB_MIXER_BOOLEAN; cval->channels = 1; /* report true if any channel is connected */ cval->min = 0; cval->max = 1; - kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval); + kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval); if (!kctl) { usb_audio_err(state->chip, "cannot malloc kcontrol\n"); kfree(cval); @@ -1605,7 +1764,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, } kctl->private_free = snd_usb_mixer_elem_free; - ret = snd_usb_copy_string_desc(state, hdr->iClockSource, + ret = snd_usb_copy_string_desc(state->chip, hdr->iClockSource, name, sizeof(name)); if (ret > 0) snprintf(kctl->id.name, sizeof(kctl->id.name), @@ -1692,7 +1851,8 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, } /* parse the source unit */ - if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0) + err = parse_audio_unit(state, hdr->bSourceID); + if (err < 0) return err; /* determine the input source type and name */ @@ -1806,16 +1966,15 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, */ static void build_mixer_unit_ctl(struct mixer_build *state, struct uac_mixer_unit_descriptor *desc, - int in_pin, int in_ch, int unitid, - struct usb_audio_term *iterm) + int in_pin, int in_ch, int num_outs, + int unitid, struct usb_audio_term *iterm) { struct usb_mixer_elem_info *cval; - unsigned int num_outs = uac_mixer_unit_bNrChannels(desc); unsigned int i, len; struct snd_kcontrol *kctl; const struct usbmix_name_map *map; - map = find_map(state, unitid, 0); + map = find_map(state->map, unitid, 0); if (check_ignored_ctl(map)) return; @@ -1848,7 +2007,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); if (!len) - len = get_term_name(state, iterm, kctl->id.name, + len = get_term_name(state->chip, iterm, kctl->id.name, sizeof(kctl->id.name), 0); if (!len) len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1); @@ -1863,16 +2022,28 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid, void *raw_desc) { struct usb_audio_term iterm; - struct uac2_input_terminal_descriptor *d = raw_desc; + unsigned int control, bmctls, term_id; - check_input_term(state, d->bTerminalID, &iterm); if (state->mixer->protocol == UAC_VERSION_2) { - /* Check for jack detection. */ - if (uac_v2v3_control_is_readable(le16_to_cpu(d->bmControls), - UAC2_TE_CONNECTOR)) { - build_connector_control(state, &iterm, true); - } + struct uac2_input_terminal_descriptor *d_v2 = raw_desc; + control = UAC2_TE_CONNECTOR; + term_id = d_v2->bTerminalID; + bmctls = le16_to_cpu(d_v2->bmControls); + } else if (state->mixer->protocol == UAC_VERSION_3) { + struct uac3_input_terminal_descriptor *d_v3 = raw_desc; + control = UAC3_TE_INSERTION; + term_id = d_v3->bTerminalID; + bmctls = le32_to_cpu(d_v3->bmControls); + } else { + return 0; /* UAC1. No Insertion control */ } + + check_input_term(state, term_id, &iterm); + + /* Check for jack detection. */ + if (uac_v2v3_control_is_readable(bmctls, control)) + build_connector_control(state, &iterm, true); + return 0; } @@ -1887,14 +2058,17 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, int input_pins, num_ins, num_outs; int pin, ich, err; - if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) || - !(num_outs = uac_mixer_unit_bNrChannels(desc))) { + err = uac_mixer_unit_get_channels(state, desc); + if (err < 0) { usb_audio_err(state->chip, "invalid MIXER UNIT descriptor %d\n", unitid); - return -EINVAL; + return err; } + num_outs = err; + input_pins = desc->bNrInPins; + num_ins = 0; ich = 0; for (pin = 0; pin < input_pins; pin++) { @@ -1921,7 +2095,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, } } if (ich_has_controls) - build_mixer_unit_ctl(state, desc, pin, ich, + build_mixer_unit_ctl(state, desc, pin, ich, num_outs, unitid, &iterm); } } @@ -2098,7 +2272,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, } for (i = 0; i < num_ins; i++) { - if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0) + err = parse_audio_unit(state, desc->baSourceID[i]); + if (err < 0) return err; } @@ -2114,7 +2289,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, if (!(controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1)))) continue; - map = find_map(state, unitid, valinfo->control); + map = find_map(state->map, unitid, valinfo->control); if (check_ignored_ctl(map)) continue; cval = kzalloc(sizeof(*cval), GFP_KERNEL); @@ -2162,7 +2337,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol); len = 0; if (nameid) - len = snd_usb_copy_string_desc(state, nameid, + len = snd_usb_copy_string_desc(state->chip, + nameid, kctl->id.name, sizeof(kctl->id.name)); if (!len) @@ -2310,14 +2486,15 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, } for (i = 0; i < desc->bNrInPins; i++) { - if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0) + err = parse_audio_unit(state, desc->baSourceID[i]); + if (err < 0) return err; } if (desc->bNrInPins == 1) /* only one ? nonsense! */ return 0; - map = find_map(state, unitid, 0); + map = find_map(state->map, unitid, 0); if (check_ignored_ctl(map)) return 0; @@ -2358,7 +2535,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, len = check_mapped_selector_name(state, unitid, i, namelist[i], MAX_ITEM_NAME_LEN); if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0) - len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0); + len = get_term_name(state->chip, &iterm, namelist[i], + MAX_ITEM_NAME_LEN, 0); if (! len) sprintf(namelist[i], "Input %u", i); } @@ -2380,12 +2558,12 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, /* if iSelector is given, use it */ nameid = uac_selector_unit_iSelector(desc); if (nameid) - len = snd_usb_copy_string_desc(state, nameid, + len = snd_usb_copy_string_desc(state->chip, nameid, kctl->id.name, sizeof(kctl->id.name)); /* ... or pick up the terminal name at next */ if (!len) - len = get_term_name(state, &state->oterm, + len = get_term_name(state->chip, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 0); /* ... or use the fixed string "USB" as the last resort */ if (!len) @@ -2458,7 +2636,7 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) } else { /* UAC_VERSION_3 */ switch (p1[2]) { case UAC_INPUT_TERMINAL: - return 0; /* NOP */ + return parse_audio_input_terminal(state, unitid, p1); case UAC3_MIXER_UNIT: return parse_audio_mixer_unit(state, unitid, p1); case UAC3_CLOCK_SOURCE: @@ -2503,6 +2681,246 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) return 0; } +/* UAC3 predefined channels configuration */ +struct uac3_badd_profile { + int subclass; + const char *name; + int c_chmask; /* capture channels mask */ + int p_chmask; /* playback channels mask */ + int st_chmask; /* side tone mixing channel mask */ +}; + +static struct uac3_badd_profile uac3_badd_profiles[] = { + { + /* + * BAIF, BAOF or combination of both + * IN: Mono or Stereo cfg, Mono alt possible + * OUT: Mono or Stereo cfg, Mono alt possible + */ + .subclass = UAC3_FUNCTION_SUBCLASS_GENERIC_IO, + .name = "GENERIC IO", + .c_chmask = -1, /* dynamic channels */ + .p_chmask = -1, /* dynamic channels */ + }, + { + /* BAOF; Stereo only cfg, Mono alt possible */ + .subclass = UAC3_FUNCTION_SUBCLASS_HEADPHONE, + .name = "HEADPHONE", + .p_chmask = 3, + }, + { + /* BAOF; Mono or Stereo cfg, Mono alt possible */ + .subclass = UAC3_FUNCTION_SUBCLASS_SPEAKER, + .name = "SPEAKER", + .p_chmask = -1, /* dynamic channels */ + }, + { + /* BAIF; Mono or Stereo cfg, Mono alt possible */ + .subclass = UAC3_FUNCTION_SUBCLASS_MICROPHONE, + .name = "MICROPHONE", + .c_chmask = -1, /* dynamic channels */ + }, + { + /* + * BAIOF topology + * IN: Mono only + * OUT: Mono or Stereo cfg, Mono alt possible + */ + .subclass = UAC3_FUNCTION_SUBCLASS_HEADSET, + .name = "HEADSET", + .c_chmask = 1, + .p_chmask = -1, /* dynamic channels */ + .st_chmask = 1, + }, + { + /* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */ + .subclass = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER, + .name = "HEADSET ADAPTER", + .c_chmask = 1, + .p_chmask = 3, + .st_chmask = 1, + }, + { + /* BAIF + BAOF; IN: Mono only; OUT: Mono only */ + .subclass = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE, + .name = "SPEAKERPHONE", + .c_chmask = 1, + .p_chmask = 1, + }, + { 0 } /* terminator */ +}; + +static bool uac3_badd_func_has_valid_channels(struct usb_mixer_interface *mixer, + struct uac3_badd_profile *f, + int c_chmask, int p_chmask) +{ + /* + * If both playback/capture channels are dynamic, make sure + * at least one channel is present + */ + if (f->c_chmask < 0 && f->p_chmask < 0) { + if (!c_chmask && !p_chmask) { + usb_audio_warn(mixer->chip, "BAAD %s: no channels?", + f->name); + return false; + } + return true; + } + + if ((f->c_chmask < 0 && !c_chmask) || + (f->c_chmask >= 0 && f->c_chmask != c_chmask)) { + usb_audio_warn(mixer->chip, "BAAD %s c_chmask mismatch", + f->name); + return false; + } + if ((f->p_chmask < 0 && !p_chmask) || + (f->p_chmask >= 0 && f->p_chmask != p_chmask)) { + usb_audio_warn(mixer->chip, "BAAD %s p_chmask mismatch", + f->name); + return false; + } + return true; +} + +/* + * create mixer controls for UAC3 BADD profiles + * + * UAC3 BADD device doesn't contain CS descriptors thus we will guess everything + * + * BADD device may contain Mixer Unit, which doesn't have any controls, skip it + */ +static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer, + int ctrlif) +{ + struct usb_device *dev = mixer->chip->dev; + struct usb_interface_assoc_descriptor *assoc; + int badd_profile = mixer->chip->badd_profile; + struct uac3_badd_profile *f; + const struct usbmix_ctl_map *map; + int p_chmask = 0, c_chmask = 0, st_chmask = 0; + int i; + + assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc; + + /* Detect BADD capture/playback channels from AS EP descriptors */ + for (i = 0; i < assoc->bInterfaceCount; i++) { + int intf = assoc->bFirstInterface + i; + + struct usb_interface *iface; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + unsigned int maxpacksize; + char dir_in; + int chmask, num; + + if (intf == ctrlif) + continue; + + iface = usb_ifnum_to_if(dev, intf); + num = iface->num_altsetting; + + if (num < 2) + return -EINVAL; + + /* + * The number of Channels in an AudioStreaming interface + * and the audio sample bit resolution (16 bits or 24 + * bits) can be derived from the wMaxPacketSize field in + * the Standard AS Audio Data Endpoint descriptor in + * Alternate Setting 1 + */ + alts = &iface->altsetting[1]; + altsd = get_iface_desc(alts); + + if (altsd->bNumEndpoints < 1) + return -EINVAL; + + /* check direction */ + dir_in = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN); + maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + + switch (maxpacksize) { + default: + usb_audio_err(mixer->chip, + "incorrect wMaxPacketSize 0x%x for BADD profile\n", + maxpacksize); + return -EINVAL; + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16: + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24: + chmask = 1; + break; + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24: + chmask = 3; + break; + } + + if (dir_in) + c_chmask = chmask; + else + p_chmask = chmask; + } + + usb_audio_dbg(mixer->chip, + "UAC3 BADD profile 0x%x: detected c_chmask=%d p_chmask=%d\n", + badd_profile, c_chmask, p_chmask); + + /* check the mapping table */ + for (map = uac3_badd_usbmix_ctl_maps; map->id; map++) { + if (map->id == badd_profile) + break; + } + + if (!map->id) + return -EINVAL; + + for (f = uac3_badd_profiles; f->name; f++) { + if (badd_profile == f->subclass) + break; + } + if (!f->name) + return -EINVAL; + if (!uac3_badd_func_has_valid_channels(mixer, f, c_chmask, p_chmask)) + return -EINVAL; + st_chmask = f->st_chmask; + + /* Playback */ + if (p_chmask) { + /* Master channel, always writable */ + build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, + UAC3_BADD_FU_ID2, map->map); + /* Mono/Stereo volume channels, always writable */ + build_feature_ctl_badd(mixer, p_chmask, UAC_FU_VOLUME, + UAC3_BADD_FU_ID2, map->map); + } + + /* Capture */ + if (c_chmask) { + /* Master channel, always writable */ + build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, + UAC3_BADD_FU_ID5, map->map); + /* Mono/Stereo volume channels, always writable */ + build_feature_ctl_badd(mixer, c_chmask, UAC_FU_VOLUME, + UAC3_BADD_FU_ID5, map->map); + } + + /* Side tone-mixing */ + if (st_chmask) { + /* Master channel, always writable */ + build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, + UAC3_BADD_FU_ID7, map->map); + /* Mono volume channel, always writable */ + build_feature_ctl_badd(mixer, 1, UAC_FU_VOLUME, + UAC3_BADD_FU_ID7, map->map); + } + + return 0; +} + /* * create mixer controls * @@ -2596,6 +3014,12 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) err = parse_audio_unit(&state, desc->bCSourceID); if (err < 0 && err != -EINVAL) return err; + + if (uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls), + UAC3_TE_INSERTION)) { + build_connector_control(&state, &state.oterm, + false); + } } } @@ -2606,9 +3030,9 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) { struct usb_mixer_elem_list *list; - for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { + for_each_mixer_elem(list, mixer, unitid) { struct usb_mixer_elem_info *info = - (struct usb_mixer_elem_info *)list; + mixer_elem_list_to_info(list); /* invalidate cache, so the value is read from the device */ info->cached = 0; snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, @@ -2619,7 +3043,7 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, struct usb_mixer_elem_list *list) { - struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; + struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN", "S8", "U8", "S16", "U16"}; snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " @@ -2645,8 +3069,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, mixer->ignore_ctl_error); snd_iprintf(buffer, "Card: %s\n", chip->card->longname); for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) { - for (list = mixer->id_elems[unitid]; list; - list = list->next_id_elem) { + for_each_mixer_elem(list, mixer, unitid) { snd_iprintf(buffer, " Unit: %i\n", list->id); if (list->kctl) snd_iprintf(buffer, @@ -2676,19 +3099,19 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, return; } - for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) + for_each_mixer_elem(list, mixer, unitid) count++; if (count == 0) return; - for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { + for_each_mixer_elem(list, mixer, unitid) { struct usb_mixer_elem_info *info; if (!list->kctl) continue; - info = (struct usb_mixer_elem_info *)list; + info = mixer_elem_list_to_info(list); if (count > 1 && info->control != control) continue; @@ -2809,6 +3232,48 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) return 0; } +static int keep_iface_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = mixer->chip->keep_iface; + return 0; +} + +static int keep_iface_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + bool keep_iface = !!ucontrol->value.integer.value[0]; + + if (mixer->chip->keep_iface == keep_iface) + return 0; + mixer->chip->keep_iface = keep_iface; + return 1; +} + +static const struct snd_kcontrol_new keep_iface_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "Keep Interface", + .info = snd_ctl_boolean_mono_info, + .get = keep_iface_ctl_get, + .put = keep_iface_ctl_put, +}; + +static int create_keep_iface_ctl(struct usb_mixer_interface *mixer) +{ + struct snd_kcontrol *kctl = snd_ctl_new1(&keep_iface_ctl, mixer); + + /* need only one control per card */ + if (snd_ctl_find_id(mixer->chip->card, &kctl->id)) { + snd_ctl_free_one(kctl); + return 0; + } + + return snd_ctl_add(mixer->chip->card, kctl); +} + int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, int ignore_error) { @@ -2847,8 +3312,21 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, break; } - if ((err = snd_usb_mixer_controls(mixer)) < 0 || - (err = snd_usb_mixer_status_create(mixer)) < 0) + if (mixer->protocol == UAC_VERSION_3 && + chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { + err = snd_usb_mixer_controls_badd(mixer, ctrlif); + if (err < 0) + goto _error; + } else { + err = snd_usb_mixer_controls(mixer); + if (err < 0) + goto _error; + err = snd_usb_mixer_status_create(mixer); + if (err < 0) + goto _error; + } + err = create_keep_iface_ctl(mixer); + if (err < 0) goto _error; snd_usb_mixer_apply_create_quirk(mixer); @@ -2909,7 +3387,7 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer) static int restore_mixer_value(struct usb_mixer_elem_list *list) { - struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; + struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); int c, err, idx; if (cval->cmask) { @@ -2945,8 +3423,7 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) if (reset_resume) { /* restore cached mixer values */ for (id = 0; id < MAX_ID_ELEMS; id++) { - for (list = mixer->id_elems[id]; list; - list = list->next_id_elem) { + for_each_mixer_elem(list, mixer, id) { if (list->resume) { err = list->resume(list); if (err < 0) @@ -2956,6 +3433,8 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) } } + snd_usb_mixer_resume_quirk(mixer); + return snd_usb_mixer_activate(mixer); } #endif diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index ba27f7ade670..e02653465e29 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -53,6 +53,12 @@ struct usb_mixer_elem_list { usb_mixer_elem_resume_func_t resume; }; +/* iterate over mixer element list of the given unit id */ +#define for_each_mixer_elem(list, mixer, id) \ + for ((list) = (mixer)->id_elems[id]; (list); (list) = (list)->next_id_elem) +#define mixer_elem_list_to_info(list) \ + container_of(list, struct usb_mixer_elem_info, head) + struct usb_mixer_elem_info { struct usb_mixer_elem_list head; unsigned int control; /* CS or ICN (high byte) */ diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index eaa03acd4686..71069e110897 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -485,3 +485,68 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { { 0 } /* terminator */ }; +/* + * Control map entries for UAC3 BADD profiles + */ + +static struct usbmix_name_map uac3_badd_generic_io_map[] = { + { UAC3_BADD_FU_ID2, "Generic Out Playback" }, + { UAC3_BADD_FU_ID5, "Generic In Capture" }, + { 0 } /* terminator */ +}; +static struct usbmix_name_map uac3_badd_headphone_map[] = { + { UAC3_BADD_FU_ID2, "Headphone Playback" }, + { 0 } /* terminator */ +}; +static struct usbmix_name_map uac3_badd_speaker_map[] = { + { UAC3_BADD_FU_ID2, "Speaker Playback" }, + { 0 } /* terminator */ +}; +static struct usbmix_name_map uac3_badd_microphone_map[] = { + { UAC3_BADD_FU_ID5, "Mic Capture" }, + { 0 } /* terminator */ +}; +/* Covers also 'headset adapter' profile */ +static struct usbmix_name_map uac3_badd_headset_map[] = { + { UAC3_BADD_FU_ID2, "Headset Playback" }, + { UAC3_BADD_FU_ID5, "Headset Capture" }, + { UAC3_BADD_FU_ID7, "Sidetone Mixing" }, + { 0 } /* terminator */ +}; +static struct usbmix_name_map uac3_badd_speakerphone_map[] = { + { UAC3_BADD_FU_ID2, "Speaker Playback" }, + { UAC3_BADD_FU_ID5, "Mic Capture" }, + { 0 } /* terminator */ +}; + +static struct usbmix_ctl_map uac3_badd_usbmix_ctl_maps[] = { + { + .id = UAC3_FUNCTION_SUBCLASS_GENERIC_IO, + .map = uac3_badd_generic_io_map, + }, + { + .id = UAC3_FUNCTION_SUBCLASS_HEADPHONE, + .map = uac3_badd_headphone_map, + }, + { + .id = UAC3_FUNCTION_SUBCLASS_SPEAKER, + .map = uac3_badd_speaker_map, + }, + { + .id = UAC3_FUNCTION_SUBCLASS_MICROPHONE, + .map = uac3_badd_microphone_map, + }, + { + .id = UAC3_FUNCTION_SUBCLASS_HEADSET, + .map = uac3_badd_headset_map, + }, + { + .id = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER, + .map = uac3_badd_headset_map, + }, + { + .id = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE, + .map = uac3_badd_speakerphone_map, + }, + { 0 } /* terminator */ +}; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 56537a156580..e82a72fea9a1 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1172,7 +1172,7 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, int unitid = 12; /* SamleRate ExtensionUnit ID */ list_for_each_entry(mixer, &chip->mixer_list, list) { - cval = (struct usb_mixer_elem_info *)mixer->id_elems[unitid]; + cval = mixer_elem_list_to_info(mixer->id_elems[unitid]); if (cval) { snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, cval->control << 8, @@ -1799,12 +1799,33 @@ static int snd_soundblaster_e1_switch_create(struct usb_mixer_interface *mixer) NULL); } +static void dell_dock_init_vol(struct snd_usb_audio *chip, int ch, int id) +{ + u16 buf = 0; + + snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + ch, snd_usb_ctrl_intf(chip) | (id << 8), + &buf, 2); +} + +static int dell_dock_mixer_init(struct usb_mixer_interface *mixer) +{ + /* fix to 0dB playback volumes */ + dell_dock_init_vol(mixer->chip, 1, 16); + dell_dock_init_vol(mixer->chip, 2, 16); + dell_dock_init_vol(mixer->chip, 1, 19); + dell_dock_init_vol(mixer->chip, 2, 19); + return 0; +} + int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) { int err = 0; struct snd_info_entry *entry; - if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0) + err = snd_usb_soundblaster_remote_init(mixer); + if (err < 0) return err; switch (mixer->chip->usb_id) { @@ -1828,8 +1849,6 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) /* EMU0204 */ case USB_ID(0x041e, 0x3f19): err = snd_emu0204_controls_create(mixer); - if (err < 0) - break; break; case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ @@ -1884,11 +1903,25 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */ err = snd_soundblaster_e1_switch_create(mixer); break; + case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */ + err = dell_dock_mixer_init(mixer); + break; } return err; } +#ifdef CONFIG_PM +void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer) +{ + switch (mixer->chip->usb_id) { + case USB_ID(0x0bda, 0x4014): /* Dell WD15 dock */ + dell_dock_mixer_init(mixer); + break; + } +} +#endif + void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer, int unitid) { diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h index b5abd328a361..52be26db558f 100644 --- a/sound/usb/mixer_quirks.h +++ b/sound/usb/mixer_quirks.h @@ -14,5 +14,9 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, struct usb_mixer_elem_info *cval, int unitid, struct snd_kcontrol *kctl); +#ifdef CONFIG_PM +void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer); +#endif + #endif /* SND_USB_MIXER_QUIRKS_H */ diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c index c33e2378089d..4aeb9488a0c9 100644 --- a/sound/usb/mixer_scarlett.c +++ b/sound/usb/mixer_scarlett.c @@ -287,8 +287,7 @@ static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl, static int scarlett_ctl_resume(struct usb_mixer_elem_list *list) { - struct usb_mixer_elem_info *elem = - container_of(list, struct usb_mixer_elem_info, head); + struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list); int i; for (i = 0; i < elem->channels; i++) @@ -447,8 +446,7 @@ static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, static int scarlett_ctl_enum_resume(struct usb_mixer_elem_list *list) { - struct usb_mixer_elem_info *elem = - container_of(list, struct usb_mixer_elem_info, head); + struct usb_mixer_elem_info *elem = mixer_elem_list_to_info(list); if (elem->cached) snd_usb_set_cur_mix_value(elem, 0, 0, *elem->cache_val); diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 3cbfae6604f9..78d1cad08a0a 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -76,10 +76,9 @@ snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, */ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_usb_substream *subs; + struct snd_usb_substream *subs = substream->runtime->private_data; unsigned int hwptr_done; - subs = (struct snd_usb_substream *)substream->runtime->private_data; if (atomic_read(&subs->stream->chip->shutdown)) return SNDRV_PCM_POS_XRUN; spin_lock(&subs->lock); @@ -164,10 +163,11 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface, ep = get_endpoint(alts, 0)->bEndpointAddress; data[0] = 1; - if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, - data, sizeof(data))) < 0) { + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, + data, sizeof(data)); + if (err < 0) { usb_audio_err(chip, "%d:%d: cannot set enable PITCH\n", iface, ep); return err; @@ -185,10 +185,11 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int iface, int err; data[0] = 1; - if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, - USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, - UAC2_EP_CS_PITCH << 8, 0, - data, sizeof(data))) < 0) { + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, + USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, + UAC2_EP_CS_PITCH << 8, 0, + data, sizeof(data)); + if (err < 0) { usb_audio_err(chip, "%d:%d: cannot set enable PITCH (v2)\n", iface, fmt->altsetting); return err; @@ -321,6 +322,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, struct usb_host_interface *alts; struct usb_interface *iface; unsigned int ep; + unsigned int ifnum; /* Implicit feedback sync EPs consumers are always playback EPs */ if (subs->direction != SNDRV_PCM_STREAM_PLAYBACK) @@ -330,44 +332,27 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ ep = 0x81; - iface = usb_ifnum_to_if(dev, 3); - - if (!iface || iface->num_altsetting == 0) - return -EINVAL; - - alts = &iface->altsetting[1]; - goto add_sync_ep; - break; + ifnum = 3; + goto add_sync_ep_from_ifnum; case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ case USB_ID(0x0763, 0x2081): ep = 0x81; - iface = usb_ifnum_to_if(dev, 2); - - if (!iface || iface->num_altsetting == 0) - return -EINVAL; - - alts = &iface->altsetting[1]; - goto add_sync_ep; - case USB_ID(0x2466, 0x8003): + ifnum = 2; + goto add_sync_ep_from_ifnum; + case USB_ID(0x2466, 0x8003): /* Fractal Audio Axe-Fx II */ ep = 0x86; - iface = usb_ifnum_to_if(dev, 2); - - if (!iface || iface->num_altsetting == 0) - return -EINVAL; - - alts = &iface->altsetting[1]; - goto add_sync_ep; - case USB_ID(0x1397, 0x0002): + ifnum = 2; + goto add_sync_ep_from_ifnum; + case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx III */ ep = 0x81; - iface = usb_ifnum_to_if(dev, 1); - - if (!iface || iface->num_altsetting == 0) - return -EINVAL; - - alts = &iface->altsetting[1]; - goto add_sync_ep; - + ifnum = 2; + goto add_sync_ep_from_ifnum; + case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */ + ep = 0x81; + ifnum = 1; + goto add_sync_ep_from_ifnum; } + if (attr == USB_ENDPOINT_SYNC_ASYNC && altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && altsd->bInterfaceProtocol == 2 && @@ -382,6 +367,14 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, /* No quirk */ return 0; +add_sync_ep_from_ifnum: + iface = usb_ifnum_to_if(dev, ifnum); + + if (!iface || iface->num_altsetting == 0) + return -EINVAL; + + alts = &iface->altsetting[1]; + add_sync_ep: subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, alts, ep, !subs->direction, @@ -507,7 +500,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) iface = usb_ifnum_to_if(dev, fmt->iface); if (WARN_ON(!iface)) return -EINVAL; - alts = &iface->altsetting[fmt->altset_idx]; + alts = usb_altnum_to_altsetting(iface, fmt->altsetting); altsd = get_iface_desc(alts); if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting)) return -EINVAL; @@ -517,21 +510,21 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* close the old interface */ if (subs->interface >= 0 && subs->interface != fmt->iface) { - err = usb_set_interface(subs->dev, subs->interface, 0); - if (err < 0) { - dev_err(&dev->dev, - "%d:%d: return to setting 0 failed (%d)\n", - fmt->iface, fmt->altsetting, err); - return -EIO; + if (!subs->stream->chip->keep_iface) { + err = usb_set_interface(subs->dev, subs->interface, 0); + if (err < 0) { + dev_err(&dev->dev, + "%d:%d: return to setting 0 failed (%d)\n", + fmt->iface, fmt->altsetting, err); + return -EIO; + } } subs->interface = -1; subs->altset_idx = 0; } /* set interface */ - if (subs->interface != fmt->iface || - subs->altset_idx != fmt->altset_idx) { - + if (iface->cur_altsetting != alts) { err = snd_usb_select_mode_quirk(subs, fmt); if (err < 0) return -EIO; @@ -545,12 +538,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) } dev_dbg(&dev->dev, "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting); - subs->interface = fmt->iface; - subs->altset_idx = fmt->altset_idx; - snd_usb_set_interface_quirk(dev); } + subs->interface = fmt->iface; + subs->altset_idx = fmt->altset_idx; subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip, alts, fmt->endpoint, subs->direction, SND_USB_ENDPOINT_TYPE_DATA); @@ -736,7 +728,11 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, struct audioformat *fmt; int ret; - ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, + if (snd_usb_use_vmalloc) + ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + else + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (ret < 0) return ret; @@ -789,7 +785,11 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) snd_usb_endpoint_deactivate(subs->data_endpoint); snd_usb_unlock_shutdown(subs->stream->chip); } - return snd_pcm_lib_free_vmalloc_buffer(substream); + + if (snd_usb_use_vmalloc) + return snd_pcm_lib_free_vmalloc_buffer(substream); + else + return snd_pcm_lib_free_pages(substream); } /* @@ -1181,9 +1181,6 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre pt = 125 * (1 << fp->datainterval); ptmin = min(ptmin, pt); } - err = snd_usb_autoresume(subs->stream->chip); - if (err < 0) - return err; param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; if (subs->speed == USB_SPEED_FULL) @@ -1192,30 +1189,37 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre if (ptmin == 1000) /* if period time doesn't go below 1 ms, no rules needed */ param_period_time_if_needed = -1; - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, - ptmin, UINT_MAX); - - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - hw_rule_rate, subs, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_CHANNELS, - param_period_time_if_needed, - -1)) < 0) - goto rep_err; - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - hw_rule_channels, subs, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_RATE, - param_period_time_if_needed, - -1)) < 0) - goto rep_err; - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, - hw_rule_format, subs, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_CHANNELS, - param_period_time_if_needed, - -1)) < 0) - goto rep_err; + + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + ptmin, UINT_MAX); + if (err < 0) + return err; + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + hw_rule_rate, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + param_period_time_if_needed, + -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_RATE, + param_period_time_if_needed, + -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format, subs, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_CHANNELS, + param_period_time_if_needed, + -1); + if (err < 0) + return err; if (param_period_time_if_needed >= 0) { err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, @@ -1225,19 +1229,18 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) - goto rep_err; + return err; } - if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) - goto rep_err; - return 0; + err = snd_usb_pcm_check_knot(runtime, subs); + if (err < 0) + return err; -rep_err: - snd_usb_autosuspend(subs->stream->chip); - return err; + return snd_usb_autoresume(subs->stream->chip); } -static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) +static int snd_usb_pcm_open(struct snd_pcm_substream *substream) { + int direction = substream->stream; struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = &as->substream[direction]; @@ -1257,14 +1260,16 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) return setup_hw_info(runtime, subs); } -static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction) +static int snd_usb_pcm_close(struct snd_pcm_substream *substream) { + int direction = substream->stream; struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_usb_substream *subs = &as->substream[direction]; stop_endpoints(subs, true); - if (subs->interface >= 0 && + if (!as->chip->keep_iface && + subs->interface >= 0 && !snd_usb_lock_shutdown(subs->stream->chip)) { usb_set_interface(subs->dev, subs->interface, 0); subs->interface = -1; @@ -1311,7 +1316,7 @@ static void retire_capture_urb(struct snd_usb_substream *subs, if (bytes % (runtime->sample_bits >> 3) != 0) { int oldbytes = bytes; bytes = frames * stride; - dev_warn(&subs->dev->dev, + dev_warn_ratelimited(&subs->dev->dev, "Corrected urb data len. %d->%d\n", oldbytes, bytes); } @@ -1619,26 +1624,6 @@ static void retire_playback_urb(struct snd_usb_substream *subs, spin_unlock_irqrestore(&subs->lock, flags); } -static int snd_usb_playback_open(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK); -} - -static int snd_usb_playback_close(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK); -} - -static int snd_usb_capture_open(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE); -} - -static int snd_usb_capture_close(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE); -} - static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -1700,8 +1685,8 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream } static const struct snd_pcm_ops snd_usb_playback_ops = { - .open = snd_usb_playback_open, - .close = snd_usb_playback_close, + .open = snd_usb_pcm_open, + .close = snd_usb_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_usb_hw_params, .hw_free = snd_usb_hw_free, @@ -1713,8 +1698,8 @@ static const struct snd_pcm_ops snd_usb_playback_ops = { }; static const struct snd_pcm_ops snd_usb_capture_ops = { - .open = snd_usb_capture_open, - .close = snd_usb_capture_close, + .open = snd_usb_pcm_open, + .close = snd_usb_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_usb_hw_params, .hw_free = snd_usb_hw_free, @@ -1725,9 +1710,50 @@ static const struct snd_pcm_ops snd_usb_capture_ops = { .mmap = snd_pcm_lib_mmap_vmalloc, }; +static const struct snd_pcm_ops snd_usb_playback_dev_ops = { + .open = snd_usb_pcm_open, + .close = snd_usb_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_usb_hw_params, + .hw_free = snd_usb_hw_free, + .prepare = snd_usb_pcm_prepare, + .trigger = snd_usb_substream_playback_trigger, + .pointer = snd_usb_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +static const struct snd_pcm_ops snd_usb_capture_dev_ops = { + .open = snd_usb_pcm_open, + .close = snd_usb_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_usb_hw_params, + .hw_free = snd_usb_hw_free, + .prepare = snd_usb_pcm_prepare, + .trigger = snd_usb_substream_capture_trigger, + .pointer = snd_usb_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream) { - snd_pcm_set_ops(pcm, stream, - stream == SNDRV_PCM_STREAM_PLAYBACK ? - &snd_usb_playback_ops : &snd_usb_capture_ops); + const struct snd_pcm_ops *ops; + + if (snd_usb_use_vmalloc) + ops = stream == SNDRV_PCM_STREAM_PLAYBACK ? + &snd_usb_playback_ops : &snd_usb_capture_ops; + else + ops = stream == SNDRV_PCM_STREAM_PLAYBACK ? + &snd_usb_playback_dev_ops : &snd_usb_capture_dev_ops; + snd_pcm_set_ops(pcm, stream, ops); +} + +void snd_usb_preallocate_buffer(struct snd_usb_substream *subs) +{ + struct snd_pcm *pcm = subs->stream->pcm; + struct snd_pcm_substream *s = pcm->streams[subs->direction].substream; + struct device *dev = subs->dev->bus->controller; + + if (!snd_usb_use_vmalloc) + snd_pcm_lib_preallocate_pages(s, SNDRV_DMA_TYPE_DEV_SG, + dev, 64*1024, 512*1024); } diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index 35740d5ef268..f77ec58bf1a1 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h @@ -10,6 +10,7 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt); +void snd_usb_preallocate_buffer(struct snd_usb_substream *subs); #endif /* __USBAUDIO_PCM_H */ diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 754e632a27bd..0e37e358ca97 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3371,5 +3371,15 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } } }, +/* Dell WD15 Dock */ +{ + USB_DEVICE(0x0bda, 0x4014), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Dell", + .product_name = "WD15 Dock", + .profile_name = "Dell-WD15-Dock", + .ifnum = QUIRK_NO_INTERFACE + } +}, #undef USB_DEVICE_VENDOR_SPEC diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index acbeb52f6fd6..f4b69173682c 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -851,6 +851,36 @@ static int snd_usb_mbox2_boot_quirk(struct usb_device *dev) return 0; /* Successful boot */ } +static int snd_usb_axefx3_boot_quirk(struct usb_device *dev) +{ + int err; + + dev_dbg(&dev->dev, "Waiting for Axe-Fx III to boot up...\n"); + + /* If the Axe-Fx III has not fully booted, it will timeout when trying + * to enable the audio streaming interface. A more generous timeout is + * used here to detect when the Axe-Fx III has finished booting as the + * set interface message will be acked once it has + */ + err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, + 1, 1, NULL, 0, 120000); + if (err < 0) { + dev_err(&dev->dev, + "failed waiting for Axe-Fx III to boot: %d\n", err); + return err; + } + + dev_dbg(&dev->dev, "Axe-Fx III is now ready\n"); + + err = usb_set_interface(dev, 1, 0); + if (err < 0) + dev_dbg(&dev->dev, + "error stopping Axe-Fx III interface: %d\n", err); + + return 0; +} + /* * Setup quirks */ @@ -1026,6 +1056,8 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, return snd_usb_fasttrackpro_boot_quirk(dev); case USB_ID(0x047f, 0xc010): /* Plantronics Gamecom 780 */ return snd_usb_gamecon780_boot_quirk(dev); + case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx 3 */ + return snd_usb_axefx3_boot_quirk(dev); } return 0; @@ -1327,20 +1359,47 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, /* XMOS based USB DACs */ switch (chip->usb_id) { + case USB_ID(0x1511, 0x0037): /* AURALiC VEGA */ + case USB_ID(0x20b1, 0x0002): /* Wyred 4 Sound DAC-2 DSD */ + case USB_ID(0x20b1, 0x2004): /* Matrix Audio X-SPDIF 2 */ case USB_ID(0x20b1, 0x3008): /* iFi Audio micro/nano iDSD */ case USB_ID(0x20b1, 0x2008): /* Matrix Audio X-Sabre */ case USB_ID(0x20b1, 0x300a): /* Matrix Audio Mini-i Pro */ case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */ + case USB_ID(0x22d9, 0x0436): /* OPPO Sonica */ + case USB_ID(0x22d9, 0x0461): /* OPPO UDP-205 */ + case USB_ID(0x2522, 0x0012): /* LH Labs VI DAC Infinity */ + case USB_ID(0x25ce, 0x001f): /* Mytek Brooklyn DAC */ + case USB_ID(0x25ce, 0x0021): /* Mytek Manhattan DAC */ + case USB_ID(0x25ce, 0x8025): /* Mytek Brooklyn DAC+ */ case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */ if (fp->altsetting == 2) return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; + case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */ + case USB_ID(0x16b0, 0x06b2): /* NuPrime DAC-10 */ + case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */ + case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */ + case USB_ID(0x1db5, 0x0003): /* Bryston BDA3 */ case USB_ID(0x20b1, 0x000a): /* Gustard DAC-X20U */ + case USB_ID(0x20b1, 0x2005): /* Denafrips Ares DAC */ case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */ case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */ + case USB_ID(0x20b1, 0x3021): /* Eastern El. MiniMax Tube DAC Supreme */ case USB_ID(0x20b1, 0x3023): /* Aune X1S 32BIT/384 DSD DAC */ + case USB_ID(0x20b1, 0x302d): /* Unison Research Unico CD Due */ + case USB_ID(0x20b1, 0x3036): /* Holo Springs Level 3 R2R DAC */ + case USB_ID(0x20b1, 0x307b): /* CH Precision C1 DAC */ + case USB_ID(0x20b1, 0x3086): /* Singxer F-1 converter board */ + case USB_ID(0x22d9, 0x0426): /* OPPO HA-2 */ + case USB_ID(0x22e1, 0xca01): /* HDTA Serenade DSD */ + case USB_ID(0x249c, 0x9326): /* M2Tech Young MkIII */ case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */ + case USB_ID(0x2622, 0x0041): /* Audiolab M-DAC+ */ + case USB_ID(0x27f7, 0x3002): /* W4S DAC-2v2SE */ + case USB_ID(0x29a2, 0x0086): /* Mutec MC3+ USB */ + case USB_ID(0x6b42, 0x0042): /* MSB Technology */ if (fp->altsetting == 3) return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 5ed334575fc7..729afd808cc4 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -106,6 +106,8 @@ static void snd_usb_init_substream(struct snd_usb_stream *as, subs->ep_num = fp->endpoint; if (fp->channels > subs->channels_max) subs->channels_max = fp->channels; + + snd_usb_preallocate_buffer(subs); } /* kctl callbacks for usb-audio channel maps */ @@ -633,6 +635,395 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, return NULL; } +static struct audioformat * +audio_format_alloc_init(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int protocol, int iface_no, int altset_idx, + int altno, int num_channels, int clock) +{ + struct audioformat *fp; + + fp = kzalloc(sizeof(*fp), GFP_KERNEL); + if (!fp) + return NULL; + + fp->iface = iface_no; + fp->altsetting = altno; + fp->altset_idx = altset_idx; + fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; + fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->datainterval = snd_usb_parse_datainterval(chip, alts); + fp->protocol = protocol; + fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + fp->channels = num_channels; + if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH) + fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) + * (fp->maxpacksize & 0x7ff); + fp->clock = clock; + INIT_LIST_HEAD(&fp->list); + + return fp; +} + +static struct audioformat * +snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int protocol, int iface_no, int altset_idx, + int altno, int stream, int bm_quirk) +{ + struct usb_device *dev = chip->dev; + struct uac_format_type_i_continuous_descriptor *fmt; + unsigned int num_channels = 0, chconfig = 0; + struct audioformat *fp; + int clock = 0; + u64 format; + + /* get audio formats */ + if (protocol == UAC_VERSION_1) { + struct uac1_as_header_descriptor *as = + snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_AS_GENERAL); + struct uac_input_terminal_descriptor *iterm; + + if (!as) { + dev_err(&dev->dev, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_AS_GENERAL desc\n", + iface_no, altno); + return NULL; + } + + format = le16_to_cpu(as->wFormatTag); /* remember the format value */ + + iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (iterm) { + num_channels = iterm->bNrChannels; + chconfig = le16_to_cpu(iterm->wChannelConfig); + } + } else { /* UAC_VERSION_2 */ + struct uac2_input_terminal_descriptor *input_term; + struct uac2_output_terminal_descriptor *output_term; + struct uac2_as_header_descriptor *as = + snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_AS_GENERAL); + + if (!as) { + dev_err(&dev->dev, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_AS_GENERAL desc\n", + iface_no, altno); + return NULL; + } + + num_channels = as->bNrChannels; + format = le32_to_cpu(as->bmFormats); + chconfig = le32_to_cpu(as->bmChannelConfig); + + /* + * lookup the terminal associated to this interface + * to extract the clock + */ + input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (input_term) { + clock = input_term->bCSourceID; + if (!chconfig && (num_channels == input_term->bNrChannels)) + chconfig = le32_to_cpu(input_term->bmChannelConfig); + goto found_clock; + } + + output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (output_term) { + clock = output_term->bCSourceID; + goto found_clock; + } + + dev_err(&dev->dev, + "%u:%d : bogus bTerminalLink %d\n", + iface_no, altno, as->bTerminalLink); + return NULL; + } + +found_clock: + /* get format type */ + fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_FORMAT_TYPE); + if (!fmt) { + dev_err(&dev->dev, + "%u:%d : no UAC_FORMAT_TYPE desc\n", + iface_no, altno); + return NULL; + } + if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) + || ((protocol == UAC_VERSION_2) && + (fmt->bLength < 6))) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_FORMAT_TYPE desc\n", + iface_no, altno); + return NULL; + } + + /* + * Blue Microphones workaround: The last altsetting is + * identical with the previous one, except for a larger + * packet size, but is actually a mislabeled two-channel + * setting; ignore it. + * + * Part 2: analyze quirk flag and format + */ + if (bm_quirk && fmt->bNrChannels == 1 && fmt->bSubframeSize == 2) + return NULL; + + fp = audio_format_alloc_init(chip, alts, protocol, iface_no, + altset_idx, altno, num_channels, clock); + if (!fp) + return ERR_PTR(-ENOMEM); + + fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, + iface_no); + + /* some quirks for attributes here */ + snd_usb_audioformat_attributes_quirk(chip, fp, stream); + + /* ok, let's parse further... */ + if (snd_usb_parse_audio_format(chip, fp, format, + fmt, stream) < 0) { + kfree(fp->rate_table); + kfree(fp); + return NULL; + } + + /* Create chmap */ + if (fp->channels != num_channels) + chconfig = 0; + + fp->chmap = convert_chmap(fp->channels, chconfig, protocol); + + return fp; +} + +static struct audioformat * +snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int iface_no, int altset_idx, + int altno, int stream) +{ + struct usb_device *dev = chip->dev; + struct uac3_input_terminal_descriptor *input_term; + struct uac3_output_terminal_descriptor *output_term; + struct uac3_cluster_header_descriptor *cluster; + struct uac3_as_header_descriptor *as = NULL; + struct uac3_hc_descriptor_header hc_header; + struct snd_pcm_chmap_elem *chmap; + unsigned char badd_profile; + u64 badd_formats = 0; + unsigned int num_channels; + struct audioformat *fp; + u16 cluster_id, wLength; + int clock = 0; + int err; + + badd_profile = chip->badd_profile; + + if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { + unsigned int maxpacksize = + le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + + switch (maxpacksize) { + default: + dev_err(&dev->dev, + "%u:%d : incorrect wMaxPacketSize for BADD profile\n", + iface_no, altno); + return NULL; + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16: + badd_formats = SNDRV_PCM_FMTBIT_S16_LE; + num_channels = 1; + break; + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24: + badd_formats = SNDRV_PCM_FMTBIT_S24_3LE; + num_channels = 1; + break; + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: + badd_formats = SNDRV_PCM_FMTBIT_S16_LE; + num_channels = 2; + break; + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24: + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24: + badd_formats = SNDRV_PCM_FMTBIT_S24_3LE; + num_channels = 2; + break; + } + + chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); + if (!chmap) + return ERR_PTR(-ENOMEM); + + if (num_channels == 1) { + chmap->map[0] = SNDRV_CHMAP_MONO; + } else { + chmap->map[0] = SNDRV_CHMAP_FL; + chmap->map[1] = SNDRV_CHMAP_FR; + } + + chmap->channels = num_channels; + clock = UAC3_BADD_CS_ID9; + goto found_clock; + } + + as = snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_AS_GENERAL); + if (!as) { + dev_err(&dev->dev, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_AS_GENERAL desc\n", + iface_no, altno); + return NULL; + } + + cluster_id = le16_to_cpu(as->wClusterDescrID); + if (!cluster_id) { + dev_err(&dev->dev, + "%u:%d : no cluster descriptor\n", + iface_no, altno); + return NULL; + } + + /* + * Get number of channels and channel map through + * High Capability Cluster Descriptor + * + * First step: get High Capability header and + * read size of Cluster Descriptor + */ + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + cluster_id, + snd_usb_ctrl_intf(chip), + &hc_header, sizeof(hc_header)); + if (err < 0) + return ERR_PTR(err); + else if (err != sizeof(hc_header)) { + dev_err(&dev->dev, + "%u:%d : can't get High Capability descriptor\n", + iface_no, altno); + return ERR_PTR(-EIO); + } + + /* + * Second step: allocate needed amount of memory + * and request Cluster Descriptor + */ + wLength = le16_to_cpu(hc_header.wLength); + cluster = kzalloc(wLength, GFP_KERNEL); + if (!cluster) + return ERR_PTR(-ENOMEM); + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + cluster_id, + snd_usb_ctrl_intf(chip), + cluster, wLength); + if (err < 0) { + kfree(cluster); + return ERR_PTR(err); + } else if (err != wLength) { + dev_err(&dev->dev, + "%u:%d : can't get Cluster Descriptor\n", + iface_no, altno); + kfree(cluster); + return ERR_PTR(-EIO); + } + + num_channels = cluster->bNrChannels; + chmap = convert_chmap_v3(cluster); + kfree(cluster); + + /* + * lookup the terminal associated to this interface + * to extract the clock + */ + input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (input_term) { + clock = input_term->bCSourceID; + goto found_clock; + } + + output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (output_term) { + clock = output_term->bCSourceID; + goto found_clock; + } + + dev_err(&dev->dev, "%u:%d : bogus bTerminalLink %d\n", + iface_no, altno, as->bTerminalLink); + kfree(chmap); + return NULL; + +found_clock: + fp = audio_format_alloc_init(chip, alts, UAC_VERSION_3, iface_no, + altset_idx, altno, num_channels, clock); + if (!fp) { + kfree(chmap); + return ERR_PTR(-ENOMEM); + } + + fp->chmap = chmap; + + if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { + fp->attributes = 0; /* No attributes */ + + fp->fmt_type = UAC_FORMAT_TYPE_I; + fp->formats = badd_formats; + + fp->nr_rates = 0; /* SNDRV_PCM_RATE_CONTINUOUS */ + fp->rate_min = UAC3_BADD_SAMPLING_RATE; + fp->rate_max = UAC3_BADD_SAMPLING_RATE; + fp->rates = SNDRV_PCM_RATE_CONTINUOUS; + + } else { + fp->attributes = parse_uac_endpoint_attributes(chip, alts, + UAC_VERSION_3, + iface_no); + /* ok, let's parse further... */ + if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) { + kfree(fp->chmap); + kfree(fp->rate_table); + kfree(fp); + return NULL; + } + } + + return fp; +} + int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) { struct usb_device *dev; @@ -640,13 +1031,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) struct usb_host_interface *alts; struct usb_interface_descriptor *altsd; int i, altno, err, stream; - u64 format = 0; - unsigned int num_channels = 0; struct audioformat *fp = NULL; - int num, protocol, clock = 0; - struct uac_format_type_i_continuous_descriptor *fmt = NULL; - struct snd_pcm_chmap_elem *chmap_v3 = NULL; - unsigned int chconfig; + int num, protocol; dev = chip->dev; @@ -695,303 +1081,48 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) protocol <= 2) protocol = UAC_VERSION_1; - chconfig = 0; - /* get audio formats */ switch (protocol) { default: dev_dbg(&dev->dev, "%u:%d: unknown interface protocol %#02x, assuming v1\n", iface_no, altno, protocol); protocol = UAC_VERSION_1; /* fall through */ - - case UAC_VERSION_1: { - struct uac1_as_header_descriptor *as = - snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); - struct uac_input_terminal_descriptor *iterm; - - if (!as) { - dev_err(&dev->dev, - "%u:%d : UAC_AS_GENERAL descriptor not found\n", - iface_no, altno); - continue; - } - - if (as->bLength < sizeof(*as)) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_AS_GENERAL desc\n", - iface_no, altno); - continue; - } - - format = le16_to_cpu(as->wFormatTag); /* remember the format value */ - - iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (iterm) { - num_channels = iterm->bNrChannels; - chconfig = le16_to_cpu(iterm->wChannelConfig); - } - - break; - } - + case UAC_VERSION_1: + /* fall through */ case UAC_VERSION_2: { - struct uac2_input_terminal_descriptor *input_term; - struct uac2_output_terminal_descriptor *output_term; - struct uac2_as_header_descriptor *as = - snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); - - if (!as) { - dev_err(&dev->dev, - "%u:%d : UAC_AS_GENERAL descriptor not found\n", - iface_no, altno); - continue; - } - - if (as->bLength < sizeof(*as)) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_AS_GENERAL desc\n", - iface_no, altno); - continue; - } - - num_channels = as->bNrChannels; - format = le32_to_cpu(as->bmFormats); - chconfig = le32_to_cpu(as->bmChannelConfig); - - /* lookup the terminal associated to this interface - * to extract the clock */ - input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (input_term) { - clock = input_term->bCSourceID; - if (!chconfig && (num_channels == input_term->bNrChannels)) - chconfig = le32_to_cpu(input_term->bmChannelConfig); - break; - } - - output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (output_term) { - clock = output_term->bCSourceID; - break; - } - - dev_err(&dev->dev, - "%u:%d : bogus bTerminalLink %d\n", - iface_no, altno, as->bTerminalLink); - continue; - } - - case UAC_VERSION_3: { - struct uac3_input_terminal_descriptor *input_term; - struct uac3_output_terminal_descriptor *output_term; - struct uac3_as_header_descriptor *as; - struct uac3_cluster_header_descriptor *cluster; - struct uac3_hc_descriptor_header hc_header; - u16 cluster_id, wLength; - - as = snd_usb_find_csint_desc(alts->extra, - alts->extralen, - NULL, UAC_AS_GENERAL); - - if (!as) { - dev_err(&dev->dev, - "%u:%d : UAC_AS_GENERAL descriptor not found\n", - iface_no, altno); - continue; - } - - if (as->bLength < sizeof(*as)) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_AS_GENERAL desc\n", - iface_no, altno); - continue; - } - - cluster_id = le16_to_cpu(as->wClusterDescrID); - if (!cluster_id) { - dev_err(&dev->dev, - "%u:%d : no cluster descriptor\n", - iface_no, altno); - continue; - } - - /* - * Get number of channels and channel map through - * High Capability Cluster Descriptor - * - * First step: get High Capability header and - * read size of Cluster Descriptor - */ - err = snd_usb_ctl_msg(chip->dev, - usb_rcvctrlpipe(chip->dev, 0), - UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - cluster_id, - snd_usb_ctrl_intf(chip), - &hc_header, sizeof(hc_header)); - if (err < 0) - return err; - else if (err != sizeof(hc_header)) { - dev_err(&dev->dev, - "%u:%d : can't get High Capability descriptor\n", - iface_no, altno); - return -EIO; - } - - /* - * Second step: allocate needed amount of memory - * and request Cluster Descriptor - */ - wLength = le16_to_cpu(hc_header.wLength); - cluster = kzalloc(wLength, GFP_KERNEL); - if (!cluster) - return -ENOMEM; - err = snd_usb_ctl_msg(chip->dev, - usb_rcvctrlpipe(chip->dev, 0), - UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - cluster_id, - snd_usb_ctrl_intf(chip), - cluster, wLength); - if (err < 0) { - kfree(cluster); - return err; - } else if (err != wLength) { - dev_err(&dev->dev, - "%u:%d : can't get Cluster Descriptor\n", - iface_no, altno); - kfree(cluster); - return -EIO; - } - - num_channels = cluster->bNrChannels; - chmap_v3 = convert_chmap_v3(cluster); - - kfree(cluster); - - format = le64_to_cpu(as->bmFormats); - - /* lookup the terminal associated to this interface - * to extract the clock */ - input_term = snd_usb_find_input_terminal_descriptor( - chip->ctrl_intf, - as->bTerminalLink); - - if (input_term) { - clock = input_term->bCSourceID; - break; - } - - output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (output_term) { - clock = output_term->bCSourceID; - break; - } - - dev_err(&dev->dev, - "%u:%d : bogus bTerminalLink %d\n", - iface_no, altno, as->bTerminalLink); - continue; - } - } - - if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { - /* get format type */ - fmt = snd_usb_find_csint_desc(alts->extra, - alts->extralen, - NULL, UAC_FORMAT_TYPE); - if (!fmt) { - dev_err(&dev->dev, - "%u:%d : no UAC_FORMAT_TYPE desc\n", - iface_no, altno); - continue; - } - if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) - || ((protocol == UAC_VERSION_2) && - (fmt->bLength < 6))) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_FORMAT_TYPE desc\n", - iface_no, altno); - continue; - } + int bm_quirk = 0; /* * Blue Microphones workaround: The last altsetting is * identical with the previous one, except for a larger * packet size, but is actually a mislabeled two-channel * setting; ignore it. + * + * Part 1: prepare quirk flag */ - if (fmt->bNrChannels == 1 && - fmt->bSubframeSize == 2 && - altno == 2 && num == 3 && + if (altno == 2 && num == 3 && fp && fp->altsetting == 1 && fp->channels == 1 && fp->formats == SNDRV_PCM_FMTBIT_S16_LE && protocol == UAC_VERSION_1 && le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == fp->maxpacksize * 2) - continue; - } - - fp = kzalloc(sizeof(*fp), GFP_KERNEL); - if (!fp) - return -ENOMEM; + bm_quirk = 1; - fp->iface = iface_no; - fp->altsetting = altno; - fp->altset_idx = i; - fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; - fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; - fp->datainterval = snd_usb_parse_datainterval(chip, alts); - fp->protocol = protocol; - fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); - fp->channels = num_channels; - if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) - fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) - * (fp->maxpacksize & 0x7ff); - fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); - fp->clock = clock; - INIT_LIST_HEAD(&fp->list); - - /* some quirks for attributes here */ - snd_usb_audioformat_attributes_quirk(chip, fp, stream); - - /* ok, let's parse further... */ - if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { - if (snd_usb_parse_audio_format(chip, fp, format, - fmt, stream) < 0) { - kfree(fp->rate_table); - kfree(fp); - fp = NULL; - continue; - } - } else { - struct uac3_as_header_descriptor *as; - - as = snd_usb_find_csint_desc(alts->extra, - alts->extralen, - NULL, UAC_AS_GENERAL); - - if (snd_usb_parse_audio_format_v3(chip, fp, as, - stream) < 0) { - kfree(fp->rate_table); - kfree(fp); - fp = NULL; - continue; - } + fp = snd_usb_get_audioformat_uac12(chip, alts, protocol, + iface_no, i, altno, + stream, bm_quirk); + break; + } + case UAC_VERSION_3: + fp = snd_usb_get_audioformat_uac3(chip, alts, + iface_no, i, altno, stream); + break; } - /* Create chmap */ - if (fp->channels != num_channels) - chconfig = 0; - - if (protocol == UAC_VERSION_3) - fp->chmap = chmap_v3; - else - fp->chmap = convert_chmap(fp->channels, chconfig, - protocol); + if (!fp) + continue; + else if (IS_ERR(fp)) + return PTR_ERR(fp); dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); err = snd_usb_add_audio_stream(chip, stream, fp); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 4d5c89a7ba2b..b9faeca645fd 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -49,6 +49,8 @@ struct snd_usb_audio { int num_suspended_intf; int sample_rate_read_error; + int badd_profile; /* UAC3 BADD profile */ + struct list_head pcm_list; /* list of pcm streams */ struct list_head ep_list; /* list of audio-related endpoints */ int pcm_devs; @@ -59,6 +61,9 @@ struct snd_usb_audio { int setup; /* from the 'device_setup' module param */ bool autoclock; /* from the 'autoclock' module param */ + bool keep_iface; /* keep interface/altset after closing + * or parameter change + */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ }; @@ -109,6 +114,7 @@ enum quirk_type { struct snd_usb_audio_quirk { const char *vendor_name; const char *product_name; + const char *profile_name; /* override the card->longname */ int16_t ifnum; uint16_t type; const void *data; @@ -121,4 +127,6 @@ struct snd_usb_audio_quirk { int snd_usb_lock_shutdown(struct snd_usb_audio *chip); void snd_usb_unlock_shutdown(struct snd_usb_audio *chip); +extern bool snd_usb_use_vmalloc; + #endif /* __USBAUDIO_H */ diff --git a/sound/xen/Kconfig b/sound/xen/Kconfig new file mode 100644 index 000000000000..4f1fceea82d2 --- /dev/null +++ b/sound/xen/Kconfig @@ -0,0 +1,10 @@ +# ALSA Xen drivers + +config SND_XEN_FRONTEND + tristate "Xen para-virtualized sound frontend driver" + depends on XEN + select SND_PCM + select XEN_XENBUS_FRONTEND + help + Choose this option if you want to enable a para-virtualized + frontend sound driver for Xen guest OSes. diff --git a/sound/xen/Makefile b/sound/xen/Makefile new file mode 100644 index 000000000000..1e6470ecc2f2 --- /dev/null +++ b/sound/xen/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 OR MIT + +snd_xen_front-objs := xen_snd_front.o \ + xen_snd_front_cfg.o \ + xen_snd_front_evtchnl.o \ + xen_snd_front_shbuf.o \ + xen_snd_front_alsa.o + +obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o diff --git a/sound/xen/xen_snd_front.c b/sound/xen/xen_snd_front.c new file mode 100644 index 000000000000..b089b13b5160 --- /dev/null +++ b/sound/xen/xen_snd_front.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> + */ + +#include <linux/delay.h> +#include <linux/module.h> + +#include <xen/page.h> +#include <xen/platform_pci.h> +#include <xen/xen.h> +#include <xen/xenbus.h> + +#include <xen/interface/io/sndif.h> + +#include "xen_snd_front.h" +#include "xen_snd_front_alsa.h" +#include "xen_snd_front_evtchnl.h" +#include "xen_snd_front_shbuf.h" + +static struct xensnd_req * +be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation) +{ + struct xensnd_req *req; + + req = RING_GET_REQUEST(&evtchnl->u.req.ring, + evtchnl->u.req.ring.req_prod_pvt); + req->operation = operation; + req->id = evtchnl->evt_next_id++; + evtchnl->evt_id = req->id; + return req; +} + +static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl) +{ + if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED)) + return -EIO; + + reinit_completion(&evtchnl->u.req.completion); + xen_snd_front_evtchnl_flush(evtchnl); + return 0; +} + +static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl) +{ + if (wait_for_completion_timeout(&evtchnl->u.req.completion, + msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0) + return -ETIMEDOUT; + + return evtchnl->u.req.resp_status; +} + +int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, + struct xensnd_query_hw_param *hw_param_req, + struct xensnd_query_hw_param *hw_param_resp) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY); + req->op.hw_param = *hw_param_req; + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + if (ret == 0) + *hw_param_resp = evtchnl->u.req.resp.hw_param; + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, + struct xen_snd_front_shbuf *sh_buf, + u8 format, unsigned int channels, + unsigned int rate, u32 buffer_sz, + u32 period_sz) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN); + req->op.open.pcm_format = format; + req->op.open.pcm_channels = channels; + req->op.open.pcm_rate = rate; + req->op.open.buffer_sz = buffer_sz; + req->op.open.period_sz = period_sz; + req->op.open.gref_directory = xen_snd_front_shbuf_get_dir_start(sh_buf); + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE); + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl, + unsigned long pos, unsigned long count) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE); + req->op.rw.length = count; + req->op.rw.offset = pos; + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl, + unsigned long pos, unsigned long count) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_READ); + req->op.rw.length = count; + req->op.rw.offset = pos; + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl, + int type) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER); + req->op.trigger.type = type; + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +static void xen_snd_drv_fini(struct xen_snd_front_info *front_info) +{ + xen_snd_front_alsa_fini(front_info); + xen_snd_front_evtchnl_free_all(front_info); +} + +static int sndback_initwait(struct xen_snd_front_info *front_info) +{ + int num_streams; + int ret; + + ret = xen_snd_front_cfg_card(front_info, &num_streams); + if (ret < 0) + return ret; + + /* create event channels for all streams and publish */ + ret = xen_snd_front_evtchnl_create_all(front_info, num_streams); + if (ret < 0) + return ret; + + return xen_snd_front_evtchnl_publish_all(front_info); +} + +static int sndback_connect(struct xen_snd_front_info *front_info) +{ + return xen_snd_front_alsa_init(front_info); +} + +static void sndback_disconnect(struct xen_snd_front_info *front_info) +{ + xen_snd_drv_fini(front_info); + xenbus_switch_state(front_info->xb_dev, XenbusStateInitialising); +} + +static void sndback_changed(struct xenbus_device *xb_dev, + enum xenbus_state backend_state) +{ + struct xen_snd_front_info *front_info = dev_get_drvdata(&xb_dev->dev); + int ret; + + dev_dbg(&xb_dev->dev, "Backend state is %s, front is %s\n", + xenbus_strstate(backend_state), + xenbus_strstate(xb_dev->state)); + + switch (backend_state) { + case XenbusStateReconfiguring: + /* fall through */ + case XenbusStateReconfigured: + /* fall through */ + case XenbusStateInitialised: + /* fall through */ + break; + + case XenbusStateInitialising: + /* Recovering after backend unexpected closure. */ + sndback_disconnect(front_info); + break; + + case XenbusStateInitWait: + /* Recovering after backend unexpected closure. */ + sndback_disconnect(front_info); + + ret = sndback_initwait(front_info); + if (ret < 0) + xenbus_dev_fatal(xb_dev, ret, "initializing frontend"); + else + xenbus_switch_state(xb_dev, XenbusStateInitialised); + break; + + case XenbusStateConnected: + if (xb_dev->state != XenbusStateInitialised) + break; + + ret = sndback_connect(front_info); + if (ret < 0) + xenbus_dev_fatal(xb_dev, ret, "initializing frontend"); + else + xenbus_switch_state(xb_dev, XenbusStateConnected); + break; + + case XenbusStateClosing: + /* + * In this state backend starts freeing resources, + * so let it go into closed state first, so we can also + * remove ours. + */ + break; + + case XenbusStateUnknown: + /* fall through */ + case XenbusStateClosed: + if (xb_dev->state == XenbusStateClosed) + break; + + sndback_disconnect(front_info); + break; + } +} + +static int xen_drv_probe(struct xenbus_device *xb_dev, + const struct xenbus_device_id *id) +{ + struct xen_snd_front_info *front_info; + + front_info = devm_kzalloc(&xb_dev->dev, + sizeof(*front_info), GFP_KERNEL); + if (!front_info) + return -ENOMEM; + + front_info->xb_dev = xb_dev; + dev_set_drvdata(&xb_dev->dev, front_info); + + return xenbus_switch_state(xb_dev, XenbusStateInitialising); +} + +static int xen_drv_remove(struct xenbus_device *dev) +{ + struct xen_snd_front_info *front_info = dev_get_drvdata(&dev->dev); + int to = 100; + + xenbus_switch_state(dev, XenbusStateClosing); + + /* + * On driver removal it is disconnected from XenBus, + * so no backend state change events come via .otherend_changed + * callback. This prevents us from exiting gracefully, e.g. + * signaling the backend to free event channels, waiting for its + * state to change to XenbusStateClosed and cleaning at our end. + * Normally when front driver removed backend will finally go into + * XenbusStateInitWait state. + * + * Workaround: read backend's state manually and wait with time-out. + */ + while ((xenbus_read_unsigned(front_info->xb_dev->otherend, "state", + XenbusStateUnknown) != XenbusStateInitWait) && + --to) + msleep(10); + + if (!to) { + unsigned int state; + + state = xenbus_read_unsigned(front_info->xb_dev->otherend, + "state", XenbusStateUnknown); + pr_err("Backend state is %s while removing driver\n", + xenbus_strstate(state)); + } + + xen_snd_drv_fini(front_info); + xenbus_frontend_closed(dev); + return 0; +} + +static const struct xenbus_device_id xen_drv_ids[] = { + { XENSND_DRIVER_NAME }, + { "" } +}; + +static struct xenbus_driver xen_driver = { + .ids = xen_drv_ids, + .probe = xen_drv_probe, + .remove = xen_drv_remove, + .otherend_changed = sndback_changed, +}; + +static int __init xen_drv_init(void) +{ + if (!xen_domain()) + return -ENODEV; + + if (!xen_has_pv_devices()) + return -ENODEV; + + /* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */ + if (XEN_PAGE_SIZE != PAGE_SIZE) { + pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n", + XEN_PAGE_SIZE, PAGE_SIZE); + return -ENODEV; + } + + pr_info("Initialising Xen " XENSND_DRIVER_NAME " frontend driver\n"); + return xenbus_register_frontend(&xen_driver); +} + +static void __exit xen_drv_fini(void) +{ + pr_info("Unregistering Xen " XENSND_DRIVER_NAME " frontend driver\n"); + xenbus_unregister_driver(&xen_driver); +} + +module_init(xen_drv_init); +module_exit(xen_drv_fini); + +MODULE_DESCRIPTION("Xen virtual sound device frontend"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("xen:" XENSND_DRIVER_NAME); +MODULE_SUPPORTED_DEVICE("{{ALSA,Virtual soundcard}}"); diff --git a/sound/xen/xen_snd_front.h b/sound/xen/xen_snd_front.h new file mode 100644 index 000000000000..a2ea2463bcc5 --- /dev/null +++ b/sound/xen/xen_snd_front.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> + */ + +#ifndef __XEN_SND_FRONT_H +#define __XEN_SND_FRONT_H + +#include "xen_snd_front_cfg.h" + +struct xen_snd_front_card_info; +struct xen_snd_front_evtchnl; +struct xen_snd_front_evtchnl_pair; +struct xen_snd_front_shbuf; +struct xensnd_query_hw_param; + +struct xen_snd_front_info { + struct xenbus_device *xb_dev; + + struct xen_snd_front_card_info *card_info; + + int num_evt_pairs; + struct xen_snd_front_evtchnl_pair *evt_pairs; + + struct xen_front_cfg_card cfg; +}; + +int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, + struct xensnd_query_hw_param *hw_param_req, + struct xensnd_query_hw_param *hw_param_resp); + +int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, + struct xen_snd_front_shbuf *sh_buf, + u8 format, unsigned int channels, + unsigned int rate, u32 buffer_sz, + u32 period_sz); + +int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl); + +int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl, + unsigned long pos, unsigned long count); + +int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl, + unsigned long pos, unsigned long count); + +int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl, + int type); + +#endif /* __XEN_SND_FRONT_H */ diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c new file mode 100644 index 000000000000..5a2bd70a2fa1 --- /dev/null +++ b/sound/xen/xen_snd_front_alsa.c @@ -0,0 +1,822 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> + */ + +#include <linux/platform_device.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include <xen/xenbus.h> + +#include "xen_snd_front.h" +#include "xen_snd_front_alsa.h" +#include "xen_snd_front_cfg.h" +#include "xen_snd_front_evtchnl.h" +#include "xen_snd_front_shbuf.h" + +struct xen_snd_front_pcm_stream_info { + struct xen_snd_front_info *front_info; + struct xen_snd_front_evtchnl_pair *evt_pair; + struct xen_snd_front_shbuf sh_buf; + int index; + + bool is_open; + struct snd_pcm_hardware pcm_hw; + + /* Number of processed frames as reported by the backend. */ + snd_pcm_uframes_t be_cur_frame; + /* Current HW pointer to be reported via .period callback. */ + atomic_t hw_ptr; + /* Modulo of the number of processed frames - for period detection. */ + u32 out_frames; +}; + +struct xen_snd_front_pcm_instance_info { + struct xen_snd_front_card_info *card_info; + struct snd_pcm *pcm; + struct snd_pcm_hardware pcm_hw; + int num_pcm_streams_pb; + struct xen_snd_front_pcm_stream_info *streams_pb; + int num_pcm_streams_cap; + struct xen_snd_front_pcm_stream_info *streams_cap; +}; + +struct xen_snd_front_card_info { + struct xen_snd_front_info *front_info; + struct snd_card *card; + struct snd_pcm_hardware pcm_hw; + int num_pcm_instances; + struct xen_snd_front_pcm_instance_info *pcm_instances; +}; + +struct alsa_sndif_sample_format { + u8 sndif; + snd_pcm_format_t alsa; +}; + +struct alsa_sndif_hw_param { + u8 sndif; + snd_pcm_hw_param_t alsa; +}; + +static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = { + { + .sndif = XENSND_PCM_FORMAT_U8, + .alsa = SNDRV_PCM_FORMAT_U8 + }, + { + .sndif = XENSND_PCM_FORMAT_S8, + .alsa = SNDRV_PCM_FORMAT_S8 + }, + { + .sndif = XENSND_PCM_FORMAT_U16_LE, + .alsa = SNDRV_PCM_FORMAT_U16_LE + }, + { + .sndif = XENSND_PCM_FORMAT_U16_BE, + .alsa = SNDRV_PCM_FORMAT_U16_BE + }, + { + .sndif = XENSND_PCM_FORMAT_S16_LE, + .alsa = SNDRV_PCM_FORMAT_S16_LE + }, + { + .sndif = XENSND_PCM_FORMAT_S16_BE, + .alsa = SNDRV_PCM_FORMAT_S16_BE + }, + { + .sndif = XENSND_PCM_FORMAT_U24_LE, + .alsa = SNDRV_PCM_FORMAT_U24_LE + }, + { + .sndif = XENSND_PCM_FORMAT_U24_BE, + .alsa = SNDRV_PCM_FORMAT_U24_BE + }, + { + .sndif = XENSND_PCM_FORMAT_S24_LE, + .alsa = SNDRV_PCM_FORMAT_S24_LE + }, + { + .sndif = XENSND_PCM_FORMAT_S24_BE, + .alsa = SNDRV_PCM_FORMAT_S24_BE + }, + { + .sndif = XENSND_PCM_FORMAT_U32_LE, + .alsa = SNDRV_PCM_FORMAT_U32_LE + }, + { + .sndif = XENSND_PCM_FORMAT_U32_BE, + .alsa = SNDRV_PCM_FORMAT_U32_BE + }, + { + .sndif = XENSND_PCM_FORMAT_S32_LE, + .alsa = SNDRV_PCM_FORMAT_S32_LE + }, + { + .sndif = XENSND_PCM_FORMAT_S32_BE, + .alsa = SNDRV_PCM_FORMAT_S32_BE + }, + { + .sndif = XENSND_PCM_FORMAT_A_LAW, + .alsa = SNDRV_PCM_FORMAT_A_LAW + }, + { + .sndif = XENSND_PCM_FORMAT_MU_LAW, + .alsa = SNDRV_PCM_FORMAT_MU_LAW + }, + { + .sndif = XENSND_PCM_FORMAT_F32_LE, + .alsa = SNDRV_PCM_FORMAT_FLOAT_LE + }, + { + .sndif = XENSND_PCM_FORMAT_F32_BE, + .alsa = SNDRV_PCM_FORMAT_FLOAT_BE + }, + { + .sndif = XENSND_PCM_FORMAT_F64_LE, + .alsa = SNDRV_PCM_FORMAT_FLOAT64_LE + }, + { + .sndif = XENSND_PCM_FORMAT_F64_BE, + .alsa = SNDRV_PCM_FORMAT_FLOAT64_BE + }, + { + .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE, + .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE + }, + { + .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE, + .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE + }, + { + .sndif = XENSND_PCM_FORMAT_IMA_ADPCM, + .alsa = SNDRV_PCM_FORMAT_IMA_ADPCM + }, + { + .sndif = XENSND_PCM_FORMAT_MPEG, + .alsa = SNDRV_PCM_FORMAT_MPEG + }, + { + .sndif = XENSND_PCM_FORMAT_GSM, + .alsa = SNDRV_PCM_FORMAT_GSM + }, +}; + +static int to_sndif_format(snd_pcm_format_t format) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++) + if (ALSA_SNDIF_FORMATS[i].alsa == format) + return ALSA_SNDIF_FORMATS[i].sndif; + + return -EINVAL; +} + +static u64 to_sndif_formats_mask(u64 alsa_formats) +{ + u64 mask; + int i; + + mask = 0; + for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++) + if (1 << ALSA_SNDIF_FORMATS[i].alsa & alsa_formats) + mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif; + + return mask; +} + +static u64 to_alsa_formats_mask(u64 sndif_formats) +{ + u64 mask; + int i; + + mask = 0; + for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++) + if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats) + mask |= 1 << ALSA_SNDIF_FORMATS[i].alsa; + + return mask; +} + +static void stream_clear(struct xen_snd_front_pcm_stream_info *stream) +{ + stream->is_open = false; + stream->be_cur_frame = 0; + stream->out_frames = 0; + atomic_set(&stream->hw_ptr, 0); + xen_snd_front_evtchnl_pair_clear(stream->evt_pair); + xen_snd_front_shbuf_clear(&stream->sh_buf); +} + +static void stream_free(struct xen_snd_front_pcm_stream_info *stream) +{ + xen_snd_front_shbuf_free(&stream->sh_buf); + stream_clear(stream); +} + +static struct xen_snd_front_pcm_stream_info * +stream_get(struct snd_pcm_substream *substream) +{ + struct xen_snd_front_pcm_instance_info *pcm_instance = + snd_pcm_substream_chip(substream); + struct xen_snd_front_pcm_stream_info *stream; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + stream = &pcm_instance->streams_pb[substream->number]; + else + stream = &pcm_instance->streams_cap[substream->number]; + + return stream; +} + +static int alsa_hw_rule(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct xen_snd_front_pcm_stream_info *stream = rule->private; + struct device *dev = &stream->front_info->xb_dev->dev; + struct snd_mask *formats = + hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_interval *rates = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *period = + hw_param_interval(params, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + struct snd_interval *buffer = + hw_param_interval(params, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE); + struct xensnd_query_hw_param req; + struct xensnd_query_hw_param resp; + struct snd_interval interval; + struct snd_mask mask; + u64 sndif_formats; + int changed, ret; + + /* Collect all the values we need for the query. */ + + req.formats = to_sndif_formats_mask((u64)formats->bits[0] | + (u64)(formats->bits[1]) << 32); + + req.rates.min = rates->min; + req.rates.max = rates->max; + + req.channels.min = channels->min; + req.channels.max = channels->max; + + req.buffer.min = buffer->min; + req.buffer.max = buffer->max; + + req.period.min = period->min; + req.period.max = period->max; + + ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req, + &req, &resp); + if (ret < 0) { + /* Check if this is due to backend communication error. */ + if (ret == -EIO || ret == -ETIMEDOUT) + dev_err(dev, "Failed to query ALSA HW parameters\n"); + return ret; + } + + /* Refine HW parameters after the query. */ + changed = 0; + + sndif_formats = to_alsa_formats_mask(resp.formats); + snd_mask_none(&mask); + mask.bits[0] = (u32)sndif_formats; + mask.bits[1] = (u32)(sndif_formats >> 32); + ret = snd_mask_refine(formats, &mask); + if (ret < 0) + return ret; + changed |= ret; + + interval.openmin = 0; + interval.openmax = 0; + interval.integer = 1; + + interval.min = resp.rates.min; + interval.max = resp.rates.max; + ret = snd_interval_refine(rates, &interval); + if (ret < 0) + return ret; + changed |= ret; + + interval.min = resp.channels.min; + interval.max = resp.channels.max; + ret = snd_interval_refine(channels, &interval); + if (ret < 0) + return ret; + changed |= ret; + + interval.min = resp.buffer.min; + interval.max = resp.buffer.max; + ret = snd_interval_refine(buffer, &interval); + if (ret < 0) + return ret; + changed |= ret; + + interval.min = resp.period.min; + interval.max = resp.period.max; + ret = snd_interval_refine(period, &interval); + if (ret < 0) + return ret; + changed |= ret; + + return changed; +} + +static int alsa_open(struct snd_pcm_substream *substream) +{ + struct xen_snd_front_pcm_instance_info *pcm_instance = + snd_pcm_substream_chip(substream); + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct xen_snd_front_info *front_info = + pcm_instance->card_info->front_info; + struct device *dev = &front_info->xb_dev->dev; + int ret; + + /* + * Return our HW properties: override defaults with those configured + * via XenStore. + */ + runtime->hw = stream->pcm_hw; + runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_DOUBLE | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_PAUSE); + runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED; + + stream->evt_pair = &front_info->evt_pairs[stream->index]; + + stream->front_info = front_info; + + stream->evt_pair->evt.u.evt.substream = substream; + + stream_clear(stream); + + xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true); + + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + alsa_hw_rule, stream, + SNDRV_PCM_HW_PARAM_FORMAT, -1); + if (ret) { + dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_FORMAT\n"); + return ret; + } + + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + alsa_hw_rule, stream, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (ret) { + dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n"); + return ret; + } + + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + alsa_hw_rule, stream, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret) { + dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n"); + return ret; + } + + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + alsa_hw_rule, stream, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + if (ret) { + dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n"); + return ret; + } + + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + alsa_hw_rule, stream, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); + if (ret) { + dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n"); + return ret; + } + + return 0; +} + +static int alsa_close(struct snd_pcm_substream *substream) +{ + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + + xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false); + return 0; +} + +static int alsa_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + int ret; + + /* + * This callback may be called multiple times, + * so free the previously allocated shared buffer if any. + */ + stream_free(stream); + + ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev, + &stream->sh_buf, + params_buffer_bytes(params)); + if (ret < 0) { + stream_free(stream); + dev_err(&stream->front_info->xb_dev->dev, + "Failed to allocate buffers for stream with index %d\n", + stream->index); + return ret; + } + + return 0; +} + +static int alsa_hw_free(struct snd_pcm_substream *substream) +{ + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + int ret; + + ret = xen_snd_front_stream_close(&stream->evt_pair->req); + stream_free(stream); + return ret; +} + +static int alsa_prepare(struct snd_pcm_substream *substream) +{ + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + + if (!stream->is_open) { + struct snd_pcm_runtime *runtime = substream->runtime; + u8 sndif_format; + int ret; + + ret = to_sndif_format(runtime->format); + if (ret < 0) { + dev_err(&stream->front_info->xb_dev->dev, + "Unsupported sample format: %d\n", + runtime->format); + return ret; + } + sndif_format = ret; + + ret = xen_snd_front_stream_prepare(&stream->evt_pair->req, + &stream->sh_buf, + sndif_format, + runtime->channels, + runtime->rate, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream)); + if (ret < 0) + return ret; + + stream->is_open = true; + } + + return 0; +} + +static int alsa_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + int type; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + type = XENSND_OP_TRIGGER_START; + break; + + case SNDRV_PCM_TRIGGER_RESUME: + type = XENSND_OP_TRIGGER_RESUME; + break; + + case SNDRV_PCM_TRIGGER_STOP: + type = XENSND_OP_TRIGGER_STOP; + break; + + case SNDRV_PCM_TRIGGER_SUSPEND: + type = XENSND_OP_TRIGGER_PAUSE; + break; + + default: + return -EINVAL; + } + + return xen_snd_front_stream_trigger(&stream->evt_pair->req, type); +} + +void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl, + u64 pos_bytes) +{ + struct snd_pcm_substream *substream = evtchnl->u.evt.substream; + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + snd_pcm_uframes_t delta, new_hw_ptr, cur_frame; + + cur_frame = bytes_to_frames(substream->runtime, pos_bytes); + + delta = cur_frame - stream->be_cur_frame; + stream->be_cur_frame = cur_frame; + + new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr); + new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size; + atomic_set(&stream->hw_ptr, (int)new_hw_ptr); + + stream->out_frames += delta; + if (stream->out_frames > substream->runtime->period_size) { + stream->out_frames %= substream->runtime->period_size; + snd_pcm_period_elapsed(substream); + } +} + +static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream) +{ + struct xen_snd_front_pcm_stream_info *stream = stream_get(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) +{ + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + + if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + return -EINVAL; + + if (copy_from_user(stream->sh_buf.buffer + pos, 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->sh_buf.buffer_sz)) + return -EINVAL; + + memcpy(stream->sh_buf.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->sh_buf.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->sh_buf.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) +{ + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + int ret; + + if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + return -EINVAL; + + ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count); + if (ret < 0) + return ret; + + memcpy(dst, stream->sh_buf.buffer + pos, count); + + return 0; +} + +static int alsa_pb_fill_silence(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + unsigned long count) +{ + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); + + if (unlikely(pos + count > stream->sh_buf.buffer_sz)) + return -EINVAL; + + memset(stream->sh_buf.buffer + pos, 0, count); + + return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); +} + +/* + * FIXME: The mmaped data transfer is asynchronous and there is no + * ack signal from user-space when it is done. This is the + * reason it is not implemented in the PV driver as we do need + * to know when the buffer can be transferred to the backend. + */ + +static struct snd_pcm_ops snd_drv_alsa_playback_ops = { + .open = alsa_open, + .close = alsa_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = alsa_hw_params, + .hw_free = alsa_hw_free, + .prepare = alsa_prepare, + .trigger = alsa_trigger, + .pointer = alsa_pointer, + .copy_user = alsa_pb_copy_user, + .copy_kernel = alsa_pb_copy_kernel, + .fill_silence = alsa_pb_fill_silence, +}; + +static struct snd_pcm_ops snd_drv_alsa_capture_ops = { + .open = alsa_open, + .close = alsa_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = alsa_hw_params, + .hw_free = alsa_hw_free, + .prepare = alsa_prepare, + .trigger = alsa_trigger, + .pointer = alsa_pointer, + .copy_user = alsa_cap_copy_user, + .copy_kernel = alsa_cap_copy_kernel, +}; + +static int new_pcm_instance(struct xen_snd_front_card_info *card_info, + struct xen_front_cfg_pcm_instance *instance_cfg, + struct xen_snd_front_pcm_instance_info *pcm_instance_info) +{ + struct snd_pcm *pcm; + int ret, i; + + dev_dbg(&card_info->front_info->xb_dev->dev, + "New PCM device \"%s\" with id %d playback %d capture %d", + instance_cfg->name, + instance_cfg->device_id, + instance_cfg->num_streams_pb, + instance_cfg->num_streams_cap); + + pcm_instance_info->card_info = card_info; + + pcm_instance_info->pcm_hw = instance_cfg->pcm_hw; + + if (instance_cfg->num_streams_pb) { + pcm_instance_info->streams_pb = + devm_kcalloc(&card_info->card->card_dev, + instance_cfg->num_streams_pb, + sizeof(struct xen_snd_front_pcm_stream_info), + GFP_KERNEL); + if (!pcm_instance_info->streams_pb) + return -ENOMEM; + } + + if (instance_cfg->num_streams_cap) { + pcm_instance_info->streams_cap = + devm_kcalloc(&card_info->card->card_dev, + instance_cfg->num_streams_cap, + sizeof(struct xen_snd_front_pcm_stream_info), + GFP_KERNEL); + if (!pcm_instance_info->streams_cap) + return -ENOMEM; + } + + pcm_instance_info->num_pcm_streams_pb = + instance_cfg->num_streams_pb; + pcm_instance_info->num_pcm_streams_cap = + instance_cfg->num_streams_cap; + + for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) { + pcm_instance_info->streams_pb[i].pcm_hw = + instance_cfg->streams_pb[i].pcm_hw; + pcm_instance_info->streams_pb[i].index = + instance_cfg->streams_pb[i].index; + } + + for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) { + pcm_instance_info->streams_cap[i].pcm_hw = + instance_cfg->streams_cap[i].pcm_hw; + pcm_instance_info->streams_cap[i].index = + instance_cfg->streams_cap[i].index; + } + + ret = snd_pcm_new(card_info->card, instance_cfg->name, + instance_cfg->device_id, + instance_cfg->num_streams_pb, + instance_cfg->num_streams_cap, + &pcm); + if (ret < 0) + return ret; + + pcm->private_data = pcm_instance_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)); + + if (instance_cfg->num_streams_pb) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_drv_alsa_playback_ops); + + if (instance_cfg->num_streams_cap) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_drv_alsa_capture_ops); + + pcm_instance_info->pcm = pcm; + return 0; +} + +int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info) +{ + struct device *dev = &front_info->xb_dev->dev; + struct xen_front_cfg_card *cfg = &front_info->cfg; + struct xen_snd_front_card_info *card_info; + struct snd_card *card; + int ret, i; + + dev_dbg(dev, "Creating virtual sound card\n"); + + ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE, + sizeof(struct xen_snd_front_card_info), &card); + if (ret < 0) + return ret; + + card_info = card->private_data; + card_info->front_info = front_info; + front_info->card_info = card_info; + card_info->card = card; + card_info->pcm_instances = + devm_kcalloc(dev, cfg->num_pcm_instances, + sizeof(struct xen_snd_front_pcm_instance_info), + GFP_KERNEL); + if (!card_info->pcm_instances) { + ret = -ENOMEM; + goto fail; + } + + card_info->num_pcm_instances = cfg->num_pcm_instances; + card_info->pcm_hw = cfg->pcm_hw; + + for (i = 0; i < cfg->num_pcm_instances; i++) { + ret = new_pcm_instance(card_info, &cfg->pcm_instances[i], + &card_info->pcm_instances[i]); + if (ret < 0) + 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)); + + ret = snd_card_register(card); + if (ret < 0) + goto fail; + + return 0; + +fail: + snd_card_free(card); + return ret; +} + +void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info) +{ + struct xen_snd_front_card_info *card_info; + struct snd_card *card; + + card_info = front_info->card_info; + if (!card_info) + return; + + card = card_info->card; + if (!card) + return; + + dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n", + card->number); + snd_card_free(card); + + /* Card_info will be freed when destroying front_info->xb_dev->dev. */ + card_info->card = NULL; +} diff --git a/sound/xen/xen_snd_front_alsa.h b/sound/xen/xen_snd_front_alsa.h new file mode 100644 index 000000000000..18abd9eec967 --- /dev/null +++ b/sound/xen/xen_snd_front_alsa.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> + */ + +#ifndef __XEN_SND_FRONT_ALSA_H +#define __XEN_SND_FRONT_ALSA_H + +struct xen_snd_front_info; + +int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info); + +void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info); + +void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl, + u64 pos_bytes); + +#endif /* __XEN_SND_FRONT_ALSA_H */ diff --git a/sound/xen/xen_snd_front_cfg.c b/sound/xen/xen_snd_front_cfg.c new file mode 100644 index 000000000000..eda077c8087a --- /dev/null +++ b/sound/xen/xen_snd_front_cfg.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> + */ + +#include <xen/xenbus.h> + +#include <xen/interface/io/sndif.h> + +#include "xen_snd_front.h" +#include "xen_snd_front_cfg.h" + +/* Maximum number of supported streams. */ +#define VSND_MAX_STREAM 8 + +struct cfg_hw_sample_rate { + const char *name; + unsigned int mask; + unsigned int value; +}; + +static const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES[] = { + { .name = "5512", .mask = SNDRV_PCM_RATE_5512, .value = 5512 }, + { .name = "8000", .mask = SNDRV_PCM_RATE_8000, .value = 8000 }, + { .name = "11025", .mask = SNDRV_PCM_RATE_11025, .value = 11025 }, + { .name = "16000", .mask = SNDRV_PCM_RATE_16000, .value = 16000 }, + { .name = "22050", .mask = SNDRV_PCM_RATE_22050, .value = 22050 }, + { .name = "32000", .mask = SNDRV_PCM_RATE_32000, .value = 32000 }, + { .name = "44100", .mask = SNDRV_PCM_RATE_44100, .value = 44100 }, + { .name = "48000", .mask = SNDRV_PCM_RATE_48000, .value = 48000 }, + { .name = "64000", .mask = SNDRV_PCM_RATE_64000, .value = 64000 }, + { .name = "96000", .mask = SNDRV_PCM_RATE_96000, .value = 96000 }, + { .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 }, + { .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 }, +}; + +struct cfg_hw_sample_format { + const char *name; + u64 mask; +}; + +static const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS[] = { + { + .name = XENSND_PCM_FORMAT_U8_STR, + .mask = SNDRV_PCM_FMTBIT_U8 + }, + { + .name = XENSND_PCM_FORMAT_S8_STR, + .mask = SNDRV_PCM_FMTBIT_S8 + }, + { + .name = XENSND_PCM_FORMAT_U16_LE_STR, + .mask = SNDRV_PCM_FMTBIT_U16_LE + }, + { + .name = XENSND_PCM_FORMAT_U16_BE_STR, + .mask = SNDRV_PCM_FMTBIT_U16_BE + }, + { + .name = XENSND_PCM_FORMAT_S16_LE_STR, + .mask = SNDRV_PCM_FMTBIT_S16_LE + }, + { + .name = XENSND_PCM_FORMAT_S16_BE_STR, + .mask = SNDRV_PCM_FMTBIT_S16_BE + }, + { + .name = XENSND_PCM_FORMAT_U24_LE_STR, + .mask = SNDRV_PCM_FMTBIT_U24_LE + }, + { + .name = XENSND_PCM_FORMAT_U24_BE_STR, + .mask = SNDRV_PCM_FMTBIT_U24_BE + }, + { + .name = XENSND_PCM_FORMAT_S24_LE_STR, + .mask = SNDRV_PCM_FMTBIT_S24_LE + }, + { + .name = XENSND_PCM_FORMAT_S24_BE_STR, + .mask = SNDRV_PCM_FMTBIT_S24_BE + }, + { + .name = XENSND_PCM_FORMAT_U32_LE_STR, + .mask = SNDRV_PCM_FMTBIT_U32_LE + }, + { + .name = XENSND_PCM_FORMAT_U32_BE_STR, + .mask = SNDRV_PCM_FMTBIT_U32_BE + }, + { + .name = XENSND_PCM_FORMAT_S32_LE_STR, + .mask = SNDRV_PCM_FMTBIT_S32_LE + }, + { + .name = XENSND_PCM_FORMAT_S32_BE_STR, + .mask = SNDRV_PCM_FMTBIT_S32_BE + }, + { + .name = XENSND_PCM_FORMAT_A_LAW_STR, + .mask = SNDRV_PCM_FMTBIT_A_LAW + }, + { + .name = XENSND_PCM_FORMAT_MU_LAW_STR, + .mask = SNDRV_PCM_FMTBIT_MU_LAW + }, + { + .name = XENSND_PCM_FORMAT_F32_LE_STR, + .mask = SNDRV_PCM_FMTBIT_FLOAT_LE + }, + { + .name = XENSND_PCM_FORMAT_F32_BE_STR, + .mask = SNDRV_PCM_FMTBIT_FLOAT_BE + }, + { + .name = XENSND_PCM_FORMAT_F64_LE_STR, + .mask = SNDRV_PCM_FMTBIT_FLOAT64_LE + }, + { + .name = XENSND_PCM_FORMAT_F64_BE_STR, + .mask = SNDRV_PCM_FMTBIT_FLOAT64_BE + }, + { + .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR, + .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE + }, + { + .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR, + .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE + }, + { + .name = XENSND_PCM_FORMAT_IMA_ADPCM_STR, + .mask = SNDRV_PCM_FMTBIT_IMA_ADPCM + }, + { + .name = XENSND_PCM_FORMAT_MPEG_STR, + .mask = SNDRV_PCM_FMTBIT_MPEG + }, + { + .name = XENSND_PCM_FORMAT_GSM_STR, + .mask = SNDRV_PCM_FMTBIT_GSM + }, +}; + +static void cfg_hw_rates(char *list, unsigned int len, + const char *path, struct snd_pcm_hardware *pcm_hw) +{ + char *cur_rate; + unsigned int cur_mask; + unsigned int cur_value; + unsigned int rates; + unsigned int rate_min; + unsigned int rate_max; + int i; + + rates = 0; + rate_min = -1; + rate_max = 0; + while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) { + for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_RATES); i++) + if (!strncasecmp(cur_rate, + CFG_HW_SUPPORTED_RATES[i].name, + XENSND_SAMPLE_RATE_MAX_LEN)) { + cur_mask = CFG_HW_SUPPORTED_RATES[i].mask; + cur_value = CFG_HW_SUPPORTED_RATES[i].value; + rates |= cur_mask; + if (rate_min > cur_value) + rate_min = cur_value; + if (rate_max < cur_value) + rate_max = cur_value; + } + } + + if (rates) { + pcm_hw->rates = rates; + pcm_hw->rate_min = rate_min; + pcm_hw->rate_max = rate_max; + } +} + +static void cfg_formats(char *list, unsigned int len, + const char *path, struct snd_pcm_hardware *pcm_hw) +{ + u64 formats; + char *cur_format; + int i; + + formats = 0; + while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) { + for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_FORMATS); i++) + if (!strncasecmp(cur_format, + CFG_HW_SUPPORTED_FORMATS[i].name, + XENSND_SAMPLE_FORMAT_MAX_LEN)) + formats |= CFG_HW_SUPPORTED_FORMATS[i].mask; + } + + if (formats) + pcm_hw->formats = formats; +} + +#define MAX_BUFFER_SIZE (64 * 1024) +#define MIN_PERIOD_SIZE 64 +#define MAX_PERIOD_SIZE MAX_BUFFER_SIZE +#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE) +#define USE_RATE (SNDRV_PCM_RATE_CONTINUOUS | \ + SNDRV_PCM_RATE_8000_48000) +#define USE_RATE_MIN 5512 +#define USE_RATE_MAX 48000 +#define USE_CHANNELS_MIN 1 +#define USE_CHANNELS_MAX 2 +#define USE_PERIODS_MIN 2 +#define USE_PERIODS_MAX (MAX_BUFFER_SIZE / MIN_PERIOD_SIZE) + +static const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = USE_FORMATS, + .rates = USE_RATE, + .rate_min = USE_RATE_MIN, + .rate_max = USE_RATE_MAX, + .channels_min = USE_CHANNELS_MIN, + .channels_max = USE_CHANNELS_MAX, + .buffer_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_min = MIN_PERIOD_SIZE, + .period_bytes_max = MAX_PERIOD_SIZE, + .periods_min = USE_PERIODS_MIN, + .periods_max = USE_PERIODS_MAX, + .fifo_size = 0, +}; + +static void cfg_read_pcm_hw(const char *path, + struct snd_pcm_hardware *parent_pcm_hw, + struct snd_pcm_hardware *pcm_hw) +{ + char *list; + int val; + size_t buf_sz; + unsigned int len; + + /* Inherit parent's PCM HW and read overrides from XenStore. */ + if (parent_pcm_hw) + *pcm_hw = *parent_pcm_hw; + else + *pcm_hw = SND_DRV_PCM_HW_DEFAULT; + + val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0); + if (val) + pcm_hw->channels_min = val; + + val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0); + if (val) + pcm_hw->channels_max = val; + + list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len); + if (!IS_ERR(list)) { + cfg_hw_rates(list, len, path, pcm_hw); + kfree(list); + } + + list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len); + if (!IS_ERR(list)) { + cfg_formats(list, len, path, pcm_hw); + kfree(list); + } + + buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0); + if (buf_sz) + pcm_hw->buffer_bytes_max = buf_sz; + + /* Update configuration to match new values. */ + if (pcm_hw->channels_min > pcm_hw->channels_max) + pcm_hw->channels_min = pcm_hw->channels_max; + + if (pcm_hw->rate_min > pcm_hw->rate_max) + pcm_hw->rate_min = pcm_hw->rate_max; + + pcm_hw->period_bytes_max = pcm_hw->buffer_bytes_max; + + pcm_hw->periods_max = pcm_hw->period_bytes_max / + pcm_hw->period_bytes_min; +} + +static int cfg_get_stream_type(const char *path, int index, + int *num_pb, int *num_cap) +{ + char *str = NULL; + char *stream_path; + int ret; + + *num_pb = 0; + *num_cap = 0; + stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index); + if (!stream_path) { + ret = -ENOMEM; + goto fail; + } + + str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL); + if (IS_ERR(str)) { + ret = PTR_ERR(str); + str = NULL; + goto fail; + } + + if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, + sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { + (*num_pb)++; + } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, + sizeof(XENSND_STREAM_TYPE_CAPTURE))) { + (*num_cap)++; + } else { + ret = -EINVAL; + goto fail; + } + ret = 0; + +fail: + kfree(stream_path); + kfree(str); + return ret; +} + +static int cfg_stream(struct xen_snd_front_info *front_info, + struct xen_front_cfg_pcm_instance *pcm_instance, + const char *path, int index, int *cur_pb, int *cur_cap, + int *stream_cnt) +{ + char *str = NULL; + char *stream_path; + struct xen_front_cfg_stream *stream; + int ret; + + stream_path = devm_kasprintf(&front_info->xb_dev->dev, + GFP_KERNEL, "%s/%d", path, index); + if (!stream_path) { + ret = -ENOMEM; + goto fail; + } + + str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL); + if (IS_ERR(str)) { + ret = PTR_ERR(str); + str = NULL; + goto fail; + } + + if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, + sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { + stream = &pcm_instance->streams_pb[(*cur_pb)++]; + } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, + sizeof(XENSND_STREAM_TYPE_CAPTURE))) { + stream = &pcm_instance->streams_cap[(*cur_cap)++]; + } else { + ret = -EINVAL; + goto fail; + } + + /* Get next stream index. */ + stream->index = (*stream_cnt)++; + stream->xenstore_path = stream_path; + /* + * Check XenStore if PCM HW configuration exists for this stream + * and update if so, e.g. we inherit all values from device's PCM HW, + * but can still override some of the values for the stream. + */ + cfg_read_pcm_hw(stream->xenstore_path, + &pcm_instance->pcm_hw, &stream->pcm_hw); + ret = 0; + +fail: + kfree(str); + return ret; +} + +static int cfg_device(struct xen_snd_front_info *front_info, + struct xen_front_cfg_pcm_instance *pcm_instance, + struct snd_pcm_hardware *parent_pcm_hw, + const char *path, int node_index, int *stream_cnt) +{ + char *str; + char *device_path; + int ret, i, num_streams; + int num_pb, num_cap; + int cur_pb, cur_cap; + char node[3]; + + device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index); + if (!device_path) + return -ENOMEM; + + str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL); + if (!IS_ERR(str)) { + strlcpy(pcm_instance->name, str, sizeof(pcm_instance->name)); + kfree(str); + } + + pcm_instance->device_id = node_index; + + /* + * Check XenStore if PCM HW configuration exists for this device + * and update if so, e.g. we inherit all values from card's PCM HW, + * but can still override some of the values for the device. + */ + cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw); + + /* Find out how many streams were configured in Xen store. */ + num_streams = 0; + do { + snprintf(node, sizeof(node), "%d", num_streams); + if (!xenbus_exists(XBT_NIL, device_path, node)) + break; + + num_streams++; + } while (num_streams < VSND_MAX_STREAM); + + pcm_instance->num_streams_pb = 0; + pcm_instance->num_streams_cap = 0; + /* Get number of playback and capture streams. */ + for (i = 0; i < num_streams; i++) { + ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap); + if (ret < 0) + goto fail; + + pcm_instance->num_streams_pb += num_pb; + pcm_instance->num_streams_cap += num_cap; + } + + if (pcm_instance->num_streams_pb) { + pcm_instance->streams_pb = + devm_kcalloc(&front_info->xb_dev->dev, + pcm_instance->num_streams_pb, + sizeof(struct xen_front_cfg_stream), + GFP_KERNEL); + if (!pcm_instance->streams_pb) { + ret = -ENOMEM; + goto fail; + } + } + + if (pcm_instance->num_streams_cap) { + pcm_instance->streams_cap = + devm_kcalloc(&front_info->xb_dev->dev, + pcm_instance->num_streams_cap, + sizeof(struct xen_front_cfg_stream), + GFP_KERNEL); + if (!pcm_instance->streams_cap) { + ret = -ENOMEM; + goto fail; + } + } + + cur_pb = 0; + cur_cap = 0; + for (i = 0; i < num_streams; i++) { + ret = cfg_stream(front_info, pcm_instance, device_path, i, + &cur_pb, &cur_cap, stream_cnt); + if (ret < 0) + goto fail; + } + ret = 0; + +fail: + kfree(device_path); + return ret; +} + +int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info, + int *stream_cnt) +{ + struct xenbus_device *xb_dev = front_info->xb_dev; + struct xen_front_cfg_card *cfg = &front_info->cfg; + int ret, num_devices, i; + char node[3]; + + *stream_cnt = 0; + num_devices = 0; + do { + snprintf(node, sizeof(node), "%d", num_devices); + if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node)) + break; + + num_devices++; + } while (num_devices < SNDRV_PCM_DEVICES); + + if (!num_devices) { + dev_warn(&xb_dev->dev, + "No devices configured for sound card at %s\n", + xb_dev->nodename); + return -ENODEV; + } + + /* Start from default PCM HW configuration for the card. */ + cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw); + + cfg->pcm_instances = + devm_kcalloc(&front_info->xb_dev->dev, num_devices, + sizeof(struct xen_front_cfg_pcm_instance), + GFP_KERNEL); + if (!cfg->pcm_instances) + return -ENOMEM; + + for (i = 0; i < num_devices; i++) { + ret = cfg_device(front_info, &cfg->pcm_instances[i], + &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt); + if (ret < 0) + return ret; + } + cfg->num_pcm_instances = num_devices; + return 0; +} + diff --git a/sound/xen/xen_snd_front_cfg.h b/sound/xen/xen_snd_front_cfg.h new file mode 100644 index 000000000000..2353fcc74889 --- /dev/null +++ b/sound/xen/xen_snd_front_cfg.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> + */ + +#ifndef __XEN_SND_FRONT_CFG_H +#define __XEN_SND_FRONT_CFG_H + +#include <sound/core.h> +#include <sound/pcm.h> + +struct xen_snd_front_info; + +struct xen_front_cfg_stream { + int index; + char *xenstore_path; + struct snd_pcm_hardware pcm_hw; +}; + +struct xen_front_cfg_pcm_instance { + char name[80]; + int device_id; + struct snd_pcm_hardware pcm_hw; + int num_streams_pb; + struct xen_front_cfg_stream *streams_pb; + int num_streams_cap; + struct xen_front_cfg_stream *streams_cap; +}; + +struct xen_front_cfg_card { + char name_short[32]; + char name_long[80]; + struct snd_pcm_hardware pcm_hw; + int num_pcm_instances; + struct xen_front_cfg_pcm_instance *pcm_instances; +}; + +int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info, + int *stream_cnt); + +#endif /* __XEN_SND_FRONT_CFG_H */ diff --git a/sound/xen/xen_snd_front_evtchnl.c b/sound/xen/xen_snd_front_evtchnl.c new file mode 100644 index 000000000000..102d6e096cc8 --- /dev/null +++ b/sound/xen/xen_snd_front_evtchnl.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> + */ + +#include <xen/events.h> +#include <xen/grant_table.h> +#include <xen/xen.h> +#include <xen/xenbus.h> + +#include "xen_snd_front.h" +#include "xen_snd_front_alsa.h" +#include "xen_snd_front_cfg.h" +#include "xen_snd_front_evtchnl.h" + +static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id) +{ + struct xen_snd_front_evtchnl *channel = dev_id; + struct xen_snd_front_info *front_info = channel->front_info; + struct xensnd_resp *resp; + RING_IDX i, rp; + + if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED)) + return IRQ_HANDLED; + + mutex_lock(&channel->ring_io_lock); + +again: + rp = channel->u.req.ring.sring->rsp_prod; + /* Ensure we see queued responses up to rp. */ + rmb(); + + /* + * Assume that the backend is trusted to always write sane values + * to the ring counters, so no overflow checks on frontend side + * are required. + */ + for (i = channel->u.req.ring.rsp_cons; i != rp; i++) { + resp = RING_GET_RESPONSE(&channel->u.req.ring, i); + if (resp->id != channel->evt_id) + continue; + switch (resp->operation) { + case XENSND_OP_OPEN: + /* fall through */ + case XENSND_OP_CLOSE: + /* fall through */ + case XENSND_OP_READ: + /* fall through */ + case XENSND_OP_WRITE: + /* fall through */ + case XENSND_OP_TRIGGER: + channel->u.req.resp_status = resp->status; + complete(&channel->u.req.completion); + break; + case XENSND_OP_HW_PARAM_QUERY: + channel->u.req.resp_status = resp->status; + channel->u.req.resp.hw_param = + resp->resp.hw_param; + complete(&channel->u.req.completion); + break; + + default: + dev_err(&front_info->xb_dev->dev, + "Operation %d is not supported\n", + resp->operation); + break; + } + } + + channel->u.req.ring.rsp_cons = i; + if (i != channel->u.req.ring.req_prod_pvt) { + int more_to_do; + + RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring, + more_to_do); + if (more_to_do) + goto again; + } else { + channel->u.req.ring.sring->rsp_event = i + 1; + } + + mutex_unlock(&channel->ring_io_lock); + return IRQ_HANDLED; +} + +static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id) +{ + struct xen_snd_front_evtchnl *channel = dev_id; + struct xensnd_event_page *page = channel->u.evt.page; + u32 cons, prod; + + if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED)) + return IRQ_HANDLED; + + mutex_lock(&channel->ring_io_lock); + + prod = page->in_prod; + /* Ensure we see ring contents up to prod. */ + virt_rmb(); + if (prod == page->in_cons) + goto out; + + /* + * Assume that the backend is trusted to always write sane values + * to the ring counters, so no overflow checks on frontend side + * are required. + */ + for (cons = page->in_cons; cons != prod; cons++) { + struct xensnd_evt *event; + + event = &XENSND_IN_RING_REF(page, cons); + if (unlikely(event->id != channel->evt_id++)) + continue; + + switch (event->type) { + case XENSND_EVT_CUR_POS: + xen_snd_front_alsa_handle_cur_pos(channel, + event->op.cur_pos.position); + break; + } + } + + page->in_cons = cons; + /* Ensure ring contents. */ + virt_wmb(); + +out: + mutex_unlock(&channel->ring_io_lock); + return IRQ_HANDLED; +} + +void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel) +{ + int notify; + + channel->u.req.ring.req_prod_pvt++; + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify); + if (notify) + notify_remote_via_irq(channel->irq); +} + +static void evtchnl_free(struct xen_snd_front_info *front_info, + struct xen_snd_front_evtchnl *channel) +{ + unsigned long page = 0; + + if (channel->type == EVTCHNL_TYPE_REQ) + page = (unsigned long)channel->u.req.ring.sring; + else if (channel->type == EVTCHNL_TYPE_EVT) + page = (unsigned long)channel->u.evt.page; + + if (!page) + return; + + channel->state = EVTCHNL_STATE_DISCONNECTED; + if (channel->type == EVTCHNL_TYPE_REQ) { + /* Release all who still waits for response if any. */ + channel->u.req.resp_status = -EIO; + complete_all(&channel->u.req.completion); + } + + if (channel->irq) + unbind_from_irqhandler(channel->irq, channel); + + if (channel->port) + xenbus_free_evtchn(front_info->xb_dev, channel->port); + + /* End access and free the page. */ + if (channel->gref != GRANT_INVALID_REF) + gnttab_end_foreign_access(channel->gref, 0, page); + else + free_page(page); + + memset(channel, 0, sizeof(*channel)); +} + +void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info) +{ + int i; + + if (!front_info->evt_pairs) + return; + + for (i = 0; i < front_info->num_evt_pairs; i++) { + evtchnl_free(front_info, &front_info->evt_pairs[i].req); + evtchnl_free(front_info, &front_info->evt_pairs[i].evt); + } + + kfree(front_info->evt_pairs); + front_info->evt_pairs = NULL; +} + +static int evtchnl_alloc(struct xen_snd_front_info *front_info, int index, + struct xen_snd_front_evtchnl *channel, + enum xen_snd_front_evtchnl_type type) +{ + struct xenbus_device *xb_dev = front_info->xb_dev; + unsigned long page; + grant_ref_t gref; + irq_handler_t handler; + char *handler_name = NULL; + int ret; + + memset(channel, 0, sizeof(*channel)); + channel->type = type; + channel->index = index; + channel->front_info = front_info; + channel->state = EVTCHNL_STATE_DISCONNECTED; + channel->gref = GRANT_INVALID_REF; + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + ret = -ENOMEM; + goto fail; + } + + handler_name = kasprintf(GFP_KERNEL, "%s-%s", XENSND_DRIVER_NAME, + type == EVTCHNL_TYPE_REQ ? + XENSND_FIELD_RING_REF : + XENSND_FIELD_EVT_RING_REF); + if (!handler_name) { + ret = -ENOMEM; + goto fail; + } + + mutex_init(&channel->ring_io_lock); + + if (type == EVTCHNL_TYPE_REQ) { + struct xen_sndif_sring *sring = (struct xen_sndif_sring *)page; + + init_completion(&channel->u.req.completion); + mutex_init(&channel->u.req.req_io_lock); + SHARED_RING_INIT(sring); + FRONT_RING_INIT(&channel->u.req.ring, sring, XEN_PAGE_SIZE); + + ret = xenbus_grant_ring(xb_dev, sring, 1, &gref); + if (ret < 0) { + channel->u.req.ring.sring = NULL; + goto fail; + } + + handler = evtchnl_interrupt_req; + } else { + ret = gnttab_grant_foreign_access(xb_dev->otherend_id, + virt_to_gfn((void *)page), 0); + if (ret < 0) + goto fail; + + channel->u.evt.page = (struct xensnd_event_page *)page; + gref = ret; + handler = evtchnl_interrupt_evt; + } + + channel->gref = gref; + + ret = xenbus_alloc_evtchn(xb_dev, &channel->port); + if (ret < 0) + goto fail; + + ret = bind_evtchn_to_irq(channel->port); + if (ret < 0) { + dev_err(&xb_dev->dev, + "Failed to bind IRQ for domid %d port %d: %d\n", + front_info->xb_dev->otherend_id, channel->port, ret); + goto fail; + } + + channel->irq = ret; + + ret = request_threaded_irq(channel->irq, NULL, handler, + IRQF_ONESHOT, handler_name, channel); + if (ret < 0) { + dev_err(&xb_dev->dev, "Failed to request IRQ %d: %d\n", + channel->irq, ret); + goto fail; + } + + kfree(handler_name); + return 0; + +fail: + if (page) + free_page(page); + kfree(handler_name); + dev_err(&xb_dev->dev, "Failed to allocate ring: %d\n", ret); + return ret; +} + +int xen_snd_front_evtchnl_create_all(struct xen_snd_front_info *front_info, + int num_streams) +{ + struct xen_front_cfg_card *cfg = &front_info->cfg; + struct device *dev = &front_info->xb_dev->dev; + int d, ret = 0; + + front_info->evt_pairs = + kcalloc(num_streams, + sizeof(struct xen_snd_front_evtchnl_pair), + GFP_KERNEL); + if (!front_info->evt_pairs) + return -ENOMEM; + + /* Iterate over devices and their streams and create event channels. */ + for (d = 0; d < cfg->num_pcm_instances; d++) { + struct xen_front_cfg_pcm_instance *pcm_instance; + int s, index; + + pcm_instance = &cfg->pcm_instances[d]; + + for (s = 0; s < pcm_instance->num_streams_pb; s++) { + index = pcm_instance->streams_pb[s].index; + + ret = evtchnl_alloc(front_info, index, + &front_info->evt_pairs[index].req, + EVTCHNL_TYPE_REQ); + if (ret < 0) { + dev_err(dev, "Error allocating control channel\n"); + goto fail; + } + + ret = evtchnl_alloc(front_info, index, + &front_info->evt_pairs[index].evt, + EVTCHNL_TYPE_EVT); + if (ret < 0) { + dev_err(dev, "Error allocating in-event channel\n"); + goto fail; + } + } + + for (s = 0; s < pcm_instance->num_streams_cap; s++) { + index = pcm_instance->streams_cap[s].index; + + ret = evtchnl_alloc(front_info, index, + &front_info->evt_pairs[index].req, + EVTCHNL_TYPE_REQ); + if (ret < 0) { + dev_err(dev, "Error allocating control channel\n"); + goto fail; + } + + ret = evtchnl_alloc(front_info, index, + &front_info->evt_pairs[index].evt, + EVTCHNL_TYPE_EVT); + if (ret < 0) { + dev_err(dev, "Error allocating in-event channel\n"); + goto fail; + } + } + } + + front_info->num_evt_pairs = num_streams; + return 0; + +fail: + xen_snd_front_evtchnl_free_all(front_info); + return ret; +} + +static int evtchnl_publish(struct xenbus_transaction xbt, + struct xen_snd_front_evtchnl *channel, + const char *path, const char *node_ring, + const char *node_chnl) +{ + struct xenbus_device *xb_dev = channel->front_info->xb_dev; + int ret; + + /* Write control channel ring reference. */ + ret = xenbus_printf(xbt, path, node_ring, "%u", channel->gref); + if (ret < 0) { + dev_err(&xb_dev->dev, "Error writing ring-ref: %d\n", ret); + return ret; + } + + /* Write event channel ring reference. */ + ret = xenbus_printf(xbt, path, node_chnl, "%u", channel->port); + if (ret < 0) { + dev_err(&xb_dev->dev, "Error writing event channel: %d\n", ret); + return ret; + } + + return 0; +} + +int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info) +{ + struct xen_front_cfg_card *cfg = &front_info->cfg; + struct xenbus_transaction xbt; + int ret, d; + +again: + ret = xenbus_transaction_start(&xbt); + if (ret < 0) { + xenbus_dev_fatal(front_info->xb_dev, ret, + "starting transaction"); + return ret; + } + + for (d = 0; d < cfg->num_pcm_instances; d++) { + struct xen_front_cfg_pcm_instance *pcm_instance; + int s, index; + + pcm_instance = &cfg->pcm_instances[d]; + + for (s = 0; s < pcm_instance->num_streams_pb; s++) { + index = pcm_instance->streams_pb[s].index; + + ret = evtchnl_publish(xbt, + &front_info->evt_pairs[index].req, + pcm_instance->streams_pb[s].xenstore_path, + XENSND_FIELD_RING_REF, + XENSND_FIELD_EVT_CHNL); + if (ret < 0) + goto fail; + + ret = evtchnl_publish(xbt, + &front_info->evt_pairs[index].evt, + pcm_instance->streams_pb[s].xenstore_path, + XENSND_FIELD_EVT_RING_REF, + XENSND_FIELD_EVT_EVT_CHNL); + if (ret < 0) + goto fail; + } + + for (s = 0; s < pcm_instance->num_streams_cap; s++) { + index = pcm_instance->streams_cap[s].index; + + ret = evtchnl_publish(xbt, + &front_info->evt_pairs[index].req, + pcm_instance->streams_cap[s].xenstore_path, + XENSND_FIELD_RING_REF, + XENSND_FIELD_EVT_CHNL); + if (ret < 0) + goto fail; + + ret = evtchnl_publish(xbt, + &front_info->evt_pairs[index].evt, + pcm_instance->streams_cap[s].xenstore_path, + XENSND_FIELD_EVT_RING_REF, + XENSND_FIELD_EVT_EVT_CHNL); + if (ret < 0) + goto fail; + } + } + ret = xenbus_transaction_end(xbt, 0); + if (ret < 0) { + if (ret == -EAGAIN) + goto again; + + xenbus_dev_fatal(front_info->xb_dev, ret, + "completing transaction"); + goto fail_to_end; + } + return 0; +fail: + xenbus_transaction_end(xbt, 1); +fail_to_end: + xenbus_dev_fatal(front_info->xb_dev, ret, "writing XenStore"); + return ret; +} + +void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, + bool is_connected) +{ + enum xen_snd_front_evtchnl_state state; + + if (is_connected) + state = EVTCHNL_STATE_CONNECTED; + else + state = EVTCHNL_STATE_DISCONNECTED; + + mutex_lock(&evt_pair->req.ring_io_lock); + evt_pair->req.state = state; + mutex_unlock(&evt_pair->req.ring_io_lock); + + mutex_lock(&evt_pair->evt.ring_io_lock); + evt_pair->evt.state = state; + mutex_unlock(&evt_pair->evt.ring_io_lock); +} + +void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pair) +{ + mutex_lock(&evt_pair->req.ring_io_lock); + evt_pair->req.evt_next_id = 0; + mutex_unlock(&evt_pair->req.ring_io_lock); + + mutex_lock(&evt_pair->evt.ring_io_lock); + evt_pair->evt.evt_next_id = 0; + mutex_unlock(&evt_pair->evt.ring_io_lock); +} + diff --git a/sound/xen/xen_snd_front_evtchnl.h b/sound/xen/xen_snd_front_evtchnl.h new file mode 100644 index 000000000000..cbe51fd1ec15 --- /dev/null +++ b/sound/xen/xen_snd_front_evtchnl.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> + */ + +#ifndef __XEN_SND_FRONT_EVTCHNL_H +#define __XEN_SND_FRONT_EVTCHNL_H + +#include <xen/interface/io/sndif.h> + +struct xen_snd_front_info; + +#ifndef GRANT_INVALID_REF +/* + * FIXME: usage of grant reference 0 as invalid grant reference: + * grant reference 0 is valid, but never exposed to a PV driver, + * because of the fact it is already in use/reserved by the PV console. + */ +#define GRANT_INVALID_REF 0 +#endif + +/* Timeout in ms to wait for backend to respond. */ +#define VSND_WAIT_BACK_MS 3000 + +enum xen_snd_front_evtchnl_state { + EVTCHNL_STATE_DISCONNECTED, + EVTCHNL_STATE_CONNECTED, +}; + +enum xen_snd_front_evtchnl_type { + EVTCHNL_TYPE_REQ, + EVTCHNL_TYPE_EVT, +}; + +struct xen_snd_front_evtchnl { + struct xen_snd_front_info *front_info; + int gref; + int port; + int irq; + int index; + /* State of the event channel. */ + enum xen_snd_front_evtchnl_state state; + enum xen_snd_front_evtchnl_type type; + /* Either response id or incoming event id. */ + u16 evt_id; + /* Next request id or next expected event id. */ + u16 evt_next_id; + /* Shared ring access lock. */ + struct mutex ring_io_lock; + union { + struct { + struct xen_sndif_front_ring ring; + struct completion completion; + /* Serializer for backend IO: request/response. */ + struct mutex req_io_lock; + + /* Latest response status. */ + int resp_status; + union { + struct xensnd_query_hw_param hw_param; + } resp; + } req; + struct { + struct xensnd_event_page *page; + /* This is needed to handle XENSND_EVT_CUR_POS event. */ + struct snd_pcm_substream *substream; + } evt; + } u; +}; + +struct xen_snd_front_evtchnl_pair { + struct xen_snd_front_evtchnl req; + struct xen_snd_front_evtchnl evt; +}; + +int xen_snd_front_evtchnl_create_all(struct xen_snd_front_info *front_info, + int num_streams); + +void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info); + +int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info); + +void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *evtchnl); + +void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, + bool is_connected); + +void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pair); + +#endif /* __XEN_SND_FRONT_EVTCHNL_H */ diff --git a/sound/xen/xen_snd_front_shbuf.c b/sound/xen/xen_snd_front_shbuf.c new file mode 100644 index 000000000000..07ac176a41ba --- /dev/null +++ b/sound/xen/xen_snd_front_shbuf.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> + */ + +#include <linux/kernel.h> +#include <xen/xen.h> +#include <xen/xenbus.h> + +#include "xen_snd_front_shbuf.h" + +grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf) +{ + if (!buf->grefs) + return GRANT_INVALID_REF; + + return buf->grefs[0]; +} + +void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf) +{ + memset(buf, 0, sizeof(*buf)); +} + +void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf) +{ + int i; + + if (buf->grefs) { + for (i = 0; i < buf->num_grefs; i++) + if (buf->grefs[i] != GRANT_INVALID_REF) + gnttab_end_foreign_access(buf->grefs[i], + 0, 0UL); + kfree(buf->grefs); + } + kfree(buf->directory); + free_pages_exact(buf->buffer, buf->buffer_sz); + xen_snd_front_shbuf_clear(buf); +} + +/* + * number of grant references a page can hold with respect to the + * xensnd_page_directory header + */ +#define XENSND_NUM_GREFS_PER_PAGE ((XEN_PAGE_SIZE - \ + offsetof(struct xensnd_page_directory, gref)) / \ + sizeof(grant_ref_t)) + +static void fill_page_dir(struct xen_snd_front_shbuf *buf, + int num_pages_dir) +{ + struct xensnd_page_directory *page_dir; + unsigned char *ptr; + int i, cur_gref, grefs_left, to_copy; + + ptr = buf->directory; + grefs_left = buf->num_grefs - num_pages_dir; + /* + * skip grant references at the beginning, they are for pages granted + * for the page directory itself + */ + cur_gref = num_pages_dir; + for (i = 0; i < num_pages_dir; i++) { + page_dir = (struct xensnd_page_directory *)ptr; + if (grefs_left <= XENSND_NUM_GREFS_PER_PAGE) { + to_copy = grefs_left; + page_dir->gref_dir_next_page = GRANT_INVALID_REF; + } else { + to_copy = XENSND_NUM_GREFS_PER_PAGE; + page_dir->gref_dir_next_page = buf->grefs[i + 1]; + } + + memcpy(&page_dir->gref, &buf->grefs[cur_gref], + to_copy * sizeof(grant_ref_t)); + + ptr += XEN_PAGE_SIZE; + grefs_left -= to_copy; + cur_gref += to_copy; + } +} + +static int grant_references(struct xenbus_device *xb_dev, + struct xen_snd_front_shbuf *buf, + int num_pages_dir, int num_pages_buffer, + int num_grefs) +{ + grant_ref_t priv_gref_head; + unsigned long frame; + int ret, i, j, cur_ref; + int otherend_id; + + ret = gnttab_alloc_grant_references(num_grefs, &priv_gref_head); + if (ret) + return ret; + + buf->num_grefs = num_grefs; + otherend_id = xb_dev->otherend_id; + j = 0; + + for (i = 0; i < num_pages_dir; i++) { + cur_ref = gnttab_claim_grant_reference(&priv_gref_head); + if (cur_ref < 0) { + ret = cur_ref; + goto fail; + } + + frame = xen_page_to_gfn(virt_to_page(buf->directory + + XEN_PAGE_SIZE * i)); + gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); + buf->grefs[j++] = cur_ref; + } + + for (i = 0; i < num_pages_buffer; i++) { + cur_ref = gnttab_claim_grant_reference(&priv_gref_head); + if (cur_ref < 0) { + ret = cur_ref; + goto fail; + } + + frame = xen_page_to_gfn(virt_to_page(buf->buffer + + XEN_PAGE_SIZE * i)); + gnttab_grant_foreign_access_ref(cur_ref, otherend_id, frame, 0); + buf->grefs[j++] = cur_ref; + } + + gnttab_free_grant_references(priv_gref_head); + fill_page_dir(buf, num_pages_dir); + return 0; + +fail: + gnttab_free_grant_references(priv_gref_head); + return ret; +} + +static int alloc_int_buffers(struct xen_snd_front_shbuf *buf, + int num_pages_dir, int num_pages_buffer, + int num_grefs) +{ + buf->grefs = kcalloc(num_grefs, sizeof(*buf->grefs), GFP_KERNEL); + if (!buf->grefs) + return -ENOMEM; + + buf->directory = kcalloc(num_pages_dir, XEN_PAGE_SIZE, GFP_KERNEL); + if (!buf->directory) + goto fail; + + buf->buffer_sz = num_pages_buffer * XEN_PAGE_SIZE; + buf->buffer = alloc_pages_exact(buf->buffer_sz, GFP_KERNEL); + if (!buf->buffer) + goto fail; + + return 0; + +fail: + kfree(buf->grefs); + buf->grefs = NULL; + kfree(buf->directory); + buf->directory = NULL; + return -ENOMEM; +} + +int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev, + struct xen_snd_front_shbuf *buf, + unsigned int buffer_sz) +{ + int num_pages_buffer, num_pages_dir, num_grefs; + int ret; + + xen_snd_front_shbuf_clear(buf); + + num_pages_buffer = DIV_ROUND_UP(buffer_sz, XEN_PAGE_SIZE); + /* number of pages the page directory consumes itself */ + num_pages_dir = DIV_ROUND_UP(num_pages_buffer, + XENSND_NUM_GREFS_PER_PAGE); + num_grefs = num_pages_buffer + num_pages_dir; + + ret = alloc_int_buffers(buf, num_pages_dir, + num_pages_buffer, num_grefs); + if (ret < 0) + return ret; + + ret = grant_references(xb_dev, buf, num_pages_dir, num_pages_buffer, + num_grefs); + if (ret < 0) + return ret; + + fill_page_dir(buf, num_pages_dir); + return 0; +} diff --git a/sound/xen/xen_snd_front_shbuf.h b/sound/xen/xen_snd_front_shbuf.h new file mode 100644 index 000000000000..d28e97c47b2c --- /dev/null +++ b/sound/xen/xen_snd_front_shbuf.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> + */ + +#ifndef __XEN_SND_FRONT_SHBUF_H +#define __XEN_SND_FRONT_SHBUF_H + +#include <xen/grant_table.h> + +#include "xen_snd_front_evtchnl.h" + +struct xen_snd_front_shbuf { + int num_grefs; + grant_ref_t *grefs; + u8 *directory; + u8 *buffer; + size_t buffer_sz; +}; + +grant_ref_t xen_snd_front_shbuf_get_dir_start(struct xen_snd_front_shbuf *buf); + +int xen_snd_front_shbuf_alloc(struct xenbus_device *xb_dev, + struct xen_snd_front_shbuf *buf, + unsigned int buffer_sz); + +void xen_snd_front_shbuf_clear(struct xen_snd_front_shbuf *buf); + +void xen_snd_front_shbuf_free(struct xen_snd_front_shbuf *buf); + +#endif /* __XEN_SND_FRONT_SHBUF_H */ |