diff options
Diffstat (limited to 'sound')
188 files changed, 9803 insertions, 2096 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig index e3e9491..a2a1e24 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -97,11 +97,11 @@ config SND_PCM_TIMER bool "PCM timer interface" if EXPERT default y help - If you disable this option, pcm timer will be inavailable, so - those stubs used pcm timer (e.g. dmix, dsnoop & co) may work + If you disable this option, pcm timer will be unavailable, so + those stubs that use pcm timer (e.g. dmix, dsnoop & co) may work incorrectlly. - For some embedded device, we may disable it to reduce memory + For some embedded devices, we may disable it to reduce memory footprint, about 20KB on x86_64 platform. config SND_SEQUENCER_OSS diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 18b8dc4..7fac3ca 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -46,6 +46,13 @@ #include <sound/compress_offload.h> #include <sound/compress_driver.h> +/* struct snd_compr_codec_caps overflows the ioctl bit size for some + * architectures, so we need to disable the relevant ioctls. + */ +#if _IOC_SIZEBITS < 14 +#define COMPR_CODEC_CAPS_OVERFLOW +#endif + /* TODO: * - add substream support for multiple devices in case of * SND_DYNAMIC_MINORS is not used @@ -440,6 +447,7 @@ out: return retval; } +#ifndef COMPR_CODEC_CAPS_OVERFLOW static int snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg) { @@ -463,6 +471,7 @@ out: kfree(caps); return retval; } +#endif /* !COMPR_CODEC_CAPS_OVERFLOW */ /* revisit this with snd_pcm_preallocate_xxx */ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, @@ -801,9 +810,11 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) case _IOC_NR(SNDRV_COMPRESS_GET_CAPS): retval = snd_compr_get_caps(stream, arg); break; +#ifndef COMPR_CODEC_CAPS_OVERFLOW case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS): retval = snd_compr_get_codec_caps(stream, arg); break; +#endif case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS): retval = snd_compr_set_params(stream, arg); break; diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index b9c0910..0608f21 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -170,6 +170,19 @@ struct snd_ctl_elem_value32 { unsigned char reserved[128]; }; +#ifdef CONFIG_X86_X32 +/* x32 has a different alignment for 64bit values from ia32 */ +struct snd_ctl_elem_value_x32 { + struct snd_ctl_elem_id id; + unsigned int indirect; /* bit-field causes misalignment */ + union { + s32 integer[128]; + unsigned char data[512]; + s64 integer64[64]; + } value; + unsigned char reserved[128]; +}; +#endif /* CONFIG_X86_X32 */ /* get the value type and count of the control */ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, @@ -219,9 +232,11 @@ static int get_elem_size(int type, int count) static int copy_ctl_value_from_user(struct snd_card *card, struct snd_ctl_elem_value *data, - struct snd_ctl_elem_value32 __user *data32, + void __user *userdata, + void __user *valuep, int *typep, int *countp) { + struct snd_ctl_elem_value32 __user *data32 = userdata; int i, type, size; int uninitialized_var(count); unsigned int indirect; @@ -239,8 +254,9 @@ static int copy_ctl_value_from_user(struct snd_card *card, if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || type == SNDRV_CTL_ELEM_TYPE_INTEGER) { for (i = 0; i < count; i++) { + s32 __user *intp = valuep; int val; - if (get_user(val, &data32->value.integer[i])) + if (get_user(val, &intp[i])) return -EFAULT; data->value.integer.value[i] = val; } @@ -250,8 +266,7 @@ static int copy_ctl_value_from_user(struct snd_card *card, dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); return -EINVAL; } - if (copy_from_user(data->value.bytes.data, - data32->value.data, size)) + if (copy_from_user(data->value.bytes.data, valuep, size)) return -EFAULT; } @@ -261,7 +276,8 @@ static int copy_ctl_value_from_user(struct snd_card *card, } /* restore the value to 32bit */ -static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32, +static int copy_ctl_value_to_user(void __user *userdata, + void __user *valuep, struct snd_ctl_elem_value *data, int type, int count) { @@ -270,22 +286,22 @@ static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32, if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || type == SNDRV_CTL_ELEM_TYPE_INTEGER) { for (i = 0; i < count; i++) { + s32 __user *intp = valuep; int val; val = data->value.integer.value[i]; - if (put_user(val, &data32->value.integer[i])) + if (put_user(val, &intp[i])) return -EFAULT; } } else { size = get_elem_size(type, count); - if (copy_to_user(data32->value.data, - data->value.bytes.data, size)) + if (copy_to_user(valuep, data->value.bytes.data, size)) return -EFAULT; } return 0; } -static int snd_ctl_elem_read_user_compat(struct snd_card *card, - struct snd_ctl_elem_value32 __user *data32) +static int ctl_elem_read_user(struct snd_card *card, + void __user *userdata, void __user *valuep) { struct snd_ctl_elem_value *data; int err, type, count; @@ -294,7 +310,9 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card, if (data == NULL) return -ENOMEM; - if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) + err = copy_ctl_value_from_user(card, data, userdata, valuep, + &type, &count); + if (err < 0) goto error; snd_power_lock(card); @@ -303,14 +321,15 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card, err = snd_ctl_elem_read(card, data); snd_power_unlock(card); if (err >= 0) - err = copy_ctl_value_to_user(data32, data, type, count); + err = copy_ctl_value_to_user(userdata, valuep, data, + type, count); error: kfree(data); return err; } -static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file, - struct snd_ctl_elem_value32 __user *data32) +static int ctl_elem_write_user(struct snd_ctl_file *file, + void __user *userdata, void __user *valuep) { struct snd_ctl_elem_value *data; struct snd_card *card = file->card; @@ -320,7 +339,9 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file, if (data == NULL) return -ENOMEM; - if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) + err = copy_ctl_value_from_user(card, data, userdata, valuep, + &type, &count); + if (err < 0) goto error; snd_power_lock(card); @@ -329,12 +350,39 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file, err = snd_ctl_elem_write(card, file, data); snd_power_unlock(card); if (err >= 0) - err = copy_ctl_value_to_user(data32, data, type, count); + err = copy_ctl_value_to_user(userdata, valuep, data, + type, count); error: kfree(data); return err; } +static int snd_ctl_elem_read_user_compat(struct snd_card *card, + struct snd_ctl_elem_value32 __user *data32) +{ + return ctl_elem_read_user(card, data32, &data32->value); +} + +static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file, + struct snd_ctl_elem_value32 __user *data32) +{ + return ctl_elem_write_user(file, data32, &data32->value); +} + +#ifdef CONFIG_X86_X32 +static int snd_ctl_elem_read_user_x32(struct snd_card *card, + struct snd_ctl_elem_value_x32 __user *data32) +{ + return ctl_elem_read_user(card, data32, &data32->value); +} + +static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file, + struct snd_ctl_elem_value_x32 __user *data32) +{ + return ctl_elem_write_user(file, data32, &data32->value); +} +#endif /* CONFIG_X86_X32 */ + /* add or replace a user control */ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file, struct snd_ctl_elem_info32 __user *data32, @@ -393,6 +441,10 @@ enum { SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32), SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32), SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32), +#ifdef CONFIG_X86_X32 + SNDRV_CTL_IOCTL_ELEM_READ_X32 = _IOWR('U', 0x12, struct snd_ctl_elem_value_x32), + SNDRV_CTL_IOCTL_ELEM_WRITE_X32 = _IOWR('U', 0x13, struct snd_ctl_elem_value_x32), +#endif /* CONFIG_X86_X32 */ }; static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -431,6 +483,12 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns return snd_ctl_elem_add_compat(ctl, argp, 0); case SNDRV_CTL_IOCTL_ELEM_REPLACE32: return snd_ctl_elem_add_compat(ctl, argp, 1); +#ifdef CONFIG_X86_X32 + case SNDRV_CTL_IOCTL_ELEM_READ_X32: + return snd_ctl_elem_read_user_x32(ctl->card, argp); + case SNDRV_CTL_IOCTL_ELEM_WRITE_X32: + return snd_ctl_elem_write_user_x32(ctl, argp); +#endif /* CONFIG_X86_X32 */ } down_read(&snd_ioctl_rwsem); diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 0e73d03..ebc9fdf 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -835,7 +835,8 @@ static int choose_rate(struct snd_pcm_substream *substream, return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL); } -static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) +static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, + bool trylock) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hw_params *params, *sparams; @@ -849,7 +850,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) struct snd_mask sformat_mask; struct snd_mask mask; - if (mutex_lock_interruptible(&runtime->oss.params_lock)) + if (trylock) { + if (!(mutex_trylock(&runtime->oss.params_lock))) + return -EAGAIN; + } else if (mutex_lock_interruptible(&runtime->oss.params_lock)) return -EINTR; sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL); params = kmalloc(sizeof(*params), GFP_KERNEL); @@ -1092,7 +1096,7 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil if (asubstream == NULL) asubstream = substream; if (substream->runtime->oss.params) { - err = snd_pcm_oss_change_params(substream); + err = snd_pcm_oss_change_params(substream, false); if (err < 0) return err; } @@ -1132,7 +1136,7 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream) return 0; runtime = substream->runtime; if (runtime->oss.params) { - err = snd_pcm_oss_change_params(substream); + err = snd_pcm_oss_change_params(substream, false); if (err < 0) return err; } @@ -2163,7 +2167,7 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre runtime = substream->runtime; if (runtime->oss.params && - (err = snd_pcm_oss_change_params(substream)) < 0) + (err = snd_pcm_oss_change_params(substream, false)) < 0) return err; info.fragsize = runtime->oss.period_bytes; @@ -2804,7 +2808,12 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area) return -EIO; if (runtime->oss.params) { - if ((err = snd_pcm_oss_change_params(substream)) < 0) + /* use mutex_trylock() for params_lock for avoiding a deadlock + * between mmap_sem and params_lock taken by + * copy_from/to_user() in snd_pcm_oss_write/read() + */ + err = snd_pcm_oss_change_params(substream, true); + if (err < 0) return err; } #ifdef CONFIG_SND_PCM_OSS_PLUGINS diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 9630e9f..1f64ab0 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -183,6 +183,14 @@ static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream return err; } +#ifdef CONFIG_X86_X32 +/* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */ +static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream, + struct snd_pcm_channel_info __user *src); +#define snd_pcm_ioctl_channel_info_x32(s, p) \ + snd_pcm_channel_info_user(s, p) +#endif /* CONFIG_X86_X32 */ + struct snd_pcm_status32 { s32 state; struct compat_timespec trigger_tstamp; @@ -243,6 +251,71 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, return err; } +#ifdef CONFIG_X86_X32 +/* X32 ABI has 64bit timespec and 64bit alignment */ +struct snd_pcm_status_x32 { + s32 state; + u32 rsvd; /* alignment */ + struct timespec trigger_tstamp; + struct timespec tstamp; + u32 appl_ptr; + u32 hw_ptr; + s32 delay; + u32 avail; + u32 avail_max; + u32 overrange; + s32 suspended_state; + u32 audio_tstamp_data; + struct timespec audio_tstamp; + struct timespec driver_tstamp; + u32 audio_tstamp_accuracy; + unsigned char reserved[52-2*sizeof(struct timespec)]; +} __packed; + +#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst)) + +static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream, + struct snd_pcm_status_x32 __user *src, + bool ext) +{ + struct snd_pcm_status status; + int err; + + memset(&status, 0, sizeof(status)); + /* + * with extension, parameters are read/write, + * get audio_tstamp_data from user, + * ignore rest of status structure + */ + if (ext && get_user(status.audio_tstamp_data, + (u32 __user *)(&src->audio_tstamp_data))) + return -EFAULT; + err = snd_pcm_status(substream, &status); + if (err < 0) + return err; + + if (clear_user(src, sizeof(*src))) + return -EFAULT; + if (put_user(status.state, &src->state) || + put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) || + put_timespec(&status.tstamp, &src->tstamp) || + put_user(status.appl_ptr, &src->appl_ptr) || + put_user(status.hw_ptr, &src->hw_ptr) || + put_user(status.delay, &src->delay) || + put_user(status.avail, &src->avail) || + put_user(status.avail_max, &src->avail_max) || + put_user(status.overrange, &src->overrange) || + put_user(status.suspended_state, &src->suspended_state) || + put_user(status.audio_tstamp_data, &src->audio_tstamp_data) || + put_timespec(&status.audio_tstamp, &src->audio_tstamp) || + put_timespec(&status.driver_tstamp, &src->driver_tstamp) || + put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy)) + return -EFAULT; + + return err; +} +#endif /* CONFIG_X86_X32 */ + /* both for HW_PARAMS and HW_REFINE */ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, int refine, @@ -469,6 +542,93 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, return 0; } +#ifdef CONFIG_X86_X32 +/* X32 ABI has 64bit timespec and 64bit alignment */ +struct snd_pcm_mmap_status_x32 { + s32 state; + s32 pad1; + u32 hw_ptr; + u32 pad2; /* alignment */ + struct timespec tstamp; + s32 suspended_state; + struct timespec audio_tstamp; +} __packed; + +struct snd_pcm_mmap_control_x32 { + u32 appl_ptr; + u32 avail_min; +}; + +struct snd_pcm_sync_ptr_x32 { + u32 flags; + u32 rsvd; /* alignment */ + union { + struct snd_pcm_mmap_status_x32 status; + unsigned char reserved[64]; + } s; + union { + struct snd_pcm_mmap_control_x32 control; + unsigned char reserved[64]; + } c; +} __packed; + +static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, + struct snd_pcm_sync_ptr_x32 __user *src) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + volatile struct snd_pcm_mmap_status *status; + volatile struct snd_pcm_mmap_control *control; + u32 sflags; + struct snd_pcm_mmap_control scontrol; + struct snd_pcm_mmap_status sstatus; + snd_pcm_uframes_t boundary; + int err; + + if (snd_BUG_ON(!runtime)) + return -EINVAL; + + if (get_user(sflags, &src->flags) || + get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || + get_user(scontrol.avail_min, &src->c.control.avail_min)) + return -EFAULT; + if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { + err = snd_pcm_hwsync(substream); + if (err < 0) + return err; + } + status = runtime->status; + control = runtime->control; + boundary = recalculate_boundary(runtime); + if (!boundary) + boundary = 0x7fffffff; + snd_pcm_stream_lock_irq(substream); + /* FIXME: we should consider the boundary for the sync from app */ + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) + control->appl_ptr = scontrol.appl_ptr; + else + scontrol.appl_ptr = control->appl_ptr % boundary; + if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) + control->avail_min = scontrol.avail_min; + else + scontrol.avail_min = control->avail_min; + sstatus.state = status->state; + sstatus.hw_ptr = status->hw_ptr % boundary; + sstatus.tstamp = status->tstamp; + sstatus.suspended_state = status->suspended_state; + sstatus.audio_tstamp = status->audio_tstamp; + snd_pcm_stream_unlock_irq(substream); + if (put_user(sstatus.state, &src->s.status.state) || + put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || + put_timespec(&sstatus.tstamp, &src->s.status.tstamp) || + put_user(sstatus.suspended_state, &src->s.status.suspended_state) || + put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) || + put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || + put_user(scontrol.avail_min, &src->c.control.avail_min)) + return -EFAULT; + + return 0; +} +#endif /* CONFIG_X86_X32 */ /* */ @@ -487,7 +647,12 @@ enum { SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32), SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32), SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32), - +#ifdef CONFIG_X86_X32 + SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info), + SNDRV_PCM_IOCTL_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32), + SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32), + SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32), +#endif /* CONFIG_X86_X32 */ }; static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -559,6 +724,16 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l return snd_pcm_ioctl_rewind_compat(substream, argp); case SNDRV_PCM_IOCTL_FORWARD32: return snd_pcm_ioctl_forward_compat(substream, argp); +#ifdef CONFIG_X86_X32 + case SNDRV_PCM_IOCTL_STATUS_X32: + return snd_pcm_status_user_x32(substream, argp, false); + case SNDRV_PCM_IOCTL_STATUS_EXT_X32: + return snd_pcm_status_user_x32(substream, argp, true); + case SNDRV_PCM_IOCTL_SYNC_PTR_X32: + return snd_pcm_ioctl_sync_ptr_x32(substream, argp); + case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32: + return snd_pcm_ioctl_channel_info_x32(substream, argp); +#endif /* CONFIG_X86_X32 */ } return -ENOIOCTLCMD; diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index ebe8444..53dc373 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -565,3 +565,33 @@ unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a, return rates_a & rates_b; } EXPORT_SYMBOL_GPL(snd_pcm_rate_mask_intersect); + +/** + * snd_pcm_rate_range_to_bits - converts rate range to SNDRV_PCM_RATE_xxx bit + * @rate_min: the minimum sample rate + * @rate_max: the maximum sample rate + * + * This function has an implicit assumption: the rates in the given range have + * only the pre-defined rates like 44100 or 16000. + * + * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate range, + * or SNDRV_PCM_RATE_KNOT for an unknown range. + */ +unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min, + unsigned int rate_max) +{ + unsigned int rates = 0; + int i; + + for (i = 0; i < snd_pcm_known_rates.count; i++) { + if (snd_pcm_known_rates.list[i] >= rate_min + && snd_pcm_known_rates.list[i] <= rate_max) + rates |= 1 << i; + } + + if (!rates) + rates = SNDRV_PCM_RATE_KNOT; + + return rates; +} +EXPORT_SYMBOL_GPL(snd_pcm_rate_range_to_bits); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index fadd3eb..9106d8e 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -74,6 +74,18 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream); static DEFINE_RWLOCK(snd_pcm_link_rwlock); static DECLARE_RWSEM(snd_pcm_link_rwsem); +/* Writer in rwsem may block readers even during its waiting in queue, + * and this may lead to a deadlock when the code path takes read sem + * twice (e.g. one in snd_pcm_action_nonatomic() and another in + * snd_pcm_stream_lock()). As a (suboptimal) workaround, let writer to + * spin until it gets the lock. + */ +static inline void down_write_nonblock(struct rw_semaphore *lock) +{ + while (!down_write_trylock(lock)) + cond_resched(); +} + /** * snd_pcm_stream_lock - Lock the PCM stream * @substream: PCM substream @@ -1813,7 +1825,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) res = -ENOMEM; goto _nolock; } - down_write(&snd_pcm_link_rwsem); + down_write_nonblock(&snd_pcm_link_rwsem); write_lock_irq(&snd_pcm_link_rwlock); if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || substream->runtime->status->state != substream1->runtime->status->state || @@ -1860,7 +1872,7 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream) struct snd_pcm_substream *s; int res = 0; - down_write(&snd_pcm_link_rwsem); + down_write_nonblock(&snd_pcm_link_rwsem); write_lock_irq(&snd_pcm_link_rwlock); if (!snd_pcm_stream_linked(substream)) { res = -EALREADY; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index a775984..795437b 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -942,31 +942,36 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, unsigned long flags; long result = 0, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; + unsigned long appl_ptr; + spin_lock_irqsave(&runtime->lock, flags); while (count > 0 && runtime->avail) { count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) count1 = count; - spin_lock_irqsave(&runtime->lock, flags); if (count1 > (int)runtime->avail) count1 = runtime->avail; + + /* update runtime->appl_ptr before unlocking for userbuf */ + appl_ptr = runtime->appl_ptr; + runtime->appl_ptr += count1; + runtime->appl_ptr %= runtime->buffer_size; + runtime->avail -= count1; + if (kernelbuf) - memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1); + memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1); if (userbuf) { spin_unlock_irqrestore(&runtime->lock, flags); if (copy_to_user(userbuf + result, - runtime->buffer + runtime->appl_ptr, count1)) { + runtime->buffer + appl_ptr, count1)) { return result > 0 ? result : -EFAULT; } spin_lock_irqsave(&runtime->lock, flags); } - runtime->appl_ptr += count1; - runtime->appl_ptr %= runtime->buffer_size; - runtime->avail -= count1; - spin_unlock_irqrestore(&runtime->lock, flags); result += count1; count -= count1; } + spin_unlock_irqrestore(&runtime->lock, flags); return result; } @@ -1055,23 +1060,16 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream) EXPORT_SYMBOL(snd_rawmidi_transmit_empty); /** - * snd_rawmidi_transmit_peek - copy data from the internal buffer + * __snd_rawmidi_transmit_peek - copy data from the internal buffer * @substream: the rawmidi substream * @buffer: the buffer pointer * @count: data size to transfer * - * Copies data from the internal output buffer to the given buffer. - * - * Call this in the interrupt handler when the midi output is ready, - * and call snd_rawmidi_transmit_ack() after the transmission is - * finished. - * - * Return: The size of copied data, or a negative error code on failure. + * This is a variant of snd_rawmidi_transmit_peek() without spinlock. */ -int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, +int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { - unsigned long flags; int result, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; @@ -1081,7 +1079,6 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, return -EINVAL; } result = 0; - spin_lock_irqsave(&runtime->lock, flags); if (runtime->avail >= runtime->buffer_size) { /* warning: lowlevel layer MUST trigger down the hardware */ goto __skip; @@ -1106,25 +1103,47 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, } } __skip: + return result; +} +EXPORT_SYMBOL(__snd_rawmidi_transmit_peek); + +/** + * snd_rawmidi_transmit_peek - copy data from the internal buffer + * @substream: the rawmidi substream + * @buffer: the buffer pointer + * @count: data size to transfer + * + * Copies data from the internal output buffer to the given buffer. + * + * Call this in the interrupt handler when the midi output is ready, + * and call snd_rawmidi_transmit_ack() after the transmission is + * finished. + * + * Return: The size of copied data, or a negative error code on failure. + */ +int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, + unsigned char *buffer, int count) +{ + struct snd_rawmidi_runtime *runtime = substream->runtime; + int result; + unsigned long flags; + + spin_lock_irqsave(&runtime->lock, flags); + result = __snd_rawmidi_transmit_peek(substream, buffer, count); spin_unlock_irqrestore(&runtime->lock, flags); return result; } EXPORT_SYMBOL(snd_rawmidi_transmit_peek); /** - * snd_rawmidi_transmit_ack - acknowledge the transmission + * __snd_rawmidi_transmit_ack - acknowledge the transmission * @substream: the rawmidi substream * @count: the transferred count * - * Advances the hardware pointer for the internal output buffer with - * the given size and updates the condition. - * Call after the transmission is finished. - * - * Return: The advanced size if successful, or a negative error code on failure. + * This is a variant of __snd_rawmidi_transmit_ack() without spinlock. */ -int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) +int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) { - unsigned long flags; struct snd_rawmidi_runtime *runtime = substream->runtime; if (runtime->buffer == NULL) { @@ -1132,7 +1151,6 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) "snd_rawmidi_transmit_ack: output is not active!!!\n"); return -EINVAL; } - spin_lock_irqsave(&runtime->lock, flags); snd_BUG_ON(runtime->avail + count > runtime->buffer_size); runtime->hw_ptr += count; runtime->hw_ptr %= runtime->buffer_size; @@ -1142,9 +1160,32 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) if (runtime->drain || snd_rawmidi_ready(substream)) wake_up(&runtime->sleep); } - spin_unlock_irqrestore(&runtime->lock, flags); return count; } +EXPORT_SYMBOL(__snd_rawmidi_transmit_ack); + +/** + * snd_rawmidi_transmit_ack - acknowledge the transmission + * @substream: the rawmidi substream + * @count: the transferred count + * + * Advances the hardware pointer for the internal output buffer with + * the given size and updates the condition. + * Call after the transmission is finished. + * + * Return: The advanced size if successful, or a negative error code on failure. + */ +int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count) +{ + struct snd_rawmidi_runtime *runtime = substream->runtime; + int result; + unsigned long flags; + + spin_lock_irqsave(&runtime->lock, flags); + result = __snd_rawmidi_transmit_ack(substream, count); + spin_unlock_irqrestore(&runtime->lock, flags); + return result; +} EXPORT_SYMBOL(snd_rawmidi_transmit_ack); /** @@ -1160,12 +1201,22 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack); int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count) { + struct snd_rawmidi_runtime *runtime = substream->runtime; + int result; + unsigned long flags; + + spin_lock_irqsave(&runtime->lock, flags); if (!substream->opened) - return -EBADFD; - count = snd_rawmidi_transmit_peek(substream, buffer, count); - if (count < 0) - return count; - return snd_rawmidi_transmit_ack(substream, count); + result = -EBADFD; + else { + count = __snd_rawmidi_transmit_peek(substream, buffer, count); + if (count <= 0) + result = count; + else + result = __snd_rawmidi_transmit_ack(substream, count); + } + spin_unlock_irqrestore(&runtime->lock, flags); + return result; } EXPORT_SYMBOL(snd_rawmidi_transmit); @@ -1177,8 +1228,9 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, unsigned long flags; long count1, result; struct snd_rawmidi_runtime *runtime = substream->runtime; + unsigned long appl_ptr; - if (snd_BUG_ON(!kernelbuf && !userbuf)) + if (!kernelbuf && !userbuf) return -EINVAL; if (snd_BUG_ON(!runtime->buffer)) return -EINVAL; @@ -1197,12 +1249,19 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, count1 = count; if (count1 > (long)runtime->avail) count1 = runtime->avail; + + /* update runtime->appl_ptr before unlocking for userbuf */ + appl_ptr = runtime->appl_ptr; + runtime->appl_ptr += count1; + runtime->appl_ptr %= runtime->buffer_size; + runtime->avail -= count1; + if (kernelbuf) - memcpy(runtime->buffer + runtime->appl_ptr, + memcpy(runtime->buffer + appl_ptr, kernelbuf + result, count1); else if (userbuf) { spin_unlock_irqrestore(&runtime->lock, flags); - if (copy_from_user(runtime->buffer + runtime->appl_ptr, + if (copy_from_user(runtime->buffer + appl_ptr, userbuf + result, count1)) { spin_lock_irqsave(&runtime->lock, flags); result = result > 0 ? result : -EFAULT; @@ -1210,9 +1269,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, } spin_lock_irqsave(&runtime->lock, flags); } - runtime->appl_ptr += count1; - runtime->appl_ptr %= runtime->buffer_size; - runtime->avail -= count1; result += count1; count -= count1; } diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c index 5268c1f..f69764d 100644 --- a/sound/core/rawmidi_compat.c +++ b/sound/core/rawmidi_compat.c @@ -85,8 +85,7 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile, if (err < 0) return err; - if (put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) || - put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) || + if (compat_put_timespec(&status.tstamp, &src->tstamp) || put_user(status.avail, &src->avail) || put_user(status.xruns, &src->xruns)) return -EFAULT; @@ -94,9 +93,58 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile, return 0; } +#ifdef CONFIG_X86_X32 +/* X32 ABI has 64bit timespec and 64bit alignment */ +struct snd_rawmidi_status_x32 { + s32 stream; + u32 rsvd; /* alignment */ + struct timespec tstamp; + u32 avail; + u32 xruns; + unsigned char reserved[16]; +} __attribute__((packed)); + +#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst)) + +static int snd_rawmidi_ioctl_status_x32(struct snd_rawmidi_file *rfile, + struct snd_rawmidi_status_x32 __user *src) +{ + int err; + struct snd_rawmidi_status status; + + if (rfile->output == NULL) + return -EINVAL; + if (get_user(status.stream, &src->stream)) + return -EFAULT; + + switch (status.stream) { + case SNDRV_RAWMIDI_STREAM_OUTPUT: + err = snd_rawmidi_output_status(rfile->output, &status); + break; + case SNDRV_RAWMIDI_STREAM_INPUT: + err = snd_rawmidi_input_status(rfile->input, &status); + break; + default: + return -EINVAL; + } + if (err < 0) + return err; + + if (put_timespec(&status.tstamp, &src->tstamp) || + put_user(status.avail, &src->avail) || + put_user(status.xruns, &src->xruns)) + return -EFAULT; + + return 0; +} +#endif /* CONFIG_X86_X32 */ + enum { SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct snd_rawmidi_params32), SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct snd_rawmidi_status32), +#ifdef CONFIG_X86_X32 + SNDRV_RAWMIDI_IOCTL_STATUS_X32 = _IOWR('W', 0x20, struct snd_rawmidi_status_x32), +#endif /* CONFIG_X86_X32 */ }; static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -115,6 +163,10 @@ static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsign return snd_rawmidi_ioctl_params_compat(rfile, argp); case SNDRV_RAWMIDI_IOCTL_STATUS32: return snd_rawmidi_ioctl_status_compat(rfile, argp); +#ifdef CONFIG_X86_X32 + case SNDRV_RAWMIDI_IOCTL_STATUS_X32: + return snd_rawmidi_ioctl_status_x32(rfile, argp); +#endif /* CONFIG_X86_X32 */ } return -ENOIOCTLCMD; } diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 8db156b..8cdf489 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -149,8 +149,6 @@ odev_release(struct inode *inode, struct file *file) if ((dp = file->private_data) == NULL) return 0; - snd_seq_oss_drain_write(dp); - mutex_lock(®ister_mutex); snd_seq_oss_release(dp); mutex_unlock(®ister_mutex); diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h index b439243..d7b4d01 100644 --- a/sound/core/seq/oss/seq_oss_device.h +++ b/sound/core/seq/oss/seq_oss_device.h @@ -127,7 +127,6 @@ int snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int co unsigned int snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait); void snd_seq_oss_reset(struct seq_oss_devinfo *dp); -void snd_seq_oss_drain_write(struct seq_oss_devinfo *dp); /* */ void snd_seq_oss_process_queue(struct seq_oss_devinfo *dp, abstime_t time); diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index b1221b2..92c96a9 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -202,7 +202,7 @@ snd_seq_oss_open(struct file *file, int level) dp->index = i; if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) { - pr_err("ALSA: seq_oss: too many applications\n"); + pr_debug("ALSA: seq_oss: too many applications\n"); rc = -ENOMEM; goto _error; } @@ -436,22 +436,6 @@ snd_seq_oss_release(struct seq_oss_devinfo *dp) /* - * Wait until the queue is empty (if we don't have nonblock) - */ -void -snd_seq_oss_drain_write(struct seq_oss_devinfo *dp) -{ - if (! dp->timer->running) - return; - if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) && - dp->writeq) { - while (snd_seq_oss_writeq_sync(dp->writeq)) - ; - } -} - - -/* * reset sequencer devices */ void diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 0f3b381..b16dbef 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -308,7 +308,7 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp) struct seq_oss_synth *rec; struct seq_oss_synthinfo *info; - if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)) + if (snd_BUG_ON(dp->max_synthdev > SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)) return; for (i = 0; i < dp->max_synthdev; i++) { info = &dp->synths[i]; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 13cfa81..58e79e0 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -678,6 +678,9 @@ static int deliver_to_subscribers(struct snd_seq_client *client, else down_read(&grp->list_mutex); list_for_each_entry(subs, &grp->list_head, src_list) { + /* both ports ready? */ + if (atomic_read(&subs->ref_count) != 2) + continue; event->dest = subs->info.dest; if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) /* convert time according to flag with subscription */ diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 8010766..c850345 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -383,15 +383,20 @@ int snd_seq_pool_init(struct snd_seq_pool *pool) if (snd_BUG_ON(!pool)) return -EINVAL; - if (pool->ptr) /* should be atomic? */ - return 0; - pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size); - if (!pool->ptr) + cellptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size); + if (!cellptr) return -ENOMEM; /* add new cells to the free cell list */ spin_lock_irqsave(&pool->lock, flags); + if (pool->ptr) { + spin_unlock_irqrestore(&pool->lock, flags); + vfree(cellptr); + return 0; + } + + pool->ptr = cellptr; pool->free = NULL; for (cell = 0; cell < pool->size; cell++) { diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 55170a2..fe686ee 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -173,10 +173,6 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, } /* */ -enum group_type { - SRC_LIST, DEST_LIST -}; - static int subscribe_port(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_port_subs_info *grp, @@ -203,6 +199,20 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr, return NULL; } +static void delete_and_unsubscribe_port(struct snd_seq_client *client, + struct snd_seq_client_port *port, + struct snd_seq_subscribers *subs, + bool is_src, bool ack); + +static inline struct snd_seq_subscribers * +get_subscriber(struct list_head *p, bool is_src) +{ + if (is_src) + return list_entry(p, struct snd_seq_subscribers, src_list); + else + return list_entry(p, struct snd_seq_subscribers, dest_list); +} + /* * remove all subscribers on the list * this is called from port_delete, for each src and dest list. @@ -210,7 +220,7 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr, static void clear_subscriber_list(struct snd_seq_client *client, struct snd_seq_client_port *port, struct snd_seq_port_subs_info *grp, - int grptype) + int is_src) { struct list_head *p, *n; @@ -219,15 +229,13 @@ static void clear_subscriber_list(struct snd_seq_client *client, struct snd_seq_client *c; struct snd_seq_client_port *aport; - if (grptype == SRC_LIST) { - subs = list_entry(p, struct snd_seq_subscribers, src_list); + subs = get_subscriber(p, is_src); + if (is_src) aport = get_client_port(&subs->info.dest, &c); - } else { - subs = list_entry(p, struct snd_seq_subscribers, dest_list); + else aport = get_client_port(&subs->info.sender, &c); - } - list_del(p); - unsubscribe_port(client, port, grp, &subs->info, 0); + delete_and_unsubscribe_port(client, port, subs, is_src, false); + if (!aport) { /* looks like the connected port is being deleted. * we decrease the counter, and when both ports are deleted @@ -235,21 +243,14 @@ static void clear_subscriber_list(struct snd_seq_client *client, */ if (atomic_dec_and_test(&subs->ref_count)) kfree(subs); - } else { - /* ok we got the connected port */ - struct snd_seq_port_subs_info *agrp; - agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src; - down_write(&agrp->list_mutex); - if (grptype == SRC_LIST) - list_del(&subs->dest_list); - else - list_del(&subs->src_list); - up_write(&agrp->list_mutex); - unsubscribe_port(c, aport, agrp, &subs->info, 1); - kfree(subs); - snd_seq_port_unlock(aport); - snd_seq_client_unlock(c); + continue; } + + /* ok we got the connected port */ + delete_and_unsubscribe_port(c, aport, subs, !is_src, true); + kfree(subs); + snd_seq_port_unlock(aport); + snd_seq_client_unlock(c); } } @@ -262,8 +263,8 @@ static int port_delete(struct snd_seq_client *client, snd_use_lock_sync(&port->use_lock); /* clear subscribers info */ - clear_subscriber_list(client, port, &port->c_src, SRC_LIST); - clear_subscriber_list(client, port, &port->c_dest, DEST_LIST); + clear_subscriber_list(client, port, &port->c_src, true); + clear_subscriber_list(client, port, &port->c_dest, false); if (port->private_free) port->private_free(port->private_data); @@ -479,85 +480,123 @@ static int match_subs_info(struct snd_seq_port_subscribe *r, return 0; } - -/* connect two ports */ -int snd_seq_port_connect(struct snd_seq_client *connector, - struct snd_seq_client *src_client, - struct snd_seq_client_port *src_port, - struct snd_seq_client *dest_client, - struct snd_seq_client_port *dest_port, - struct snd_seq_port_subscribe *info) +static int check_and_subscribe_port(struct snd_seq_client *client, + struct snd_seq_client_port *port, + struct snd_seq_subscribers *subs, + bool is_src, bool exclusive, bool ack) { - struct snd_seq_port_subs_info *src = &src_port->c_src; - struct snd_seq_port_subs_info *dest = &dest_port->c_dest; - struct snd_seq_subscribers *subs, *s; - int err, src_called = 0; - unsigned long flags; - int exclusive; - - subs = kzalloc(sizeof(*subs), GFP_KERNEL); - if (! subs) - return -ENOMEM; - - subs->info = *info; - atomic_set(&subs->ref_count, 2); + struct snd_seq_port_subs_info *grp; + struct list_head *p; + struct snd_seq_subscribers *s; + int err; - down_write(&src->list_mutex); - down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING); - - exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0; + grp = is_src ? &port->c_src : &port->c_dest; err = -EBUSY; + down_write(&grp->list_mutex); if (exclusive) { - if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head)) + if (!list_empty(&grp->list_head)) goto __error; } else { - if (src->exclusive || dest->exclusive) + if (grp->exclusive) goto __error; /* check whether already exists */ - list_for_each_entry(s, &src->list_head, src_list) { - if (match_subs_info(info, &s->info)) - goto __error; - } - list_for_each_entry(s, &dest->list_head, dest_list) { - if (match_subs_info(info, &s->info)) + list_for_each(p, &grp->list_head) { + s = get_subscriber(p, is_src); + if (match_subs_info(&subs->info, &s->info)) goto __error; } } - if ((err = subscribe_port(src_client, src_port, src, info, - connector->number != src_client->number)) < 0) - goto __error; - src_called = 1; - - if ((err = subscribe_port(dest_client, dest_port, dest, info, - connector->number != dest_client->number)) < 0) + err = subscribe_port(client, port, grp, &subs->info, ack); + if (err < 0) { + grp->exclusive = 0; goto __error; + } /* add to list */ - write_lock_irqsave(&src->list_lock, flags); - // write_lock(&dest->list_lock); // no other lock yet - list_add_tail(&subs->src_list, &src->list_head); - list_add_tail(&subs->dest_list, &dest->list_head); - // write_unlock(&dest->list_lock); // no other lock yet - write_unlock_irqrestore(&src->list_lock, flags); + write_lock_irq(&grp->list_lock); + if (is_src) + list_add_tail(&subs->src_list, &grp->list_head); + else + list_add_tail(&subs->dest_list, &grp->list_head); + grp->exclusive = exclusive; + atomic_inc(&subs->ref_count); + write_unlock_irq(&grp->list_lock); + err = 0; - src->exclusive = dest->exclusive = exclusive; + __error: + up_write(&grp->list_mutex); + return err; +} + +static void delete_and_unsubscribe_port(struct snd_seq_client *client, + struct snd_seq_client_port *port, + struct snd_seq_subscribers *subs, + bool is_src, bool ack) +{ + struct snd_seq_port_subs_info *grp; + struct list_head *list; + bool empty; + + grp = is_src ? &port->c_src : &port->c_dest; + list = is_src ? &subs->src_list : &subs->dest_list; + down_write(&grp->list_mutex); + write_lock_irq(&grp->list_lock); + empty = list_empty(list); + if (!empty) + list_del_init(list); + grp->exclusive = 0; + write_unlock_irq(&grp->list_lock); + up_write(&grp->list_mutex); + + if (!empty) + unsubscribe_port(client, port, grp, &subs->info, ack); +} + +/* connect two ports */ +int snd_seq_port_connect(struct snd_seq_client *connector, + struct snd_seq_client *src_client, + struct snd_seq_client_port *src_port, + struct snd_seq_client *dest_client, + struct snd_seq_client_port *dest_port, + struct snd_seq_port_subscribe *info) +{ + struct snd_seq_subscribers *subs; + bool exclusive; + int err; + + subs = kzalloc(sizeof(*subs), GFP_KERNEL); + if (!subs) + return -ENOMEM; + + subs->info = *info; + atomic_set(&subs->ref_count, 0); + INIT_LIST_HEAD(&subs->src_list); + INIT_LIST_HEAD(&subs->dest_list); + + exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE); + + err = check_and_subscribe_port(src_client, src_port, subs, true, + exclusive, + connector->number != src_client->number); + if (err < 0) + goto error; + err = check_and_subscribe_port(dest_client, dest_port, subs, false, + exclusive, + connector->number != dest_client->number); + if (err < 0) + goto error_dest; - up_write(&dest->list_mutex); - up_write(&src->list_mutex); return 0; - __error: - if (src_called) - unsubscribe_port(src_client, src_port, src, info, - connector->number != src_client->number); + error_dest: + delete_and_unsubscribe_port(src_client, src_port, subs, true, + connector->number != src_client->number); + error: kfree(subs); - up_write(&dest->list_mutex); - up_write(&src->list_mutex); return err; } - /* remove the connection */ int snd_seq_port_disconnect(struct snd_seq_client *connector, struct snd_seq_client *src_client, @@ -567,37 +606,28 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, struct snd_seq_port_subscribe *info) { struct snd_seq_port_subs_info *src = &src_port->c_src; - struct snd_seq_port_subs_info *dest = &dest_port->c_dest; struct snd_seq_subscribers *subs; int err = -ENOENT; - unsigned long flags; down_write(&src->list_mutex); - down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING); - /* look for the connection */ list_for_each_entry(subs, &src->list_head, src_list) { if (match_subs_info(info, &subs->info)) { - write_lock_irqsave(&src->list_lock, flags); - // write_lock(&dest->list_lock); // no lock yet - list_del(&subs->src_list); - list_del(&subs->dest_list); - // write_unlock(&dest->list_lock); - write_unlock_irqrestore(&src->list_lock, flags); - src->exclusive = dest->exclusive = 0; - unsubscribe_port(src_client, src_port, src, info, - connector->number != src_client->number); - unsubscribe_port(dest_client, dest_port, dest, info, - connector->number != dest_client->number); - kfree(subs); + atomic_dec(&subs->ref_count); /* mark as not ready */ err = 0; break; } } - - up_write(&dest->list_mutex); up_write(&src->list_mutex); - return err; + if (err < 0) + return err; + + delete_and_unsubscribe_port(src_client, src_port, subs, true, + connector->number != src_client->number); + delete_and_unsubscribe_port(dest_client, dest_port, subs, false, + connector->number != dest_client->number); + kfree(subs); + return 0; } diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 82b220c..2931049 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -90,6 +90,9 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr) void snd_seq_timer_defaults(struct snd_seq_timer * tmr) { + unsigned long flags; + + spin_lock_irqsave(&tmr->lock, flags); /* setup defaults */ tmr->ppq = 96; /* 96 PPQ */ tmr->tempo = 500000; /* 120 BPM */ @@ -105,21 +108,25 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr) tmr->preferred_resolution = seq_default_timer_resolution; tmr->skew = tmr->skew_base = SKEW_BASE; + spin_unlock_irqrestore(&tmr->lock, flags); } -void snd_seq_timer_reset(struct snd_seq_timer * tmr) +static void seq_timer_reset(struct snd_seq_timer *tmr) { - unsigned long flags; - - spin_lock_irqsave(&tmr->lock, flags); - /* reset time & songposition */ tmr->cur_time.tv_sec = 0; tmr->cur_time.tv_nsec = 0; tmr->tick.cur_tick = 0; tmr->tick.fraction = 0; +} + +void snd_seq_timer_reset(struct snd_seq_timer *tmr) +{ + unsigned long flags; + spin_lock_irqsave(&tmr->lock, flags); + seq_timer_reset(tmr); spin_unlock_irqrestore(&tmr->lock, flags); } @@ -138,8 +145,11 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, tmr = q->timer; if (tmr == NULL) return; - if (!tmr->running) + spin_lock_irqsave(&tmr->lock, flags); + if (!tmr->running) { + spin_unlock_irqrestore(&tmr->lock, flags); return; + } resolution *= ticks; if (tmr->skew != tmr->skew_base) { @@ -148,8 +158,6 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, (((resolution & 0xffff) * tmr->skew) >> 16); } - spin_lock_irqsave(&tmr->lock, flags); - /* update timer */ snd_seq_inc_time_nsec(&tmr->cur_time, resolution); @@ -296,26 +304,30 @@ int snd_seq_timer_open(struct snd_seq_queue *q) t->callback = snd_seq_timer_interrupt; t->callback_data = q; t->flags |= SNDRV_TIMER_IFLG_AUTO; + spin_lock_irq(&tmr->lock); tmr->timeri = t; + spin_unlock_irq(&tmr->lock); return 0; } int snd_seq_timer_close(struct snd_seq_queue *q) { struct snd_seq_timer *tmr; + struct snd_timer_instance *t; tmr = q->timer; if (snd_BUG_ON(!tmr)) return -EINVAL; - if (tmr->timeri) { - snd_timer_stop(tmr->timeri); - snd_timer_close(tmr->timeri); - tmr->timeri = NULL; - } + spin_lock_irq(&tmr->lock); + t = tmr->timeri; + tmr->timeri = NULL; + spin_unlock_irq(&tmr->lock); + if (t) + snd_timer_close(t); return 0; } -int snd_seq_timer_stop(struct snd_seq_timer * tmr) +static int seq_timer_stop(struct snd_seq_timer *tmr) { if (! tmr->timeri) return -EINVAL; @@ -326,6 +338,17 @@ int snd_seq_timer_stop(struct snd_seq_timer * tmr) return 0; } +int snd_seq_timer_stop(struct snd_seq_timer *tmr) +{ + unsigned long flags; + int err; + + spin_lock_irqsave(&tmr->lock, flags); + err = seq_timer_stop(tmr); + spin_unlock_irqrestore(&tmr->lock, flags); + return err; +} + static int initialize_timer(struct snd_seq_timer *tmr) { struct snd_timer *t; @@ -358,13 +381,13 @@ static int initialize_timer(struct snd_seq_timer *tmr) return 0; } -int snd_seq_timer_start(struct snd_seq_timer * tmr) +static int seq_timer_start(struct snd_seq_timer *tmr) { if (! tmr->timeri) return -EINVAL; if (tmr->running) - snd_seq_timer_stop(tmr); - snd_seq_timer_reset(tmr); + seq_timer_stop(tmr); + seq_timer_reset(tmr); if (initialize_timer(tmr) < 0) return -EINVAL; snd_timer_start(tmr->timeri, tmr->ticks); @@ -373,14 +396,25 @@ int snd_seq_timer_start(struct snd_seq_timer * tmr) return 0; } -int snd_seq_timer_continue(struct snd_seq_timer * tmr) +int snd_seq_timer_start(struct snd_seq_timer *tmr) +{ + unsigned long flags; + int err; + + spin_lock_irqsave(&tmr->lock, flags); + err = seq_timer_start(tmr); + spin_unlock_irqrestore(&tmr->lock, flags); + return err; +} + +static int seq_timer_continue(struct snd_seq_timer *tmr) { if (! tmr->timeri) return -EINVAL; if (tmr->running) return -EBUSY; if (! tmr->initialized) { - snd_seq_timer_reset(tmr); + seq_timer_reset(tmr); if (initialize_timer(tmr) < 0) return -EINVAL; } @@ -390,11 +424,24 @@ int snd_seq_timer_continue(struct snd_seq_timer * tmr) return 0; } +int snd_seq_timer_continue(struct snd_seq_timer *tmr) +{ + unsigned long flags; + int err; + + spin_lock_irqsave(&tmr->lock, flags); + err = seq_timer_continue(tmr); + spin_unlock_irqrestore(&tmr->lock, flags); + return err; +} + /* return current 'real' time. use timeofday() to get better granularity. */ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) { snd_seq_real_time_t cur_time; + unsigned long flags; + spin_lock_irqsave(&tmr->lock, flags); cur_time = tmr->cur_time; if (tmr->running) { struct timeval tm; @@ -410,7 +457,7 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) } snd_seq_sanity_real_time(&cur_time); } - + spin_unlock_irqrestore(&tmr->lock, flags); return cur_time; } diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 3da2d48..c82ed3e 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -155,21 +155,26 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, struct snd_virmidi *vmidi = substream->runtime->private_data; int count, res; unsigned char buf[32], *pbuf; + unsigned long flags; if (up) { vmidi->trigger = 1; if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH && !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) { - snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail); - return; /* ignored */ + while (snd_rawmidi_transmit(substream, buf, + sizeof(buf)) > 0) { + /* ignored */ + } + return; } if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0) return; vmidi->event.type = SNDRV_SEQ_EVENT_NONE; } + spin_lock_irqsave(&substream->runtime->lock, flags); while (1) { - count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf)); + count = __snd_rawmidi_transmit_peek(substream, buf, sizeof(buf)); if (count <= 0) break; pbuf = buf; @@ -179,16 +184,18 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, snd_midi_event_reset_encode(vmidi->parser); continue; } - snd_rawmidi_transmit_ack(substream, res); + __snd_rawmidi_transmit_ack(substream, res); pbuf += res; count -= res; if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) { if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0) - return; + goto out; vmidi->event.type = SNDRV_SEQ_EVENT_NONE; } } } + out: + spin_unlock_irqrestore(&substream->runtime->lock, flags); } else { vmidi->trigger = 0; } @@ -254,9 +261,13 @@ static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream) */ static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream) { + struct snd_virmidi_dev *rdev = substream->rmidi->private_data; struct snd_virmidi *vmidi = substream->runtime->private_data; - snd_midi_event_free(vmidi->parser); + + write_lock_irq(&rdev->filelist_lock); list_del(&vmidi->list); + write_unlock_irq(&rdev->filelist_lock); + snd_midi_event_free(vmidi->parser); substream->runtime->private_data = NULL; kfree(vmidi); return 0; diff --git a/sound/core/timer.c b/sound/core/timer.c index af1f68f..dca817f 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -422,7 +422,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) spin_lock_irqsave(&timer->lock, flags); list_for_each_entry(ts, &ti->slave_active_head, active_list) if (ts->ccallback) - ts->ccallback(ti, event + 100, &tstamp, resolution); + ts->ccallback(ts, event + 100, &tstamp, resolution); spin_unlock_irqrestore(&timer->lock, flags); } @@ -451,6 +451,10 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri) unsigned long flags; spin_lock_irqsave(&slave_active_lock, flags); + if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { + spin_unlock_irqrestore(&slave_active_lock, flags); + return -EBUSY; + } timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; if (timeri->master && timeri->timer) { spin_lock(&timeri->timer->lock); @@ -475,7 +479,8 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) return -EINVAL; if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { result = snd_timer_start_slave(timeri); - snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); + if (result >= 0) + snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); return result; } timer = timeri->timer; @@ -484,11 +489,18 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) if (timer->card && timer->card->shutdown) return -ENODEV; spin_lock_irqsave(&timer->lock, flags); + if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | + SNDRV_TIMER_IFLG_START)) { + result = -EBUSY; + goto unlock; + } timeri->ticks = timeri->cticks = ticks; timeri->pticks = 0; result = snd_timer_start1(timer, timeri, ticks); + unlock: spin_unlock_irqrestore(&timer->lock, flags); - snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); + if (result >= 0) + snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); return result; } @@ -502,9 +514,17 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { spin_lock_irqsave(&slave_active_lock, flags); + if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { + spin_unlock_irqrestore(&slave_active_lock, flags); + return -EBUSY; + } + if (timeri->timer) + spin_lock(&timeri->timer->lock); timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); + if (timeri->timer) + spin_unlock(&timeri->timer->lock); spin_unlock_irqrestore(&slave_active_lock, flags); goto __end; } @@ -512,6 +532,11 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) if (!timer) return -EINVAL; spin_lock_irqsave(&timer->lock, flags); + if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | + SNDRV_TIMER_IFLG_START))) { + spin_unlock_irqrestore(&timer->lock, flags); + return -EBUSY; + } list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); if (timer->card && timer->card->shutdown) { @@ -581,10 +606,15 @@ int snd_timer_continue(struct snd_timer_instance *timeri) if (timer->card && timer->card->shutdown) return -ENODEV; spin_lock_irqsave(&timer->lock, flags); + if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { + result = -EBUSY; + goto unlock; + } if (!timeri->cticks) timeri->cticks = 1; timeri->pticks = 0; result = snd_timer_start1(timer, timeri, timer->sticks); + unlock: spin_unlock_irqrestore(&timer->lock, flags); snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE); return result; @@ -718,8 +748,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) ti->cticks = ti->ticks; } else { ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; - if (--timer->running) - list_del_init(&ti->active_list); + --timer->running; + list_del_init(&ti->active_list); } if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || (ti->flags & SNDRV_TIMER_IFLG_FAST)) @@ -1032,11 +1062,21 @@ static int snd_timer_s_stop(struct snd_timer * timer) return 0; } +static int snd_timer_s_close(struct snd_timer *timer) +{ + struct snd_timer_system_private *priv; + + priv = (struct snd_timer_system_private *)timer->private_data; + del_timer_sync(&priv->tlist); + return 0; +} + static struct snd_timer_hardware snd_timer_system = { .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, .resolution = 1000000000L / HZ, .ticks = 10000000L, + .close = snd_timer_s_close, .start = snd_timer_s_start, .stop = snd_timer_s_stop }; @@ -1893,6 +1933,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, { struct snd_timer_user *tu; long result = 0, unit; + int qhead; int err = 0; tu = file->private_data; @@ -1904,7 +1945,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { err = -EAGAIN; - break; + goto _error; } set_current_state(TASK_INTERRUPTIBLE); @@ -1919,42 +1960,37 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, if (tu->disconnected) { err = -ENODEV; - break; + goto _error; } if (signal_pending(current)) { err = -ERESTARTSYS; - break; + goto _error; } } + qhead = tu->qhead++; + tu->qhead %= tu->queue_size; spin_unlock_irq(&tu->qlock); - if (err < 0) - goto _error; if (tu->tread) { - if (copy_to_user(buffer, &tu->tqueue[tu->qhead++], - sizeof(struct snd_timer_tread))) { + if (copy_to_user(buffer, &tu->tqueue[qhead], + sizeof(struct snd_timer_tread))) err = -EFAULT; - goto _error; - } } else { - if (copy_to_user(buffer, &tu->queue[tu->qhead++], - sizeof(struct snd_timer_read))) { + if (copy_to_user(buffer, &tu->queue[qhead], + sizeof(struct snd_timer_read))) err = -EFAULT; - goto _error; - } } - tu->qhead %= tu->queue_size; - - result += unit; - buffer += unit; - spin_lock_irq(&tu->qlock); tu->qused--; + if (err < 0) + goto _error; + result += unit; + buffer += unit; } - spin_unlock_irq(&tu->qlock); _error: + spin_unlock_irq(&tu->qlock); return result > 0 ? result : err; } diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c index e05802a..2e90822 100644 --- a/sound/core/timer_compat.c +++ b/sound/core/timer_compat.c @@ -70,13 +70,14 @@ static int snd_timer_user_status_compat(struct file *file, struct snd_timer_status32 __user *_status) { struct snd_timer_user *tu; - struct snd_timer_status status; + struct snd_timer_status32 status; tu = file->private_data; if (snd_BUG_ON(!tu->timeri)) return -ENXIO; memset(&status, 0, sizeof(status)); - status.tstamp = tu->tstamp; + status.tstamp.tv_sec = tu->tstamp.tv_sec; + status.tstamp.tv_nsec = tu->tstamp.tv_nsec; status.resolution = snd_timer_resolution(tu->timeri); status.lost = tu->timeri->lost; status.overrun = tu->overrun; @@ -88,12 +89,21 @@ static int snd_timer_user_status_compat(struct file *file, return 0; } +#ifdef CONFIG_X86_X32 +/* X32 ABI has the same struct as x86-64 */ +#define snd_timer_user_status_x32(file, s) \ + snd_timer_user_status(file, s) +#endif /* CONFIG_X86_X32 */ + /* */ enum { SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct snd_timer_info32), SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct snd_timer_status32), +#ifdef CONFIG_X86_X32 + SNDRV_TIMER_IOCTL_STATUS_X32 = _IOW('T', 0x14, struct snd_timer_status), +#endif /* CONFIG_X86_X32 */ }; static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) @@ -122,6 +132,10 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, uns return snd_timer_user_info_compat(file, argp); case SNDRV_TIMER_IOCTL_STATUS32: return snd_timer_user_status_compat(file, argp); +#ifdef CONFIG_X86_X32 + case SNDRV_TIMER_IOCTL_STATUS_X32: + return snd_timer_user_status_x32(file, argp); +#endif /* CONFIG_X86_X32 */ } return -ENOIOCTLCMD; } diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 75b7485..c0f8f61 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -109,6 +109,9 @@ struct dummy_timer_ops { snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *); }; +#define get_dummy_ops(substream) \ + (*(const struct dummy_timer_ops **)(substream)->runtime->private_data) + struct dummy_model { const char *name; int (*playback_constraints)(struct snd_pcm_runtime *runtime); @@ -137,7 +140,6 @@ struct snd_dummy { int iobox; struct snd_kcontrol *cd_volume_ctl; struct snd_kcontrol *cd_switch_ctl; - const struct dummy_timer_ops *timer_ops; }; /* @@ -231,6 +233,8 @@ static struct dummy_model *dummy_models[] = { */ struct dummy_systimer_pcm { + /* ops must be the first item */ + const struct dummy_timer_ops *timer_ops; spinlock_t lock; struct timer_list timer; unsigned long base_time; @@ -366,6 +370,8 @@ static const struct dummy_timer_ops dummy_systimer_ops = { */ struct dummy_hrtimer_pcm { + /* ops must be the first item */ + const struct dummy_timer_ops *timer_ops; ktime_t base_time; ktime_t period_time; atomic_t running; @@ -492,31 +498,25 @@ static const struct dummy_timer_ops dummy_hrtimer_ops = { static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { - struct snd_dummy *dummy = snd_pcm_substream_chip(substream); - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: - return dummy->timer_ops->start(substream); + return get_dummy_ops(substream)->start(substream); case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: - return dummy->timer_ops->stop(substream); + return get_dummy_ops(substream)->stop(substream); } return -EINVAL; } static int dummy_pcm_prepare(struct snd_pcm_substream *substream) { - struct snd_dummy *dummy = snd_pcm_substream_chip(substream); - - return dummy->timer_ops->prepare(substream); + return get_dummy_ops(substream)->prepare(substream); } static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_dummy *dummy = snd_pcm_substream_chip(substream); - - return dummy->timer_ops->pointer(substream); + return get_dummy_ops(substream)->pointer(substream); } static struct snd_pcm_hardware dummy_pcm_hardware = { @@ -562,17 +562,19 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream) struct snd_dummy *dummy = snd_pcm_substream_chip(substream); struct dummy_model *model = dummy->model; struct snd_pcm_runtime *runtime = substream->runtime; + const struct dummy_timer_ops *ops; int err; - dummy->timer_ops = &dummy_systimer_ops; + ops = &dummy_systimer_ops; #ifdef CONFIG_HIGH_RES_TIMERS if (hrtimer) - dummy->timer_ops = &dummy_hrtimer_ops; + ops = &dummy_hrtimer_ops; #endif - err = dummy->timer_ops->create(substream); + err = ops->create(substream); if (err < 0) return err; + get_dummy_ops(substream) = ops; runtime->hw = dummy->pcm_hw; if (substream->pcm->device & 1) { @@ -594,7 +596,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream) err = model->capture_constraints(substream->runtime); } if (err < 0) { - dummy->timer_ops->free(substream); + get_dummy_ops(substream)->free(substream); return err; } return 0; @@ -602,8 +604,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream) static int dummy_pcm_close(struct snd_pcm_substream *substream) { - struct snd_dummy *dummy = snd_pcm_substream_chip(substream); - dummy->timer_ops->free(substream); + get_dummy_ops(substream)->free(substream); return 0; } diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 926e5dc..5022c9b 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -47,14 +47,16 @@ static const unsigned int bridgeco_freq_table[] = { [6] = 0x07, }; -static unsigned int -get_formation_index(unsigned int rate) +static int +get_formation_index(unsigned int rate, unsigned int *index) { unsigned int i; for (i = 0; i < ARRAY_SIZE(snd_bebob_rate_table); i++) { - if (snd_bebob_rate_table[i] == rate) - return i; + if (snd_bebob_rate_table[i] == rate) { + *index = i; + return 0; + } } return -EINVAL; } @@ -425,7 +427,9 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate) goto end; /* confirm params for both streams */ - index = get_formation_index(rate); + err = get_formation_index(rate, &index); + if (err < 0) + goto end; pcm_channels = bebob->tx_stream_formations[index].pcm; midi_channels = bebob->tx_stream_formations[index].midi; err = amdtp_am824_set_parameters(&bebob->tx_stream, rate, diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index b02a5e8c..0ac92ab 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -63,7 +63,7 @@ struct amdtp_dot { #define BYTE_PER_SAMPLE (4) #define MAGIC_DOT_BYTE (2) #define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE) -static const u8 dot_scrt(const u8 idx, const unsigned int off) +static u8 dot_scrt(const u8 idx, const unsigned int off) { /* * the length of the added pattern only depends on the lower nibble diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c index 904ce03..040a96d 100644 --- a/sound/firewire/tascam/tascam-transaction.c +++ b/sound/firewire/tascam/tascam-transaction.c @@ -230,6 +230,7 @@ int snd_tscm_transaction_register(struct snd_tscm *tscm) return err; error: fw_core_remove_address_handler(&tscm->async_handler); + tscm->async_handler.callback_data = NULL; return err; } @@ -276,6 +277,9 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm) __be32 reg; unsigned int i; + if (tscm->async_handler.callback_data == NULL) + return; + /* Turn off FireWire LED. */ reg = cpu_to_be32(0x0000008e); snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, @@ -297,6 +301,8 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm) ®, sizeof(reg), 0); fw_core_remove_address_handler(&tscm->async_handler); + tscm->async_handler.callback_data = NULL; + for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) snd_fw_async_midi_port_destroy(&tscm->out_ports[i]); } diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index ee0bc18..e281c33 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -21,7 +21,6 @@ static struct snd_tscm_spec model_specs[] = { .pcm_playback_analog_channels = 8, .midi_capture_ports = 4, .midi_playback_ports = 4, - .is_controller = true, }, { .name = "FW-1082", @@ -31,9 +30,16 @@ static struct snd_tscm_spec model_specs[] = { .pcm_playback_analog_channels = 2, .midi_capture_ports = 2, .midi_playback_ports = 2, - .is_controller = true, }, - /* FW-1804 may be supported. */ + { + .name = "FW-1804", + .has_adat = true, + .has_spdif = true, + .pcm_capture_analog_channels = 8, + .pcm_playback_analog_channels = 2, + .midi_capture_ports = 2, + .midi_playback_ports = 4, + }, }; static int identify_model(struct snd_tscm *tscm) diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 2d028d2..30ab77e 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -39,7 +39,6 @@ struct snd_tscm_spec { unsigned int pcm_playback_analog_channels; unsigned int midi_capture_ports; unsigned int midi_playback_ports; - bool is_controller; }; #define TSCM_MIDI_IN_PORT_MAX 4 @@ -72,9 +71,6 @@ struct snd_tscm { struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX]; u8 running_status[TSCM_MIDI_OUT_PORT_MAX]; bool on_sysex[TSCM_MIDI_OUT_PORT_MAX]; - - /* For control messages. */ - struct snd_firewire_tascam_status *status; }; #define TSCM_ADDR_BASE 0xffff00000000ull diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index b5a17cb..8c48623 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -426,18 +426,22 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip); * @bus: HD-audio core bus * @status: INTSTS register value * @ask: callback to be called for woken streams + * + * Returns the bits of handled streams, or zero if no stream is handled. */ -void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, +int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, void (*ack)(struct hdac_bus *, struct hdac_stream *)) { struct hdac_stream *azx_dev; u8 sd_status; + int handled = 0; list_for_each_entry(azx_dev, &bus->stream_list, list) { if (status & azx_dev->sd_int_sta_mask) { sd_status = snd_hdac_stream_readb(azx_dev, SD_STS); snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); + handled |= 1 << azx_dev->index; if (!azx_dev->substream || !azx_dev->running || !(sd_status & SD_INT_COMPLETE)) continue; @@ -445,6 +449,7 @@ void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, ack(bus, azx_dev); } } + return handled; } EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq); diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index 0216475..37adcc6 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -3,6 +3,7 @@ config SND_WSS_LIB tristate select SND_PCM + select SND_TIMER config SND_SB_COMMON tristate @@ -42,6 +43,7 @@ config SND_AD1816A select SND_OPL3_LIB select SND_MPU401_UART select SND_PCM + select SND_TIMER help Say Y here to include support for Analog Devices SoundPort AD1816A or compatible sound chips. @@ -209,6 +211,7 @@ config SND_GUSCLASSIC tristate "Gravis UltraSound Classic" select SND_RAWMIDI select SND_PCM + select SND_TIMER help Say Y here to include support for Gravis UltraSound Classic soundcards. @@ -221,6 +224,7 @@ config SND_GUSEXTREME select SND_OPL3_LIB select SND_MPU401_UART select SND_PCM + select SND_TIMER help Say Y here to include support for Gravis UltraSound Extreme soundcards. diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 656ce39..8f6594a 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -155,6 +155,7 @@ config SND_AZT3328 select SND_PCM select SND_RAWMIDI select SND_AC97_CODEC + select SND_TIMER depends on ZONE_DMA help Say Y here to include support for Aztech AZF3328 (PCI168) @@ -463,6 +464,7 @@ config SND_EMU10K1 select SND_HWDEP select SND_RAWMIDI select SND_AC97_CODEC + select SND_TIMER depends on ZONE_DMA help Say Y to include support for Sound Blaster PCI 512, Live!, @@ -889,6 +891,7 @@ config SND_YMFPCI select SND_OPL3_LIB select SND_MPU401_UART select SND_AC97_CODEC + select SND_TIMER help Say Y here to include support for Yamaha PCI audio chips - YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754. diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 28e2f8b..8914534 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1141,6 +1141,14 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) emu->emu1010.firmware_thread = kthread_create(emu1010_firmware_thread, emu, "emu1010_firmware"); + if (IS_ERR(emu->emu1010.firmware_thread)) { + err = PTR_ERR(emu->emu1010.firmware_thread); + emu->emu1010.firmware_thread = NULL; + dev_info(emu->card->dev, + "emu1010: Creating thread failed\n"); + return err; + } + wake_up_process(emu->emu1010.firmware_thread); } diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 37cf9ce..27de801 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -930,6 +930,8 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) struct azx *chip = dev_id; struct hdac_bus *bus = azx_bus(chip); u32 status; + bool active, handled = false; + int repeat = 0; /* count for avoiding endless loop */ #ifdef CONFIG_PM if (azx_has_pm_runtime(chip)) @@ -939,33 +941,36 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) spin_lock(&bus->reg_lock); - if (chip->disabled) { - spin_unlock(&bus->reg_lock); - return IRQ_NONE; - } - - status = azx_readl(chip, INTSTS); - if (status == 0 || status == 0xffffffff) { - spin_unlock(&bus->reg_lock); - return IRQ_NONE; - } + if (chip->disabled) + goto unlock; - snd_hdac_bus_handle_stream_irq(bus, status, stream_update); + do { + status = azx_readl(chip, INTSTS); + if (status == 0 || status == 0xffffffff) + break; - /* clear rirb int */ - status = azx_readb(chip, RIRBSTS); - if (status & RIRB_INT_MASK) { - if (status & RIRB_INT_RESPONSE) { - if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) - udelay(80); - snd_hdac_bus_update_rirb(bus); + handled = true; + active = false; + if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update)) + active = true; + + /* clear rirb int */ + status = azx_readb(chip, RIRBSTS); + if (status & RIRB_INT_MASK) { + active = true; + if (status & RIRB_INT_RESPONSE) { + if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) + udelay(80); + snd_hdac_bus_update_rirb(bus); + } + azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); } - azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); - } + } while (active && ++repeat < 10); + unlock: spin_unlock(&bus->reg_lock); - return IRQ_HANDLED; + return IRQ_RETVAL(handled); } EXPORT_SYMBOL_GPL(azx_interrupt); diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 30c8efe..7ca5b89 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -4028,9 +4028,9 @@ static void pin_power_callback(struct hda_codec *codec, struct hda_jack_callback *jack, bool on) { - if (jack && jack->tbl->nid) + if (jack && jack->nid) sync_power_state_change(codec, - set_pin_power_jack(codec, jack->tbl->nid, on)); + set_pin_power_jack(codec, jack->nid, on)); } /* callback only doing power up -- called at first */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 256e6cd..e5240cb 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -90,6 +90,8 @@ enum { #define NVIDIA_HDA_ENABLE_COHBIT 0x01 /* Defines for Intel SCH HDA snoop control */ +#define INTEL_HDA_CGCTL 0x48 +#define INTEL_HDA_CGCTL_MISCBDCGE (0x1 << 6) #define INTEL_SCH_HDA_DEVC 0x78 #define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11) @@ -361,7 +363,10 @@ enum { ((pci)->device == 0x0d0c) || \ ((pci)->device == 0x160c)) -#define IS_BROXTON(pci) ((pci)->device == 0x5a98) +#define IS_SKL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa170) +#define IS_SKL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d70) +#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98) +#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci)) static char *driver_short_names[] = { [AZX_DRIVER_ICH] = "HDA Intel", @@ -534,15 +539,26 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset) { struct hdac_bus *bus = azx_bus(chip); struct pci_dev *pci = chip->pci; + u32 val; if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) snd_hdac_set_codec_wakeup(bus, true); + if (IS_SKL_PLUS(pci)) { + pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val); + val = val & ~INTEL_HDA_CGCTL_MISCBDCGE; + pci_write_config_dword(pci, INTEL_HDA_CGCTL, val); + } azx_init_chip(chip, full_reset); + if (IS_SKL_PLUS(pci)) { + pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val); + val = val | INTEL_HDA_CGCTL_MISCBDCGE; + pci_write_config_dword(pci, INTEL_HDA_CGCTL, val); + } if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) snd_hdac_set_codec_wakeup(bus, false); /* reduce dma latency to avoid noise */ - if (IS_BROXTON(pci)) + if (IS_BXT(pci)) bxt_reduce_dma_latency(chip); } @@ -964,11 +980,6 @@ static int azx_resume(struct device *dev) /* put codec down to D3 at hibernation for Intel SKL+; * otherwise BIOS may still access the codec and screw up the driver */ -#define IS_SKL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa170) -#define IS_SKL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d70) -#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98) -#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci)) - static int azx_freeze_noirq(struct device *dev) { struct pci_dev *pci = to_pci_dev(dev); @@ -2155,10 +2166,10 @@ static void azx_remove(struct pci_dev *pci) struct hda_intel *hda; if (card) { - /* flush the pending probing work */ + /* cancel the pending probing work */ chip = card->private_data; hda = container_of(chip, struct hda_intel, chip); - flush_work(&hda->probe_work); + cancel_work_sync(&hda->probe_work); snd_card_free(card); } diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index c945e25..a33234e 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -259,7 +259,7 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, if (!callback) return ERR_PTR(-ENOMEM); callback->func = func; - callback->tbl = jack; + callback->nid = jack->nid; callback->next = jack->callback; jack->callback = callback; } diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 858708a..e9814c0 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -21,7 +21,7 @@ struct hda_jack_callback; typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *); struct hda_jack_callback { - struct hda_jack_tbl *tbl; + hda_nid_t nid; hda_jack_callback_fn func; unsigned int private_data; /* arbitrary data */ struct hda_jack_callback *next; diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4ef2259..9ceb2bc 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4427,13 +4427,16 @@ static void ca0132_process_dsp_response(struct hda_codec *codec, static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) { struct ca0132_spec *spec = codec->spec; + struct hda_jack_tbl *tbl; /* Delay enabling the HP amp, to let the mic-detection * state machine run. */ cancel_delayed_work_sync(&spec->unsol_hp_work); schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500)); - cb->tbl->block_report = 1; + tbl = snd_hda_jack_tbl_get(codec, cb->nid); + if (tbl) + tbl->block_report = 1; } static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index a12ae8a..c1c855a 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -614,6 +614,7 @@ enum { CS4208_MAC_AUTO, CS4208_MBA6, CS4208_MBP11, + CS4208_MACMINI, CS4208_GPIO0, }; @@ -621,6 +622,7 @@ static const struct hda_model_fixup cs4208_models[] = { { .id = CS4208_GPIO0, .name = "gpio0" }, { .id = CS4208_MBA6, .name = "mba6" }, { .id = CS4208_MBP11, .name = "mbp11" }, + { .id = CS4208_MACMINI, .name = "macmini" }, {} }; @@ -632,6 +634,7 @@ static const struct snd_pci_quirk cs4208_fixup_tbl[] = { /* codec SSID matching */ static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11), + SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI), SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6), SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11), @@ -666,6 +669,24 @@ static void cs4208_fixup_mac(struct hda_codec *codec, snd_hda_apply_fixup(codec, action); } +/* MacMini 7,1 has the inverted jack detection */ +static void cs4208_fixup_macmini(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const struct hda_pintbl pincfgs[] = { + { 0x18, 0x00ab9150 }, /* mic (audio-in) jack: disable detect */ + { 0x21, 0x004be140 }, /* SPDIF: disable detect */ + { } + }; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + /* HP pin (0x10) has an inverted detection */ + codec->inv_jack_detect = 1; + /* disable the bogus Mic and SPDIF jack detections */ + snd_hda_apply_pincfgs(codec, pincfgs); + } +} + static int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -709,6 +730,12 @@ static const struct hda_fixup cs4208_fixups[] = { .chained = true, .chain_id = CS4208_GPIO0, }, + [CS4208_MACMINI] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs4208_fixup_macmini, + .chained = true, + .chain_id = CS4208_GPIO0, + }, [CS4208_GPIO0] = { .type = HDA_FIXUP_FUNC, .v.func = cs4208_fixup_gpio0, diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 426a29a..bcbc4ee 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -448,7 +448,8 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, eld = &per_pin->sink_eld; mutex_lock(&per_pin->lock); - if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { + if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || + eld->eld_size > ELD_MAX_SIZE) { mutex_unlock(&per_pin->lock); snd_BUG(); return -EINVAL; @@ -1193,7 +1194,7 @@ static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid) static void jack_callback(struct hda_codec *codec, struct hda_jack_callback *jack) { - check_presence_and_report(codec, jack->tbl->nid); + check_presence_and_report(codec, jack->nid); } static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) @@ -2476,13 +2477,6 @@ static int patch_generic_hdmi(struct hda_codec *codec) is_broxton(codec)) codec->core.link_power_control = 1; - if (codec_has_acomp(codec)) { - codec->depop_delay = 0; - spec->i915_audio_ops.audio_ptr = codec; - spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify; - snd_hdac_i915_register_notifier(&spec->i915_audio_ops); - } - if (hdmi_parse_codec(codec) < 0) { if (spec->i915_bound) snd_hdac_i915_exit(&codec->bus->core); @@ -2504,6 +2498,18 @@ static int patch_generic_hdmi(struct hda_codec *codec) init_channel_allocations(); + if (codec_has_acomp(codec)) { + codec->depop_delay = 0; + spec->i915_audio_ops.audio_ptr = codec; + /* intel_audio_codec_enable() or intel_audio_codec_disable() + * will call pin_eld_notify with using audio_ptr pointer + * We need make sure audio_ptr is really setup + */ + wmb(); + spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify; + snd_hdac_i915_register_notifier(&spec->i915_audio_ops); + } + return 0; } @@ -3653,6 +3659,7 @@ HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3375324..93d2156 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -282,7 +282,7 @@ static void alc_update_knob_master(struct hda_codec *codec, uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); if (!uctl) return; - val = snd_hda_codec_read(codec, jack->tbl->nid, 0, + val = snd_hda_codec_read(codec, jack->nid, 0, AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); val &= HDA_AMP_VOLMASK; uctl->value.integer.value[0] = val; @@ -327,6 +327,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0292: alc_update_coef_idx(codec, 0x4, 1<<15, 0); break; + case 0x10ec0225: case 0x10ec0233: case 0x10ec0255: case 0x10ec0256: @@ -900,6 +901,7 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = { { 0x10ec0899, 0x1028, 0, "ALC3861" }, { 0x10ec0298, 0x1028, 0, "ALC3266" }, { 0x10ec0256, 0x1028, 0, "ALC3246" }, + { 0x10ec0225, 0x1028, 0, "ALC3253" }, { 0x10ec0670, 0x1025, 0, "ALC669X" }, { 0x10ec0676, 0x1025, 0, "ALC679X" }, { 0x10ec0282, 0x1043, 0, "ALC3229" }, @@ -1785,7 +1787,6 @@ enum { ALC882_FIXUP_NO_PRIMARY_HP, ALC887_FIXUP_ASUS_BASS, ALC887_FIXUP_BASS_CHMAP, - ALC882_FIXUP_DISABLE_AAMIX, }; static void alc889_fixup_coef(struct hda_codec *codec, @@ -1947,8 +1948,6 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec, static void alc_fixup_bass_chmap(struct hda_codec *codec, const struct hda_fixup *fix, int action); -static void alc_fixup_disable_aamix(struct hda_codec *codec, - const struct hda_fixup *fix, int action); static const struct hda_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { @@ -2186,10 +2185,6 @@ static const struct hda_fixup alc882_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_bass_chmap, }, - [ALC882_FIXUP_DISABLE_AAMIX] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc_fixup_disable_aamix, - }, }; static const struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -2228,6 +2223,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT), SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP), SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP), + SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP), /* All Apple entries are in codec SSIDs */ SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF), @@ -2257,7 +2253,6 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), - SND_PCI_QUIRK(0x1458, 0xa182, "Gigabyte Z170X-UD3", ALC882_FIXUP_DISABLE_AAMIX), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), @@ -2651,6 +2646,7 @@ enum { ALC269_TYPE_ALC298, ALC269_TYPE_ALC255, ALC269_TYPE_ALC256, + ALC269_TYPE_ALC225, }; /* @@ -2680,6 +2676,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC298: case ALC269_TYPE_ALC255: case ALC269_TYPE_ALC256: + case ALC269_TYPE_ALC225: ssids = alc269_ssids; break; default: @@ -3658,6 +3655,16 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) WRITE_COEF(0xb7, 0x802b), {} }; + static struct coef_fw coef0225[] = { + UPDATE_COEF(0x4a, 1<<8, 0), + UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), + UPDATE_COEF(0x63, 3<<14, 3<<14), + UPDATE_COEF(0x4a, 3<<4, 2<<4), + UPDATE_COEF(0x4a, 3<<10, 3<<10), + UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10), + UPDATE_COEF(0x4a, 3<<10, 0), + {} + }; switch (codec->core.vendor_id) { case 0x10ec0255: @@ -3682,6 +3689,9 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) case 0x10ec0668: alc_process_coef_fw(codec, coef0668); break; + case 0x10ec0225: + alc_process_coef_fw(codec, coef0225); + break; } codec_dbg(codec, "Headset jack set to unplugged mode.\n"); } @@ -3727,6 +3737,13 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, UPDATE_COEF(0xc3, 0, 1<<12), {} }; + static struct coef_fw coef0225[] = { + UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), + UPDATE_COEF(0x4a, 3<<4, 2<<4), + UPDATE_COEF(0x63, 3<<14, 0), + {} + }; + switch (codec->core.vendor_id) { case 0x10ec0255: @@ -3772,12 +3789,22 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, alc_process_coef_fw(codec, coef0688); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); break; + case 0x10ec0225: + alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0225); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; } codec_dbg(codec, "Headset jack set to mic-in mode.\n"); } static void alc_headset_mode_default(struct hda_codec *codec) { + static struct coef_fw coef0225[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10), + {} + }; static struct coef_fw coef0255[] = { WRITE_COEF(0x45, 0xc089), WRITE_COEF(0x45, 0xc489), @@ -3819,6 +3846,9 @@ static void alc_headset_mode_default(struct hda_codec *codec) }; switch (codec->core.vendor_id) { + case 0x10ec0225: + alc_process_coef_fw(codec, coef0225); + break; case 0x10ec0255: case 0x10ec0256: alc_process_coef_fw(codec, coef0255); @@ -3884,6 +3914,13 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) WRITE_COEF(0xc3, 0x0000), {} }; + static struct coef_fw coef0225[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10), + UPDATE_COEF(0x49, 1<<8, 1<<8), + UPDATE_COEF(0x4a, 7<<6, 7<<6), + UPDATE_COEF(0x4a, 3<<4, 3<<4), + {} + }; switch (codec->core.vendor_id) { case 0x10ec0255: @@ -3912,6 +3949,9 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) case 0x10ec0668: alc_process_coef_fw(codec, coef0688); break; + case 0x10ec0225: + alc_process_coef_fw(codec, coef0225); + break; } codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n"); } @@ -3955,6 +3995,13 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) WRITE_COEF(0xc3, 0x0000), {} }; + static struct coef_fw coef0225[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10), + UPDATE_COEF(0x49, 1<<8, 1<<8), + UPDATE_COEF(0x4a, 7<<6, 7<<6), + UPDATE_COEF(0x4a, 3<<4, 3<<4), + {} + }; switch (codec->core.vendor_id) { case 0x10ec0255: @@ -3983,6 +4030,9 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) case 0x10ec0668: alc_process_coef_fw(codec, coef0688); break; + case 0x10ec0225: + alc_process_coef_fw(codec, coef0225); + break; } codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n"); } @@ -4014,6 +4064,11 @@ static void alc_determine_headset_type(struct hda_codec *codec) WRITE_COEF(0xc3, 0x0c00), {} }; + static struct coef_fw coef0225[] = { + UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10), + UPDATE_COEF(0x49, 1<<8, 1<<8), + {} + }; switch (codec->core.vendor_id) { case 0x10ec0255: @@ -4058,6 +4113,12 @@ static void alc_determine_headset_type(struct hda_codec *codec) val = alc_read_coef_idx(codec, 0xbe); is_ctia = (val & 0x1c02) == 0x1c02; break; + case 0x10ec0225: + alc_process_coef_fw(codec, coef0225); + msleep(800); + val = alc_read_coef_idx(codec, 0x46); + is_ctia = (val & 0x00f0) == 0x00f0; + break; } codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n", @@ -4695,6 +4756,9 @@ enum { ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE, ALC293_FIXUP_LENOVO_SPK_NOISE, ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, + ALC255_FIXUP_DELL_SPK_NOISE, + ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC280_FIXUP_HP_HEADSET_MIC, }; static const struct hda_fixup alc269_fixups[] = { @@ -5314,6 +5378,29 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_fixup_lenovo_line2_mic_hotkey, }, + [ALC255_FIXUP_DELL_SPK_NOISE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Disable pass-through path for FRONT 14h */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 }, + {} + }, + .chained = true, + .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC280_FIXUP_HP_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_disable_aamix, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -5325,6 +5412,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK), SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), + SND_PCI_QUIRK(0x1025, 0x0762, "Acer Aspire E1-472", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK), @@ -5356,6 +5444,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE), + SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), @@ -5416,6 +5505,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), 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(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -5560,6 +5650,9 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC292_FIXUP_TPT440, .name = "tpt440"}, {} }; +#define ALC225_STANDARD_PINS \ + {0x12, 0xb7a60130}, \ + {0x21, 0x04211020} #define ALC256_STANDARD_PINS \ {0x12, 0x90a60140}, \ @@ -5581,6 +5674,12 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {0x21, 0x03211020} static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { + SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC225_STANDARD_PINS, + {0x14, 0x901701a0}), + SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC225_STANDARD_PINS, + {0x14, 0x901701b0}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, {0x14, 0x90170110}, {0x21, 0x02211020}), @@ -5906,6 +6005,9 @@ static int patch_alc269(struct hda_codec *codec) spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/ break; + case 0x10ec0225: + spec->codec_variant = ALC269_TYPE_ALC225; + break; } if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { @@ -6796,6 +6898,7 @@ static int patch_alc680(struct hda_codec *codec) */ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269), HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269), HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269), HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269), diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 2c7c5eb..37b70f8 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -493,9 +493,9 @@ static void jack_update_power(struct hda_codec *codec, if (!spec->num_pwrs) return; - if (jack && jack->tbl->nid) { - stac_toggle_power_map(codec, jack->tbl->nid, - snd_hda_jack_detect(codec, jack->tbl->nid), + if (jack && jack->nid) { + stac_toggle_power_map(codec, jack->nid, + snd_hda_jack_detect(codec, jack->nid), true); return; } diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 2875b4f..7c8941b 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -2879,7 +2879,7 @@ static int snd_hdsp_get_dds_offset(struct snd_kcontrol *kcontrol, struct snd_ctl { struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = hdsp_dds_offset(hdsp); + ucontrol->value.integer.value[0] = hdsp_dds_offset(hdsp); return 0; } @@ -2891,7 +2891,7 @@ static int snd_hdsp_put_dds_offset(struct snd_kcontrol *kcontrol, struct snd_ctl if (!snd_hdsp_use_is_exclusive(hdsp)) return -EBUSY; - val = ucontrol->value.enumerated.item[0]; + val = ucontrol->value.integer.value[0]; spin_lock_irq(&hdsp->lock); if (val != hdsp_dds_offset(hdsp)) change = (hdsp_set_dds_offset(hdsp, val) == 0) ? 1 : 0; diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 8bc8016..a4a999a 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1601,6 +1601,9 @@ static void hdspm_set_dds_value(struct hdspm *hdspm, int rate) { u64 n; + if (snd_BUG_ON(rate <= 0)) + return; + if (rate >= 112000) rate /= 4; else if (rate >= 56000) @@ -2215,6 +2218,8 @@ static int hdspm_get_system_sample_rate(struct hdspm *hdspm) } else { /* slave mode, return external sample rate */ rate = hdspm_external_sample_rate(hdspm); + if (!rate) + rate = hdspm->system_sample_rate; } } @@ -2260,8 +2265,11 @@ static int snd_hdspm_put_system_sample_rate(struct snd_kcontrol *kcontrol, ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int rate = ucontrol->value.integer.value[0]; - hdspm_set_dds_value(hdspm, ucontrol->value.enumerated.item[0]); + if (rate < 27000 || rate > 207000) + return -EINVAL; + hdspm_set_dds_value(hdspm, ucontrol->value.integer.value[0]); return 0; } @@ -4449,7 +4457,7 @@ static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol, { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = hdspm->tco->term; + ucontrol->value.integer.value[0] = hdspm->tco->term; return 0; } @@ -4460,8 +4468,8 @@ static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol, { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - if (hdspm->tco->term != ucontrol->value.enumerated.item[0]) { - hdspm->tco->term = ucontrol->value.enumerated.item[0]; + if (hdspm->tco->term != ucontrol->value.integer.value[0]) { + hdspm->tco->term = ucontrol->value.integer.value[0]; hdspm_tco_write(hdspm); diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 3191e0a..d1fb035 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -635,6 +635,7 @@ static int acp_dma_open(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { dev_err(prtd->platform->dev, "set integer constraint failed\n"); + kfree(adata); return ret; } diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index ba8def5..2768970 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -285,7 +285,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; + struct platform_device *pdev = to_platform_device(dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; int ret; @@ -346,7 +347,8 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; + struct platform_device *pdev = to_platform_device(dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; @@ -392,7 +394,8 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; + struct platform_device *pdev = to_platform_device(cpu_dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; ssc_p->daifmt = fmt; return 0; @@ -404,7 +407,8 @@ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai, static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div) { - struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; + struct platform_device *pdev = to_platform_device(cpu_dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; switch (div_id) { case ATMEL_SSC_CMR_DIV: @@ -445,7 +449,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - int id = dai->id; + struct platform_device *pdev = to_platform_device(dai->dev); + int id = pdev->id; struct atmel_ssc_info *ssc_p = &ssc_info[id]; struct ssc_device *ssc = ssc_p->ssc; struct atmel_pcm_dma_params *dma_params; @@ -772,7 +777,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, static int atmel_ssc_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; + struct platform_device *pdev = to_platform_device(dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir; @@ -795,7 +801,8 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream, static int atmel_ssc_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; + struct platform_device *pdev = to_platform_device(dai->dev); + struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir; @@ -824,11 +831,12 @@ static int atmel_ssc_trigger(struct snd_pcm_substream *substream, static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai) { struct atmel_ssc_info *ssc_p; + struct platform_device *pdev = to_platform_device(cpu_dai->dev); if (!cpu_dai->active) return 0; - ssc_p = &ssc_info[cpu_dai->id]; + ssc_p = &ssc_info[pdev->id]; /* Save the status register before disabling transmit and receive */ ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR); @@ -852,12 +860,13 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai) static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) { struct atmel_ssc_info *ssc_p; + struct platform_device *pdev = to_platform_device(cpu_dai->dev); u32 cr; if (!cpu_dai->active) return 0; - ssc_p = &ssc_info[cpu_dai->id]; + ssc_p = &ssc_info[pdev->id]; /* restore SSC register settings */ ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr); diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 3303d5f..1c1f221 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -37,6 +37,7 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/of_address.h> #include <linux/slab.h> #include <sound/core.h> @@ -46,55 +47,6 @@ #include <sound/pcm_params.h> #include <sound/soc.h> -/* Clock registers */ -#define BCM2835_CLK_PCMCTL_REG 0x00 -#define BCM2835_CLK_PCMDIV_REG 0x04 - -/* Clock register settings */ -#define BCM2835_CLK_PASSWD (0x5a000000) -#define BCM2835_CLK_PASSWD_MASK (0xff000000) -#define BCM2835_CLK_MASH(v) ((v) << 9) -#define BCM2835_CLK_FLIP BIT(8) -#define BCM2835_CLK_BUSY BIT(7) -#define BCM2835_CLK_KILL BIT(5) -#define BCM2835_CLK_ENAB BIT(4) -#define BCM2835_CLK_SRC(v) (v) - -#define BCM2835_CLK_SHIFT (12) -#define BCM2835_CLK_DIVI(v) ((v) << BCM2835_CLK_SHIFT) -#define BCM2835_CLK_DIVF(v) (v) -#define BCM2835_CLK_DIVF_MASK (0xFFF) - -enum { - BCM2835_CLK_MASH_0 = 0, - BCM2835_CLK_MASH_1, - BCM2835_CLK_MASH_2, - BCM2835_CLK_MASH_3, -}; - -enum { - BCM2835_CLK_SRC_GND = 0, - BCM2835_CLK_SRC_OSC, - BCM2835_CLK_SRC_DBG0, - BCM2835_CLK_SRC_DBG1, - BCM2835_CLK_SRC_PLLA, - BCM2835_CLK_SRC_PLLC, - BCM2835_CLK_SRC_PLLD, - BCM2835_CLK_SRC_HDMI, -}; - -/* Most clocks are not useable (freq = 0) */ -static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = { - [BCM2835_CLK_SRC_GND] = 0, - [BCM2835_CLK_SRC_OSC] = 19200000, - [BCM2835_CLK_SRC_DBG0] = 0, - [BCM2835_CLK_SRC_DBG1] = 0, - [BCM2835_CLK_SRC_PLLA] = 0, - [BCM2835_CLK_SRC_PLLC] = 0, - [BCM2835_CLK_SRC_PLLD] = 500000000, - [BCM2835_CLK_SRC_HDMI] = 0, -}; - /* I2S registers */ #define BCM2835_I2S_CS_A_REG 0x00 #define BCM2835_I2S_FIFO_A_REG 0x04 @@ -158,10 +110,6 @@ static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = { #define BCM2835_I2S_INT_RXR BIT(1) #define BCM2835_I2S_INT_TXW BIT(0) -/* I2S DMA interface */ -/* FIXME: Needs IOMMU support */ -#define BCM2835_VCMMU_SHIFT (0x7E000000 - 0x20000000) - /* General device struct */ struct bcm2835_i2s_dev { struct device *dev; @@ -169,21 +117,23 @@ struct bcm2835_i2s_dev { unsigned int fmt; unsigned int bclk_ratio; - struct regmap *i2s_regmap; - struct regmap *clk_regmap; + struct regmap *i2s_regmap; + struct clk *clk; + bool clk_prepared; }; static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev) { - /* Start the clock if in master mode */ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; + if (dev->clk_prepared) + return; + switch (master) { case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFM: - regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, - BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB, - BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB); + clk_prepare_enable(dev->clk); + dev->clk_prepared = true; break; default: break; @@ -192,28 +142,9 @@ static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev) static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev) { - uint32_t clkreg; - int timeout = 1000; - - /* Stop clock */ - regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, - BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB, - BCM2835_CLK_PASSWD); - - /* Wait for the BUSY flag going down */ - while (--timeout) { - regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg); - if (!(clkreg & BCM2835_CLK_BUSY)) - break; - } - - if (!timeout) { - /* KILL the clock */ - dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n"); - regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, - BCM2835_CLK_KILL | BCM2835_CLK_PASSWD_MASK, - BCM2835_CLK_KILL | BCM2835_CLK_PASSWD); - } + if (dev->clk_prepared) + clk_disable_unprepare(dev->clk); + dev->clk_prepared = false; } static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, @@ -223,8 +154,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, uint32_t syncval; uint32_t csreg; uint32_t i2s_active_state; - uint32_t clkreg; - uint32_t clk_active_state; + bool clk_was_prepared; uint32_t off; uint32_t clr; @@ -238,15 +168,10 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg); i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON); - regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg); - clk_active_state = clkreg & BCM2835_CLK_ENAB; - /* Start clock if not running */ - if (!clk_active_state) { - regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, - BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB, - BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB); - } + clk_was_prepared = dev->clk_prepared; + if (!clk_was_prepared) + bcm2835_i2s_start_clock(dev); /* Stop I2S module */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0); @@ -280,7 +205,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, dev_err(dev->dev, "I2S SYNC error!\n"); /* Stop clock if it was not running before */ - if (!clk_active_state) + if (!clk_was_prepared) bcm2835_i2s_stop_clock(dev); /* Restore I2S state */ @@ -309,19 +234,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - unsigned int sampling_rate = params_rate(params); unsigned int data_length, data_delay, bclk_ratio; unsigned int ch1pos, ch2pos, mode, format; - unsigned int mash = BCM2835_CLK_MASH_1; - unsigned int divi, divf, target_frequency; - int clk_src = -1; - unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; - bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS - || master == SND_SOC_DAIFMT_CBS_CFM); - - bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS - || master == SND_SOC_DAIFMT_CBM_CFS); uint32_t csreg; /* @@ -343,11 +258,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: data_length = 16; - bclk_ratio = 40; break; case SNDRV_PCM_FORMAT_S32_LE: data_length = 32; - bclk_ratio = 80; break; default: return -EINVAL; @@ -356,69 +269,12 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, /* If bclk_ratio already set, use that one. */ if (dev->bclk_ratio) bclk_ratio = dev->bclk_ratio; + else + /* otherwise calculate a fitting block ratio */ + bclk_ratio = 2 * data_length; - /* - * Clock Settings - * - * The target frequency of the bit clock is - * sampling rate * frame length - * - * Integer mode: - * Sampling rates that are multiples of 8000 kHz - * can be driven by the oscillator of 19.2 MHz - * with an integer divider as long as the frame length - * is an integer divider of 19200000/8000=2400 as set up above. - * This is no longer possible if the sampling rate - * is too high (e.g. 192 kHz), because the oscillator is too slow. - * - * MASH mode: - * For all other sampling rates, it is not possible to - * have an integer divider. Approximate the clock - * with the MASH module that induces a slight frequency - * variance. To minimize that it is best to have the fastest - * clock here. That is PLLD with 500 MHz. - */ - target_frequency = sampling_rate * bclk_ratio; - clk_src = BCM2835_CLK_SRC_OSC; - mash = BCM2835_CLK_MASH_0; - - if (bcm2835_clk_freq[clk_src] % target_frequency == 0 - && bit_master && frame_master) { - divi = bcm2835_clk_freq[clk_src] / target_frequency; - divf = 0; - } else { - uint64_t dividend; - - if (!dev->bclk_ratio) { - /* - * Overwrite bclk_ratio, because the - * above trick is not needed or can - * not be used. - */ - bclk_ratio = 2 * data_length; - } - - target_frequency = sampling_rate * bclk_ratio; - - clk_src = BCM2835_CLK_SRC_PLLD; - mash = BCM2835_CLK_MASH_1; - - dividend = bcm2835_clk_freq[clk_src]; - dividend <<= BCM2835_CLK_SHIFT; - do_div(dividend, target_frequency); - divi = dividend >> BCM2835_CLK_SHIFT; - divf = dividend & BCM2835_CLK_DIVF_MASK; - } - - /* Set clock divider */ - regmap_write(dev->clk_regmap, BCM2835_CLK_PCMDIV_REG, BCM2835_CLK_PASSWD - | BCM2835_CLK_DIVI(divi) - | BCM2835_CLK_DIVF(divf)); - - /* Setup clock, but don't start it yet */ - regmap_write(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, BCM2835_CLK_PASSWD - | BCM2835_CLK_MASH(mash) - | BCM2835_CLK_SRC(clk_src)); + /* set target clock rate*/ + clk_set_rate(dev->clk, sampling_rate * bclk_ratio); /* Setup the frame format */ format = BCM2835_I2S_CHEN; @@ -692,7 +548,7 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .trigger = bcm2835_i2s_trigger, .hw_params = bcm2835_i2s_hw_params, .set_fmt = bcm2835_i2s_set_dai_fmt, - .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio + .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, }; static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) @@ -750,34 +606,14 @@ static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg) }; } -static bool bcm2835_clk_volatile_reg(struct device *dev, unsigned int reg) -{ - switch (reg) { - case BCM2835_CLK_PCMCTL_REG: - return true; - default: - return false; - }; -} - -static const struct regmap_config bcm2835_regmap_config[] = { - { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = BCM2835_I2S_GRAY_REG, - .precious_reg = bcm2835_i2s_precious_reg, - .volatile_reg = bcm2835_i2s_volatile_reg, - .cache_type = REGCACHE_RBTREE, - }, - { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .max_register = BCM2835_CLK_PCMDIV_REG, - .volatile_reg = bcm2835_clk_volatile_reg, - .cache_type = REGCACHE_RBTREE, - }, +static const struct regmap_config bcm2835_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = BCM2835_I2S_GRAY_REG, + .precious_reg = bcm2835_i2s_precious_reg, + .volatile_reg = bcm2835_i2s_volatile_reg, + .cache_type = REGCACHE_RBTREE, }; static const struct snd_soc_component_driver bcm2835_i2s_component = { @@ -787,42 +623,50 @@ static const struct snd_soc_component_driver bcm2835_i2s_component = { static int bcm2835_i2s_probe(struct platform_device *pdev) { struct bcm2835_i2s_dev *dev; - int i; int ret; - struct regmap *regmap[2]; - struct resource *mem[2]; - - /* Request both ioareas */ - for (i = 0; i <= 1; i++) { - void __iomem *base; - - mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i); - base = devm_ioremap_resource(&pdev->dev, mem[i]); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap[i] = devm_regmap_init_mmio(&pdev->dev, base, - &bcm2835_regmap_config[i]); - if (IS_ERR(regmap[i])) - return PTR_ERR(regmap[i]); - } + struct resource *mem; + void __iomem *base; + const __be32 *addr; + dma_addr_t dma_base; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; - dev->i2s_regmap = regmap[0]; - dev->clk_regmap = regmap[1]; + /* get the clock */ + dev->clk_prepared = false; + dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) { + dev_err(&pdev->dev, "could not get clk: %ld\n", + PTR_ERR(dev->clk)); + return PTR_ERR(dev->clk); + } + + /* Request ioarea */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) + return PTR_ERR(base); + + dev->i2s_regmap = devm_regmap_init_mmio(&pdev->dev, base, + &bcm2835_regmap_config); + if (IS_ERR(dev->i2s_regmap)) + return PTR_ERR(dev->i2s_regmap); + + /* Set the DMA address - we have to parse DT ourselves */ + addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL); + if (!addr) { + dev_err(&pdev->dev, "could not get DMA-register address\n"); + return -EINVAL; + } + dma_base = be32_to_cpup(addr); - /* Set the DMA address */ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = - (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG - + BCM2835_VCMMU_SHIFT; + dma_base + BCM2835_I2S_FIFO_A_REG; dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = - (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG - + BCM2835_VCMMU_SHIFT; + dma_base + BCM2835_I2S_FIFO_A_REG; /* Set the bus width */ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width = diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 1de2598..649e92a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -79,7 +79,9 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX98090 if I2C select SND_SOC_MAX98095 if I2C select SND_SOC_MAX98357A if GPIOLIB + select SND_SOC_MAX9867 if I2C select SND_SOC_MAX98925 if I2C + select SND_SOC_MAX98926 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9768 if I2C select SND_SOC_MAX9877 if I2C @@ -87,7 +89,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_ML26124 if I2C select SND_SOC_NAU8825 if I2C select SND_SOC_PCM1681 if I2C - select SND_SOC_PCM179X if SPI_MASTER + select SND_SOC_PCM179X_I2C if I2C + select SND_SOC_PCM179X_SPI if SPI_MASTER select SND_SOC_PCM3008 select SND_SOC_PCM3168A_I2C if I2C select SND_SOC_PCM3168A_SPI if SPI_MASTER @@ -95,6 +98,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT286 if I2C select SND_SOC_RT298 if I2C + select SND_SOC_RT5514 if I2C select SND_SOC_RT5616 if I2C select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C @@ -490,6 +494,7 @@ config SND_SOC_GTM601 config SND_SOC_HDAC_HDMI tristate select SND_HDA_EXT_CORE + select SND_PCM_ELD select HDMI config SND_SOC_ICS43432 @@ -497,6 +502,7 @@ config SND_SOC_ICS43432 config SND_SOC_INNO_RK3036 tristate "Inno codec driver for RK3036 SoC" + select REGMAP_MMIO config SND_SOC_ISABELLE tristate @@ -516,9 +522,15 @@ config SND_SOC_MAX98095 config SND_SOC_MAX98357A tristate +config SND_SOC_MAX9867 + tristate + config SND_SOC_MAX98925 tristate +config SND_SOC_MAX98926 + tristate + config SND_SOC_MAX9850 tristate @@ -527,8 +539,23 @@ config SND_SOC_PCM1681 depends on I2C config SND_SOC_PCM179X - tristate "Texas Instruments PCM179X CODEC" + tristate + +config SND_SOC_PCM179X_I2C + tristate "Texas Instruments PCM179X CODEC (I2C)" + depends on I2C + select SND_SOC_PCM179X + help + Enable support for Texas Instruments PCM179x CODEC. + Select this if your PCM179x is connected via an I2C bus. + +config SND_SOC_PCM179X_SPI + tristate "Texas Instruments PCM179X CODEC (SPI)" depends on SPI_MASTER + select SND_SOC_PCM179X + help + Enable support for Texas Instruments PCM179x CODEC. + Select this if your PCM179x is connected via an SPI bus. config SND_SOC_PCM3008 tristate @@ -565,6 +592,7 @@ config SND_SOC_PCM512x_SPI config SND_SOC_RL6231 tristate + default y if SND_SOC_RT5514=y default y if SND_SOC_RT5616=y default y if SND_SOC_RT5640=y default y if SND_SOC_RT5645=y @@ -572,6 +600,7 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5659=y default y if SND_SOC_RT5670=y default y if SND_SOC_RT5677=y + default m if SND_SOC_RT5514=m default m if SND_SOC_RT5616=m default m if SND_SOC_RT5640=m default m if SND_SOC_RT5645=m @@ -595,6 +624,9 @@ config SND_SOC_RT298 tristate depends on I2C +config SND_SOC_RT5514 + tristate + config SND_SOC_RT5616 tristate "Realtek RT5616 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d44f7d3..185a712 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -74,13 +74,17 @@ snd-soc-max98088-objs := max98088.o snd-soc-max98090-objs := max98090.o snd-soc-max98095-objs := max98095.o snd-soc-max98357a-objs := max98357a.o +snd-soc-max9867-objs := max9867.o snd-soc-max98925-objs := max98925.o +snd-soc-max98926-objs := max98926.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o snd-soc-nau8825-objs := nau8825.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm179x-codec-objs := pcm179x.o +snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o +snd-soc-pcm179x-spi-objs := pcm179x-spi.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-pcm3168a-objs := pcm3168a.o snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o @@ -92,6 +96,7 @@ snd-soc-rl6231-objs := rl6231.o snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt286-objs := rt286.o snd-soc-rt298-objs := rt298.o +snd-soc-rt5514-objs := rt5514.o snd-soc-rt5616-objs := rt5616.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o @@ -278,13 +283,17 @@ obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o +obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o +obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o +obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o +obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o @@ -296,6 +305,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o +obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index affb192..8b1d0c1 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -1130,7 +1130,7 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol, struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); mutex_lock(&drvdata->ctrl_lock); - ucontrol->value.integer.value[0] = drvdata->sid_status; + ucontrol->value.enumerated.item[0] = drvdata->sid_status; mutex_unlock(&drvdata->ctrl_lock); return 0; @@ -1147,7 +1147,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol, dev_dbg(codec->dev, "%s: Enter\n", __func__); - if (ucontrol->value.integer.value[0] != SID_APPLY_FIR) { + if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) { dev_err(codec->dev, "%s: ERROR: This control supports '%s' only!\n", __func__, enum_sid_state[SID_APPLY_FIR]); @@ -1199,7 +1199,7 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol, struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); mutex_lock(&drvdata->ctrl_lock); - ucontrol->value.integer.value[0] = drvdata->anc_status; + ucontrol->value.enumerated.item[0] = drvdata->anc_status; mutex_unlock(&drvdata->ctrl_lock); return 0; @@ -1220,7 +1220,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, mutex_lock(&drvdata->ctrl_lock); - req = ucontrol->value.integer.value[0]; + req = ucontrol->value.enumerated.item[0]; if (req >= ARRAY_SIZE(enum_anc_state)) { status = -EINVAL; goto cleanup; @@ -2134,7 +2134,6 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) "%s: ERROR: Unsupporter master mask 0x%x\n", __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); return -EINVAL; - break; } snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val); diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index 348ccb1..8de010f 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec * * Copyright 2014 Analog Devices Inc. * Author: Lars-Peter Clausen <lars@metafoo.de> @@ -44,9 +44,21 @@ static const struct i2c_device_id adau1761_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids); +#if defined(CONFIG_OF) +static const struct of_device_id adau1761_i2c_dt_ids[] = { + { .compatible = "adi,adau1361", }, + { .compatible = "adi,adau1461", }, + { .compatible = "adi,adau1761", }, + { .compatible = "adi,adau1961", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1761_i2c_dt_ids); +#endif + static struct i2c_driver adau1761_i2c_driver = { .driver = { .name = "adau1761", + .of_match_table = of_match_ptr(adau1761_i2c_dt_ids), }, .probe = adau1761_i2c_probe, .remove = adau1761_i2c_remove, diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c index 8bc1fbd..d917124 100644 --- a/sound/soc/codecs/adau1761-spi.c +++ b/sound/soc/codecs/adau1761-spi.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec * * Copyright 2014 Analog Devices Inc. * Author: Lars-Peter Clausen <lars@metafoo.de> @@ -61,9 +61,21 @@ static const struct spi_device_id adau1761_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, adau1761_spi_id); +#if defined(CONFIG_OF) +static const struct of_device_id adau1761_spi_dt_ids[] = { + { .compatible = "adi,adau1361", }, + { .compatible = "adi,adau1461", }, + { .compatible = "adi,adau1761", }, + { .compatible = "adi,adau1961", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1761_spi_dt_ids); +#endif + static struct spi_driver adau1761_spi_driver = { .driver = { .name = "adau1761", + .of_match_table = of_match_ptr(adau1761_spi_dt_ids), }, .probe = adau1761_spi_probe, .remove = adau1761_spi_remove, diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c index 2f12477..b95d29d 100644 --- a/sound/soc/codecs/adau1761.c +++ b/sound/soc/codecs/adau1761.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec * * Copyright 2011-2013 Analog Devices Inc. * Author: Lars-Peter Clausen <lars@metafoo.de> @@ -456,13 +456,17 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: + regcache_cache_only(adau->regmap, false); regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, ADAU17X1_CLOCK_CONTROL_SYSCLK_EN); + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) + regcache_sync(adau->regmap); break; case SND_SOC_BIAS_OFF: regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0); + regcache_cache_only(adau->regmap, true); break; } @@ -783,6 +787,10 @@ int adau1761_probe(struct device *dev, struct regmap *regmap, if (ret) return ret; + /* Enable cache only mode as we could miss writes before bias level + * reaches standby and the core clock is enabled */ + regcache_cache_only(regmap, true); + return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1); } EXPORT_SYMBOL_GPL(adau1761_probe); diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c index 0e32bba..06cbca8 100644 --- a/sound/soc/codecs/adau1781-i2c.c +++ b/sound/soc/codecs/adau1781-i2c.c @@ -42,9 +42,19 @@ static const struct i2c_device_id adau1781_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids); +#if defined(CONFIG_OF) +static const struct of_device_id adau1781_i2c_dt_ids[] = { + { .compatible = "adi,adau1381", }, + { .compatible = "adi,adau1781", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1781_i2c_dt_ids); +#endif + static struct i2c_driver adau1781_i2c_driver = { .driver = { .name = "adau1781", + .of_match_table = of_match_ptr(adau1781_i2c_dt_ids), }, .probe = adau1781_i2c_probe, .remove = adau1781_i2c_remove, diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c index 33a73ff..3d965a0 100644 --- a/sound/soc/codecs/adau1781-spi.c +++ b/sound/soc/codecs/adau1781-spi.c @@ -59,9 +59,19 @@ static const struct spi_device_id adau1781_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, adau1781_spi_id); +#if defined(CONFIG_OF) +static const struct of_device_id adau1781_spi_dt_ids[] = { + { .compatible = "adi,adau1381", }, + { .compatible = "adi,adau1781", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1781_spi_dt_ids); +#endif + static struct spi_driver adau1781_spi_driver = { .driver = { .name = "adau1781", + .of_match_table = of_match_ptr(adau1781_spi_dt_ids), }, .probe = adau1781_spi_probe, .remove = adau1781_spi_remove, diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c index fde9068..bc1bb56 100644 --- a/sound/soc/codecs/adau1781.c +++ b/sound/soc/codecs/adau1781.c @@ -1,5 +1,5 @@ /* - * Driver for ADAU1781/ADAU1781 codec + * Driver for ADAU1381/ADAU1781 codec * * Copyright 2011-2013 Analog Devices Inc. * Author: Lars-Peter Clausen <lars@metafoo.de> diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h index e13583e..5ae87a0 100644 --- a/sound/soc/codecs/adau17x1.h +++ b/sound/soc/codecs/adau17x1.h @@ -103,9 +103,9 @@ bool adau17x1_has_dsp(struct adau *adau); #define ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL BIT(3) #define ADAU17X1_CLOCK_CONTROL_SYSCLK_EN BIT(0) -#define ADAU17X1_SERIAL_PORT1_BCLK32 (0x0 << 5) -#define ADAU17X1_SERIAL_PORT1_BCLK48 (0x1 << 5) -#define ADAU17X1_SERIAL_PORT1_BCLK64 (0x2 << 5) +#define ADAU17X1_SERIAL_PORT1_BCLK64 (0x0 << 5) +#define ADAU17X1_SERIAL_PORT1_BCLK32 (0x1 << 5) +#define ADAU17X1_SERIAL_PORT1_BCLK48 (0x2 << 5) #define ADAU17X1_SERIAL_PORT1_BCLK128 (0x3 << 5) #define ADAU17X1_SERIAL_PORT1_BCLK256 (0x4 << 5) #define ADAU17X1_SERIAL_PORT1_BCLK_MASK (0x7 << 5) diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c index 1222282..c5be1bd 100644 --- a/sound/soc/codecs/ads117x.c +++ b/sound/soc/codecs/ads117x.c @@ -20,6 +20,8 @@ #include <sound/initval.h> #include <sound/soc.h> +#include <linux/of.h> + #define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000) #define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) @@ -75,9 +77,19 @@ static int ads117x_remove(struct platform_device *pdev) return 0; } +#if defined(CONFIG_OF) +static const struct of_device_id ads117x_dt_ids[] = { + { .compatible = "ti,ads1174" }, + { .compatible = "ti,ads1178" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ads117x_dt_ids); +#endif + static struct platform_driver ads117x_codec_driver = { .driver = { .name = "ads117x-codec", + .of_match_table = of_match_ptr(ads117x_dt_ids), }, .probe = ads117x_probe, diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 33143fe..92d22a0 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1398,29 +1398,6 @@ static const int arizona_48k_bclk_rates[] = { 24576000, }; -static const unsigned int arizona_48k_rates[] = { - 12000, - 24000, - 48000, - 96000, - 192000, - 384000, - 768000, - 4000, - 8000, - 16000, - 32000, - 64000, - 128000, - 256000, - 512000, -}; - -static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = { - .count = ARRAY_SIZE(arizona_48k_rates), - .list = arizona_48k_rates, -}; - static const int arizona_44k1_bclk_rates[] = { -1, 44100, @@ -1443,22 +1420,7 @@ static const int arizona_44k1_bclk_rates[] = { 22579200, }; -static const unsigned int arizona_44k1_rates[] = { - 11025, - 22050, - 44100, - 88200, - 176400, - 352800, - 705600, -}; - -static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = { - .count = ARRAY_SIZE(arizona_44k1_rates), - .list = arizona_44k1_rates, -}; - -static int arizona_sr_vals[] = { +static const unsigned int arizona_sr_vals[] = { 0, 12000, 24000, @@ -1485,13 +1447,21 @@ static int arizona_sr_vals[] = { 512000, }; +#define ARIZONA_48K_RATE_MASK 0x0F003E +#define ARIZONA_44K1_RATE_MASK 0x003E00 +#define ARIZONA_RATE_MASK (ARIZONA_48K_RATE_MASK | ARIZONA_44K1_RATE_MASK) + +static const struct snd_pcm_hw_constraint_list arizona_constraint = { + .count = ARRAY_SIZE(arizona_sr_vals), + .list = arizona_sr_vals, +}; + static int arizona_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; - const struct snd_pcm_hw_constraint_list *constraint; unsigned int base_rate; if (!substream->runtime) @@ -1509,16 +1479,15 @@ static int arizona_startup(struct snd_pcm_substream *substream, } if (base_rate == 0) - return 0; - - if (base_rate % 8000) - constraint = &arizona_44k1_constraint; + dai_priv->constraint.mask = ARIZONA_RATE_MASK; + else if (base_rate % 8000) + dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK; else - constraint = &arizona_48k_constraint; + dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK; return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - constraint); + &dai_priv->constraint); } static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec, @@ -1911,6 +1880,7 @@ int arizona_init_dai(struct arizona_priv *priv, int id) struct arizona_dai_priv *dai_priv = &priv->dai[id]; dai_priv->clk = ARIZONA_CLK_SYSCLK; + dai_priv->constraint = arizona_constraint; return 0; } @@ -1929,6 +1899,25 @@ static struct { { 1000000, 13500000, 0, 1 }, }; +static const unsigned int pseudo_fref_max[ARIZONA_FLL_MAX_FRATIO] = { + 13500000, + 6144000, + 6144000, + 3072000, + 3072000, + 2822400, + 2822400, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 768000, +}; + static struct { unsigned int min; unsigned int max; @@ -2042,16 +2031,32 @@ static int arizona_calc_fratio(struct arizona_fll *fll, /* Adjust FRATIO/refdiv to avoid integer mode if possible */ refdiv = cfg->refdiv; + arizona_fll_dbg(fll, "pseudo: initial ratio=%u fref=%u refdiv=%u\n", + init_ratio, Fref, refdiv); + while (div <= ARIZONA_FLL_MAX_REFDIV) { for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; ratio++) { if ((ARIZONA_FLL_VCO_CORNER / 2) / - (fll->vco_mult * ratio) < Fref) + (fll->vco_mult * ratio) < Fref) { + arizona_fll_dbg(fll, "pseudo: hit VCO corner\n"); break; + } + + if (Fref > pseudo_fref_max[ratio - 1]) { + arizona_fll_dbg(fll, + "pseudo: exceeded max fref(%u) for ratio=%u\n", + pseudo_fref_max[ratio - 1], + ratio); + break; + } if (target % (ratio * Fref)) { cfg->refdiv = refdiv; cfg->fratio = ratio - 1; + arizona_fll_dbg(fll, + "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n", + Fref, refdiv, div, ratio); return ratio; } } @@ -2060,6 +2065,9 @@ static int arizona_calc_fratio(struct arizona_fll *fll, if (target % (ratio * Fref)) { cfg->refdiv = refdiv; cfg->fratio = ratio - 1; + arizona_fll_dbg(fll, + "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n", + Fref, refdiv, div, ratio); return ratio; } } @@ -2068,6 +2076,9 @@ static int arizona_calc_fratio(struct arizona_fll *fll, Fref /= 2; refdiv++; init_ratio = arizona_find_fratio(Fref, NULL); + arizona_fll_dbg(fll, + "pseudo: change fref=%u refdiv=%d(%d) ratio=%u\n", + Fref, refdiv, div, init_ratio); } arizona_fll_warn(fll, "Falling back to integer mode operation\n"); @@ -2138,11 +2149,12 @@ static int arizona_calc_fll(struct arizona_fll *fll, return -EINVAL; } - arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", + arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n", cfg->n, cfg->theta, cfg->lambda); - arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", - cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv); - arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain); + arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n", + cfg->fratio, ratio, cfg->outdiv, + cfg->refdiv, 1 << cfg->refdiv); + arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain); return 0; diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 8b6adb5..1ea8e4e 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -57,7 +57,7 @@ #define ARIZONA_CLK_98MHZ 5 #define ARIZONA_CLK_147MHZ 6 -#define ARIZONA_MAX_DAI 8 +#define ARIZONA_MAX_DAI 10 #define ARIZONA_MAX_ADSP 4 #define ARIZONA_DVFS_SR1_RQ 0x001 @@ -68,6 +68,8 @@ struct wm_adsp; struct arizona_dai_priv { int clk; + + struct snd_pcm_hw_constraint_list constraint; }; struct arizona_priv { diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index b395152..35488f1 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -60,15 +60,15 @@ static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, switch (value) { default: case 0: - ucontrol->value.integer.value[0] = 0; + ucontrol->value.enumerated.item[0] = 0; break; /* same value : (L+R)/2 and (R+L)/2 */ case 1: case 2: - ucontrol->value.integer.value[0] = 1; + ucontrol->value.enumerated.item[0] = 1; break; case 3: - ucontrol->value.integer.value[0] = 2; + ucontrol->value.enumerated.item[0] = 2; break; } @@ -85,7 +85,7 @@ static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); unsigned char val; - switch (ucontrol->value.integer.value[0]) { + switch (ucontrol->value.enumerated.item[0]) { default: case 0: val = CHAN_MIX_NORMAL; diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index d562e1b..1179101 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -44,6 +44,7 @@ struct cs42xx8_priv { bool slave_mode; unsigned long sysclk; + u32 tx_channels; }; /* -127.5dB to 0dB with step of 0.5dB */ @@ -257,6 +258,9 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream, u32 ratio = cs42xx8->sysclk / params_rate(params); u32 i, fm, val, mask; + if (tx) + cs42xx8->tx_channels = params_channels(params); + for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { if (cs42xx8_ratios[i].ratio == ratio) break; @@ -283,9 +287,11 @@ static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); + u8 dac_unmute = cs42xx8->tx_channels ? + ~((0x1 << cs42xx8->tx_channels) - 1) : 0; - regmap_update_bits(cs42xx8->regmap, CS42XX8_DACMUTE, - CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0); + regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, + mute ? CS42XX8_DACMUTE_ALL : dac_unmute); return 0; } diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index dc5ae7f..576087b 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -57,6 +57,25 @@ static const struct wm_adsp_region *cs47l24_dsp_regions[] = { cs47l24_dsp3_regions, }; +static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + unsigned int v; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); + if (ret != 0) { + dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret); + return ret; + } + + v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; + + return wm_adsp2_early_event(w, kcontrol, event, v); +} + static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); @@ -405,8 +424,8 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, NULL, 0), -WM_ADSP2("DSP2", 1), -WM_ADSP2("DSP3", 2), +WM_ADSP2("DSP2", 1, cs47l24_adsp_power_ev), +WM_ADSP2("DSP3", 2, cs47l24_adsp_power_ev), SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), @@ -779,6 +798,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { { "AIF2 Capture", NULL, "SYSCLK" }, { "AIF3 Capture", NULL, "SYSCLK" }, + { "Voice Control DSP", NULL, "DSP3" }, + { "Voice Control DSP", NULL, "SYSCLK" }, + { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -901,7 +923,7 @@ static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source, } } -#define CS47L24_RATES SNDRV_PCM_RATE_8000_192000 +#define CS47L24_RATES SNDRV_PCM_RATE_KNOT #define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) @@ -973,12 +995,68 @@ static struct snd_soc_dai_driver cs47l24_dai[] = { .symmetric_rates = 1, .symmetric_samplebits = 1, }, + { + .name = "cs47l24-cpu-voicectrl", + .capture = { + .stream_name = "Voice Control CPU", + .channels_min = 1, + .channels_max = 1, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .compress_new = snd_soc_new_compress, + }, + { + .name = "cs47l24-dsp-voicectrl", + .capture = { + .stream_name = "Voice Control DSP", + .channels_min = 1, + .channels_max = 1, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + }, }; +static int cs47l24_open(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(rtd->codec); + struct arizona *arizona = priv->core.arizona; + int n_adsp; + + if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) { + n_adsp = 2; + } else { + dev_err(arizona->dev, + "No suitable compressed stream for DAI '%s'\n", + rtd->codec_dai->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream); +} + +static irqreturn_t cs47l24_adsp2_irq(int irq, void *data) +{ + struct cs47l24_priv *priv = data; + struct arizona *arizona = priv->core.arizona; + int ret; + + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]); + if (ret == -ENODEV) { + dev_err(arizona->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + static int cs47l24_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->core.arizona; int ret; priv->core.arizona->dapm = dapm; @@ -987,6 +1065,14 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec) arizona_init_gpio(codec); arizona_init_mono(codec); + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", cs47l24_adsp2_irq, + priv); + if (ret != 0) { + dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret); + return ret; + } + ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec); if (ret) goto err_adsp2_codec_probe; @@ -1014,13 +1100,14 @@ err_adsp2_codec_probe: static int cs47l24_codec_remove(struct snd_soc_codec *codec) { struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); - + struct arizona *arizona = priv->core.arizona; wm_adsp2_codec_remove(&priv->core.adsp[1], codec); wm_adsp2_codec_remove(&priv->core.adsp[2], codec); priv->core.arizona->dapm = NULL; + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv); return 0; } @@ -1057,6 +1144,19 @@ static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = { .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes), }; +static struct snd_compr_ops cs47l24_compr_ops = { + .open = cs47l24_open, + .free = wm_adsp_compr_free, + .set_params = wm_adsp_compr_set_params, + .get_caps = wm_adsp_compr_get_caps, + .trigger = wm_adsp_compr_trigger, + .pointer = wm_adsp_compr_pointer, + .copy = wm_adsp_compr_copy, +}; + +static struct snd_soc_platform_driver cs47l24_compr_platform = { + .compr_ops = &cs47l24_compr_ops, +}; static int cs47l24_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -1120,12 +1220,25 @@ static int cs47l24_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); - return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, + ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); + return ret; + } + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, cs47l24_dai, ARRAY_SIZE(cs47l24_dai)); + + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); + snd_soc_unregister_platform(&pdev->dev); + } + + return ret; } static int cs47l24_remove(struct platform_device *pdev) { + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 1d5a89c..461506a 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -334,7 +334,7 @@ static int da732x_hpf_set(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value; unsigned int reg = enum_ctrl->reg; - unsigned int sel = ucontrol->value.integer.value[0]; + unsigned int sel = ucontrol->value.enumerated.item[0]; unsigned int bits; switch (sel) { @@ -368,13 +368,13 @@ static int da732x_hpf_get(struct snd_kcontrol *kcontrol, switch (val) { case DA732X_HPF_VOICE_EN: - ucontrol->value.integer.value[0] = DA732X_HPF_VOICE; + ucontrol->value.enumerated.item[0] = DA732X_HPF_VOICE; break; case DA732X_HPF_MUSIC_EN: - ucontrol->value.integer.value[0] = DA732X_HPF_MUSIC; + ucontrol->value.enumerated.item[0] = DA732X_HPF_MUSIC; break; default: - ucontrol->value.integer.value[0] = DA732X_HPF_DISABLED; + ucontrol->value.enumerated.item[0] = DA732X_HPF_DISABLED; break; } diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 5a1ec0f..26f9459 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -22,11 +22,17 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/hdmi.h> +#include <drm/drm_edid.h> #include <sound/pcm_params.h> +#include <sound/jack.h> #include <sound/soc.h> #include <sound/hdaudio_ext.h> #include <sound/hda_i915.h> +#include <sound/pcm_drm_eld.h> #include "../../hda/local.h" +#include "hdac_hdmi.h" + +#define NAME_SIZE 32 #define AMP_OUT_MUTE 0xb080 #define AMP_OUT_UNMUTE 0xb000 @@ -34,6 +40,11 @@ #define HDA_MAX_CONNECTIONS 32 +#define HDA_MAX_CVTS 3 + +#define ELD_MAX_SIZE 256 +#define ELD_FIXED_BYTES 20 + struct hdac_hdmi_cvt_params { unsigned int channels_min; unsigned int channels_max; @@ -45,14 +56,34 @@ struct hdac_hdmi_cvt_params { struct hdac_hdmi_cvt { struct list_head head; hda_nid_t nid; + const char *name; struct hdac_hdmi_cvt_params params; }; +struct hdac_hdmi_eld { + bool monitor_present; + bool eld_valid; + int eld_size; + char eld_buffer[ELD_MAX_SIZE]; +}; + struct hdac_hdmi_pin { struct list_head head; hda_nid_t nid; int num_mux_nids; hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; + struct hdac_hdmi_eld eld; + struct hdac_ext_device *edev; + int repoll_count; + struct delayed_work work; +}; + +struct hdac_hdmi_pcm { + struct list_head head; + int pcm_id; + struct hdac_hdmi_pin *pin; + struct hdac_hdmi_cvt *cvt; + struct snd_jack *jack; }; struct hdac_hdmi_dai_pin_map { @@ -62,11 +93,13 @@ struct hdac_hdmi_dai_pin_map { }; struct hdac_hdmi_priv { - struct hdac_hdmi_dai_pin_map dai_map[3]; + struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS]; struct list_head pin_list; struct list_head cvt_list; + struct list_head pcm_list; int num_pin; int num_cvt; + struct mutex pin_mutex; }; static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) @@ -76,6 +109,119 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) return to_ehdac_device(hdac); } +static unsigned int sad_format(const u8 *sad) +{ + return ((sad[0] >> 0x3) & 0x1f); +} + +static unsigned int sad_sample_bits_lpcm(const u8 *sad) +{ + return (sad[2] & 7); +} + +static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime, + void *eld) +{ + u64 formats = SNDRV_PCM_FMTBIT_S16; + int i; + const u8 *sad, *eld_buf = eld; + + sad = drm_eld_sad(eld_buf); + if (!sad) + goto format_constraint; + + for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) { + if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */ + + /* + * the controller support 20 and 24 bits in 32 bit + * container so we set S32 + */ + if (sad_sample_bits_lpcm(sad) & 0x6) + formats |= SNDRV_PCM_FMTBIT_S32; + } + } + +format_constraint: + return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, + formats); + +} + + /* HDMI ELD routines */ +static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec, + hda_nid_t nid, int byte_index) +{ + unsigned int val; + + val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD, + byte_index); + + dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n", + byte_index, val); + + return val; +} + +static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid) +{ + return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, + AC_DIPSIZE_ELD_BUF); +} + +/* + * This function queries the ELD size and ELD data and fills in the buffer + * passed by user + */ +static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size) +{ + int i, size, ret = 0; + + /* + * ELD size is initialized to zero in caller function. If no errors and + * ELD is valid, actual eld_size is assigned. + */ + + size = hdac_hdmi_get_eld_size(codec, nid); + if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) { + dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size); + return -ERANGE; + } + + /* set ELD buffer */ + for (i = 0; i < size; i++) { + unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i); + /* + * Graphics driver might be writing to ELD buffer right now. + * Just abort. The caller will repoll after a while. + */ + if (!(val & AC_ELDD_ELD_VALID)) { + dev_err(&codec->dev, + "HDMI: invalid ELD data byte %d\n", i); + ret = -EINVAL; + goto error; + } + val &= AC_ELDD_ELD_DATA; + /* + * The first byte cannot be zero. This can happen on some DVI + * connections. Some Intel chips may also need some 250ms delay + * to return non-zero ELD data, even when the graphics driver + * correctly writes ELD content before setting ELD_valid bit. + */ + if (!val && !i) { + dev_err(&codec->dev, "HDMI: 0 ELD data\n"); + ret = -EINVAL; + goto error; + } + buf[i] = val; + } + + *eld_size = size; +error: + return ret; +} + static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac, hda_nid_t cvt_nid, hda_nid_t pin_nid, u32 stream_tag, int format) @@ -107,27 +253,74 @@ hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid, AC_VERB_SET_HDMI_DIP_INDEX, val); } +struct dp_audio_infoframe { + u8 type; /* 0x84 */ + u8 len; /* 0x1b */ + u8 ver; /* 0x11 << 2 */ + + u8 CC02_CT47; /* match with HDMI infoframe from this on */ + u8 SS01_SF24; + u8 CXT04; + u8 CA; + u8 LFEPBL01_LSV36_DM_INH7; +}; + static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, hda_nid_t cvt_nid, hda_nid_t pin_nid) { uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; struct hdmi_audio_infoframe frame; - u8 *dip = (u8 *)&frame; + struct dp_audio_infoframe dp_ai; + struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_hdmi_pin *pin; + u8 *dip; int ret; int i; + const u8 *eld_buf; + u8 conn_type; + int channels = 2; - hdmi_audio_infoframe_init(&frame); + list_for_each_entry(pin, &hdmi->pin_list, head) { + if (pin->nid == pin_nid) + break; + } - /* Default stereo for now */ - frame.channels = 2; + eld_buf = pin->eld.eld_buffer; + conn_type = drm_eld_get_conn_type(eld_buf); /* setup channel count */ snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1); + AC_VERB_SET_CVT_CHAN_COUNT, channels - 1); - ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); - if (ret < 0) - return ret; + switch (conn_type) { + case DRM_ELD_CONN_TYPE_HDMI: + hdmi_audio_infoframe_init(&frame); + + /* Default stereo for now */ + frame.channels = channels; + + ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + break; + + case DRM_ELD_CONN_TYPE_DP: + memset(&dp_ai, 0, sizeof(dp_ai)); + dp_ai.type = 0x84; + dp_ai.len = 0x1b; + dp_ai.ver = 0x11 << 2; + dp_ai.CC02_CT47 = channels - 1; + dp_ai.CA = 0; + + dip = (u8 *)&dp_ai; + break; + + default: + dev_err(&hdac->hdac.dev, "Invalid connection type: %d\n", + conn_type); + return -EIO; + } /* stop infoframe transmission */ hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); @@ -137,9 +330,15 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, /* Fill infoframe. Index auto-incremented */ hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); - for (i = 0; i < sizeof(frame); i++) - snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, + if (conn_type == DRM_ELD_CONN_TYPE_HDMI) { + for (i = 0; i < sizeof(buffer); i++) + snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, + AC_VERB_SET_HDMI_DIP_DATA, buffer[i]); + } else { + for (i = 0; i < sizeof(dp_ai); i++) + snd_hdac_codec_write(&hdac->hdac, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, dip[i]); + } /* Start infoframe */ hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0); @@ -174,11 +373,6 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream, struct hdac_ext_dma_params *dd; int ret; - if (dai->id > 0) { - dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); - return -ENODEV; - } - dai_map = &hdmi->dai_map[dai->id]; dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); @@ -198,16 +392,30 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai) { struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); + struct hdac_hdmi_priv *hdmi = hdac->private_data; + struct hdac_hdmi_dai_pin_map *dai_map; + struct hdac_hdmi_pin *pin; struct hdac_ext_dma_params *dd; - if (dai->id > 0) { - dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); + dai_map = &hdmi->dai_map[dai->id]; + pin = dai_map->pin; + + if (!pin) + return -ENODEV; + + if ((!pin->eld.monitor_present) || (!pin->eld.eld_valid)) { + dev_err(&hdac->hdac.dev, "device is not configured for this pin: %d\n", + pin->nid); return -ENODEV; } - dd = kzalloc(sizeof(*dd), GFP_KERNEL); - if (!dd) - return -ENOMEM; + dd = snd_soc_dai_get_dma_data(dai, substream); + if (!dd) { + dd = kzalloc(sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + } + dd->format = snd_hdac_calc_stream_format(params_rate(hparams), params_channels(hparams), params_format(hparams), 24, 0); @@ -227,50 +435,187 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream, dai_map = &hdmi->dai_map[dai->id]; + dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); + + if (dd) { + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(dd); + } + + return 0; +} + +static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev, + struct hdac_hdmi_dai_pin_map *dai_map) +{ + /* Enable transmission */ snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, 0); + AC_VERB_SET_DIGI_CONVERT_1, 1); + + /* Category Code (CC) to zero */ snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0, - AC_VERB_SET_STREAM_FORMAT, 0); + AC_VERB_SET_DIGI_CONVERT_2, 0); +} - dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); - snd_soc_dai_set_dma_data(dai, substream, NULL); +static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac, + struct hdac_hdmi_dai_pin_map *dai_map) +{ + int mux_idx; + struct hdac_hdmi_pin *pin = dai_map->pin; + + for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) { + if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) { + snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + AC_VERB_SET_CONNECT_SEL, mux_idx); + break; + } + } + + if (mux_idx == pin->num_mux_nids) + return -EIO; + + /* Enable out path for this pin widget */ + snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); - kfree(dd); + hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0); + + snd_hdac_codec_write(&hdac->hdac, pin->nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); return 0; } +static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac, + struct hdac_hdmi_pin *pin) +{ + if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) { + dev_warn(&hdac->hdac.dev, + "HDMI: pin %d wcaps %#x does not support connection list\n", + pin->nid, get_wcaps(&hdac->hdac, pin->nid)); + return -EINVAL; + } + + pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid, + pin->mux_nids, HDA_MAX_CONNECTIONS); + if (pin->num_mux_nids == 0) + dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n", + pin->nid); + + dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n", + pin->num_mux_nids, pin->nid); + + return pin->num_mux_nids; +} + +/* + * Query pcm list and return pin widget to which stream is routed. + * + * Also query connection list of the pin, to validate the cvt to pin map. + * + * Same stream rendering to multiple pins simultaneously can be done + * possibly, but not supported for now in driver. So return the first pin + * connected. + */ +static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt( + struct hdac_ext_device *edev, + struct hdac_hdmi_priv *hdmi, + struct hdac_hdmi_cvt *cvt) +{ + struct hdac_hdmi_pcm *pcm; + struct hdac_hdmi_pin *pin = NULL; + int ret, i; + + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->cvt == cvt) { + pin = pcm->pin; + break; + } + } + + if (pin) { + ret = hdac_hdmi_query_pin_connlist(edev, pin); + if (ret < 0) + return NULL; + + for (i = 0; i < pin->num_mux_nids; i++) { + if (pin->mux_nids[i] == cvt->nid) + return pin; + } + } + + return NULL; +} + +/* + * This tries to get a valid pin and set the HW constraints based on the + * ELD. Even if a valid pin is not found return success so that device open + * doesn't fail. + */ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_priv *hdmi = hdac->private_data; struct hdac_hdmi_dai_pin_map *dai_map; - int val; - - if (dai->id > 0) { - dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n"); - return -ENODEV; - } + struct hdac_hdmi_cvt *cvt; + struct hdac_hdmi_pin *pin; + int ret; dai_map = &hdmi->dai_map[dai->id]; - val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0, - AC_VERB_GET_PIN_SENSE, 0); - dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val); + cvt = dai_map->cvt; + pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt); - if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) { - dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val); - return -ENODEV; + /* + * To make PA and other userland happy. + * userland scans devices so returning error does not help. + */ + if (!pin) + return 0; + + if ((!pin->eld.monitor_present) || + (!pin->eld.eld_valid)) { + + dev_warn(&hdac->hdac.dev, + "Failed: montior present? %d ELD valid?: %d for pin: %d\n", + pin->eld.monitor_present, pin->eld.eld_valid, pin->nid); + + return 0; } - hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0); + dai_map->pin = pin; - snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + hdac_hdmi_enable_cvt(hdac, dai_map); + ret = hdac_hdmi_enable_pin(hdac, dai_map); + if (ret < 0) + return ret; - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, 2); + ret = hdac_hdmi_eld_limit_formats(substream->runtime, + pin->eld.eld_buffer); + if (ret < 0) + return ret; + + return snd_pcm_hw_constraint_eld(substream->runtime, + pin->eld.eld_buffer); +} + +static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct hdac_hdmi_dai_pin_map *dai_map; + struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); + struct hdac_hdmi_priv *hdmi = hdac->private_data; + int ret; + + dai_map = &hdmi->dai_map[dai->id]; + if (cmd == SNDRV_PCM_TRIGGER_RESUME) { + ret = hdac_hdmi_enable_pin(hdac, dai_map); + if (ret < 0) + return ret; + + return hdac_hdmi_playback_prepare(substream, dai); + } return 0; } @@ -284,10 +629,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, dai_map = &hdmi->dai_map[dai->id]; - hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3); + if (dai_map->pin) { + snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, 0); + snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0, + AC_VERB_SET_STREAM_FORMAT, 0); + + hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3); - snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, + snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + dai_map->pin = NULL; + } } static int @@ -310,85 +664,326 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) return err; } -static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w, - enum snd_soc_dapm_type id, - const char *wname, const char *stream) +static int hdac_hdmi_fill_widget_info(struct device *dev, + struct snd_soc_dapm_widget *w, + enum snd_soc_dapm_type id, void *priv, + const char *wname, const char *stream, + struct snd_kcontrol_new *wc, int numkc) { w->id = id; - w->name = wname; + w->name = devm_kstrdup(dev, wname, GFP_KERNEL); + if (!w->name) + return -ENOMEM; + w->sname = stream; w->reg = SND_SOC_NOPM; w->shift = 0; - w->kcontrol_news = NULL; - w->num_kcontrols = 0; - w->priv = NULL; + w->kcontrol_news = wc; + w->num_kcontrols = numkc; + w->priv = priv; + + return 0; } static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, - const char *sink, const char *control, const char *src) + const char *sink, const char *control, const char *src, + int (*handler)(struct snd_soc_dapm_widget *src, + struct snd_soc_dapm_widget *sink)) { route->sink = sink; route->source = src; route->control = control; - route->connected = NULL; + route->connected = handler; } -static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm, - struct hdac_hdmi_dai_pin_map *dai_map) +static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev, + struct hdac_hdmi_pin *pin) { - struct snd_soc_dapm_route route[1]; - struct snd_soc_dapm_widget widgets[2] = { {0} }; + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = NULL; - memset(&route, 0, sizeof(route)); + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->pin == pin) + return pcm; + } - hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output, - "hif1 Output", NULL); - hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in, - "Coverter 1", "hif1"); + return NULL; +} - hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1"); +/* + * Based on user selection, map the PINs with the PCMs. + */ +static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_context *dapm = w->dapm; + struct hdac_hdmi_pin *pin = w->priv; + struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = NULL; + const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; - snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets)); - snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route)); + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + if (ret < 0) + return ret; + + mutex_lock(&hdmi->pin_mutex); + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->pin == pin) + pcm->pin = NULL; + + /* + * Jack status is not reported during device probe as the + * PCMs are not registered by then. So report it here. + */ + if (!strcmp(cvt_name, pcm->cvt->name) && !pcm->pin) { + pcm->pin = pin; + if (pin->eld.monitor_present && pin->eld.eld_valid) { + dev_dbg(&edev->hdac.dev, + "jack report for pcm=%d\n", + pcm->pcm_id); + + snd_jack_report(pcm->jack, SND_JACK_AVOUT); + } + mutex_unlock(&hdmi->pin_mutex); + return ret; + } + } + mutex_unlock(&hdmi->pin_mutex); + + return ret; } -static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) +/* + * Ideally the Mux inputs should be based on the num_muxs enumerated, but + * the display driver seem to be programming the connection list for the pin + * widget runtime. + * + * So programming all the possible inputs for the mux, the user has to take + * care of selecting the right one and leaving all other inputs selected to + * "NONE" + */ +static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev, + struct hdac_hdmi_pin *pin, + struct snd_soc_dapm_widget *widget, + const char *widget_name) +{ + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct snd_kcontrol_new *kc; + struct hdac_hdmi_cvt *cvt; + struct soc_enum *se; + char kc_name[NAME_SIZE]; + char mux_items[NAME_SIZE]; + /* To hold inputs to the Pin mux */ + char *items[HDA_MAX_CONNECTIONS]; + int i = 0; + int num_items = hdmi->num_cvt + 1; + + kc = devm_kzalloc(&edev->hdac.dev, sizeof(*kc), GFP_KERNEL); + if (!kc) + return -ENOMEM; + + se = devm_kzalloc(&edev->hdac.dev, sizeof(*se), GFP_KERNEL); + if (!se) + return -ENOMEM; + + sprintf(kc_name, "Pin %d Input", pin->nid); + kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL); + if (!kc->name) + return -ENOMEM; + + kc->private_value = (long)se; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = 0; + kc->info = snd_soc_info_enum_double; + kc->put = hdac_hdmi_set_pin_mux; + kc->get = snd_soc_dapm_get_enum_double; + + se->reg = SND_SOC_NOPM; + + /* enum texts: ["NONE", "cvt #", "cvt #", ...] */ + se->items = num_items; + se->mask = roundup_pow_of_two(se->items) - 1; + + sprintf(mux_items, "NONE"); + items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL); + if (!items[i]) + return -ENOMEM; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + i++; + sprintf(mux_items, "cvt %d", cvt->nid); + items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL); + if (!items[i]) + return -ENOMEM; + } + + se->texts = devm_kmemdup(&edev->hdac.dev, items, + (num_items * sizeof(char *)), GFP_KERNEL); + if (!se->texts) + return -ENOMEM; + + return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget, + snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1); +} + +/* Add cvt <- input <- mux route map */ +static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev, + struct snd_soc_dapm_widget *widgets, + struct snd_soc_dapm_route *route, int rindex) +{ + struct hdac_hdmi_priv *hdmi = edev->private_data; + const struct snd_kcontrol_new *kc; + struct soc_enum *se; + int mux_index = hdmi->num_cvt + hdmi->num_pin; + int i, j; + + for (i = 0; i < hdmi->num_pin; i++) { + kc = widgets[mux_index].kcontrol_news; + se = (struct soc_enum *)kc->private_value; + for (j = 0; j < hdmi->num_cvt; j++) { + hdac_hdmi_fill_route(&route[rindex], + widgets[mux_index].name, + se->texts[j + 1], + widgets[j].name, NULL); + + rindex++; + } + + mux_index++; + } +} + +/* + * Widgets are added in the below sequence + * Converter widgets for num converters enumerated + * Pin widgets for num pins enumerated + * Pin mux widgets to represent connenction list of pin widget + * + * Total widgets elements = num_cvt + num_pin + num_pin; + * + * Routes are added as below: + * pin mux -> pin (based on num_pins) + * cvt -> "Input sel control" -> pin_mux + * + * Total route elements: + * num_pins + (pin_muxes * num_cvt) + */ +static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) { + struct snd_soc_dapm_widget *widgets; + struct snd_soc_dapm_route *route; + struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev); struct hdac_hdmi_priv *hdmi = edev->private_data; - struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0]; + struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv; + char widget_name[NAME_SIZE]; struct hdac_hdmi_cvt *cvt; struct hdac_hdmi_pin *pin; + int ret, i = 0, num_routes = 0; if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list)) return -EINVAL; - /* - * Currently on board only 1 pin and 1 converter is enabled for - * simplification, more will be added eventually - * So using fixed map for dai_id:pin:cvt - */ - cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head); - pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head); + widgets = devm_kzalloc(dapm->dev, + (sizeof(*widgets) * ((2 * hdmi->num_pin) + hdmi->num_cvt)), + GFP_KERNEL); - dai_map->dai_id = 0; - dai_map->pin = pin; + if (!widgets) + return -ENOMEM; - dai_map->cvt = cvt; + /* DAPM widgets to represent each converter widget */ + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + sprintf(widget_name, "Converter %d", cvt->nid); + ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], + snd_soc_dapm_aif_in, &cvt->nid, + widget_name, dai_drv[i].playback.stream_name, NULL, 0); + if (ret < 0) + return ret; + i++; + } - /* Enable out path for this pin widget */ - snd_hdac_codec_write(&edev->hdac, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + list_for_each_entry(pin, &hdmi->pin_list, head) { + sprintf(widget_name, "hif%d Output", pin->nid); + ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], + snd_soc_dapm_output, &pin->nid, + widget_name, NULL, NULL, 0); + if (ret < 0) + return ret; + i++; + } - /* Enable transmission */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, 1); + /* DAPM widgets to represent the connection list to pin widget */ + list_for_each_entry(pin, &hdmi->pin_list, head) { + sprintf(widget_name, "Pin %d Mux", pin->nid); + ret = hdac_hdmi_create_pin_muxs(edev, pin, &widgets[i], + widget_name); + if (ret < 0) + return ret; + i++; - /* Category Code (CC) to zero */ - snd_hdac_codec_write(&edev->hdac, cvt->nid, 0, - AC_VERB_SET_DIGI_CONVERT_2, 0); + /* For cvt to pin_mux mapping */ + num_routes += hdmi->num_cvt; + + /* For pin_mux to pin mapping */ + num_routes++; + } - snd_hdac_codec_write(&edev->hdac, pin->nid, 0, - AC_VERB_SET_CONNECT_SEL, 0); + route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes), + GFP_KERNEL); + if (!route) + return -ENOMEM; + + i = 0; + /* Add pin <- NULL <- mux route map */ + list_for_each_entry(pin, &hdmi->pin_list, head) { + int sink_index = i + hdmi->num_cvt; + int src_index = sink_index + hdmi->num_pin; + + hdac_hdmi_fill_route(&route[i], + widgets[sink_index].name, NULL, + widgets[src_index].name, NULL); + i++; + + } + + hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i); + + snd_soc_dapm_new_controls(dapm, widgets, + ((2 * hdmi->num_pin) + hdmi->num_cvt)); + + snd_soc_dapm_add_routes(dapm, route, num_routes); + snd_soc_dapm_new_widgets(dapm->card); + + return 0; + +} + +static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev) +{ + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_dai_pin_map *dai_map; + struct hdac_hdmi_cvt *cvt; + int dai_id = 0; + + if (list_empty(&hdmi->cvt_list)) + return -EINVAL; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + dai_map = &hdmi->dai_map[dai_id]; + dai_map->dai_id = dai_id; + dai_map->cvt = cvt; + + dai_id++; + + if (dai_id == HDA_MAX_CVTS) { + dev_warn(&edev->hdac.dev, + "Max dais supported: %d\n", dai_id); + break; + } + } return 0; } @@ -397,12 +992,15 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) { struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_cvt *cvt; + char name[NAME_SIZE]; cvt = kzalloc(sizeof(*cvt), GFP_KERNEL); if (!cvt) return -ENOMEM; cvt->nid = nid; + sprintf(name, "cvt %d", cvt->nid); + cvt->name = kstrdup(name, GFP_KERNEL); list_add_tail(&cvt->head, &hdmi->cvt_list); hdmi->num_cvt++; @@ -410,6 +1008,106 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) return hdac_hdmi_query_cvt_params(&edev->hdac, cvt); } +static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) +{ + struct hdac_ext_device *edev = pin->edev; + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm; + int val; + + pin->repoll_count = repoll; + + pm_runtime_get_sync(&edev->hdac.dev); + val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0, + AC_VERB_GET_PIN_SENSE, 0); + + dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n", + val, pin->nid); + + + mutex_lock(&hdmi->pin_mutex); + pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE); + pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV); + + pcm = hdac_hdmi_get_pcm(edev, pin); + + if (!pin->eld.monitor_present || !pin->eld.eld_valid) { + + dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n", + __func__, pin->nid); + + /* + * PCMs are not registered during device probe, so don't + * report jack here. It will be done in usermode mux + * control select. + */ + if (pcm) { + dev_dbg(&edev->hdac.dev, + "jack report for pcm=%d\n", pcm->pcm_id); + + snd_jack_report(pcm->jack, 0); + } + + mutex_unlock(&hdmi->pin_mutex); + goto put_hdac_device; + } + + if (pin->eld.monitor_present && pin->eld.eld_valid) { + /* TODO: use i915 component for reading ELD later */ + if (hdac_hdmi_get_eld(&edev->hdac, pin->nid, + pin->eld.eld_buffer, + &pin->eld.eld_size) == 0) { + + if (pcm) { + dev_dbg(&edev->hdac.dev, + "jack report for pcm=%d\n", + pcm->pcm_id); + + snd_jack_report(pcm->jack, SND_JACK_AVOUT); + } + + print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET, + pin->eld.eld_buffer, pin->eld.eld_size); + } else { + pin->eld.monitor_present = false; + pin->eld.eld_valid = false; + + if (pcm) { + dev_dbg(&edev->hdac.dev, + "jack report for pcm=%d\n", + pcm->pcm_id); + + snd_jack_report(pcm->jack, 0); + } + } + } + + mutex_unlock(&hdmi->pin_mutex); + + /* + * Sometimes the pin_sense may present invalid monitor + * present and eld_valid. If ELD data is not valid, loop few + * more times to get correct pin sense and valid ELD. + */ + if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll) + schedule_delayed_work(&pin->work, msecs_to_jiffies(300)); + +put_hdac_device: + pm_runtime_put_sync(&edev->hdac.dev); +} + +static void hdac_hdmi_repoll_eld(struct work_struct *work) +{ + struct hdac_hdmi_pin *pin = + container_of(to_delayed_work(work), struct hdac_hdmi_pin, work); + + /* picked from legacy HDA driver */ + if (pin->repoll_count++ > 6) + pin->repoll_count = 0; + + hdac_hdmi_present_sense(pin, pin->repoll_count); +} + static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) { struct hdac_hdmi_priv *hdmi = edev->private_data; @@ -424,6 +1122,120 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) list_add_tail(&pin->head, &hdmi->pin_list); hdmi->num_pin++; + pin->edev = edev; + INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld); + + return 0; +} + +#define INTEL_VENDOR_NID 0x08 +#define INTEL_GET_VENDOR_VERB 0xf81 +#define INTEL_SET_VENDOR_VERB 0x781 +#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ +#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ + +static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac) +{ + unsigned int vendor_param; + + vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) + return; + + vendor_param |= INTEL_EN_ALL_PIN_CVTS; + vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + INTEL_SET_VENDOR_VERB, vendor_param); + if (vendor_param == -1) + return; +} + +static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac) +{ + unsigned int vendor_param; + + vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) + return; + + /* enable DP1.2 mode */ + vendor_param |= INTEL_EN_DP12; + vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0, + INTEL_SET_VENDOR_VERB, vendor_param); + if (vendor_param == -1) + return; + +} + +static struct snd_soc_dai_ops hdmi_dai_ops = { + .startup = hdac_hdmi_pcm_open, + .shutdown = hdac_hdmi_pcm_close, + .hw_params = hdac_hdmi_set_hw_params, + .prepare = hdac_hdmi_playback_prepare, + .trigger = hdac_hdmi_trigger, + .hw_free = hdac_hdmi_playback_cleanup, +}; + +/* + * Each converter can support a stream independently. So a dai is created + * based on the number of converter queried. + */ +static int hdac_hdmi_create_dais(struct hdac_device *hdac, + struct snd_soc_dai_driver **dais, + struct hdac_hdmi_priv *hdmi, int num_dais) +{ + struct snd_soc_dai_driver *hdmi_dais; + struct hdac_hdmi_cvt *cvt; + char name[NAME_SIZE], dai_name[NAME_SIZE]; + int i = 0; + u32 rates, bps; + unsigned int rate_max = 384000, rate_min = 8000; + u64 formats; + int ret; + + hdmi_dais = devm_kzalloc(&hdac->dev, + (sizeof(*hdmi_dais) * num_dais), + GFP_KERNEL); + if (!hdmi_dais) + return -ENOMEM; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + ret = snd_hdac_query_supported_pcm(hdac, cvt->nid, + &rates, &formats, &bps); + if (ret) + return ret; + + sprintf(dai_name, "intel-hdmi-hifi%d", i+1); + hdmi_dais[i].name = devm_kstrdup(&hdac->dev, + dai_name, GFP_KERNEL); + + if (!hdmi_dais[i].name) + return -ENOMEM; + + snprintf(name, sizeof(name), "hifi%d", i+1); + hdmi_dais[i].playback.stream_name = + devm_kstrdup(&hdac->dev, name, GFP_KERNEL); + if (!hdmi_dais[i].playback.stream_name) + return -ENOMEM; + + /* + * Set caps based on capability queried from the converter. + * It will be constrained runtime based on ELD queried. + */ + hdmi_dais[i].playback.formats = formats; + hdmi_dais[i].playback.rates = rates; + hdmi_dais[i].playback.rate_max = rate_max; + hdmi_dais[i].playback.rate_min = rate_min; + hdmi_dais[i].playback.channels_min = 2; + hdmi_dais[i].playback.channels_max = 2; + hdmi_dais[i].ops = &hdmi_dai_ops; + + i++; + } + + *dais = hdmi_dais; + return 0; } @@ -431,7 +1243,8 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) * Parse all nodes and store the cvt/pin nids in array * Add one time initialization for pin and cvt widgets */ -static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev) +static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, + struct snd_soc_dai_driver **dais, int *num_dais) { hda_nid_t nid; int i, num_nodes; @@ -439,6 +1252,9 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev) struct hdac_hdmi_priv *hdmi = edev->private_data; int ret; + hdac_hdmi_skl_enable_all_pins(hdac); + hdac_hdmi_skl_enable_dp12(hdac); + num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid); if (!nid || num_nodes <= 0) { dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n"); @@ -479,19 +1295,107 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev) if (!hdmi->num_pin || !hdmi->num_cvt) return -EIO; + ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt); + if (ret) { + dev_err(&hdac->dev, "Failed to create dais with err: %d\n", + ret); + return ret; + } + + *num_dais = hdmi->num_cvt; + return hdac_hdmi_init_dai_map(edev); } +static void hdac_hdmi_eld_notify_cb(void *aptr, int port) +{ + struct hdac_ext_device *edev = aptr; + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pin *pin; + struct snd_soc_codec *codec = edev->scodec; + + /* Don't know how this mapping is derived */ + hda_nid_t pin_nid = port + 0x04; + + dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid); + + /* + * skip notification during system suspend (but not in runtime PM); + * the state will be updated at resume. Also since the ELD and + * connection states are updated in anyway at the end of the resume, + * we can skip it when received during PM process. + */ + if (snd_power_get_state(codec->component.card->snd_card) != + SNDRV_CTL_POWER_D0) + return; + + if (atomic_read(&edev->hdac.in_pm)) + return; + + list_for_each_entry(pin, &hdmi->pin_list, head) { + if (pin->nid == pin_nid) + hdac_hdmi_present_sense(pin, 1); + } +} + +static struct i915_audio_component_audio_ops aops = { + .pin_eld_notify = hdac_hdmi_eld_notify_cb, +}; + +int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) +{ + char jack_name[NAME_SIZE]; + struct snd_soc_codec *codec = dai->codec; + struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(&codec->component); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm; + + /* + * this is a new PCM device, create new pcm and + * add to the pcm list + */ + pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + pcm->pcm_id = device; + pcm->cvt = hdmi->dai_map[dai->id].cvt; + + list_add_tail(&pcm->head, &hdmi->pcm_list); + + sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device); + + return snd_jack_new(dapm->card->snd_card, jack_name, + SND_JACK_AVOUT, &pcm->jack, true, false); +} +EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); + static int hdmi_codec_probe(struct snd_soc_codec *codec) { struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); struct hdac_hdmi_priv *hdmi = edev->private_data; struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(&codec->component); + struct hdac_hdmi_pin *pin; + int ret; edev->scodec = codec; - create_fill_widget_route_map(dapm, &hdmi->dai_map[0]); + ret = create_fill_widget_route_map(dapm); + if (ret < 0) + return ret; + + aops.audio_ptr = edev; + ret = snd_hdac_i915_register_notifier(&aops); + if (ret < 0) { + dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n", + ret); + return ret; + } + + list_for_each_entry(pin, &hdmi->pin_list, head) + hdac_hdmi_present_sense(pin, 1); /* Imp: Store the card pointer in hda_codec */ edev->card = dapm->card->snd_card; @@ -515,44 +1419,73 @@ static int hdmi_codec_remove(struct snd_soc_codec *codec) return 0; } +#ifdef CONFIG_PM +static int hdmi_codec_resume(struct snd_soc_codec *codec) +{ + struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pin *pin; + struct hdac_device *hdac = &edev->hdac; + struct hdac_bus *bus = hdac->bus; + int err; + unsigned long timeout; + + hdac_hdmi_skl_enable_all_pins(&edev->hdac); + hdac_hdmi_skl_enable_dp12(&edev->hdac); + + /* Power up afg */ + if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) { + + snd_hdac_codec_write(hdac, hdac->afg, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D0); + + /* Wait till power state is set to D0 */ + timeout = jiffies + msecs_to_jiffies(1000); + while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0) + && time_before(jiffies, timeout)) { + msleep(50); + } + } + + /* + * As the ELD notify callback request is not entertained while the + * device is in suspend state. Need to manually check detection of + * all pins here. + */ + list_for_each_entry(pin, &hdmi->pin_list, head) + hdac_hdmi_present_sense(pin, 1); + + /* + * Codec power is turned ON during controller resume. + * Turn it OFF here + */ + err = snd_hdac_display_power(bus, false); + if (err < 0) { + dev_err(bus->dev, + "Cannot turn OFF display power on i915, err: %d\n", + err); + return err; + } + + return 0; +} +#else +#define hdmi_codec_resume NULL +#endif + static struct snd_soc_codec_driver hdmi_hda_codec = { .probe = hdmi_codec_probe, .remove = hdmi_codec_remove, + .resume = hdmi_codec_resume, .idle_bias_off = true, }; -static struct snd_soc_dai_ops hdmi_dai_ops = { - .startup = hdac_hdmi_pcm_open, - .shutdown = hdac_hdmi_pcm_close, - .hw_params = hdac_hdmi_set_hw_params, - .prepare = hdac_hdmi_playback_prepare, - .hw_free = hdac_hdmi_playback_cleanup, -}; - -static struct snd_soc_dai_driver hdmi_dais[] = { - { .name = "intel-hdmi-hif1", - .playback = { - .stream_name = "hif1", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S20_3LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - - }, - .ops = &hdmi_dai_ops, - }, -}; - static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) { struct hdac_device *codec = &edev->hdac; struct hdac_hdmi_priv *hdmi_priv; + struct snd_soc_dai_driver *hdmi_dais = NULL; + int num_dais = 0; int ret = 0; hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL); @@ -565,14 +1498,31 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) INIT_LIST_HEAD(&hdmi_priv->pin_list); INIT_LIST_HEAD(&hdmi_priv->cvt_list); + INIT_LIST_HEAD(&hdmi_priv->pcm_list); + mutex_init(&hdmi_priv->pin_mutex); - ret = hdac_hdmi_parse_and_map_nid(edev); - if (ret < 0) + /* + * Turned off in the runtime_suspend during the first explicit + * pm_runtime_suspend call. + */ + ret = snd_hdac_display_power(edev->hdac.bus, true); + if (ret < 0) { + dev_err(&edev->hdac.dev, + "Cannot turn on display power on i915 err: %d\n", + ret); return ret; + } + + ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais); + if (ret < 0) { + dev_err(&codec->dev, + "Failed in parse and map nid with err: %d\n", ret); + return ret; + } /* ASoC specific initialization */ return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, - hdmi_dais, ARRAY_SIZE(hdmi_dais)); + hdmi_dais, num_dais); } static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) @@ -580,11 +1530,20 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_pin *pin, *pin_next; struct hdac_hdmi_cvt *cvt, *cvt_next; + struct hdac_hdmi_pcm *pcm, *pcm_next; snd_soc_unregister_codec(&edev->hdac.dev); + list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) { + pcm->cvt = NULL; + pcm->pin = NULL; + list_del(&pcm->head); + kfree(pcm); + } + list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) { list_del(&cvt->head); + kfree(cvt->name); kfree(cvt); } @@ -602,6 +1561,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) struct hdac_ext_device *edev = to_hda_ext_device(dev); struct hdac_device *hdac = &edev->hdac; struct hdac_bus *bus = hdac->bus; + unsigned long timeout; int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -611,10 +1571,19 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) return 0; /* Power down afg */ - if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) + if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) { snd_hdac_codec_write(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + /* Wait till power state is set to D3 */ + timeout = jiffies + msecs_to_jiffies(1000); + while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3) + && time_before(jiffies, timeout)) { + + msleep(50); + } + } + err = snd_hdac_display_power(bus, false); if (err < 0) { dev_err(bus->dev, "Cannot turn on display power on i915\n"); @@ -643,6 +1612,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev) return err; } + hdac_hdmi_skl_enable_all_pins(&edev->hdac); + hdac_hdmi_skl_enable_dp12(&edev->hdac); + /* Power up afg */ if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) snd_hdac_codec_write(hdac, hdac->afg, 0, @@ -661,6 +1633,7 @@ static const struct dev_pm_ops hdac_hdmi_pm = { static const struct hda_device_id hdmi_list[] = { HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0), + HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0), {} }; diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h new file mode 100644 index 0000000..8dfd1e0 --- /dev/null +++ b/sound/soc/codecs/hdac_hdmi.h @@ -0,0 +1,6 @@ +#ifndef __HDAC_HDMI_H__ +#define __HDAC_HDMI_H__ + +int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm); + +#endif /* __HDAC_HDMI_H__ */ diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 20dcc49..fc22804 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1496,7 +1496,7 @@ static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol, struct max98088_pdata *pdata = max98088->pdata; int channel = max98088_get_channel(codec, kcontrol->id.name); struct max98088_cdata *cdata; - int sel = ucontrol->value.integer.value[0]; + int sel = ucontrol->value.enumerated.item[0]; if (channel < 0) return channel; diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 1fedac5..3577003 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1499,7 +1499,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, struct max98095_pdata *pdata = max98095->pdata; int channel = max98095_get_eq_channel(kcontrol->id.name); struct max98095_cdata *cdata; - unsigned int sel = ucontrol->value.integer.value[0]; + unsigned int sel = ucontrol->value.enumerated.item[0]; struct max98095_eq_cfg *coef_set; int fs, best, best_val, i; int regmask, regsave; @@ -1653,7 +1653,7 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, struct max98095_pdata *pdata = max98095->pdata; int channel = max98095_get_bq_channel(codec, kcontrol->id.name); struct max98095_cdata *cdata; - unsigned int sel = ucontrol->value.integer.value[0]; + unsigned int sel = ucontrol->value.enumerated.item[0]; struct max98095_biquad_cfg *coef_set; int fs, best, best_val, i; int regmask, regsave; diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c new file mode 100755 index 0000000..2a22fdd --- /dev/null +++ b/sound/soc/codecs/max9867.c @@ -0,0 +1,546 @@ +/* + * max9867.c -- max9867 ALSA SoC Audio driver + * + * Copyright 2013-15 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "max9867.h" + +static const char *const max9867_spmode[] = { + "Stereo Diff", "Mono Diff", + "Stereo Cap", "Mono Cap", + "Stereo Single", "Mono Single", + "Stereo Single Fast", "Mono Single Fast" +}; +static const char *const max9867_sidetone_text[] = { + "None", "Left", "Right", "LeftRight", "LeftRightDiv2", +}; +static const char *const max9867_filter_text[] = {"IIR", "FIR"}; + +static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7, + max9867_filter_text); +static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0, + max9867_spmode); +static SOC_ENUM_SINGLE_DECL(max9867_sidetone, MAX9867_DACGAIN, 6, + max9867_sidetone_text); +static DECLARE_TLV_DB_SCALE(max9860_capture_tlv, -600, 200, 0); +static DECLARE_TLV_DB_SCALE(max9860_mic_tlv, 2000, 100, 1); +static DECLARE_TLV_DB_SCALE(max9860_adc_left_tlv, -1200, 100, 1); +static DECLARE_TLV_DB_SCALE(max9860_adc_right_tlv, -1200, 100, 1); +static const unsigned int max98088_micboost_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), +}; + +static const struct snd_kcontrol_new max9867_snd_controls[] = { + SOC_DOUBLE_R("Master Playback Volume", MAX9867_LEFTVOL, + MAX9867_RIGHTVOL, 0, 63, 1), + SOC_DOUBLE_R_TLV("Capture Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, + 0, 15, 1, max9860_capture_tlv), + SOC_DOUBLE_R_TLV("Mic Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, 0, 31, 1, max9860_mic_tlv), + SOC_DOUBLE_R_TLV("Mic Boost Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, 5, 3, 0, max98088_micboost_tlv), + SOC_ENUM("Digital Sidetone Src", max9867_sidetone), + SOC_SINGLE("Sidetone Volume", MAX9867_DACGAIN, 0, 31, 1), + SOC_SINGLE("DAC Volume", MAX9867_DACLEVEL, 4, 3, 0), + SOC_SINGLE("DAC Attenuation", MAX9867_DACLEVEL, 0, 15, 1), + SOC_SINGLE_TLV("ADC Left Volume", MAX9867_ADCLEVEL, + 4, 15, 1, max9860_adc_left_tlv), + SOC_SINGLE_TLV("ADC Right Volume", MAX9867_ADCLEVEL, + 0, 15, 1, max9860_adc_right_tlv), + SOC_ENUM("Speaker Mode", max9867_spkmode), + SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), + SOC_SINGLE("ZCD Switch", MAX9867_MODECONFIG, 5, 1, 0), + SOC_ENUM("DSP Filter", max9867_filter), +}; + +static const char *const max9867_mux[] = {"None", "Mic", "Line", "Mic_Line"}; + +static SOC_ENUM_SINGLE_DECL(max9867_mux_enum, + MAX9867_INPUTCONFIG, MAX9867_INPUT_SHIFT, + max9867_mux); + +static const struct snd_kcontrol_new max9867_dapm_mux_controls = + SOC_DAPM_ENUM("Route", max9867_mux_enum); + +static const struct snd_kcontrol_new max9867_left_dapm_control = + SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 6, 1, 0); +static const struct snd_kcontrol_new max9867_right_dapm_control = + SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 5, 1, 0); +static const struct snd_kcontrol_new max9867_line_dapm_control = + SOC_DAPM_SINGLE("Switch", MAX9867_LEFTLINELVL, 6, 1, 1); + +static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("Left DAC", NULL, MAX9867_PWRMAN, 3, 0), + SND_SOC_DAPM_DAC("Right DAC", NULL, MAX9867_PWRMAN, 2, 0), + SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("HPOUT"), + + SND_SOC_DAPM_AIF_IN("DAI_IN", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("Left ADC", "HiFi Capture", MAX9867_PWRMAN, 1, 0), + SND_SOC_DAPM_ADC("Right ADC", "HiFi Capture", MAX9867_PWRMAN, 0, 0), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, + &max9867_dapm_mux_controls), + + SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("Left Line", MAX9867_LEFTLINELVL, 6, 1, + &max9867_left_dapm_control), + SND_SOC_DAPM_SWITCH("Right Line", MAX9867_RIGTHLINELVL, 6, 1, + &max9867_right_dapm_control), + SND_SOC_DAPM_SWITCH("Line Mixer", SND_SOC_NOPM, 0, 0, + &max9867_line_dapm_control), + SND_SOC_DAPM_INPUT("LINE_IN"), +}; + +static const struct snd_soc_dapm_route max9867_audio_map[] = { + {"Left DAC", NULL, "DAI_OUT"}, + {"Right DAC", NULL, "DAI_OUT"}, + {"Output Mixer", NULL, "Left DAC"}, + {"Output Mixer", NULL, "Right DAC"}, + {"HPOUT", NULL, "Output Mixer"}, + + {"Left ADC", NULL, "DAI_IN"}, + {"Right ADC", NULL, "DAI_IN"}, + {"Input Mixer", NULL, "Left ADC"}, + {"Input Mixer", NULL, "Right ADC"}, + {"Input Mux", "Line", "Input Mixer"}, + {"Input Mux", "Mic", "Input Mixer"}, + {"Input Mux", "Mic_Line", "Input Mixer"}, + {"Right Line", "Switch", "Input Mux"}, + {"Left Line", "Switch", "Input Mux"}, + {"LINE_IN", NULL, "Left Line"}, + {"LINE_IN", NULL, "Right Line"}, +}; + +enum rates { + pcm_rate_8, pcm_rate_16, pcm_rate_24, + pcm_rate_32, pcm_rate_44, + pcm_rate_48, max_pcm_rate, +}; + +struct ni_div_rates { + u32 mclk; + u16 ni[max_pcm_rate]; +} ni_div[] = { + {11289600, {0x116A, 0x22D4, 0x343F, 0x45A9, 0x6000, 0x687D} }, + {12000000, {0x1062, 0x20C5, 0x3127, 0x4189, 0x5A51, 0x624E} }, + {12288000, {0x1000, 0x2000, 0x3000, 0x4000, 0x5833, 0x6000} }, + {13000000, {0x0F20, 0x1E3F, 0x2D5F, 0x3C7F, 0x535F, 0x5ABE} }, + {19200000, {0x0A3D, 0x147B, 0x1EB8, 0x28F6, 0x3873, 0x3D71} }, + {24000000, {0x1062, 0x20C5, 0x1893, 0x4189, 0x5A51, 0x624E} }, + {26000000, {0x0F20, 0x1E3F, 0x16AF, 0x3C7F, 0x535F, 0x5ABE} }, + {27000000, {0x0E90, 0x1D21, 0x15D8, 0x3A41, 0x5048, 0x5762} }, +}; + +static inline int get_ni_value(int mclk, int rate) +{ + int i, ret = 0; + + /* find the closest rate index*/ + for (i = 0; i < ARRAY_SIZE(ni_div); i++) { + if (ni_div[i].mclk >= mclk) + break; + } + if (i == ARRAY_SIZE(ni_div)) + return -EINVAL; + + switch (rate) { + case 8000: + return ni_div[i].ni[pcm_rate_8]; + case 16000: + return ni_div[i].ni[pcm_rate_16]; + case 32000: + return ni_div[i].ni[pcm_rate_32]; + case 44100: + return ni_div[i].ni[pcm_rate_44]; + case 48000: + return ni_div[i].ni[pcm_rate_48]; + default: + pr_err("%s wrong rate %d\n", __func__, rate); + ret = -EINVAL; + } + return ret; +} + +static int max9867_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + unsigned int ni_h, ni_l; + int value; + + value = get_ni_value(max9867->sysclk, params_rate(params)); + if (value < 0) + return value; + + ni_h = (0xFF00 & value) >> 8; + ni_l = 0x00FF & value; + /* set up the ni value */ + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, + MAX9867_NI_HIGH_MASK, ni_h); + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, + MAX9867_NI_LOW_MASK, ni_l); + if (!max9867->master) { + /* + * digital pll locks on to any externally supplied LRCLK signal + * and also enable rapid lock mode. + */ + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, + MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, + MAX9867_PLL, MAX9867_PLL); + } else { + unsigned long int bclk_rate, pclk_bclk_ratio; + int bclk_value; + + bclk_rate = params_rate(params) * 2 * params_width(params); + pclk_bclk_ratio = max9867->pclk/bclk_rate; + switch (params_width(params)) { + case 8: + case 16: + switch (pclk_bclk_ratio) { + case 2: + bclk_value = MAX9867_IFC1B_PCLK_2; + break; + case 4: + bclk_value = MAX9867_IFC1B_PCLK_4; + break; + case 8: + bclk_value = MAX9867_IFC1B_PCLK_8; + break; + case 16: + bclk_value = MAX9867_IFC1B_PCLK_16; + break; + default: + dev_err(codec->dev, + "unsupported sampling rate\n"); + return -EINVAL; + } + break; + case 24: + bclk_value = MAX9867_IFC1B_24BIT; + break; + case 32: + bclk_value = MAX9867_IFC1B_32BIT; + break; + default: + dev_err(codec->dev, "unsupported sampling rate\n"); + return -EINVAL; + } + regmap_update_bits(max9867->regmap, MAX9867_IFC1B, + MAX9867_IFC1B_BCLK_MASK, bclk_value); + } + return 0; +} + +static int max9867_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + + regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK); + return 0; +} + +static int max9867_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + + if (mute) + regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, + MAX9867_DAC_MUTE_MASK, MAX9867_DAC_MUTE_MASK); + else + regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, + MAX9867_DAC_MUTE_MASK, 0); + return 0; +} + +static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + int value = 0; + + /* Set the prescaler based on the master clock frequency*/ + if (freq >= 10000000 && freq <= 20000000) { + value |= MAX9867_PSCLK_10_20; + max9867->pclk = freq; + } else if (freq >= 20000000 && freq <= 40000000) { + value |= MAX9867_PSCLK_20_40; + max9867->pclk = freq/2; + } else if (freq >= 40000000 && freq <= 60000000) { + value |= MAX9867_PSCLK_40_60; + max9867->pclk = freq/4; + } else { + pr_err("bad clock frequency %d", freq); + return -EINVAL; + } + value = value << MAX9867_PSCLK_SHIFT; + max9867->sysclk = freq; + /* exact integer mode is not supported */ + value &= ~MAX9867_FREQ_MASK; + regmap_update_bits(max9867->regmap, MAX9867_SYSCLK, + MAX9867_PSCLK_MASK, value); + return 0; +} + +static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + u8 iface1A = 0, iface1B = 0; + int ret; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + max9867->master = 1; + iface1A |= MAX9867_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + max9867->master = 0; + iface1A &= ~MAX9867_MASTER; + break; + default: + return -EINVAL; + } + + /* for i2s compatible mode */ + iface1A |= MAX9867_I2S_DLY; + /* SDOUT goes to hiz state after all data is transferred */ + iface1A |= MAX9867_SDOUT_HIZ; + + /* Clock inversion bits, BCI and WCI */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE; + break; + case SND_SOC_DAIFMT_IB_NF: + iface1A |= MAX9867_BCI_MODE; + break; + case SND_SOC_DAIFMT_NB_IF: + iface1A |= MAX9867_WCI_MODE; + break; + default: + return -EINVAL; + } + + ret = regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); + ret = regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B); + return 0; +} + +static struct snd_soc_dai_ops max9867_dai_ops = { + .set_fmt = max9867_dai_set_fmt, + .set_sysclk = max9867_set_dai_sysclk, + .prepare = max9867_prepare, + .digital_mute = max9867_mute, + .hw_params = max9867_dai_hw_params, +}; + +#define MAX9867_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define MAX9867_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +static struct snd_soc_dai_driver max9867_dai[] = { + { + .name = "max9867-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX9867_RATES, + .formats = MAX9867_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX9867_RATES, + .formats = MAX9867_FORMATS, + }, + .ops = &max9867_dai_ops, + } +}; + +#ifdef CONFIG_PM_SLEEP +static int max9867_suspend(struct device *dev) +{ + struct max9867_priv *max9867 = dev_get_drvdata(dev); + + /* Drop down to power saving mode when system is suspended */ + regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_SHTDOWN_MASK, ~MAX9867_SHTDOWN_MASK); + return 0; +} + +static int max9867_resume(struct device *dev) +{ + struct max9867_priv *max9867 = dev_get_drvdata(dev); + + regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK); + return 0; +} +#endif + +static int max9867_probe(struct snd_soc_codec *codec) +{ + struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "max98090_probe\n"); + max9867->codec = codec; + return 0; +} + +static struct snd_soc_codec_driver max9867_codec = { + .probe = max9867_probe, + .controls = max9867_snd_controls, + .num_controls = ARRAY_SIZE(max9867_snd_controls), + .dapm_routes = max9867_audio_map, + .num_dapm_routes = ARRAY_SIZE(max9867_audio_map), + .dapm_widgets = max9867_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets), +}; + +static bool max9867_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9867_STATUS: + case MAX9867_JACKSTATUS: + case MAX9867_AUXHIGH: + case MAX9867_AUXLOW: + return true; + default: + return false; + } +} + +static const struct reg_default max9867_reg[] = { + { 0x04, 0x00 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x00 }, + { 0x0A, 0x00 }, + { 0x0B, 0x00 }, + { 0x0C, 0x00 }, + { 0x0D, 0x00 }, + { 0x0E, 0x00 }, + { 0x0F, 0x00 }, + { 0x10, 0x00 }, + { 0x11, 0x00 }, + { 0x12, 0x00 }, + { 0x13, 0x00 }, + { 0x14, 0x00 }, + { 0x15, 0x00 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, +}; + +static const struct regmap_config max9867_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX9867_REVISION, + .reg_defaults = max9867_reg, + .num_reg_defaults = ARRAY_SIZE(max9867_reg), + .volatile_reg = max9867_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max9867_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max9867_priv *max9867; + int ret = 0, reg; + + max9867 = devm_kzalloc(&i2c->dev, + sizeof(*max9867), GFP_KERNEL); + if (!max9867) + return -ENOMEM; + + i2c_set_clientdata(i2c, max9867); + max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap); + if (IS_ERR(max9867->regmap)) { + ret = PTR_ERR(max9867->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + ret = regmap_read(max9867->regmap, + MAX9867_REVISION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read: %d\n", ret); + return ret; + } + dev_info(&i2c->dev, "device revision: %x\n", reg); + ret = snd_soc_register_codec(&i2c->dev, &max9867_codec, + max9867_dai, ARRAY_SIZE(max9867_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + return ret; + } + return ret; +} + +static int max9867_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id max9867_i2c_id[] = { + { "max9867", 0 }, + { } +}; + +static const struct of_device_id max9867_of_match[] = { + { .compatible = "maxim,max9867", }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, max9867_i2c_id); + +static const struct dev_pm_ops max9867_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume) +}; + +static struct i2c_driver max9867_i2c_driver = { + .driver = { + .name = "max9867", + .of_match_table = of_match_ptr(max9867_of_match), + .pm = &max9867_pm_ops, + }, + .probe = max9867_i2c_probe, + .remove = max9867_i2c_remove, + .id_table = max9867_i2c_id, +}; + +module_i2c_driver(max9867_i2c_driver); + +MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>"); +MODULE_DESCRIPTION("ALSA SoC MAX9867 driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h new file mode 100755 index 0000000..65590b4 --- /dev/null +++ b/sound/soc/codecs/max9867.h @@ -0,0 +1,83 @@ +/* + * max9867.h -- MAX9867 ALSA SoC Audio driver + * + * Copyright 2013-2015 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX9867_H +#define _MAX9867_H + +/* MAX9867 register space */ + +#define MAX9867_STATUS 0x00 +#define MAX9867_JACKSTATUS 0x01 +#define MAX9867_AUXHIGH 0x02 +#define MAX9867_AUXLOW 0x03 +#define MAX9867_INTEN 0x04 +#define MAX9867_SYSCLK 0x05 +#define MAX9867_FREQ_MASK 0xF +#define MAX9867_PSCLK_SHIFT 0x4 +#define MAX9867_PSCLK_WIDTH 0x2 +#define MAX9867_PSCLK_MASK (0x03<<MAX9867_PSCLK_SHIFT) +#define MAX9867_PSCLK_10_20 0x1 +#define MAX9867_PSCLK_20_40 0x2 +#define MAX9867_PSCLK_40_60 0x3 +#define MAX9867_AUDIOCLKHIGH 0x06 +#define MAX9867_NI_HIGH_WIDTH 0x7 +#define MAX9867_NI_HIGH_MASK 0x7F +#define MAX9867_NI_LOW_MASK 0x7F +#define MAX9867_NI_LOW_SHIFT 0x1 +#define MAX9867_PLL (1<<7) +#define MAX9867_AUDIOCLKLOW 0x07 +#define MAX9867_RAPID_LOCK 0x01 +#define MAX9867_IFC1A 0x08 +#define MAX9867_MASTER (1<<7) +#define MAX9867_I2S_DLY (1<<4) +#define MAX9867_SDOUT_HIZ (1<<3) +#define MAX9867_TDM_MODE (1<<2) +#define MAX9867_WCI_MODE (1<<6) +#define MAX9867_BCI_MODE (1<<5) +#define MAX9867_IFC1B 0x09 +#define MAX9867_IFC1B_BCLK_MASK 7 +#define MAX9867_IFC1B_32BIT 0x01 +#define MAX9867_IFC1B_24BIT 0x02 +#define MAX9867_IFC1B_PCLK_2 4 +#define MAX9867_IFC1B_PCLK_4 5 +#define MAX9867_IFC1B_PCLK_8 6 +#define MAX9867_IFC1B_PCLK_16 7 +#define MAX9867_CODECFLTR 0x0a +#define MAX9867_DACGAIN 0x0b +#define MAX9867_DACLEVEL 0x0c +#define MAX9867_DAC_MUTE_SHIFT 0x6 +#define MAX9867_DAC_MUTE_WIDTH 0x1 +#define MAX9867_DAC_MUTE_MASK (0x1<<MAX9867_DAC_MUTE_SHIFT) +#define MAX9867_ADCLEVEL 0x0d +#define MAX9867_LEFTLINELVL 0x0e +#define MAX9867_RIGTHLINELVL 0x0f +#define MAX9867_LEFTVOL 0x10 +#define MAX9867_RIGHTVOL 0x11 +#define MAX9867_LEFTMICGAIN 0x12 +#define MAX9867_RIGHTMICGAIN 0x13 +#define MAX9867_INPUTCONFIG 0x14 +#define MAX9867_INPUT_SHIFT 0x6 +#define MAX9867_MICCONFIG 0x15 +#define MAX9867_MODECONFIG 0x16 +#define MAX9867_PWRMAN 0x17 +#define MAX9867_SHTDOWN_MASK (1<<7) +#define MAX9867_REVISION 0xff + +#define MAX9867_CACHEREGNUM 10 + +/* codec private data */ +struct max9867_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + unsigned int sysclk; + unsigned int pclk; + unsigned int master; +}; +#endif diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c new file mode 100644 index 0000000..8d14ada --- /dev/null +++ b/sound/soc/codecs/max98926.c @@ -0,0 +1,606 @@ +/* + * max98926.c -- ALSA SoC MAX98926 driver + * Copyright 2013-15 Maxim Integrated Products + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "max98926.h" + +static const char * const max98926_boost_voltage_txt[] = { + "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V", + "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V" +}; + +static const char * const max98926_boost_current_txt[] = { + "0.6", "0.8", "1.0", "1.2", "1.4", "1.6", "1.8", "2.0", + "2.2", "2.4", "2.6", "2.8", "3.2", "3.6", "4.0", "4.4" +}; + +static const char *const max98926_dai_txt[] = { + "Left", "Right", "LeftRight", "LeftRightDiv2", +}; + +static const char *const max98926_pdm_ch_text[] = { + "Current", "Voltage", +}; + +static const char *const max98926_hpf_cutoff_txt[] = { + "Disable", "DC Block", "100Hz", + "200Hz", "400Hz", "800Hz", +}; + +static const struct reg_default max98926_reg[] = { + { 0x0B, 0x00 }, /* IRQ Enable0 */ + { 0x0C, 0x00 }, /* IRQ Enable1 */ + { 0x0D, 0x00 }, /* IRQ Enable2 */ + { 0x0E, 0x00 }, /* IRQ Clear0 */ + { 0x0F, 0x00 }, /* IRQ Clear1 */ + { 0x10, 0x00 }, /* IRQ Clear2 */ + { 0x11, 0xC0 }, /* Map0 */ + { 0x12, 0x00 }, /* Map1 */ + { 0x13, 0x00 }, /* Map2 */ + { 0x14, 0xF0 }, /* Map3 */ + { 0x15, 0x00 }, /* Map4 */ + { 0x16, 0xAB }, /* Map5 */ + { 0x17, 0x89 }, /* Map6 */ + { 0x18, 0x00 }, /* Map7 */ + { 0x19, 0x00 }, /* Map8 */ + { 0x1A, 0x04 }, /* DAI Clock Mode 1 */ + { 0x1B, 0x00 }, /* DAI Clock Mode 2 */ + { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */ + { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */ + { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */ + { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */ + { 0x20, 0x50 }, /* Format */ + { 0x21, 0x00 }, /* TDM Slot Select */ + { 0x22, 0x00 }, /* DOUT Configuration VMON */ + { 0x23, 0x00 }, /* DOUT Configuration IMON */ + { 0x24, 0x00 }, /* DOUT Configuration VBAT */ + { 0x25, 0x00 }, /* DOUT Configuration VBST */ + { 0x26, 0x00 }, /* DOUT Configuration FLAG */ + { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */ + { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */ + { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */ + { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */ + { 0x2B, 0x02 }, /* DOUT Drive Strength */ + { 0x2C, 0x90 }, /* Filters */ + { 0x2D, 0x00 }, /* Gain */ + { 0x2E, 0x02 }, /* Gain Ramping */ + { 0x2F, 0x00 }, /* Speaker Amplifier */ + { 0x30, 0x0A }, /* Threshold */ + { 0x31, 0x00 }, /* ALC Attack */ + { 0x32, 0x80 }, /* ALC Atten and Release */ + { 0x33, 0x00 }, /* ALC Infinite Hold Release */ + { 0x34, 0x92 }, /* ALC Configuration */ + { 0x35, 0x01 }, /* Boost Converter */ + { 0x36, 0x00 }, /* Block Enable */ + { 0x37, 0x00 }, /* Configuration */ + { 0x38, 0x00 }, /* Global Enable */ + { 0x3A, 0x00 }, /* Boost Limiter */ +}; + +static const struct soc_enum max98926_voltage_enum[] = { + SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 0, + ARRAY_SIZE(max98926_pdm_ch_text), + max98926_pdm_ch_text), +}; + +static const struct snd_kcontrol_new max98926_voltage_control = + SOC_DAPM_ENUM("Route", max98926_voltage_enum); + +static const struct soc_enum max98926_current_enum[] = { + SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_SOURCE_1_SHIFT, + ARRAY_SIZE(max98926_pdm_ch_text), + max98926_pdm_ch_text), +}; + +static const struct snd_kcontrol_new max98926_current_control = + SOC_DAPM_ENUM("Route", max98926_current_enum); + +static const struct snd_kcontrol_new max98926_mixer_controls[] = { + SOC_DAPM_SINGLE("PCM Single Switch", MAX98926_SPK_AMP, + MAX98926_INSELECT_MODE_SHIFT, 0, 0), + SOC_DAPM_SINGLE("PDM Single Switch", MAX98926_SPK_AMP, + MAX98926_INSELECT_MODE_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new max98926_dai_controls[] = { + SOC_DAPM_SINGLE("Left", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 0, 0), + SOC_DAPM_SINGLE("Right", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LeftRight", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 2, 0), + SOC_DAPM_SINGLE("(Left+Right)/2 Switch", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 3, 0), +}; + +static const struct snd_soc_dapm_widget max98926_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("Amp Enable", NULL, MAX98926_BLOCK_ENABLE, + MAX98926_SPK_EN_SHIFT, 0), + SND_SOC_DAPM_SUPPLY("Global Enable", MAX98926_GLOBAL_ENABLE, + MAX98926_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VI Enable", MAX98926_BLOCK_ENABLE, + MAX98926_ADC_IMON_EN_WIDTH | + MAX98926_ADC_VMON_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("BST Enable", MAX98926_BLOCK_ENABLE, + MAX98926_BST_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_MIXER("PCM Sel", MAX98926_SPK_AMP, + MAX98926_INSELECT_MODE_SHIFT, 0, + &max98926_mixer_controls[0], + ARRAY_SIZE(max98926_mixer_controls)), + SND_SOC_DAPM_MIXER("DAI Sel", + MAX98926_GAIN, MAX98926_DAC_IN_SEL_SHIFT, 0, + &max98926_dai_controls[0], + ARRAY_SIZE(max98926_dai_controls)), + SND_SOC_DAPM_MUX("PDM CH1 Source", + MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_CURRENT_SHIFT, + 0, &max98926_current_control), + SND_SOC_DAPM_MUX("PDM CH0 Source", + MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_VOLTAGE_SHIFT, + 0, &max98926_voltage_control), +}; + +static const struct snd_soc_dapm_route max98926_audio_map[] = { + {"VI Enable", NULL, "DAI_OUT"}, + {"DAI Sel", "Left", "VI Enable"}, + {"DAI Sel", "Right", "VI Enable"}, + {"DAI Sel", "LeftRight", "VI Enable"}, + {"DAI Sel", "LeftRightDiv2", "VI Enable"}, + {"PCM Sel", "PCM", "DAI Sel"}, + + {"PDM CH1 Source", "Current", "DAI_OUT"}, + {"PDM CH1 Source", "Voltage", "DAI_OUT"}, + {"PDM CH0 Source", "Current", "DAI_OUT"}, + {"PDM CH0 Source", "Voltage", "DAI_OUT"}, + {"PCM Sel", "Analog", "PDM CH1 Source"}, + {"PCM Sel", "Analog", "PDM CH0 Source"}, + {"Amp Enable", NULL, "PCM Sel"}, + + {"BST Enable", NULL, "Amp Enable"}, + {"BE_OUT", NULL, "BST Enable"}, +}; + +static bool max98926_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98926_VBAT_DATA: + case MAX98926_VBST_DATA: + case MAX98926_LIVE_STATUS0: + case MAX98926_LIVE_STATUS1: + case MAX98926_LIVE_STATUS2: + case MAX98926_STATE0: + case MAX98926_STATE1: + case MAX98926_STATE2: + case MAX98926_FLAG0: + case MAX98926_FLAG1: + case MAX98926_FLAG2: + case MAX98926_VERSION: + return true; + default: + return false; + } +} + +static bool max98926_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98926_IRQ_CLEAR0: + case MAX98926_IRQ_CLEAR1: + case MAX98926_IRQ_CLEAR2: + case MAX98926_ALC_HOLD_RLS: + return false; + default: + return true; + } +}; + +DECLARE_TLV_DB_SCALE(max98926_spk_tlv, -600, 100, 0); +DECLARE_TLV_DB_RANGE(max98926_current_tlv, + 0, 11, TLV_DB_SCALE_ITEM(20, 20, 0), + 12, 15, TLV_DB_SCALE_ITEM(320, 40, 0), +); + +static SOC_ENUM_SINGLE_DECL(max98926_dac_hpf_cutoff, + MAX98926_FILTERS, MAX98926_DAC_HPF_SHIFT, + max98926_hpf_cutoff_txt); + +static SOC_ENUM_SINGLE_DECL(max98926_boost_voltage, + MAX98926_CONFIGURATION, MAX98926_BST_VOUT_SHIFT, + max98926_boost_voltage_txt); + +static const struct snd_kcontrol_new max98926_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", MAX98926_GAIN, + MAX98926_SPK_GAIN_SHIFT, + (1<<MAX98926_SPK_GAIN_WIDTH)-1, 0, + max98926_spk_tlv), + SOC_SINGLE("Ramp Switch", MAX98926_GAIN_RAMPING, + MAX98926_SPK_RMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ZCD Switch", MAX98926_GAIN_RAMPING, + MAX98926_SPK_ZCD_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Switch", MAX98926_THRESHOLD, + MAX98926_ALC_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Threshold", MAX98926_THRESHOLD, + MAX98926_ALC_TH_SHIFT, + (1<<MAX98926_ALC_TH_WIDTH)-1, 0), + SOC_ENUM("Boost Output Voltage", max98926_boost_voltage), + SOC_SINGLE_TLV("Boost Current Limit", MAX98926_BOOST_LIMITER, + MAX98926_BST_ILIM_SHIFT, + (1<<MAX98926_BST_ILIM_SHIFT)-1, 0, + max98926_current_tlv), + SOC_ENUM("DAC HPF Cutoff", max98926_dac_hpf_cutoff), + SOC_DOUBLE("PDM Channel One", MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_CHANNEL_1_SHIFT, + MAX98926_PDM_CHANNEL_1_HIZ, 1, 0), + SOC_DOUBLE("PDM Channel Zero", MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_CHANNEL_0_SHIFT, + MAX98926_PDM_CHANNEL_0_HIZ, 1, 0), +}; + +static const struct { + int rate; + int sr; +} rate_table[] = { + { + .rate = 8000, + .sr = 0, + }, + { + .rate = 11025, + .sr = 1, + }, + { + .rate = 12000, + .sr = 2, + }, + { + .rate = 16000, + .sr = 3, + }, + { + .rate = 22050, + .sr = 4, + }, + { + .rate = 24000, + .sr = 5, + }, + { + .rate = 32000, + .sr = 6, + }, + { + .rate = 44100, + .sr = 7, + }, + { + .rate = 48000, + .sr = 8, + }, +}; + +static void max98926_set_sense_data(struct max98926_priv *max98926) +{ + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_VMON, + MAX98926_DAI_VMON_EN_MASK, + MAX98926_DAI_VMON_EN_MASK); + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_IMON, + MAX98926_DAI_IMON_EN_MASK, + MAX98926_DAI_IMON_EN_MASK); + + if (!max98926->interleave_mode) { + /* set VMON slots */ + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_VMON, + MAX98926_DAI_VMON_SLOT_MASK, + max98926->v_slot); + /* set IMON slots */ + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_IMON, + MAX98926_DAI_IMON_SLOT_MASK, + max98926->i_slot); + } else { + /* enable interleave mode */ + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_INTERLEAVE_MASK, + MAX98926_DAI_INTERLEAVE_MASK); + /* set interleave slots */ + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_VBAT, + MAX98926_DAI_INTERLEAVE_SLOT_MASK, + max98926->v_slot); + } +} + +static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec); + unsigned int invert = 0; + + dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + max98926_set_sense_data(max98926); + break; + default: + dev_err(codec->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + invert = MAX98926_DAI_WCI_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98926_DAI_BCI_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK; + break; + default: + dev_err(codec->dev, "DAI invert mode unsupported"); + return -EINVAL; + } + + regmap_write(max98926->regmap, + MAX98926_FORMAT, MAX98926_DAI_DLY_MASK); + regmap_update_bits(max98926->regmap, MAX98926_FORMAT, + MAX98926_DAI_BCI_MASK, invert); + return 0; +} + +static int max98926_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int dai_sr = -EINVAL; + int rate = params_rate(params), i; + struct snd_soc_codec *codec = dai->codec; + struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec); + int blr_clk_ratio; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_CHANSZ_MASK, + MAX98926_DAI_CHANSZ_16); + max98926->ch_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_CHANSZ_MASK, + MAX98926_DAI_CHANSZ_24); + max98926->ch_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_CHANSZ_MASK, + MAX98926_DAI_CHANSZ_32); + max98926->ch_size = 32; + break; + default: + dev_dbg(codec->dev, "format unsupported %d", + params_format(params)); + return -EINVAL; + } + + /* BCLK/LRCLK ratio calculation */ + blr_clk_ratio = params_channels(params) * max98926->ch_size; + + switch (blr_clk_ratio) { + case 32: + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_BSEL_MASK, + MAX98926_DAI_BSEL_32); + break; + case 48: + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_BSEL_MASK, + MAX98926_DAI_BSEL_48); + break; + case 64: + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_BSEL_MASK, + MAX98926_DAI_BSEL_64); + break; + default: + return -EINVAL; + } + + /* find the closest rate */ + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i].rate >= rate) { + dai_sr = rate_table[i].sr; + break; + } + } + if (dai_sr < 0) + return -EINVAL; + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_SR_MASK, dai_sr << MAX98926_DAI_SR_SHIFT); + return 0; +} + +#define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops max98926_dai_ops = { + .set_fmt = max98926_dai_set_fmt, + .hw_params = max98926_dai_hw_params, +}; + +static struct snd_soc_dai_driver max98926_dai[] = { +{ + .name = "max98926-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98926_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98926_FORMATS, + }, + .ops = &max98926_dai_ops, +} +}; + +static int max98926_probe(struct snd_soc_codec *codec) +{ + struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec); + + max98926->codec = codec; + codec->control_data = max98926->regmap; + /* Hi-Z all the slots */ + regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_max98926 = { + .probe = max98926_probe, + .controls = max98926_snd_controls, + .num_controls = ARRAY_SIZE(max98926_snd_controls), + .dapm_routes = max98926_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98926_audio_map), + .dapm_widgets = max98926_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets), +}; + +static const struct regmap_config max98926_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX98926_VERSION, + .reg_defaults = max98926_reg, + .num_reg_defaults = ARRAY_SIZE(max98926_reg), + .volatile_reg = max98926_volatile_register, + .readable_reg = max98926_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98926_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret, reg; + u32 value; + struct max98926_priv *max98926; + + max98926 = devm_kzalloc(&i2c->dev, + sizeof(*max98926), GFP_KERNEL); + if (!max98926) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98926); + max98926->regmap = devm_regmap_init_i2c(i2c, &max98926_regmap); + if (IS_ERR(max98926->regmap)) { + ret = PTR_ERR(max98926->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + goto err_out; + } + if (of_property_read_bool(i2c->dev.of_node, "interleave-mode")) + max98926->interleave_mode = true; + + if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { + if (value > MAX98926_DAI_VMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "vmon slot number is wrong:\n"); + return -EINVAL; + } + max98926->v_slot = value; + } + if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) { + if (value > MAX98926_DAI_IMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "imon slot number is wrong:\n"); + return -EINVAL; + } + max98926->i_slot = value; + } + ret = regmap_read(max98926->regmap, + MAX98926_VERSION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read: %x\n", reg); + return ret; + } + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98926, + max98926_dai, ARRAY_SIZE(max98926_dai)); + if (ret < 0) + dev_err(&i2c->dev, + "Failed to register codec: %d\n", ret); + dev_info(&i2c->dev, "device version: %x\n", reg); +err_out: + return ret; +} + +static int max98926_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id max98926_i2c_id[] = { + { "max98926", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98926_i2c_id); + +static const struct of_device_id max98926_of_match[] = { + { .compatible = "maxim,max98926", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98926_of_match); + +static struct i2c_driver max98926_i2c_driver = { + .driver = { + .name = "max98926", + .of_match_table = of_match_ptr(max98926_of_match), + .pm = NULL, + }, + .probe = max98926_i2c_probe, + .remove = max98926_i2c_remove, + .id_table = max98926_i2c_id, +}; + +module_i2c_driver(max98926_i2c_driver) +MODULE_DESCRIPTION("ALSA SoC MAX98926 driver"); +MODULE_AUTHOR("Anish kumar <anish.kumar@maximintegrated.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98926.h b/sound/soc/codecs/max98926.h new file mode 100644 index 0000000..9d7ab6d --- /dev/null +++ b/sound/soc/codecs/max98926.h @@ -0,0 +1,848 @@ +/* + * max98926.h -- MAX98926 ALSA SoC Audio driver + * Copyright 2013-2015 Maxim Integrated Products + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX98926_H +#define _MAX98926_H + +#define MAX98926_CHIP_VERSION 0x40 +#define MAX98926_CHIP_VERSION1 0x50 + +#define MAX98926_VBAT_DATA 0x00 +#define MAX98926_VBST_DATA 0x01 +#define MAX98926_LIVE_STATUS0 0x02 +#define MAX98926_LIVE_STATUS1 0x03 +#define MAX98926_LIVE_STATUS2 0x04 +#define MAX98926_STATE0 0x05 +#define MAX98926_STATE1 0x06 +#define MAX98926_STATE2 0x07 +#define MAX98926_FLAG0 0x08 +#define MAX98926_FLAG1 0x09 +#define MAX98926_FLAG2 0x0A +#define MAX98926_IRQ_ENABLE0 0x0B +#define MAX98926_IRQ_ENABLE1 0x0C +#define MAX98926_IRQ_ENABLE2 0x0D +#define MAX98926_IRQ_CLEAR0 0x0E +#define MAX98926_IRQ_CLEAR1 0x0F +#define MAX98926_IRQ_CLEAR2 0x10 +#define MAX98926_MAP0 0x11 +#define MAX98926_MAP1 0x12 +#define MAX98926_MAP2 0x13 +#define MAX98926_MAP3 0x14 +#define MAX98926_MAP4 0x15 +#define MAX98926_MAP5 0x16 +#define MAX98926_MAP6 0x17 +#define MAX98926_MAP7 0x18 +#define MAX98926_MAP8 0x19 +#define MAX98926_DAI_CLK_MODE1 0x1A +#define MAX98926_DAI_CLK_MODE2 0x1B +#define MAX98926_DAI_CLK_DIV_M_MSBS 0x1C +#define MAX98926_DAI_CLK_DIV_M_LSBS 0x1D +#define MAX98926_DAI_CLK_DIV_N_MSBS 0x1E +#define MAX98926_DAI_CLK_DIV_N_LSBS 0x1F +#define MAX98926_FORMAT 0x20 +#define MAX98926_TDM_SLOT_SELECT 0x21 +#define MAX98926_DOUT_CFG_VMON 0x22 +#define MAX98926_DOUT_CFG_IMON 0x23 +#define MAX98926_DOUT_CFG_VBAT 0x24 +#define MAX98926_DOUT_CFG_VBST 0x25 +#define MAX98926_DOUT_CFG_FLAG 0x26 +#define MAX98926_DOUT_HIZ_CFG1 0x27 +#define MAX98926_DOUT_HIZ_CFG2 0x28 +#define MAX98926_DOUT_HIZ_CFG3 0x29 +#define MAX98926_DOUT_HIZ_CFG4 0x2A +#define MAX98926_DOUT_DRV_STRENGTH 0x2B +#define MAX98926_FILTERS 0x2C +#define MAX98926_GAIN 0x2D +#define MAX98926_GAIN_RAMPING 0x2E +#define MAX98926_SPK_AMP 0x2F +#define MAX98926_THRESHOLD 0x30 +#define MAX98926_ALC_ATTACK 0x31 +#define MAX98926_ALC_ATTEN_RLS 0x32 +#define MAX98926_ALC_HOLD_RLS 0x33 +#define MAX98926_ALC_CONFIGURATION 0x34 +#define MAX98926_BOOST_CONVERTER 0x35 +#define MAX98926_BLOCK_ENABLE 0x36 +#define MAX98926_CONFIGURATION 0x37 +#define MAX98926_GLOBAL_ENABLE 0x38 +#define MAX98926_BOOST_LIMITER 0x3A +#define MAX98926_VERSION 0xFF + +#define MAX98926_REG_CNT (MAX98926_R03A_BOOST_LIMITER+1) + +#define MAX98926_PDM_CURRENT_MASK (1<<7) +#define MAX98926_PDM_CURRENT_SHIFT 7 +#define MAX98926_PDM_VOLTAGE_MASK (1<<3) +#define MAX98926_PDM_VOLTAGE_SHIFT 3 +#define MAX98926_PDM_CHANNEL_0_MASK (1<<2) +#define MAX98926_PDM_CHANNEL_0_SHIFT 2 +#define MAX98926_PDM_CHANNEL_1_MASK (1<<6) +#define MAX98926_PDM_CHANNEL_1_SHIFT 6 +#define MAX98926_PDM_CHANNEL_1_HIZ 5 +#define MAX98926_PDM_CHANNEL_0_HIZ 1 +#define MAX98926_PDM_SOURCE_0_SHIFT 0 +#define MAX98926_PDM_SOURCE_0_MASK (1<<0) +#define MAX98926_PDM_SOURCE_1_MASK (1<<4) +#define MAX98926_PDM_SOURCE_1_SHIFT 4 + +/* MAX98926 Register Bit Fields */ + +/* MAX98926_R002_LIVE_STATUS0 */ +#define MAX98926_THERMWARN_STATUS_MASK (1<<3) +#define MAX98926_THERMWARN_STATUS_SHIFT 3 +#define MAX98926_THERMWARN_STATUS_WIDTH 1 +#define MAX98926_THERMSHDN_STATUS_MASK (1<<1) +#define MAX98926_THERMSHDN_STATUS_SHIFT 1 +#define MAX98926_THERMSHDN_STATUS_WIDTH 1 + +/* MAX98926_R003_LIVE_STATUS1 */ +#define MAX98926_SPKCURNT_STATUS_MASK (1<<5) +#define MAX98926_SPKCURNT_STATUS_SHIFT 5 +#define MAX98926_SPKCURNT_STATUS_WIDTH 1 +#define MAX98926_WATCHFAIL_STATUS_MASK (1<<4) +#define MAX98926_WATCHFAIL_STATUS_SHIFT 4 +#define MAX98926_WATCHFAIL_STATUS_WIDTH 1 +#define MAX98926_ALCINFH_STATUS_MASK (1<<3) +#define MAX98926_ALCINFH_STATUS_SHIFT 3 +#define MAX98926_ALCINFH_STATUS_WIDTH 1 +#define MAX98926_ALCACT_STATUS_MASK (1<<2) +#define MAX98926_ALCACT_STATUS_SHIFT 2 +#define MAX98926_ALCACT_STATUS_WIDTH 1 +#define MAX98926_ALCMUT_STATUS_MASK (1<<1) +#define MAX98926_ALCMUT_STATUS_SHIFT 1 +#define MAX98926_ALCMUT_STATUS_WIDTH 1 +#define MAX98926_ACLP_STATUS_MASK (1<<0) +#define MAX98926_ACLP_STATUS_SHIFT 0 +#define MAX98926_ACLP_STATUS_WIDTH 1 + +/* MAX98926_R004_LIVE_STATUS2 */ +#define MAX98926_SLOTOVRN_STATUS_MASK (1<<6) +#define MAX98926_SLOTOVRN_STATUS_SHIFT 6 +#define MAX98926_SLOTOVRN_STATUS_WIDTH 1 +#define MAX98926_INVALSLOT_STATUS_MASK (1<<5) +#define MAX98926_INVALSLOT_STATUS_SHIFT 5 +#define MAX98926_INVALSLOT_STATUS_WIDTH 1 +#define MAX98926_SLOTCNFLT_STATUS_MASK (1<<4) +#define MAX98926_SLOTCNFLT_STATUS_SHIFT 4 +#define MAX98926_SLOTCNFLT_STATUS_WIDTH 1 +#define MAX98926_VBSTOVFL_STATUS_MASK (1<<3) +#define MAX98926_VBSTOVFL_STATUS_SHIFT 3 +#define MAX98926_VBSTOVFL_STATUS_WIDTH 1 +#define MAX98926_VBATOVFL_STATUS_MASK (1<<2) +#define MAX98926_VBATOVFL_STATUS_SHIFT 2 +#define MAX98926_VBATOVFL_STATUS_WIDTH 1 +#define MAX98926_IMONOVFL_STATUS_MASK (1<<1) +#define MAX98926_IMONOVFL_STATUS_SHIFT 1 +#define MAX98926_IMONOVFL_STATUS_WIDTH 1 +#define MAX98926_VMONOVFL_STATUS_MASK (1<<0) +#define MAX98926_VMONOVFL_STATUS_SHIFT 0 +#define MAX98926_VMONOVFL_STATUS_WIDTH 1 + +/* MAX98926_R005_STATE0 */ +#define MAX98926_THERMWARN_END_STATE_MASK (1<<3) +#define MAX98926_THERMWARN_END_STATE_SHIFT 3 +#define MAX98926_THERMWARN_END_STATE_WIDTH 1 +#define MAX98926_THERMWARN_BGN_STATE_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_STATE_SHIFT 1 +#define MAX98926_THERMWARN_BGN_STATE_WIDTH 1 +#define MAX98926_THERMSHDN_END_STATE_MASK (1<<1) +#define MAX98926_THERMSHDN_END_STATE_SHIFT 1 +#define MAX98926_THERMSHDN_END_STATE_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_STATE_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_STATE_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_STATE_WIDTH 1 + +/* MAX98926_R006_STATE1 */ +#define MAX98926_SPRCURNT_STATE_MASK (1<<5) +#define MAX98926_SPRCURNT_STATE_SHIFT 5 +#define MAX98926_SPRCURNT_STATE_WIDTH 1 +#define MAX98926_WATCHFAIL_STATE_MASK (1<<4) +#define MAX98926_WATCHFAIL_STATE_SHIFT 4 +#define MAX98926_WATCHFAIL_STATE_WIDTH 1 +#define MAX98926_ALCINFH_STATE_MASK (1<<3) +#define MAX98926_ALCINFH_STATE_SHIFT 3 +#define MAX98926_ALCINFH_STATE_WIDTH 1 +#define MAX98926_ALCACT_STATE_MASK (1<<2) +#define MAX98926_ALCACT_STATE_SHIFT 2 +#define MAX98926_ALCACT_STATE_WIDTH 1 +#define MAX98926_ALCMUT_STATE_MASK (1<<1) +#define MAX98926_ALCMUT_STATE_SHIFT 1 +#define MAX98926_ALCMUT_STATE_WIDTH 1 +#define MAX98926_ALCP_STATE_MASK (1<<0) +#define MAX98926_ALCP_STATE_SHIFT 0 +#define MAX98926_ALCP_STATE_WIDTH 1 + +/* MAX98926_R007_STATE2 */ +#define MAX98926_SLOTOVRN_STATE_MASK (1<<6) +#define MAX98926_SLOTOVRN_STATE_SHIFT 6 +#define MAX98926_SLOTOVRN_STATE_WIDTH 1 +#define MAX98926_INVALSLOT_STATE_MASK (1<<5) +#define MAX98926_INVALSLOT_STATE_SHIFT 5 +#define MAX98926_INVALSLOT_STATE_WIDTH 1 +#define MAX98926_SLOTCNFLT_STATE_MASK (1<<4) +#define MAX98926_SLOTCNFLT_STATE_SHIFT 4 +#define MAX98926_SLOTCNFLT_STATE_WIDTH 1 +#define MAX98926_VBSTOVFL_STATE_MASK (1<<3) +#define MAX98926_VBSTOVFL_STATE_SHIFT 3 +#define MAX98926_VBSTOVFL_STATE_WIDTH 1 +#define MAX98926_VBATOVFL_STATE_MASK (1<<2) +#define MAX98926_VBATOVFL_STATE_SHIFT 2 +#define MAX98926_VBATOVFL_STATE_WIDTH 1 +#define MAX98926_IMONOVFL_STATE_MASK (1<<1) +#define MAX98926_IMONOVFL_STATE_SHIFT 1 +#define MAX98926_IMONOVFL_STATE_WIDTH 1 +#define MAX98926_VMONOVFL_STATE_MASK (1<<0) +#define MAX98926_VMONOVFL_STATE_SHIFT 0 +#define MAX98926_VMONOVFL_STATE_WIDTH 1 + +/* MAX98926_R008_FLAG0 */ +#define MAX98926_THERMWARN_END_FLAG_MASK (1<<3) +#define MAX98926_THERMWARN_END_FLAG_SHIFT 3 +#define MAX98926_THERMWARN_END_FLAG_WIDTH 1 +#define MAX98926_THERMWARN_BGN_FLAG_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_FLAG_SHIFT 2 +#define MAX98926_THERMWARN_BGN_FLAG_WIDTH 1 +#define MAX98926_THERMSHDN_END_FLAG_MASK (1<<1) +#define MAX98926_THERMSHDN_END_FLAG_SHIFT 1 +#define MAX98926_THERMSHDN_END_FLAG_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_FLAG_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_FLAG_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_FLAG_WIDTH 1 + +/* MAX98926_R009_FLAG1 */ +#define MAX98926_SPKCURNT_FLAG_MASK (1<<5) +#define MAX98926_SPKCURNT_FLAG_SHIFT 5 +#define MAX98926_SPKCURNT_FLAG_WIDTH 1 +#define MAX98926_WATCHFAIL_FLAG_MASK (1<<4) +#define MAX98926_WATCHFAIL_FLAG_SHIFT 4 +#define MAX98926_WATCHFAIL_FLAG_WIDTH 1 +#define MAX98926_ALCINFH_FLAG_MASK (1<<3) +#define MAX98926_ALCINFH_FLAG_SHIFT 3 +#define MAX98926_ALCINFH_FLAG_WIDTH 1 +#define MAX98926_ALCACT_FLAG_MASK (1<<2) +#define MAX98926_ALCACT_FLAG_SHIFT 2 +#define MAX98926_ALCACT_FLAG_WIDTH 1 +#define MAX98926_ALCMUT_FLAG_MASK (1<<1) +#define MAX98926_ALCMUT_FLAG_SHIFT 1 +#define MAX98926_ALCMUT_FLAG_WIDTH 1 +#define MAX98926_ALCP_FLAG_MASK (1<<0) +#define MAX98926_ALCP_FLAG_SHIFT 0 +#define MAX98926_ALCP_FLAG_WIDTH 1 + +/* MAX98926_R00A_FLAG2 */ +#define MAX98926_SLOTOVRN_FLAG_MASK (1<<6) +#define MAX98926_SLOTOVRN_FLAG_SHIFT 6 +#define MAX98926_SLOTOVRN_FLAG_WIDTH 1 +#define MAX98926_INVALSLOT_FLAG_MASK (1<<5) +#define MAX98926_INVALSLOT_FLAG_SHIFT 5 +#define MAX98926_INVALSLOT_FLAG_WIDTH 1 +#define MAX98926_SLOTCNFLT_FLAG_MASK (1<<4) +#define MAX98926_SLOTCNFLT_FLAG_SHIFT 4 +#define MAX98926_SLOTCNFLT_FLAG_WIDTH 1 +#define MAX98926_VBSTOVFL_FLAG_MASK (1<<3) +#define MAX98926_VBSTOVFL_FLAG_SHIFT 3 +#define MAX98926_VBSTOVFL_FLAG_WIDTH 1 +#define MAX98926_VBATOVFL_FLAG_MASK (1<<2) +#define MAX98926_VBATOVFL_FLAG_SHIFT 2 +#define MAX98926_VBATOVFL_FLAG_WIDTH 1 +#define MAX98926_IMONOVFL_FLAG_MASK (1<<1) +#define MAX98926_IMONOVFL_FLAG_SHIFT 1 +#define MAX98926_IMONOVFL_FLAG_WIDTH 1 +#define MAX98926_VMONOVFL_FLAG_MASK (1<<0) +#define MAX98926_VMONOVFL_FLAG_SHIFT 0 +#define MAX98926_VMONOVFL_FLAG_WIDTH 1 + +/* MAX98926_R00B_IRQ_ENABLE0 */ +#define MAX98926_THERMWARN_END_EN_MASK (1<<3) +#define MAX98926_THERMWARN_END_EN_SHIFT 3 +#define MAX98926_THERMWARN_END_EN_WIDTH 1 +#define MAX98926_THERMWARN_BGN_EN_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_EN_SHIFT 2 +#define MAX98926_THERMWARN_BGN_EN_WIDTH 1 +#define MAX98926_THERMSHDN_END_EN_MASK (1<<1) +#define MAX98926_THERMSHDN_END_EN_SHIFT 1 +#define MAX98926_THERMSHDN_END_EN_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_EN_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_EN_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_EN_WIDTH 1 + +/* MAX98926_R00C_IRQ_ENABLE1 */ +#define MAX98926_SPKCURNT_EN_MASK (1<<5) +#define MAX98926_SPKCURNT_EN_SHIFT 5 +#define MAX98926_SPKCURNT_EN_WIDTH 1 +#define MAX98926_WATCHFAIL_EN_MASK (1<<4) +#define MAX98926_WATCHFAIL_EN_SHIFT 4 +#define MAX98926_WATCHFAIL_EN_WIDTH 1 +#define MAX98926_ALCINFH_EN_MASK (1<<3) +#define MAX98926_ALCINFH_EN_SHIFT 3 +#define MAX98926_ALCINFH_EN_WIDTH 1 +#define MAX98926_ALCACT_EN_MASK (1<<2) +#define MAX98926_ALCACT_EN_SHIFT 2 +#define MAX98926_ALCACT_EN_WIDTH 1 +#define MAX98926_ALCMUT_EN_MASK (1<<1) +#define MAX98926_ALCMUT_EN_SHIFT 1 +#define MAX98926_ALCMUT_EN_WIDTH 1 +#define MAX98926_ALCP_EN_MASK (1<<0) +#define MAX98926_ALCP_EN_SHIFT 0 +#define MAX98926_ALCP_EN_WIDTH 1 + +/* MAX98926_R00D_IRQ_ENABLE2 */ +#define MAX98926_SLOTOVRN_EN_MASK (1<<6) +#define MAX98926_SLOTOVRN_EN_SHIFT 6 +#define MAX98926_SLOTOVRN_EN_WIDTH 1 +#define MAX98926_INVALSLOT_EN_MASK (1<<5) +#define MAX98926_INVALSLOT_EN_SHIFT 5 +#define MAX98926_INVALSLOT_EN_WIDTH 1 +#define MAX98926_SLOTCNFLT_EN_MASK (1<<4) +#define MAX98926_SLOTCNFLT_EN_SHIFT 4 +#define MAX98926_SLOTCNFLT_EN_WIDTH 1 +#define MAX98926_VBSTOVFL_EN_MASK (1<<3) +#define MAX98926_VBSTOVFL_EN_SHIFT 3 +#define MAX98926_VBSTOVFL_EN_WIDTH 1 +#define MAX98926_VBATOVFL_EN_MASK (1<<2) +#define MAX98926_VBATOVFL_EN_SHIFT 2 +#define MAX98926_VBATOVFL_EN_WIDTH 1 +#define MAX98926_IMONOVFL_EN_MASK (1<<1) +#define MAX98926_IMONOVFL_EN_SHIFT 1 +#define MAX98926_IMONOVFL_EN_WIDTH 1 +#define MAX98926_VMONOVFL_EN_MASK (1<<0) +#define MAX98926_VMONOVFL_EN_SHIFT 0 +#define MAX98926_VMONOVFL_EN_WIDTH 1 + +/* MAX98926_R00E_IRQ_CLEAR0 */ +#define MAX98926_THERMWARN_END_CLR_MASK (1<<3) +#define MAX98926_THERMWARN_END_CLR_SHIFT 3 +#define MAX98926_THERMWARN_END_CLR_WIDTH 1 +#define MAX98926_THERMWARN_BGN_CLR_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_CLR_SHIFT 2 +#define MAX98926_THERMWARN_BGN_CLR_WIDTH 1 +#define MAX98926_THERMSHDN_END_CLR_MASK (1<<1) +#define MAX98926_THERMSHDN_END_CLR_SHIFT 1 +#define MAX98926_THERMSHDN_END_CLR_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_CLR_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_CLR_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_CLR_WIDTH 1 + +/* MAX98926_R00F_IRQ_CLEAR1 */ +#define MAX98926_SPKCURNT_CLR_MASK (1<<5) +#define MAX98926_SPKCURNT_CLR_SHIFT 5 +#define MAX98926_SPKCURNT_CLR_WIDTH 1 +#define MAX98926_WATCHFAIL_CLR_MASK (1<<4) +#define MAX98926_WATCHFAIL_CLR_SHIFT 4 +#define MAX98926_WATCHFAIL_CLR_WIDTH 1 +#define MAX98926_ALCINFH_CLR_MASK (1<<3) +#define MAX98926_ALCINFH_CLR_SHIFT 3 +#define MAX98926_ALCINFH_CLR_WIDTH 1 +#define MAX98926_ALCACT_CLR_MASK (1<<2) +#define MAX98926_ALCACT_CLR_SHIFT 2 +#define MAX98926_ALCACT_CLR_WIDTH 1 +#define MAX98926_ALCMUT_CLR_MASK (1<<1) +#define MAX98926_ALCMUT_CLR_SHIFT 1 +#define MAX98926_ALCMUT_CLR_WIDTH 1 +#define MAX98926_ALCP_CLR_MASK (1<<0) +#define MAX98926_ALCP_CLR_SHIFT 0 +#define MAX98926_ALCP_CLR_WIDTH 1 + +/* MAX98926_R010_IRQ_CLEAR2 */ +#define MAX98926_SLOTOVRN_CLR_MASK (1<<6) +#define MAX98926_SLOTOVRN_CLR_SHIFT 6 +#define MAX98926_SLOTOVRN_CLR_WIDTH 1 +#define MAX98926_INVALSLOT_CLR_MASK (1<<5) +#define MAX98926_INVALSLOT_CLR_SHIFT 5 +#define MAX98926_INVALSLOT_CLR_WIDTH 1 +#define MAX98926_SLOTCNFLT_CLR_MASK (1<<4) +#define MAX98926_SLOTCNFLT_CLR_SHIFT 4 +#define MAX98926_SLOTCNFLT_CLR_WIDTH 1 +#define MAX98926_VBSTOVFL_CLR_MASK (1<<3) +#define MAX98926_VBSTOVFL_CLR_SHIFT 3 +#define MAX98926_VBSTOVFL_CLR_WIDTH 1 +#define MAX98926_VBATOVFL_CLR_MASK (1<<2) +#define MAX98926_VBATOVFL_CLR_SHIFT 2 +#define MAX98926_VBATOVFL_CLR_WIDTH 1 +#define MAX98926_IMONOVFL_CLR_MASK (1<<1) +#define MAX98926_IMONOVFL_CLR_SHIFT 1 +#define MAX98926_IMONOVFL_CLR_WIDTH 1 +#define MAX98926_VMONOVFL_CLR_MASK (1<<0) +#define MAX98926_VMONOVFL_CLR_SHIFT 0 +#define MAX98926_VMONOVFL_CLR_WIDTH 1 + +/* MAX98926_R011_MAP0 */ +#define MAX98926_ER_THERMWARN_EN_MASK (1<<7) +#define MAX98926_ER_THERMWARN_EN_SHIFT 7 +#define MAX98926_ER_THERMWARN_EN_WIDTH 1 +#define MAX98926_ER_THERMWARN_MAP_MASK (0x07<<4) +#define MAX98926_ER_THERMWARN_MAP_SHIFT 4 +#define MAX98926_ER_THERMWARN_MAP_WIDTH 3 + +/* MAX98926_R012_MAP1 */ +#define MAX98926_ER_ALCMUT_EN_MASK (1<<7) +#define MAX98926_ER_ALCMUT_EN_SHIFT 7 +#define MAX98926_ER_ALCMUT_EN_WIDTH 1 +#define MAX98926_ER_ALCMUT_MAP_MASK (0x07<<4) +#define MAX98926_ER_ALCMUT_MAP_SHIFT 4 +#define MAX98926_ER_ALCMUT_MAP_WIDTH 3 +#define MAX98926_ER_ALCP_EN_MASK (1<<3) +#define MAX98926_ER_ALCP_EN_SHIFT 3 +#define MAX98926_ER_ALCP_EN_WIDTH 1 +#define MAX98926_ER_ALCP_MAP_MASK (0x07<<0) +#define MAX98926_ER_ALCP_MAP_SHIFT 0 +#define MAX98926_ER_ALCP_MAP_WIDTH 3 + +/* MAX98926_R013_MAP2 */ +#define MAX98926_ER_ALCINFH_EN_MASK (1<<7) +#define MAX98926_ER_ALCINFH_EN_SHIFT 7 +#define MAX98926_ER_ALCINFH_EN_WIDTH 1 +#define MAX98926_ER_ALCINFH_MAP_MASK (0x07<<4) +#define MAX98926_ER_ALCINFH_MAP_SHIFT 4 +#define MAX98926_ER_ALCINFH_MAP_WIDTH 3 +#define MAX98926_ER_ALCACT_EN_MASK (1<<3) +#define MAX98926_ER_ALCACT_EN_SHIFT 3 +#define MAX98926_ER_ALCACT_EN_WIDTH 1 +#define MAX98926_ER_ALCACT_MAP_MASK (0x07<<0) +#define MAX98926_ER_ALCACT_MAP_SHIFT 0 +#define MAX98926_ER_ALCACT_MAP_WIDTH 3 + +/* MAX98926_R014_MAP3 */ +#define MAX98926_ER_SPKCURNT_EN_MASK (1<<7) +#define MAX98926_ER_SPKCURNT_EN_SHIFT 7 +#define MAX98926_ER_SPKCURNT_EN_WIDTH 1 +#define MAX98926_ER_SPKCURNT_MAP_MASK (0x07<<4) +#define MAX98926_ER_SPKCURNT_MAP_SHIFT 4 +#define MAX98926_ER_SPKCURNT_MAP_WIDTH 3 + +/* MAX98926_R015_MAP4 */ +/* RESERVED */ + +/* MAX98926_R016_MAP5 */ +#define MAX98926_ER_IMONOVFL_EN_MASK (1<<7) +#define MAX98926_ER_IMONOVFL_EN_SHIFT 7 +#define MAX98926_ER_IMONOVFL_EN_WIDTH 1 +#define MAX98926_ER_IMONOVFL_MAP_MASK (0x07<<4) +#define MAX98926_ER_IMONOVFL_MAP_SHIFT 4 +#define MAX98926_ER_IMONOVFL_MAP_WIDTH 3 +#define MAX98926_ER_VMONOVFL_EN_MASK (1<<3) +#define MAX98926_ER_VMONOVFL_EN_SHIFT 3 +#define MAX98926_ER_VMONOVFL_EN_WIDTH 1 +#define MAX98926_ER_VMONOVFL_MAP_MASK (0x07<<0) +#define MAX98926_ER_VMONOVFL_MAP_SHIFT 0 +#define MAX98926_ER_VMONOVFL_MAP_WIDTH 3 + +/* MAX98926_R017_MAP6 */ +#define MAX98926_ER_VBSTOVFL_EN_MASK (1<<7) +#define MAX98926_ER_VBSTOVFL_EN_SHIFT 7 +#define MAX98926_ER_VBSTOVFL_EN_WIDTH 1 +#define MAX98926_ER_VBSTOVFL_MAP_MASK (0x07<<4) +#define MAX98926_ER_VBSTOVFL_MAP_SHIFT 4 +#define MAX98926_ER_VBSTOVFL_MAP_WIDTH 3 +#define MAX98926_ER_VBATOVFL_EN_MASK (1<<3) +#define MAX98926_ER_VBATOVFL_EN_SHIFT 3 +#define MAX98926_ER_VBATOVFL_EN_WIDTH 1 +#define MAX98926_ER_VBATOVFL_MAP_MASK (0x07<<0) +#define MAX98926_ER_VBATOVFL_MAP_SHIFT 0 +#define MAX98926_ER_VBATOVFL_MAP_WIDTH 3 + +/* MAX98926_R018_MAP7 */ +#define MAX98926_ER_INVALSLOT_EN_MASK (1<<7) +#define MAX98926_ER_INVALSLOT_EN_SHIFT 7 +#define MAX98926_ER_INVALSLOT_EN_WIDTH 1 +#define MAX98926_ER_INVALSLOT_MAP_MASK (0x07<<4) +#define MAX98926_ER_INVALSLOT_MAP_SHIFT 4 +#define MAX98926_ER_INVALSLOT_MAP_WIDTH 3 +#define MAX98926_ER_SLOTCNFLT_EN_MASK (1<<3) +#define MAX98926_ER_SLOTCNFLT_EN_SHIFT 3 +#define MAX98926_ER_SLOTCNFLT_EN_WIDTH 1 +#define MAX98926_ER_SLOTCNFLT_MAP_MASK (0x07<<0) +#define MAX98926_ER_SLOTCNFLT_MAP_SHIFT 0 +#define MAX98926_ER_SLOTCNFLT_MAP_WIDTH 3 + +/* MAX98926_R019_MAP8 */ +#define MAX98926_ER_SLOTOVRN_EN_MASK (1<<3) +#define MAX98926_ER_SLOTOVRN_EN_SHIFT 3 +#define MAX98926_ER_SLOTOVRN_EN_WIDTH 1 +#define MAX98926_ER_SLOTOVRN_MAP_MASK (0x07<<0) +#define MAX98926_ER_SLOTOVRN_MAP_SHIFT 0 +#define MAX98926_ER_SLOTOVRN_MAP_WIDTH 3 + +/* MAX98926_R01A_DAI_CLK_MODE1 */ +#define MAX98926_DAI_CLK_SOURCE_MASK (1<<6) +#define MAX98926_DAI_CLK_SOURCE_SHIFT 6 +#define MAX98926_DAI_CLK_SOURCE_WIDTH 1 +#define MAX98926_MDLL_MULT_MASK (0x0F<<0) +#define MAX98926_MDLL_MULT_SHIFT 0 +#define MAX98926_MDLL_MULT_WIDTH 4 + +#define MAX98926_MDLL_MULT_MCLKx8 6 +#define MAX98926_MDLL_MULT_MCLKx16 8 + +/* MAX98926_R01B_DAI_CLK_MODE2 */ +#define MAX98926_DAI_SR_MASK (0x0F<<4) +#define MAX98926_DAI_SR_SHIFT 4 +#define MAX98926_DAI_SR_WIDTH 4 +#define MAX98926_DAI_MAS_MASK (1<<3) +#define MAX98926_DAI_MAS_SHIFT 3 +#define MAX98926_DAI_MAS_WIDTH 1 +#define MAX98926_DAI_BSEL_MASK (0x07<<0) +#define MAX98926_DAI_BSEL_SHIFT 0 +#define MAX98926_DAI_BSEL_WIDTH 3 + +#define MAX98926_DAI_BSEL_32 (0 << MAX98926_DAI_BSEL_SHIFT) +#define MAX98926_DAI_BSEL_48 (1 << MAX98926_DAI_BSEL_SHIFT) +#define MAX98926_DAI_BSEL_64 (2 << MAX98926_DAI_BSEL_SHIFT) +#define MAX98926_DAI_BSEL_256 (6 << MAX98926_DAI_BSEL_SHIFT) + +/* MAX98926_R01C_DAI_CLK_DIV_M_MSBS */ +#define MAX98926_DAI_M_MSBS_MASK (0xFF<<0) +#define MAX98926_DAI_M_MSBS_SHIFT 0 +#define MAX98926_DAI_M_MSBS_WIDTH 8 + +/* MAX98926_R01D_DAI_CLK_DIV_M_LSBS */ +#define MAX98926_DAI_M_LSBS_MASK (0xFF<<0) +#define MAX98926_DAI_M_LSBS_SHIFT 0 +#define MAX98926_DAI_M_LSBS_WIDTH 8 + +/* MAX98926_R01E_DAI_CLK_DIV_N_MSBS */ +#define MAX98926_DAI_N_MSBS_MASK (0x7F<<0) +#define MAX98926_DAI_N_MSBS_SHIFT 0 +#define MAX98926_DAI_N_MSBS_WIDTH 7 + +/* MAX98926_R01F_DAI_CLK_DIV_N_LSBS */ +#define MAX98926_DAI_N_LSBS_MASK (0xFF<<0) +#define MAX98926_DAI_N_LSBS_SHIFT 0 +#define MAX98926_DAI_N_LSBS_WIDTH 8 + +/* MAX98926_R020_FORMAT */ +#define MAX98926_DAI_CHANSZ_MASK (0x03<<6) +#define MAX98926_DAI_CHANSZ_SHIFT 6 +#define MAX98926_DAI_CHANSZ_WIDTH 2 +#define MAX98926_DAI_INTERLEAVE_MASK (1<<5) +#define MAX98926_DAI_INTERLEAVE_SHIFT 5 +#define MAX98926_DAI_INTERLEAVE_WIDTH 1 +#define MAX98926_DAI_EXTBCLK_HIZ_MASK (1<<4) +#define MAX98926_DAI_EXTBCLK_HIZ_SHIFT 4 +#define MAX98926_DAI_EXTBCLK_HIZ_WIDTH 1 +#define MAX98926_DAI_WCI_MASK (1<<3) +#define MAX98926_DAI_WCI_SHIFT 3 +#define MAX98926_DAI_WCI_WIDTH 1 +#define MAX98926_DAI_BCI_MASK (1<<2) +#define MAX98926_DAI_BCI_SHIFT 2 +#define MAX98926_DAI_BCI_WIDTH 1 +#define MAX98926_DAI_DLY_MASK (1<<1) +#define MAX98926_DAI_DLY_SHIFT 1 +#define MAX98926_DAI_DLY_WIDTH 1 +#define MAX98926_DAI_TDM_MASK (1<<0) +#define MAX98926_DAI_TDM_SHIFT 0 +#define MAX98926_DAI_TDM_WIDTH 1 + +#define MAX98926_DAI_CHANSZ_16 (1 << MAX98926_DAI_CHANSZ_SHIFT) +#define MAX98926_DAI_CHANSZ_24 (2 << MAX98926_DAI_CHANSZ_SHIFT) +#define MAX98926_DAI_CHANSZ_32 (3 << MAX98926_DAI_CHANSZ_SHIFT) + +/* MAX98926_R021_TDM_SLOT_SELECT */ +#define MAX98926_DAI_DO_EN_MASK (1<<7) +#define MAX98926_DAI_DO_EN_SHIFT 7 +#define MAX98926_DAI_DO_EN_WIDTH 1 +#define MAX98926_DAI_DIN_EN_MASK (1<<6) +#define MAX98926_DAI_DIN_EN_SHIFT 6 +#define MAX98926_DAI_DIN_EN_WIDTH 1 +#define MAX98926_DAI_INR_SOURCE_MASK (0x07<<3) +#define MAX98926_DAI_INR_SOURCE_SHIFT 3 +#define MAX98926_DAI_INR_SOURCE_WIDTH 3 +#define MAX98926_DAI_INL_SOURCE_MASK (0x07<<0) +#define MAX98926_DAI_INL_SOURCE_SHIFT 0 +#define MAX98926_DAI_INL_SOURCE_WIDTH 3 + +/* MAX98926_R022_DOUT_CFG_VMON */ +#define MAX98926_DAI_VMON_EN_MASK (1<<5) +#define MAX98926_DAI_VMON_EN_SHIFT 5 +#define MAX98926_DAI_VMON_EN_WIDTH 1 +#define MAX98926_DAI_VMON_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_VMON_SLOT_SHIFT 0 +#define MAX98926_DAI_VMON_SLOT_WIDTH 5 + +#define MAX98926_DAI_VMON_SLOT_00_01 (0 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_01_02 (1 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_02_03 (2 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_03_04 (3 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_04_05 (4 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_05_06 (5 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_06_07 (6 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_07_08 (7 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_08_09 (8 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_09_0A (9 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0A_0B (10 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0B_0C (11 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0C_0D (12 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0D_0E (13 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0E_0F (14 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0F_10 (15 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_10_11 (16 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_11_12 (17 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_12_13 (18 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_13_14 (19 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_14_15 (20 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_15_16 (21 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_16_17 (22 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_17_18 (23 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_18_19 (24 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_19_1A (25 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1A_1B (26 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1B_1C (27 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1C_1D (28 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1D_1E (29 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1E_1F (30 << MAX98926_DAI_VMON_SLOT_SHIFT) + +/* MAX98926_R023_DOUT_CFG_IMON */ +#define MAX98926_DAI_IMON_EN_MASK (1<<5) +#define MAX98926_DAI_IMON_EN_SHIFT 5 +#define MAX98926_DAI_IMON_EN_WIDTH 1 +#define MAX98926_DAI_IMON_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_IMON_SLOT_SHIFT 0 +#define MAX98926_DAI_IMON_SLOT_WIDTH 5 + +#define MAX98926_DAI_IMON_SLOT_00_01 (0 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_01_02 (1 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_02_03 (2 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_03_04 (3 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_04_05 (4 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_05_06 (5 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_06_07 (6 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_07_08 (7 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_08_09 (8 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_09_0A (9 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0A_0B (10 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0B_0C (11 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0C_0D (12 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0D_0E (13 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0E_0F (14 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0F_10 (15 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_10_11 (16 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_11_12 (17 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_12_13 (18 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_13_14 (19 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_14_15 (20 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_15_16 (21 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_16_17 (22 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_17_18 (23 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_18_19 (24 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_19_1A (25 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1A_1B (26 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1B_1C (27 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1C_1D (28 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1D_1E (29 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1E_1F (30 << MAX98926_DAI_IMON_SLOT_SHIFT) + +/* MAX98926_R024_DOUT_CFG_VBAT */ +#define MAX98926_DAI_INTERLEAVE_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_INTERLEAVE_SLOT_SHIFT 0 +#define MAX98926_DAI_INTERLEAVE_SLOT_WIDTH 5 + +/* MAX98926_R025_DOUT_CFG_VBST */ +#define MAX98926_DAI_VBST_EN_MASK (1<<5) +#define MAX98926_DAI_VBST_EN_SHIFT 5 +#define MAX98926_DAI_VBST_EN_WIDTH 1 +#define MAX98926_DAI_VBST_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_VBST_SLOT_SHIFT 0 +#define MAX98926_DAI_VBST_SLOT_WIDTH 5 + +/* MAX98926_R026_DOUT_CFG_FLAG */ +#define MAX98926_DAI_FLAG_EN_MASK (1<<5) +#define MAX98926_DAI_FLAG_EN_SHIFT 5 +#define MAX98926_DAI_FLAG_EN_WIDTH 1 +#define MAX98926_DAI_FLAG_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_FLAG_SLOT_SHIFT 0 +#define MAX98926_DAI_FLAG_SLOT_WIDTH 5 + +/* MAX98926_R027_DOUT_HIZ_CFG1 */ +#define MAX98926_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG1_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG1_WIDTH 8 + +/* MAX98926_R028_DOUT_HIZ_CFG2 */ +#define MAX98926_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG2_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG2_WIDTH 8 + +/* MAX98926_R029_DOUT_HIZ_CFG3 */ +#define MAX98926_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG3_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG3_WIDTH 8 + +/* MAX98926_R02A_DOUT_HIZ_CFG4 */ +#define MAX98926_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG4_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG4_WIDTH 8 + +/* MAX98926_R02B_DOUT_DRV_STRENGTH */ +#define MAX98926_DAI_OUT_DRIVE_MASK (0x03<<0) +#define MAX98926_DAI_OUT_DRIVE_SHIFT 0 +#define MAX98926_DAI_OUT_DRIVE_WIDTH 2 + +/* MAX98926_R02C_FILTERS */ +#define MAX98926_ADC_DITHER_EN_MASK (1<<7) +#define MAX98926_ADC_DITHER_EN_SHIFT 7 +#define MAX98926_ADC_DITHER_EN_WIDTH 1 +#define MAX98926_IV_DCB_EN_MASK (1<<6) +#define MAX98926_IV_DCB_EN_SHIFT 6 +#define MAX98926_IV_DCB_EN_WIDTH 1 +#define MAX98926_DAC_DITHER_EN_MASK (1<<4) +#define MAX98926_DAC_DITHER_EN_SHIFT 4 +#define MAX98926_DAC_DITHER_EN_WIDTH 1 +#define MAX98926_DAC_FILTER_MODE_MASK (1<<3) +#define MAX98926_DAC_FILTER_MODE_SHIFT 3 +#define MAX98926_DAC_FILTER_MODE_WIDTH 1 +#define MAX98926_DAC_HPF_MASK (0x07<<0) +#define MAX98926_DAC_HPF_SHIFT 0 +#define MAX98926_DAC_HPF_WIDTH 3 +#define MAX98926_DAC_HPF_DISABLE (0 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_DC_BLOCK (1 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_100 (2 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_200 (3 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_400 (4 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_800 (5 << MAX98926_DAC_HPF_SHIFT) + +/* MAX98926_R02D_GAIN */ +#define MAX98926_DAC_IN_SEL_MASK (0x03<<5) +#define MAX98926_DAC_IN_SEL_SHIFT 5 +#define MAX98926_DAC_IN_SEL_WIDTH 2 +#define MAX98926_SPK_GAIN_MASK (0x1F<<0) +#define MAX98926_SPK_GAIN_SHIFT 0 +#define MAX98926_SPK_GAIN_WIDTH 5 + +#define MAX98926_DAC_IN_SEL_LEFT_DAI (0 << MAX98926_DAC_IN_SEL_SHIFT) +#define MAX98926_DAC_IN_SEL_RIGHT_DAI (1 << MAX98926_DAC_IN_SEL_SHIFT) +#define MAX98926_DAC_IN_SEL_SUMMED_DAI (2 << MAX98926_DAC_IN_SEL_SHIFT) +#define MAX98926_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << MAX98926_DAC_IN_SEL_SHIFT) + +/* MAX98926_R02E_GAIN_RAMPING */ +#define MAX98926_SPK_RMP_EN_MASK (1<<1) +#define MAX98926_SPK_RMP_EN_SHIFT 1 +#define MAX98926_SPK_RMP_EN_WIDTH 1 +#define MAX98926_SPK_ZCD_EN_MASK (1<<0) +#define MAX98926_SPK_ZCD_EN_SHIFT 0 +#define MAX98926_SPK_ZCD_EN_WIDTH 1 + +/* MAX98926_R02F_SPK_AMP */ +#define MAX98926_SPK_MODE_MASK (1<<0) +#define MAX98926_SPK_MODE_SHIFT 0 +#define MAX98926_SPK_MODE_WIDTH 1 +#define MAX98926_INSELECT_MODE_MASK (1<<1) +#define MAX98926_INSELECT_MODE_SHIFT 1 +#define MAX98926_INSELECT_MODE_WIDTH 1 + +/* MAX98926_R030_THRESHOLD */ +#define MAX98926_ALC_EN_MASK (1<<5) +#define MAX98926_ALC_EN_SHIFT 5 +#define MAX98926_ALC_EN_WIDTH 1 +#define MAX98926_ALC_TH_MASK (0x1F<<0) +#define MAX98926_ALC_TH_SHIFT 0 +#define MAX98926_ALC_TH_WIDTH 5 + +/* MAX98926_R031_ALC_ATTACK */ +#define MAX98926_ALC_ATK_STEP_MASK (0x0F<<4) +#define MAX98926_ALC_ATK_STEP_SHIFT 4 +#define MAX98926_ALC_ATK_STEP_WIDTH 4 +#define MAX98926_ALC_ATK_RATE_MASK (0x7<<0) +#define MAX98926_ALC_ATK_RATE_SHIFT 0 +#define MAX98926_ALC_ATK_RATE_WIDTH 3 + +/* MAX98926_R032_ALC_ATTEN_RLS */ +#define MAX98926_ALC_MAX_ATTEN_MASK (0x0F<<4) +#define MAX98926_ALC_MAX_ATTEN_SHIFT 4 +#define MAX98926_ALC_MAX_ATTEN_WIDTH 4 +#define MAX98926_ALC_RLS_RATE_MASK (0x7<<0) +#define MAX98926_ALC_RLS_RATE_SHIFT 0 +#define MAX98926_ALC_RLS_RATE_WIDTH 3 + +/* MAX98926_R033_ALC_HOLD_RLS */ +#define MAX98926_ALC_RLS_TGR_MASK (1<<0) +#define MAX98926_ALC_RLS_TGR_SHIFT 0 +#define MAX98926_ALC_RLS_TGR_WIDTH 1 + +/* MAX98926_R034_ALC_CONFIGURATION */ +#define MAX98926_ALC_MUTE_EN_MASK (1<<7) +#define MAX98926_ALC_MUTE_EN_SHIFT 7 +#define MAX98926_ALC_MUTE_EN_WIDTH 1 +#define MAX98926_ALC_MUTE_DLY_MASK (0x07<<4) +#define MAX98926_ALC_MUTE_DLY_SHIFT 4 +#define MAX98926_ALC_MUTE_DLY_WIDTH 3 +#define MAX98926_ALC_RLS_DBT_MASK (0x07<<0) +#define MAX98926_ALC_RLS_DBT_SHIFT 0 +#define MAX98926_ALC_RLS_DBT_WIDTH 3 + +/* MAX98926_R035_BOOST_CONVERTER */ +#define MAX98926_BST_SYNC_MASK (1<<7) +#define MAX98926_BST_SYNC_SHIFT 7 +#define MAX98926_BST_SYNC_WIDTH 1 +#define MAX98926_BST_PHASE_MASK (0x03<<4) +#define MAX98926_BST_PHASE_SHIFT 4 +#define MAX98926_BST_PHASE_WIDTH 2 +#define MAX98926_BST_SKIP_MODE_MASK (0x03<<0) +#define MAX98926_BST_SKIP_MODE_SHIFT 0 +#define MAX98926_BST_SKIP_MODE_WIDTH 2 + +/* MAX98926_R036_BLOCK_ENABLE */ +#define MAX98926_BST_EN_MASK (1<<7) +#define MAX98926_BST_EN_SHIFT 7 +#define MAX98926_BST_EN_WIDTH 1 +#define MAX98926_WATCH_EN_MASK (1<<6) +#define MAX98926_WATCH_EN_SHIFT 6 +#define MAX98926_WATCH_EN_WIDTH 1 +#define MAX98926_CLKMON_EN_MASK (1<<5) +#define MAX98926_CLKMON_EN_SHIFT 5 +#define MAX98926_CLKMON_EN_WIDTH 1 +#define MAX98926_SPK_EN_MASK (1<<4) +#define MAX98926_SPK_EN_SHIFT 4 +#define MAX98926_SPK_EN_WIDTH 1 +#define MAX98926_ADC_VBST_EN_MASK (1<<3) +#define MAX98926_ADC_VBST_EN_SHIFT 3 +#define MAX98926_ADC_VBST_EN_WIDTH 1 +#define MAX98926_ADC_VBAT_EN_MASK (1<<2) +#define MAX98926_ADC_VBAT_EN_SHIFT 2 +#define MAX98926_ADC_VBAT_EN_WIDTH 1 +#define MAX98926_ADC_IMON_EN_MASK (1<<1) +#define MAX98926_ADC_IMON_EN_SHIFT 1 +#define MAX98926_ADC_IMON_EN_WIDTH 1 +#define MAX98926_ADC_VMON_EN_MASK (1<<0) +#define MAX98926_ADC_VMON_EN_SHIFT 0 +#define MAX98926_ADC_VMON_EN_WIDTH 1 + +/* MAX98926_R037_CONFIGURATION */ +#define MAX98926_BST_VOUT_MASK (0x0F<<4) +#define MAX98926_BST_VOUT_SHIFT 4 +#define MAX98926_BST_VOUT_WIDTH 4 +#define MAX98926_THERMWARN_LEVEL_MASK (0x03<<2) +#define MAX98926_THERMWARN_LEVEL_SHIFT 2 +#define MAX98926_THERMWARN_LEVEL_WIDTH 2 +#define MAX98926_WATCH_TIME_MASK (0x03<<0) +#define MAX98926_WATCH_TIME_SHIFT 0 +#define MAX98926_WATCH_TIME_WIDTH 2 + +/* MAX98926_R038_GLOBAL_ENABLE */ +#define MAX98926_EN_MASK (1<<7) +#define MAX98926_EN_SHIFT 7 +#define MAX98926_EN_WIDTH 1 + +/* MAX98926_R03A_BOOST_LIMITER */ +#define MAX98926_BST_ILIM_MASK (0xF<<4) +#define MAX98926_BST_ILIM_SHIFT 4 +#define MAX98926_BST_ILIM_WIDTH 4 + +/* MAX98926_R0FF_VERSION */ +#define MAX98926_REV_ID_MASK (0xFF<<0) +#define MAX98926_REV_ID_SHIFT 0 +#define MAX98926_REV_ID_WIDTH 8 + +struct max98926_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + unsigned int sysclk; + unsigned int v_slot; + unsigned int i_slot; + unsigned int ch_size; + unsigned int interleave_mode; +}; +#endif diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index c1b87c5..1c87299 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -84,6 +84,7 @@ static const struct nau8825_fll_attr fll_pre_scalar[] = { static const struct reg_default nau8825_reg_defaults[] = { { NAU8825_REG_ENA_CTRL, 0x00ff }, + { NAU8825_REG_IIC_ADDR_SET, 0x0 }, { NAU8825_REG_CLK_DIVIDER, 0x0050 }, { NAU8825_REG_FLL1, 0x0 }, { NAU8825_REG_FLL2, 0x3126 }, @@ -158,8 +159,7 @@ static const struct reg_default nau8825_reg_defaults[] = { static bool nau8825_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { - case NAU8825_REG_ENA_CTRL: - case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_ENA_CTRL ... NAU8825_REG_FLL_VCO_RSV: case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL: case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: @@ -184,8 +184,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg) static bool nau8825_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { - case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL: - case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_RESET ... NAU8825_REG_FLL_VCO_RSV: case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: case NAU8825_REG_INTERRUPT_MASK: case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL: @@ -227,10 +226,42 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg) static int nau8825_pump_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + switch (event) { case SND_SOC_DAPM_POST_PMU: /* Prevent startup click by letting charge pump to ramp up */ msleep(10); + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_JAMNODCLOW, NAU8825_JAMNODCLOW); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_JAMNODCLOW, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disables the TESTDAC to let DAC signal pass through. */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_TESTDAC_EN, 0); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); break; default: return -EINVAL; @@ -316,10 +347,10 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL, NAU8825_SAR_ADC_EN_SFT, 0), - SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0), - SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0), - SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8825_REG_RDAC, 8, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8825_REG_RDAC, 9, 0, NULL, 0), SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_DACR_SFT, 0), @@ -330,29 +361,48 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux), SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux), - SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, - 0), + SND_SOC_DAPM_PGA_S("HP amp L", 0, + NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("HP amp R", 0, + NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0, - nau8825_pump_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8825_REG_CHARGE_PUMP, 5, 0, + nau8825_pump_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_PGA("Output Driver R Stage 1", + SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4, NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0), - SND_SOC_DAPM_PGA("Output Driver L Stage 1", + SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4, NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("Output Driver R Stage 2", + SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5, NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0), - SND_SOC_DAPM_PGA("Output Driver L Stage 2", + SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5, NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0), - SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1, + SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6, NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1, + SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6, NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), - SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0), - SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0), + SND_SOC_DAPM_PGA_S("Output DACL", 7, + NAU8825_REG_CHARGE_PUMP, 8, 1, nau8825_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("Output DACR", 7, + NAU8825_REG_CHARGE_PUMP, 9, 1, nau8825_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */ + SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8, + NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8, + NAU8825_REG_HSD_CTRL, 1, 1, NULL, 0), + + /* High current HPOL/R boost driver */ + SND_SOC_DAPM_PGA_S("HP Boost Driver", 9, + NAU8825_REG_BOOST, 9, 1, NULL, 0), + + /* Class G operation control*/ + SND_SOC_DAPM_PGA_S("Class G", 10, + NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("HPOL"), SND_SOC_DAPM_OUTPUT("HPOR"), @@ -375,24 +425,27 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = { {"DACR Mux", "DACR", "DDACR"}, {"HP amp L", NULL, "DACL Mux"}, {"HP amp R", NULL, "DACR Mux"}, - {"HP amp L", NULL, "HP amp power"}, - {"HP amp R", NULL, "HP amp power"}, - {"ADACL", NULL, "HP amp L"}, - {"ADACR", NULL, "HP amp R"}, - {"ADACL", NULL, "ADACL Clock"}, - {"ADACR", NULL, "ADACR Clock"}, - {"Output Driver L Stage 1", NULL, "ADACL"}, - {"Output Driver R Stage 1", NULL, "ADACR"}, + {"Charge Pump", NULL, "HP amp L"}, + {"Charge Pump", NULL, "HP amp R"}, + {"ADACL", NULL, "Charge Pump"}, + {"ADACR", NULL, "Charge Pump"}, + {"ADACL Clock", NULL, "ADACL"}, + {"ADACR Clock", NULL, "ADACR"}, + {"Output Driver L Stage 1", NULL, "ADACL Clock"}, + {"Output Driver R Stage 1", NULL, "ADACR Clock"}, {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"}, {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"}, {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"}, {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"}, {"Output DACL", NULL, "Output Driver L Stage 3"}, {"Output DACR", NULL, "Output Driver R Stage 3"}, - {"HPOL", NULL, "Output DACL"}, - {"HPOR", NULL, "Output DACR"}, - {"HPOL", NULL, "Charge Pump"}, - {"HPOR", NULL, "Charge Pump"}, + {"HPOL Pulldown", NULL, "Output DACL"}, + {"HPOR Pulldown", NULL, "Output DACR"}, + {"HP Boost Driver", NULL, "HPOL Pulldown"}, + {"HP Boost Driver", NULL, "HPOR Pulldown"}, + {"Class G", NULL, "HP Boost Driver"}, + {"HPOL", NULL, "Class G"}, + {"HPOR", NULL, "Class G"}, }; static int nau8825_hw_params(struct snd_pcm_substream *substream, @@ -659,11 +712,10 @@ static int nau8825_jack_insert(struct nau8825 *nau8825) break; } - if (type & SND_JACK_HEADPHONE) { - /* Unground HPL/R */ - regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0); - } - + /* Leaving HPOL/R grounded after jack insert by default. They will be + * ungrounded as part of the widget power up sequence at the beginning + * of playback to reduce pop. + */ return type; } @@ -768,6 +820,8 @@ static void nau8825_init_regs(struct nau8825 *nau8825) { struct regmap *regmap = nau8825->regmap; + /* Latch IIC LSB value */ + regmap_write(regmap, NAU8825_REG_IIC_ADDR_SET, 0x0001); /* Enable Bias/Vmid */ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_VMID, NAU8825_BIAS_VMID); @@ -780,10 +834,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825) nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT); /* Disable Boost Driver, Automatic Short circuit protection enable */ regmap_update_bits(regmap, NAU8825_REG_BOOST, - NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | - NAU8825_SHORT_SHUTDOWN_EN, - NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | - NAU8825_SHORT_SHUTDOWN_EN); + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS | + NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN, + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS | + NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN); regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, NAU8825_JKDET_OUTPUT_EN, @@ -822,6 +876,35 @@ static void nau8825_init_regs(struct nau8825 *nau8825) NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128); regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128); + /* Disable DACR/L power */ + regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input + * signal to avoid any glitches due to power up transients in both + * the analog and digital DAC circuit. + */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); + /* CICCLP off */ + regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, + NAU8825_DAC_CLIP_OFF, NAU8825_DAC_CLIP_OFF); + + /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */ + regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_2, + NAU8825_HP_NON_CLASSG_CURRENT_2xADJ | + NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB, + NAU8825_HP_NON_CLASSG_CURRENT_2xADJ | + NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB); + /* Class G timer 64ms */ + regmap_update_bits(regmap, NAU8825_REG_CLASSG_CTRL, + NAU8825_CLASSG_TIMER_MASK, + 0x20 << NAU8825_CLASSG_TIMER_SFT); + /* DAC clock delay 2ns, VREF */ + regmap_update_bits(regmap, NAU8825_REG_RDAC, + NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK, + (0x2 << NAU8825_RDAC_CLK_DELAY_SFT) | + (0x3 << NAU8825_RDAC_VREF_SFT)); } static const struct regmap_config nau8825_regmap_config = { diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index dff8edb..8ceb5f3 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -14,6 +14,7 @@ #define NAU8825_REG_RESET 0x00 #define NAU8825_REG_ENA_CTRL 0x01 +#define NAU8825_REG_IIC_ADDR_SET 0x02 #define NAU8825_REG_CLK_DIVIDER 0x03 #define NAU8825_REG_FLL1 0x04 #define NAU8825_REG_FLL2 0x05 @@ -129,7 +130,7 @@ /* HSD_CTRL (0xc) */ #define NAU8825_HSD_AUTO_MODE (1 << 6) -/* 0 - short to GND, 1 - open */ +/* 0 - open, 1 - short to GND */ #define NAU8825_SPKR_DWN1R (1 << 1) #define NAU8825_SPKR_DWN1L (1 << 0) @@ -251,12 +252,18 @@ /* DACR_CTRL (0x34) */ #define NAU8825_DACR_CH_SEL_SFT 9 +/* CLASSG_CTRL (0x50) */ +#define NAU8825_CLASSG_TIMER_SFT 8 +#define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT) +#define NAU8825_CLASSG_EN (1 << 0) + /* I2C_DEVICE_ID (0x58) */ #define NAU8825_GPIO2JD1 (1 << 7) #define NAU8825_SOFTWARE_ID_MASK 0x3 #define NAU8825_SOFTWARE_ID_NAU8825 0x0 /* BIAS_ADJ (0x66) */ +#define NAU8825_BIAS_TESTDAC_EN (0x3 << 8) #define NAU8825_BIAS_VMID (1 << 6) #define NAU8825_BIAS_VMID_SEL_SFT 4 #define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT) @@ -274,6 +281,12 @@ #define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8) #define NAU8825_POWERUP_ADCL (1 << 6) +/* RDAC (0x73) */ +#define NAU8825_RDAC_CLK_DELAY_SFT 4 +#define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT) +#define NAU8825_RDAC_VREF_SFT 2 +#define NAU8825_RDAC_VREF_MASK (0x3 << NAU8825_RDAC_VREF_SFT) + /* MIC_BIAS (0x74) */ #define NAU8825_MICBIAS_JKSLV (1 << 14) #define NAU8825_MICBIAS_JKR2 (1 << 12) @@ -284,6 +297,7 @@ /* BOOST (0x76) */ #define NAU8825_PRECHARGE_DIS (1 << 13) #define NAU8825_GLOBAL_BIAS_EN (1 << 12) +#define NAU8825_HP_BOOST_DIS (1 << 9) #define NAU8825_HP_BOOST_G_DIS (1 << 8) #define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c new file mode 100644 index 0000000..4118106 --- /dev/null +++ b/sound/soc/codecs/pcm179x-i2c.c @@ -0,0 +1,73 @@ +/* + * PCM179X ASoC I2C driver + * + * Copyright (c) Teenage Engineering AB 2016 + * + * Jacob Siverskog <jacob@teenage.engineering> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include "pcm179x.h" + +static int pcm179x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_i2c(client, &pcm179x_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + return pcm179x_common_init(&client->dev, regmap); +} + +static int pcm179x_i2c_remove(struct i2c_client *client) +{ + return pcm179x_common_exit(&client->dev); +} + +static const struct of_device_id pcm179x_of_match[] = { + { .compatible = "ti,pcm1792a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm179x_of_match); + +static const struct i2c_device_id pcm179x_i2c_ids[] = { + { "pcm179x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids); + +static struct i2c_driver pcm179x_i2c_driver = { + .driver = { + .name = "pcm179x", + .of_match_table = of_match_ptr(pcm179x_of_match), + }, + .id_table = pcm179x_i2c_ids, + .probe = pcm179x_i2c_probe, + .remove = pcm179x_i2c_remove, +}; + +module_i2c_driver(pcm179x_i2c_driver); + +MODULE_DESCRIPTION("ASoC PCM179X I2C driver"); +MODULE_AUTHOR("Jacob Siverskog <jacob@teenage.engineering>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c new file mode 100644 index 0000000..da924d4 --- /dev/null +++ b/sound/soc/codecs/pcm179x-spi.c @@ -0,0 +1,72 @@ +/* + * PCM179X ASoC SPI driver + * + * Copyright (c) Amarula Solutions B.V. 2013 + * + * Michael Trimarchi <michael@amarulasolutions.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> + +#include "pcm179x.h" + +static int pcm179x_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_spi(spi, &pcm179x_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&spi->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + return pcm179x_common_init(&spi->dev, regmap); +} + +static int pcm179x_spi_remove(struct spi_device *spi) +{ + return pcm179x_common_exit(&spi->dev); +} + +static const struct of_device_id pcm179x_of_match[] = { + { .compatible = "ti,pcm1792a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm179x_of_match); + +static const struct spi_device_id pcm179x_spi_ids[] = { + { "pcm179x", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids); + +static struct spi_driver pcm179x_spi_driver = { + .driver = { + .name = "pcm179x", + .of_match_table = of_match_ptr(pcm179x_of_match), + }, + .id_table = pcm179x_spi_ids, + .probe = pcm179x_spi_probe, + .remove = pcm179x_spi_remove, +}; + +module_spi_driver(pcm179x_spi_driver); + +MODULE_DESCRIPTION("ASoC PCM179X SPI driver"); +MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c index a56c7b7..06a6657 100644 --- a/sound/soc/codecs/pcm179x.c +++ b/sound/soc/codecs/pcm179x.c @@ -20,7 +20,6 @@ #include <linux/slab.h> #include <linux/kernel.h> #include <linux/device.h> -#include <linux/spi/spi.h> #include <sound/core.h> #include <sound/pcm.h> @@ -29,7 +28,6 @@ #include <sound/soc.h> #include <sound/tlv.h> #include <linux/of.h> -#include <linux/of_device.h> #include "pcm179x.h" @@ -189,18 +187,14 @@ static struct snd_soc_dai_driver pcm179x_dai = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, - .rates = PCM1792A_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 10000, + .rate_max = 200000, .formats = PCM1792A_FORMATS, }, .ops = &pcm179x_dai_ops, }; -static const struct of_device_id pcm179x_of_match[] = { - { .compatible = "ti,pcm1792a", }, - { } -}; -MODULE_DEVICE_TABLE(of, pcm179x_of_match); - -static const struct regmap_config pcm179x_regmap = { +const struct regmap_config pcm179x_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 23, @@ -209,6 +203,7 @@ static const struct regmap_config pcm179x_regmap = { .writeable_reg = pcm179x_writeable_reg, .readable_reg = pcm179x_accessible_reg, }; +EXPORT_SYMBOL_GPL(pcm179x_regmap_config); static struct snd_soc_codec_driver soc_codec_dev_pcm179x = { .controls = pcm179x_controls, @@ -219,52 +214,29 @@ static struct snd_soc_codec_driver soc_codec_dev_pcm179x = { .num_dapm_routes = ARRAY_SIZE(pcm179x_dapm_routes), }; -static int pcm179x_spi_probe(struct spi_device *spi) +int pcm179x_common_init(struct device *dev, struct regmap *regmap) { struct pcm179x_private *pcm179x; - int ret; - pcm179x = devm_kzalloc(&spi->dev, sizeof(struct pcm179x_private), + pcm179x = devm_kzalloc(dev, sizeof(struct pcm179x_private), GFP_KERNEL); if (!pcm179x) return -ENOMEM; - spi_set_drvdata(spi, pcm179x); - - pcm179x->regmap = devm_regmap_init_spi(spi, &pcm179x_regmap); - if (IS_ERR(pcm179x->regmap)) { - ret = PTR_ERR(pcm179x->regmap); - dev_err(&spi->dev, "Failed to register regmap: %d\n", ret); - return ret; - } + pcm179x->regmap = regmap; + dev_set_drvdata(dev, pcm179x); - return snd_soc_register_codec(&spi->dev, + return snd_soc_register_codec(dev, &soc_codec_dev_pcm179x, &pcm179x_dai, 1); } +EXPORT_SYMBOL_GPL(pcm179x_common_init); -static int pcm179x_spi_remove(struct spi_device *spi) +int pcm179x_common_exit(struct device *dev) { - snd_soc_unregister_codec(&spi->dev); + snd_soc_unregister_codec(dev); return 0; } - -static const struct spi_device_id pcm179x_spi_ids[] = { - { "pcm179x", 0 }, - { }, -}; -MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids); - -static struct spi_driver pcm179x_codec_driver = { - .driver = { - .name = "pcm179x", - .of_match_table = of_match_ptr(pcm179x_of_match), - }, - .id_table = pcm179x_spi_ids, - .probe = pcm179x_spi_probe, - .remove = pcm179x_spi_remove, -}; - -module_spi_driver(pcm179x_codec_driver); +EXPORT_SYMBOL_GPL(pcm179x_common_exit); MODULE_DESCRIPTION("ASoC PCM179X driver"); MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>"); diff --git a/sound/soc/codecs/pcm179x.h b/sound/soc/codecs/pcm179x.h index c6fdc06..11e3312 100644 --- a/sound/soc/codecs/pcm179x.h +++ b/sound/soc/codecs/pcm179x.h @@ -17,11 +17,12 @@ #ifndef __PCM179X_H__ #define __PCM179X_H__ -#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ - SNDRV_PCM_RATE_192000) - #define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S16_LE) +extern const struct regmap_config pcm179x_regmap_config; + +int pcm179x_common_init(struct device *dev, struct regmap *regmap); +int pcm179x_common_exit(struct device *dev); + #endif diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 44b268a..992a77e 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -299,10 +299,15 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec); + int ret; if (freq > PCM1368A_MAX_SYSCLK) return -EINVAL; + ret = clk_set_rate(pcm3168a->scki, freq); + if (ret) + return ret; + pcm3168a->sysclk = freq; return 0; @@ -395,13 +400,12 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec); bool tx, master_mode; u32 val, mask, shift, reg; - unsigned int rate, channels, fmt, ratio, max_ratio; + unsigned int rate, fmt, ratio, max_ratio; int i, min_frame_size; snd_pcm_format_t format; rate = params_rate(params); format = params_format(params); - channels = params_channels(params); ratio = pcm3168a->sysclk / rate; diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index bc08f0c..1bd3164 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -266,6 +266,8 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) } else { *mic = false; regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20); + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0x0400, 0x0000); } } else { regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf); @@ -470,24 +472,6 @@ static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w, return 0; } -static int rt286_vref_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, - RT286_CBJ_CTRL1, 0x0400, 0x0000); - mdelay(50); - break; - default: - return 0; - } - - return 0; -} - static int rt286_ldo2_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -536,7 +520,7 @@ static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1, 12, 1, NULL, 0), SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1, - 0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU), + 0, 1, NULL, 0), SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2, 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1, @@ -911,8 +895,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_ON: mdelay(10); snd_soc_update_bits(codec, - RT286_CBJ_CTRL1, 0x0400, 0x0400); - snd_soc_update_bits(codec, RT286_DC_GAIN, 0x200, 0x0); break; @@ -920,8 +902,6 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: snd_soc_write(codec, RT286_SET_AUDIO_POWER, AC_PWRST_D3); - snd_soc_update_bits(codec, - RT286_CBJ_CTRL1, 0x0400, 0x0000); break; default: diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 30c6de6..f0e6c06 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1224,7 +1224,12 @@ static int rt298_i2c_probe(struct i2c_client *i2c, regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000); regmap_update_bits(rt298->regmap, RT298_WIND_FILTER_CTRL, 0x0082, 0x0082); - regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2); + + regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81); + regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82); + regmap_write(rt298->regmap, RT298_UNSOLICITED_MIC1, 0x84); + regmap_update_bits(rt298->regmap, RT298_IRQ_FLAG_CTRL, 0x2, 0x2); + rt298->is_hp_in = -1; if (rt298->i2c->irq) { diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h index 31da162..d66f884 100644 --- a/sound/soc/codecs/rt298.h +++ b/sound/soc/codecs/rt298.h @@ -34,6 +34,7 @@ #define RT298_HP_OUT 0x21 #define RT298_MIXER_IN1 0x22 #define RT298_MIXER_IN2 0x23 +#define RT298_INLINE_CMD 0x55 #define RT298_SET_PIN_SFT 6 #define RT298_SET_PIN_ENABLE 0x40 @@ -124,6 +125,12 @@ VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0) #define RT298_PROC_COEF\ VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0) +#define RT298_UNSOLICITED_INLINE_CMD\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_INLINE_CMD, 0) +#define RT298_UNSOLICITED_HP_OUT\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_HP_OUT, 0) +#define RT298_UNSOLICITED_MIC1\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_MIC1, 0) /* Index registers */ #define RT298_A_BIAS_CTRL1 0x01 @@ -148,6 +155,7 @@ #define RT298_DEPOP_CTRL2 0x67 #define RT298_DEPOP_CTRL3 0x68 #define RT298_DEPOP_CTRL4 0x69 +#define RT298_IRQ_FLAG_CTRL 0x7c /* SPDIF (0x06) */ #define RT298_SPDIF_SEL_SFT 0 diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c new file mode 100644 index 0000000..879bf60 --- /dev/null +++ b/sound/soc/codecs/rt5514.c @@ -0,0 +1,982 @@ +/* + * rt5514.c -- RT5514 ALSA SoC audio codec driver + * + * Copyright 2015 Realtek Semiconductor Corp. + * Author: Oder Chiou <oder_chiou@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "rl6231.h" +#include "rt5514.h" + +static const struct reg_sequence rt5514_i2c_patch[] = { + {0x1800101c, 0x00000000}, + {0x18001100, 0x0000031f}, + {0x18001104, 0x00000007}, + {0x18001108, 0x00000000}, + {0x1800110c, 0x00000000}, + {0x18001110, 0x00000000}, + {0x18001114, 0x00000001}, + {0x18001118, 0x00000000}, + {0x18002f08, 0x00000006}, + {0x18002f00, 0x00055149}, + {0x18002f00, 0x0005514b}, + {0x18002f00, 0x00055149}, + {0xfafafafa, 0x00000001}, + {0x18002f10, 0x00000001}, + {0x18002f10, 0x00000000}, + {0x18002f10, 0x00000001}, + {0xfafafafa, 0x00000001}, + {0x18002000, 0x000010ec}, + {0xfafafafa, 0x00000000}, +}; + +static const struct reg_sequence rt5514_patch[] = { + {RT5514_DIG_IO_CTRL, 0x00000040}, + {RT5514_CLK_CTRL1, 0x38020041}, + {RT5514_SRC_CTRL, 0x44000eee}, + {RT5514_ANA_CTRL_LDO10, 0x00028604}, + {RT5514_ANA_CTRL_ADCFED, 0x00000800}, +}; + +static const struct reg_default rt5514_reg[] = { + {RT5514_RESET, 0x00000000}, + {RT5514_PWR_ANA1, 0x00808880}, + {RT5514_PWR_ANA2, 0x00220000}, + {RT5514_I2S_CTRL1, 0x00000330}, + {RT5514_I2S_CTRL2, 0x20000000}, + {RT5514_VAD_CTRL6, 0xc00007d2}, + {RT5514_EXT_VAD_CTRL, 0x80000080}, + {RT5514_DIG_IO_CTRL, 0x00000040}, + {RT5514_PAD_CTRL1, 0x00804000}, + {RT5514_DMIC_DATA_CTRL, 0x00000005}, + {RT5514_DIG_SOURCE_CTRL, 0x00000002}, + {RT5514_SRC_CTRL, 0x44000eee}, + {RT5514_DOWNFILTER2_CTRL1, 0x0000882f}, + {RT5514_PLL_SOURCE_CTRL, 0x00000004}, + {RT5514_CLK_CTRL1, 0x38020041}, + {RT5514_CLK_CTRL2, 0x00000000}, + {RT5514_PLL3_CALIB_CTRL1, 0x00400200}, + {RT5514_PLL3_CALIB_CTRL5, 0x40220012}, + {RT5514_DELAY_BUF_CTRL1, 0x7fff006a}, + {RT5514_DELAY_BUF_CTRL3, 0x00000000}, + {RT5514_DOWNFILTER0_CTRL1, 0x00020c2f}, + {RT5514_DOWNFILTER0_CTRL2, 0x00020c2f}, + {RT5514_DOWNFILTER0_CTRL3, 0x00000362}, + {RT5514_DOWNFILTER1_CTRL1, 0x00020c2f}, + {RT5514_DOWNFILTER1_CTRL2, 0x00020c2f}, + {RT5514_DOWNFILTER1_CTRL3, 0x00000362}, + {RT5514_ANA_CTRL_LDO10, 0x00028604}, + {RT5514_ANA_CTRL_LDO18_16, 0x02000345}, + {RT5514_ANA_CTRL_ADC12, 0x0000a2a8}, + {RT5514_ANA_CTRL_ADC21, 0x00001180}, + {RT5514_ANA_CTRL_ADC22, 0x0000aaa8}, + {RT5514_ANA_CTRL_ADC23, 0x00151427}, + {RT5514_ANA_CTRL_MICBST, 0x00002000}, + {RT5514_ANA_CTRL_ADCFED, 0x00000800}, + {RT5514_ANA_CTRL_INBUF, 0x00000143}, + {RT5514_ANA_CTRL_VREF, 0x00008d50}, + {RT5514_ANA_CTRL_PLL3, 0x0000000e}, + {RT5514_ANA_CTRL_PLL1_1, 0x00000000}, + {RT5514_ANA_CTRL_PLL1_2, 0x00030220}, + {RT5514_DMIC_LP_CTRL, 0x00000000}, + {RT5514_MISC_CTRL_DSP, 0x00000000}, + {RT5514_DSP_CTRL1, 0x00055149}, + {RT5514_DSP_CTRL3, 0x00000006}, + {RT5514_DSP_CTRL4, 0x00000001}, + {RT5514_VENDOR_ID1, 0x00000001}, + {RT5514_VENDOR_ID2, 0x10ec5514}, +}; + +static bool rt5514_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5514_VENDOR_ID1: + case RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +static bool rt5514_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5514_RESET: + case RT5514_PWR_ANA1: + case RT5514_PWR_ANA2: + case RT5514_I2S_CTRL1: + case RT5514_I2S_CTRL2: + case RT5514_VAD_CTRL6: + case RT5514_EXT_VAD_CTRL: + case RT5514_DIG_IO_CTRL: + case RT5514_PAD_CTRL1: + case RT5514_DMIC_DATA_CTRL: + case RT5514_DIG_SOURCE_CTRL: + case RT5514_SRC_CTRL: + case RT5514_DOWNFILTER2_CTRL1: + case RT5514_PLL_SOURCE_CTRL: + case RT5514_CLK_CTRL1: + case RT5514_CLK_CTRL2: + case RT5514_PLL3_CALIB_CTRL1: + case RT5514_PLL3_CALIB_CTRL5: + case RT5514_DELAY_BUF_CTRL1: + case RT5514_DELAY_BUF_CTRL3: + case RT5514_DOWNFILTER0_CTRL1: + case RT5514_DOWNFILTER0_CTRL2: + case RT5514_DOWNFILTER0_CTRL3: + case RT5514_DOWNFILTER1_CTRL1: + case RT5514_DOWNFILTER1_CTRL2: + case RT5514_DOWNFILTER1_CTRL3: + case RT5514_ANA_CTRL_LDO10: + case RT5514_ANA_CTRL_LDO18_16: + case RT5514_ANA_CTRL_ADC12: + case RT5514_ANA_CTRL_ADC21: + case RT5514_ANA_CTRL_ADC22: + case RT5514_ANA_CTRL_ADC23: + case RT5514_ANA_CTRL_MICBST: + case RT5514_ANA_CTRL_ADCFED: + case RT5514_ANA_CTRL_INBUF: + case RT5514_ANA_CTRL_VREF: + case RT5514_ANA_CTRL_PLL3: + case RT5514_ANA_CTRL_PLL1_1: + case RT5514_ANA_CTRL_PLL1_2: + case RT5514_DMIC_LP_CTRL: + case RT5514_MISC_CTRL_DSP: + case RT5514_DSP_CTRL1: + case RT5514_DSP_CTRL3: + case RT5514_DSP_CTRL4: + case RT5514_VENDOR_ID1: + case RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +static bool rt5514_i2c_readable_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case RT5514_DSP_MAPPING | RT5514_RESET: + case RT5514_DSP_MAPPING | RT5514_PWR_ANA1: + case RT5514_DSP_MAPPING | RT5514_PWR_ANA2: + case RT5514_DSP_MAPPING | RT5514_I2S_CTRL1: + case RT5514_DSP_MAPPING | RT5514_I2S_CTRL2: + case RT5514_DSP_MAPPING | RT5514_VAD_CTRL6: + case RT5514_DSP_MAPPING | RT5514_EXT_VAD_CTRL: + case RT5514_DSP_MAPPING | RT5514_DIG_IO_CTRL: + case RT5514_DSP_MAPPING | RT5514_PAD_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DMIC_DATA_CTRL: + case RT5514_DSP_MAPPING | RT5514_DIG_SOURCE_CTRL: + case RT5514_DSP_MAPPING | RT5514_SRC_CTRL: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER2_CTRL1: + case RT5514_DSP_MAPPING | RT5514_PLL_SOURCE_CTRL: + case RT5514_DSP_MAPPING | RT5514_CLK_CTRL1: + case RT5514_DSP_MAPPING | RT5514_CLK_CTRL2: + case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL1: + case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL5: + case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL2: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL2: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL3: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO10: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO18_16: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC12: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC21: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC22: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC23: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_MICBST: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADCFED: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_INBUF: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_VREF: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL3: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_1: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_2: + case RT5514_DSP_MAPPING | RT5514_DMIC_LP_CTRL: + case RT5514_DSP_MAPPING | RT5514_MISC_CTRL_DSP: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL4: + case RT5514_DSP_MAPPING | RT5514_VENDOR_ID1: + case RT5514_DSP_MAPPING | RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +/* {-3, 0, +3, +4.5, +7.5, +9.5, +12, +14, +17} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-300, 300, 0), + 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0), + 4, 4, TLV_DB_SCALE_ITEM(750, 0, 0), + 5, 5, TLV_DB_SCALE_ITEM(950, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(1200, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(1400, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0) +); + +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); + +static const struct snd_kcontrol_new rt5514_snd_controls[] = { + SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST, + RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv), + SOC_DOUBLE_R_TLV("ADC1 Capture Volume", RT5514_DOWNFILTER0_CTRL1, + RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 127, 0, + adc_vol_tlv), + SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1, + RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0, + adc_vol_tlv), +}; + +/* ADC Mixer*/ +static const struct snd_kcontrol_new rt5514_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +/* DMIC Source */ +static const char * const rt5514_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL, + RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src); + +static const struct snd_kcontrol_new rt5514_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum); + +static const SOC_ENUM_SINGLE_DECL( + rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL, + RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src); + +static const struct snd_kcontrol_new rt5514_sto2_dmic_mux = + SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5514_stereo2_dmic_enum); + +/** + * rt5514_calc_dmic_clk - Calculate the frequency divider parameter of dmic. + * + * @rate: base clock rate. + * + * Choose divider parameter that gives the highest possible DMIC frequency in + * 1MHz - 3MHz range. + */ +static int rt5514_calc_dmic_clk(struct snd_soc_codec *codec, int rate) +{ + int div[] = {2, 3, 4, 8, 12, 16, 24, 32}; + int i; + + if (rate < 1000000 * div[0]) { + pr_warn("Base clock rate %d is too low\n", rate); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(div); i++) { + /* find divider that gives DMIC frequency below 3.072MHz */ + if (3072000 * div[i] >= rate) + return i; + } + + dev_warn(codec->dev, "Base clock rate %d is too high\n", rate); + return -EINVAL; +} + +static int rt5514_set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + int idx; + + idx = rt5514_calc_dmic_clk(codec, rt5514->sysclk); + if (idx < 0) + dev_err(codec->dev, "Failed to set DMIC clock\n"); + else + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1, + RT5514_CLK_DMIC_OUT_SEL_MASK, + idx << RT5514_CLK_DMIC_OUT_SEL_SFT); + + return idx; +} + +static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + + if (rt5514->sysclk_src == RT5514_SCLK_S_PLL1) + return 1; + else + return 0; +} + +static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = { + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1L"), + SND_SOC_DAPM_INPUT("DMIC1R"), + SND_SOC_DAPM_INPUT("DMIC2L"), + SND_SOC_DAPM_INPUT("DMIC2R"), + + SND_SOC_DAPM_INPUT("AMICL"), + SND_SOC_DAPM_INPUT("AMICR"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + rt5514_set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("ADC CLK", RT5514_CLK_CTRL1, + RT5514_CLK_AD_ANA1_EN_BIT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("LDO18 IN", RT5514_PWR_ANA1, + RT5514_POW_LDO18_IN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO18 ADC", RT5514_PWR_ANA1, + RT5514_POW_LDO18_ADC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO21", RT5514_PWR_ANA1, RT5514_POW_LDO21_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BG LDO18 IN", RT5514_PWR_ANA1, + RT5514_POW_BG_LDO18_IN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG LDO21", RT5514_PWR_ANA1, + RT5514_POW_BG_LDO21_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG MBIAS", RT5514_PWR_ANA2, + RT5514_POW_BG_MBIAS_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS", RT5514_PWR_ANA2, RT5514_POW_MBIAS_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF2", RT5514_PWR_ANA2, RT5514_POW_VREF2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF1", RT5514_PWR_ANA2, RT5514_POW_VREF1_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + + SND_SOC_DAPM_SUPPLY("LDO16L", RT5514_PWR_ANA2, RT5514_POWL_LDO16_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1L", RT5514_PWR_ANA2, RT5514_POW_ADC1_L_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTL2", RT5514_PWR_ANA2, RT5514_POW2_BSTL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTL", RT5514_PWR_ANA2, RT5514_POW_BSTL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCFEDL", RT5514_PWR_ANA2, RT5514_POW_ADCFEDL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCL Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("LDO16R", RT5514_PWR_ANA2, RT5514_POWR_LDO16_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1R", RT5514_PWR_ANA2, RT5514_POW_ADC1_R_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTR2", RT5514_PWR_ANA2, RT5514_POW2_BSTR_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTR", RT5514_PWR_ANA2, RT5514_POW_BSTR_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCFEDR", RT5514_PWR_ANA2, RT5514_POW_ADCFEDR_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCR Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PLL1 LDO ENABLE", RT5514_ANA_CTRL_PLL1_2, + RT5514_EN_LDO_PLL1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1 LDO", RT5514_PWR_ANA2, + RT5514_POW_PLL1_LDO_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5514_PWR_ANA2, RT5514_POW_PLL1_BIT, 0, + NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5514_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5514_sto2_dmic_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5514_CLK_CTRL1, + RT5514_CLK_AD0_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5514_CLK_CTRL1, + RT5514_CLK_AD1_EN_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5514_sto1_adc_l_mix, ARRAY_SIZE(rt5514_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5514_sto1_adc_r_mix, ARRAY_SIZE(rt5514_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5514_sto2_adc_l_mix, ARRAY_SIZE(rt5514_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5514_sto2_adc_r_mix, ARRAY_SIZE(rt5514_sto2_adc_r_mix)), + + SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo2 ADC MIXL", NULL, RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo2 ADC MIXR", NULL, RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_AD_MUTE_BIT, 1), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt5514_dapm_routes[] = { + { "DMIC1", NULL, "DMIC1L" }, + { "DMIC1", NULL, "DMIC1R" }, + { "DMIC2", NULL, "DMIC2L" }, + { "DMIC2", NULL, "DMIC2R" }, + + { "DMIC1L", NULL, "DMIC CLK" }, + { "DMIC1R", NULL, "DMIC CLK" }, + { "DMIC2L", NULL, "DMIC CLK" }, + { "DMIC2R", NULL, "DMIC CLK" }, + + { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" }, + + { "Sto1 ADC MIXL", "DMIC Switch", "Stereo1 DMIC Mux" }, + { "Sto1 ADC MIXL", "ADC Switch", "AMICL" }, + { "Sto1 ADC MIXR", "DMIC Switch", "Stereo1 DMIC Mux" }, + { "Sto1 ADC MIXR", "ADC Switch", "AMICR" }, + + { "ADC Power", NULL, "LDO18 IN" }, + { "ADC Power", NULL, "LDO18 ADC" }, + { "ADC Power", NULL, "LDO21" }, + { "ADC Power", NULL, "BG LDO18 IN" }, + { "ADC Power", NULL, "BG LDO21" }, + { "ADC Power", NULL, "BG MBIAS" }, + { "ADC Power", NULL, "MBIAS" }, + { "ADC Power", NULL, "VREF2" }, + { "ADC Power", NULL, "VREF1" }, + + { "ADCL Power", NULL, "LDO16L" }, + { "ADCL Power", NULL, "ADC1L" }, + { "ADCL Power", NULL, "BSTL2" }, + { "ADCL Power", NULL, "BSTL" }, + { "ADCL Power", NULL, "ADCFEDL" }, + + { "ADCR Power", NULL, "LDO16R" }, + { "ADCR Power", NULL, "ADC1R" }, + { "ADCR Power", NULL, "BSTR2" }, + { "ADCR Power", NULL, "BSTR" }, + { "ADCR Power", NULL, "ADCFEDR" }, + + { "AMICL", NULL, "ADC CLK" }, + { "AMICL", NULL, "ADC Power" }, + { "AMICL", NULL, "ADCL Power" }, + { "AMICR", NULL, "ADC CLK" }, + { "AMICR", NULL, "ADC Power" }, + { "AMICR", NULL, "ADCR Power" }, + + { "PLL1 LDO", NULL, "PLL1 LDO ENABLE" }, + { "PLL1", NULL, "PLL1 LDO" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" }, + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" }, + { "Stereo1 ADC MIX", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll }, + + { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" }, + + { "Sto2 ADC MIXL", "DMIC Switch", "Stereo2 DMIC Mux" }, + { "Sto2 ADC MIXL", "ADC Switch", "AMICL" }, + { "Sto2 ADC MIXR", "DMIC Switch", "Stereo2 DMIC Mux" }, + { "Sto2 ADC MIXR", "ADC Switch", "AMICR" }, + + { "Stereo2 ADC MIXL", NULL, "Sto2 ADC MIXL" }, + { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" }, + + { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" }, + { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" }, + { "Stereo2 ADC MIX", NULL, "adc stereo2 filter" }, + { "adc stereo2 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll }, + + { "AIF1TX", NULL, "Stereo1 ADC MIX"}, + { "AIF1TX", NULL, "Stereo2 ADC MIX"}, +}; + +static int rt5514_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + int pre_div, bclk_ms, frame_size; + unsigned int val_len = 0; + + rt5514->lrck = params_rate(params); + pre_div = rl6231_get_clk_info(rt5514->sysclk, rt5514->lrck); + if (pre_div < 0) { + dev_err(codec->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + + bclk_ms = frame_size > 32; + rt5514->bclk = rt5514->lrck * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5514->bclk, rt5514->lrck); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val_len = RT5514_I2S_DL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val_len = RT5514_I2S_DL_24; + break; + case SNDRV_PCM_FORMAT_S8: + val_len = RT5514_I2S_DL_8; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_I2S_DL_MASK, + val_len); + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_DIV_OUT_MASK | RT5514_SEL_ADC_OSR_MASK, + pre_div << RT5514_CLK_SYS_DIV_OUT_SFT | + pre_div << RT5514_SEL_ADC_OSR_SFT); + + return 0; +} + +static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + + case SND_SOC_DAIFMT_NB_IF: + reg_val |= RT5514_I2S_LR_INV; + break; + + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5514_I2S_BP_INV; + break; + + case SND_SOC_DAIFMT_IB_IF: + reg_val |= RT5514_I2S_BP_INV | RT5514_I2S_LR_INV; + 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 |= RT5514_I2S_DF_LEFT; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5514_I2S_DF_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5514_I2S_DF_PCM_B; + break; + + default: + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, + RT5514_I2S_DF_MASK | RT5514_I2S_BP_MASK | RT5514_I2S_LR_MASK, + reg_val); + + return 0; +} + +static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + if (freq == rt5514->sysclk && clk_id == rt5514->sysclk_src) + return 0; + + switch (clk_id) { + case RT5514_SCLK_S_MCLK: + reg_val |= RT5514_CLK_SYS_PRE_SEL_MCLK; + break; + + case RT5514_SCLK_S_PLL1: + reg_val |= RT5514_CLK_SYS_PRE_SEL_PLL; + break; + + default: + dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_PRE_SEL_MASK, reg_val); + + rt5514->sysclk = freq; + rt5514->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + struct rl6231_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(codec->dev, "PLL disabled\n"); + + rt5514->pll_in = 0; + rt5514->pll_out = 0; + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_PRE_SEL_MASK, + RT5514_CLK_SYS_PRE_SEL_MCLK); + + return 0; + } + + if (source == rt5514->pll_src && freq_in == rt5514->pll_in && + freq_out == rt5514->pll_out) + return 0; + + switch (source) { + case RT5514_PLL1_S_MCLK: + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, + RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_MCLK); + break; + + case RT5514_PLL1_S_BCLK: + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, + RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_SCLK); + break; + + default: + dev_err(codec->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(codec->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(codec->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); + + regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL1_1, + pll_code.k_code << RT5514_PLL_K_SFT | + pll_code.n_code << RT5514_PLL_N_SFT | + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5514_PLL_M_SFT); + regmap_update_bits(rt5514->regmap, RT5514_ANA_CTRL_PLL1_2, + RT5514_PLL_M_BP, pll_code.m_bp << RT5514_PLL_M_BP_SFT); + + rt5514->pll_in = freq_in; + rt5514->pll_out = freq_out; + rt5514->pll_src = source; + + return 0; +} + +static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + + if (rx_mask || tx_mask) + val |= RT5514_TDM_MODE; + + if (slots == 4) + val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH; + + + switch (slot_width) { + case 20: + val |= RT5514_CH_LEN_RX_20 | RT5514_CH_LEN_TX_20; + break; + + case 24: + val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24; + break; + + case 32: + val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32; + break; + + case 16: + default: + break; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE | + RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK | + RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK, val); + + return 0; +} + +static int rt5514_probe(struct snd_soc_codec *codec) +{ + struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec); + + rt5514->codec = codec; + + return 0; +} + +static int rt5514_i2c_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + struct rt5514_priv *rt5514 = i2c_get_clientdata(client); + + regmap_read(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val); + + return 0; +} + +static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + struct rt5514_priv *rt5514 = i2c_get_clientdata(client); + + regmap_write(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val); + + return 0; +} + +#define RT5514_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +struct snd_soc_dai_ops rt5514_aif_dai_ops = { + .hw_params = rt5514_hw_params, + .set_fmt = rt5514_set_dai_fmt, + .set_sysclk = rt5514_set_dai_sysclk, + .set_pll = rt5514_set_dai_pll, + .set_tdm_slot = rt5514_set_tdm_slot, +}; + +struct snd_soc_dai_driver rt5514_dai[] = { + { + .name = "rt5514-aif1", + .id = 0, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT5514_STEREO_RATES, + .formats = RT5514_FORMATS, + }, + .ops = &rt5514_aif_dai_ops, + } +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5514 = { + .probe = rt5514_probe, + .idle_bias_off = true, + .controls = rt5514_snd_controls, + .num_controls = ARRAY_SIZE(rt5514_snd_controls), + .dapm_widgets = rt5514_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets), + .dapm_routes = rt5514_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes), +}; + +static const struct regmap_config rt5514_i2c_regmap = { + .name = "i2c", + .reg_bits = 32, + .val_bits = 32, + + .max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2, + .readable_reg = rt5514_i2c_readable_register, + + .cache_type = REGCACHE_NONE, +}; + +static const struct regmap_config rt5514_regmap = { + .reg_bits = 16, + .val_bits = 32, + + .max_register = RT5514_VENDOR_ID2, + .volatile_reg = rt5514_volatile_register, + .readable_reg = rt5514_readable_register, + .reg_read = rt5514_i2c_read, + .reg_write = rt5514_i2c_write, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5514_reg, + .num_reg_defaults = ARRAY_SIZE(rt5514_reg), + .use_single_rw = true, +}; + +static const struct i2c_device_id rt5514_i2c_id[] = { + { "rt5514", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt5514_of_match[] = { + { .compatible = "realtek,rt5514", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5514_of_match); +#endif + +static int rt5514_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5514_priv *rt5514; + int ret; + unsigned int val; + + rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv), + GFP_KERNEL); + if (rt5514 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5514); + + rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap); + if (IS_ERR(rt5514->i2c_regmap)) { + ret = PTR_ERR(rt5514->i2c_regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + rt5514->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5514_regmap); + if (IS_ERR(rt5514->regmap)) { + ret = PTR_ERR(rt5514->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val); + if (val != RT5514_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5514\n", val); + return -ENODEV; + } + + ret = regmap_register_patch(rt5514->i2c_regmap, rt5514_i2c_patch, + ARRAY_SIZE(rt5514_i2c_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n", + ret); + + ret = regmap_register_patch(rt5514->regmap, rt5514_patch, + ARRAY_SIZE(rt5514_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5514, + rt5514_dai, ARRAY_SIZE(rt5514_dai)); +} + +static int rt5514_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +struct i2c_driver rt5514_i2c_driver = { + .driver = { + .name = "rt5514", + .of_match_table = of_match_ptr(rt5514_of_match), + }, + .probe = rt5514_i2c_probe, + .remove = rt5514_i2c_remove, + .id_table = rt5514_i2c_id, +}; +module_i2c_driver(rt5514_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5514 driver"); +MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h new file mode 100644 index 0000000..6ad8a61 --- /dev/null +++ b/sound/soc/codecs/rt5514.h @@ -0,0 +1,252 @@ +/* + * rt5514.h -- RT5514 ALSA SoC audio driver + * + * Copyright 2015 Realtek Microelectronics + * Author: Oder Chiou <oder_chiou@realtek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __RT5514_H__ +#define __RT5514_H__ + +#define RT5514_DEVICE_ID 0x10ec5514 + +#define RT5514_RESET 0x2000 +#define RT5514_PWR_ANA1 0x2004 +#define RT5514_PWR_ANA2 0x2008 +#define RT5514_I2S_CTRL1 0x2010 +#define RT5514_I2S_CTRL2 0x2014 +#define RT5514_VAD_CTRL6 0x2030 +#define RT5514_EXT_VAD_CTRL 0x206c +#define RT5514_DIG_IO_CTRL 0x2070 +#define RT5514_PAD_CTRL1 0x2080 +#define RT5514_DMIC_DATA_CTRL 0x20a0 +#define RT5514_DIG_SOURCE_CTRL 0x20a4 +#define RT5514_SRC_CTRL 0x20ac +#define RT5514_DOWNFILTER2_CTRL1 0x20d0 +#define RT5514_PLL_SOURCE_CTRL 0x2100 +#define RT5514_CLK_CTRL1 0x2104 +#define RT5514_CLK_CTRL2 0x2108 +#define RT5514_PLL3_CALIB_CTRL1 0x2110 +#define RT5514_PLL3_CALIB_CTRL5 0x2124 +#define RT5514_DELAY_BUF_CTRL1 0x2140 +#define RT5514_DELAY_BUF_CTRL3 0x2148 +#define RT5514_DOWNFILTER0_CTRL1 0x2190 +#define RT5514_DOWNFILTER0_CTRL2 0x2194 +#define RT5514_DOWNFILTER0_CTRL3 0x2198 +#define RT5514_DOWNFILTER1_CTRL1 0x21a0 +#define RT5514_DOWNFILTER1_CTRL2 0x21a4 +#define RT5514_DOWNFILTER1_CTRL3 0x21a8 +#define RT5514_ANA_CTRL_LDO10 0x2200 +#define RT5514_ANA_CTRL_LDO18_16 0x2204 +#define RT5514_ANA_CTRL_ADC12 0x2210 +#define RT5514_ANA_CTRL_ADC21 0x2214 +#define RT5514_ANA_CTRL_ADC22 0x2218 +#define RT5514_ANA_CTRL_ADC23 0x221c +#define RT5514_ANA_CTRL_MICBST 0x2220 +#define RT5514_ANA_CTRL_ADCFED 0x2224 +#define RT5514_ANA_CTRL_INBUF 0x2228 +#define RT5514_ANA_CTRL_VREF 0x222c +#define RT5514_ANA_CTRL_PLL3 0x2240 +#define RT5514_ANA_CTRL_PLL1_1 0x2260 +#define RT5514_ANA_CTRL_PLL1_2 0x2264 +#define RT5514_DMIC_LP_CTRL 0x2e00 +#define RT5514_MISC_CTRL_DSP 0x2e04 +#define RT5514_DSP_CTRL1 0x2f00 +#define RT5514_DSP_CTRL3 0x2f08 +#define RT5514_DSP_CTRL4 0x2f10 +#define RT5514_VENDOR_ID1 0x2ff0 +#define RT5514_VENDOR_ID2 0x2ff4 + +#define RT5514_DSP_MAPPING 0x18000000 + +/* RT5514_PWR_ANA1 (0x2004) */ +#define RT5514_POW_LDO18_IN (0x1 << 5) +#define RT5514_POW_LDO18_IN_BIT 5 +#define RT5514_POW_LDO18_ADC (0x1 << 4) +#define RT5514_POW_LDO18_ADC_BIT 4 +#define RT5514_POW_LDO21 (0x1 << 3) +#define RT5514_POW_LDO21_BIT 3 +#define RT5514_POW_BG_LDO18_IN (0x1 << 2) +#define RT5514_POW_BG_LDO18_IN_BIT 2 +#define RT5514_POW_BG_LDO21 (0x1 << 1) +#define RT5514_POW_BG_LDO21_BIT 1 + +/* RT5514_PWR_ANA2 (0x2008) */ +#define RT5514_POW_PLL1 (0x1 << 18) +#define RT5514_POW_PLL1_BIT 18 +#define RT5514_POW_PLL1_LDO (0x1 << 16) +#define RT5514_POW_PLL1_LDO_BIT 16 +#define RT5514_POW_BG_MBIAS (0x1 << 15) +#define RT5514_POW_BG_MBIAS_BIT 15 +#define RT5514_POW_MBIAS (0x1 << 14) +#define RT5514_POW_MBIAS_BIT 14 +#define RT5514_POW_VREF2 (0x1 << 13) +#define RT5514_POW_VREF2_BIT 13 +#define RT5514_POW_VREF1 (0x1 << 12) +#define RT5514_POW_VREF1_BIT 12 +#define RT5514_POWR_LDO16 (0x1 << 11) +#define RT5514_POWR_LDO16_BIT 11 +#define RT5514_POWL_LDO16 (0x1 << 10) +#define RT5514_POWL_LDO16_BIT 10 +#define RT5514_POW_ADC2 (0x1 << 9) +#define RT5514_POW_ADC2_BIT 9 +#define RT5514_POW_INPUT_BUF (0x1 << 8) +#define RT5514_POW_INPUT_BUF_BIT 8 +#define RT5514_POW_ADC1_R (0x1 << 7) +#define RT5514_POW_ADC1_R_BIT 7 +#define RT5514_POW_ADC1_L (0x1 << 6) +#define RT5514_POW_ADC1_L_BIT 6 +#define RT5514_POW2_BSTR (0x1 << 5) +#define RT5514_POW2_BSTR_BIT 5 +#define RT5514_POW2_BSTL (0x1 << 4) +#define RT5514_POW2_BSTL_BIT 4 +#define RT5514_POW_BSTR (0x1 << 3) +#define RT5514_POW_BSTR_BIT 3 +#define RT5514_POW_BSTL (0x1 << 2) +#define RT5514_POW_BSTL_BIT 2 +#define RT5514_POW_ADCFEDR (0x1 << 1) +#define RT5514_POW_ADCFEDR_BIT 1 +#define RT5514_POW_ADCFEDL (0x1 << 0) +#define RT5514_POW_ADCFEDL_BIT 0 + +/* RT5514_I2S_CTRL1 (0x2010) */ +#define RT5514_TDM_MODE (0x1 << 28) +#define RT5514_TDM_MODE_SFT 28 +#define RT5514_I2S_LR_MASK (0x1 << 26) +#define RT5514_I2S_LR_SFT 26 +#define RT5514_I2S_LR_NOR (0x0 << 26) +#define RT5514_I2S_LR_INV (0x1 << 26) +#define RT5514_I2S_BP_MASK (0x1 << 25) +#define RT5514_I2S_BP_SFT 25 +#define RT5514_I2S_BP_NOR (0x0 << 25) +#define RT5514_I2S_BP_INV (0x1 << 25) +#define RT5514_I2S_DF_MASK (0x7 << 16) +#define RT5514_I2S_DF_SFT 16 +#define RT5514_I2S_DF_I2S (0x0 << 16) +#define RT5514_I2S_DF_LEFT (0x1 << 16) +#define RT5514_I2S_DF_PCM_A (0x2 << 16) +#define RT5514_I2S_DF_PCM_B (0x3 << 16) +#define RT5514_TDMSLOT_SEL_RX_MASK (0x3 << 10) +#define RT5514_TDMSLOT_SEL_RX_SFT 10 +#define RT5514_TDMSLOT_SEL_RX_4CH (0x1 << 10) +#define RT5514_CH_LEN_RX_MASK (0x3 << 8) +#define RT5514_CH_LEN_RX_SFT 8 +#define RT5514_CH_LEN_RX_16 (0x0 << 8) +#define RT5514_CH_LEN_RX_20 (0x1 << 8) +#define RT5514_CH_LEN_RX_24 (0x2 << 8) +#define RT5514_CH_LEN_RX_32 (0x3 << 8) +#define RT5514_TDMSLOT_SEL_TX_MASK (0x3 << 6) +#define RT5514_TDMSLOT_SEL_TX_SFT 6 +#define RT5514_TDMSLOT_SEL_TX_4CH (0x1 << 6) +#define RT5514_CH_LEN_TX_MASK (0x3 << 4) +#define RT5514_CH_LEN_TX_SFT 4 +#define RT5514_CH_LEN_TX_16 (0x0 << 4) +#define RT5514_CH_LEN_TX_20 (0x1 << 4) +#define RT5514_CH_LEN_TX_24 (0x2 << 4) +#define RT5514_CH_LEN_TX_32 (0x3 << 4) +#define RT5514_I2S_DL_MASK (0x3 << 0) +#define RT5514_I2S_DL_SFT 0 +#define RT5514_I2S_DL_16 (0x0 << 0) +#define RT5514_I2S_DL_20 (0x1 << 0) +#define RT5514_I2S_DL_24 (0x2 << 0) +#define RT5514_I2S_DL_8 (0x3 << 0) + +/* RT5514_DIG_SOURCE_CTRL (0x20a4) */ +#define RT5514_AD1_DMIC_INPUT_SEL (0x1 << 1) +#define RT5514_AD1_DMIC_INPUT_SEL_SFT 1 +#define RT5514_AD0_DMIC_INPUT_SEL (0x1 << 0) +#define RT5514_AD0_DMIC_INPUT_SEL_SFT 0 + +/* RT5514_PLL_SOURCE_CTRL (0x2100) */ +#define RT5514_PLL_1_SEL_MASK (0x7 << 12) +#define RT5514_PLL_1_SEL_SFT 12 +#define RT5514_PLL_1_SEL_SCLK (0x3 << 12) +#define RT5514_PLL_1_SEL_MCLK (0x4 << 12) + +/* RT5514_CLK_CTRL1 (0x2104) */ +#define RT5514_CLK_AD_ANA1_EN (0x1 << 31) +#define RT5514_CLK_AD_ANA1_EN_BIT 31 +#define RT5514_CLK_AD1_EN (0x1 << 24) +#define RT5514_CLK_AD1_EN_BIT 24 +#define RT5514_CLK_AD0_EN (0x1 << 23) +#define RT5514_CLK_AD0_EN_BIT 23 +#define RT5514_CLK_DMIC_OUT_SEL_MASK (0x7 << 8) +#define RT5514_CLK_DMIC_OUT_SEL_SFT 8 + +/* RT5514_CLK_CTRL2 (0x2108) */ +#define RT5514_CLK_SYS_DIV_OUT_MASK (0x7 << 8) +#define RT5514_CLK_SYS_DIV_OUT_SFT 8 +#define RT5514_SEL_ADC_OSR_MASK (0x7 << 4) +#define RT5514_SEL_ADC_OSR_SFT 4 +#define RT5514_CLK_SYS_PRE_SEL_MASK (0x3 << 0) +#define RT5514_CLK_SYS_PRE_SEL_SFT 0 +#define RT5514_CLK_SYS_PRE_SEL_MCLK (0x2 << 0) +#define RT5514_CLK_SYS_PRE_SEL_PLL (0x3 << 0) + +/* RT5514_DOWNFILTER_CTRL (0x2190 0x2194 0x21a0 0x21a4) */ +#define RT5514_AD_DMIC_MIX (0x1 << 11) +#define RT5514_AD_DMIC_MIX_BIT 11 +#define RT5514_AD_AD_MIX (0x1 << 10) +#define RT5514_AD_AD_MIX_BIT 10 +#define RT5514_AD_AD_MUTE (0x1 << 7) +#define RT5514_AD_AD_MUTE_BIT 7 +#define RT5514_AD_GAIN_MASK (0x7f << 0) +#define RT5514_AD_GAIN_SFT 0 + +/* RT5514_ANA_CTRL_MICBST (0x2220) */ +#define RT5514_SEL_BSTL_MASK (0xf << 4) +#define RT5514_SEL_BSTL_SFT 4 +#define RT5514_SEL_BSTR_MASK (0xf << 0) +#define RT5514_SEL_BSTR_SFT 0 + +/* RT5514_ANA_CTRL_PLL1_1 (0x2260) */ +#define RT5514_PLL_K_MAX 0x1f +#define RT5514_PLL_K_MASK (RT5514_PLL_K_MAX << 16) +#define RT5514_PLL_K_SFT 16 +#define RT5514_PLL_N_MAX 0x1ff +#define RT5514_PLL_N_MASK (RT5514_PLL_N_MAX << 7) +#define RT5514_PLL_N_SFT 4 +#define RT5514_PLL_M_MAX 0xf +#define RT5514_PLL_M_MASK (RT5514_PLL_M_MAX << 0) +#define RT5514_PLL_M_SFT 0 + +/* RT5514_ANA_CTRL_PLL1_2 (0x2264) */ +#define RT5514_PLL_M_BP (0x1 << 2) +#define RT5514_PLL_M_BP_SFT 2 +#define RT5514_PLL_K_BP (0x1 << 1) +#define RT5514_PLL_K_BP_SFT 1 +#define RT5514_EN_LDO_PLL1 (0x1 << 0) +#define RT5514_EN_LDO_PLL1_BIT 0 + +#define RT5514_PLL_INP_MAX 40000000 +#define RT5514_PLL_INP_MIN 256000 + +/* System Clock Source */ +enum { + RT5514_SCLK_S_MCLK, + RT5514_SCLK_S_PLL1, +}; + +/* PLL1 Source */ +enum { + RT5514_PLL1_S_MCLK, + RT5514_PLL1_S_BCLK, +}; + +struct rt5514_priv { + struct snd_soc_codec *codec; + struct regmap *i2c_regmap, *regmap; + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int pll_src; + int pll_in; + int pll_out; +}; + +#endif /* __RT5514_H__ */ diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index c61d38b..7af5e73 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -776,7 +776,7 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = { /* IN1/IN2 Control */ SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1, - RT5645_BST_SFT1, 8, 0, bst_tlv), + RT5645_BST_SFT1, 12, 0, bst_tlv), SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL, RT5645_BST_SFT2, 8, 0, bst_tlv), @@ -1674,7 +1674,7 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on) regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); - msleep(70); + msleep(90); rt5645->hp_on = true; } else { /* depop parameters */ @@ -3029,13 +3029,18 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, RT5645_PWR_BG | RT5645_PWR_VREF2, RT5645_PWR_VREF1 | RT5645_PWR_MB | RT5645_PWR_BG | RT5645_PWR_VREF2); + mdelay(10); snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_FV1 | RT5645_PWR_FV2, RT5645_PWR_FV1 | RT5645_PWR_FV2); - if (rt5645->en_button_func && - snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) - queue_delayed_work(system_power_efficient_wq, - &rt5645->jack_detect_work, msecs_to_jiffies(0)); + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140); + msleep(40); + if (rt5645->en_button_func) + queue_delayed_work(system_power_efficient_wq, + &rt5645->jack_detect_work, + msecs_to_jiffies(0)); + } break; case SND_SOC_BIAS_OFF: diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index 820d8fa..fb8ea05 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -3985,7 +3985,6 @@ static int rt5659_i2c_probe(struct i2c_client *i2c, if (rt5659 == NULL) return -ENOMEM; - rt5659->i2c = i2c; i2c_set_clientdata(i2c, rt5659); if (pdata) @@ -4157,24 +4156,17 @@ static int rt5659_i2c_probe(struct i2c_client *i2c, INIT_DELAYED_WORK(&rt5659->jack_detect_work, rt5659_jack_detect_work); - if (rt5659->i2c->irq) { - ret = request_threaded_irq(rt5659->i2c->irq, NULL, rt5659_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5659", rt5659); if (ret) dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); } - ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5659, + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5659, rt5659_dai, ARRAY_SIZE(rt5659_dai)); - - if (ret) { - if (rt5659->i2c->irq) - free_irq(rt5659->i2c->irq, rt5659); - } - - return 0; } static int rt5659_i2c_remove(struct i2c_client *i2c) @@ -4191,24 +4183,29 @@ void rt5659_i2c_shutdown(struct i2c_client *client) regmap_write(rt5659->regmap, RT5659_RESET, 0); } +#ifdef CONFIG_OF static const struct of_device_id rt5659_of_match[] = { { .compatible = "realtek,rt5658", }, { .compatible = "realtek,rt5659", }, - {}, + { }, }; +MODULE_DEVICE_TABLE(of, rt5659_of_match); +#endif +#ifdef CONFIG_ACPI static struct acpi_device_id rt5659_acpi_match[] = { - { "10EC5658", 0}, - { "10EC5659", 0}, - { }, + { "10EC5658", 0, }, + { "10EC5659", 0, }, + { }, }; MODULE_DEVICE_TABLE(acpi, rt5659_acpi_match); +#endif struct i2c_driver rt5659_i2c_driver = { .driver = { .name = "rt5659", .owner = THIS_MODULE, - .of_match_table = rt5659_of_match, + .of_match_table = of_match_ptr(rt5659_of_match), .acpi_match_table = ACPI_PTR(rt5659_acpi_match), }, .probe = rt5659_i2c_probe, diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h index 8f07ee9..d31c9e5 100644 --- a/sound/soc/codecs/rt5659.h +++ b/sound/soc/codecs/rt5659.h @@ -1792,7 +1792,6 @@ struct rt5659_priv { struct snd_soc_codec *codec; struct rt5659_platform_data pdata; struct regmap *regmap; - struct i2c_client *i2c; struct gpio_desc *gpiod_ldo1_en; struct gpio_desc *gpiod_reset; struct snd_soc_jack *hs_jack; diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c index 21ca3a5..d374c18 100644 --- a/sound/soc/codecs/sigmadsp-i2c.c +++ b/sound/soc/codecs/sigmadsp-i2c.c @@ -31,7 +31,10 @@ static int sigmadsp_write_i2c(void *control_data, kfree(buf); - return ret; + if (ret < 0) + return ret; + + return 0; } static int sigmadsp_read_i2c(void *control_data, diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 781398fb..f7a6ce7 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -446,7 +446,7 @@ static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.integer.value[0] = dac33->fifo_mode; + ucontrol->value.enumerated.item[0] = dac33->fifo_mode; return 0; } @@ -458,17 +458,16 @@ static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol, struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); int ret = 0; - if (dac33->fifo_mode == ucontrol->value.integer.value[0]) + if (dac33->fifo_mode == ucontrol->value.enumerated.item[0]) return 0; /* Do not allow changes while stream is running*/ if (snd_soc_codec_is_active(codec)) return -EPERM; - if (ucontrol->value.integer.value[0] < 0 || - ucontrol->value.integer.value[0] >= DAC33_FIFO_LAST_MODE) + if (ucontrol->value.enumerated.item[0] >= DAC33_FIFO_LAST_MODE) ret = -EINVAL; else - dac33->fifo_mode = ucontrol->value.integer.value[0]; + dac33->fifo_mode = ucontrol->value.enumerated.item[0]; return ret; } diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 7693c11..1b79778 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -175,7 +175,7 @@ static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.integer.value[0] = wl1273->mode; + ucontrol->value.enumerated.item[0] = wl1273->mode; return 0; } @@ -193,18 +193,17 @@ static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); - if (wl1273->mode == ucontrol->value.integer.value[0]) + if (wl1273->mode == ucontrol->value.enumerated.item[0]) return 0; /* Do not allow changes while stream is running */ if (snd_soc_codec_is_active(codec)) return -EPERM; - if (ucontrol->value.integer.value[0] < 0 || - ucontrol->value.integer.value[0] >= ARRAY_SIZE(wl1273_audio_route)) + if (ucontrol->value.enumerated.item[0] >= ARRAY_SIZE(wl1273_audio_route)) return -EINVAL; - wl1273->mode = ucontrol->value.integer.value[0]; + wl1273->mode = ucontrol->value.enumerated.item[0]; return 1; } @@ -219,7 +218,7 @@ static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol, dev_dbg(codec->dev, "%s: enter.\n", __func__); - ucontrol->value.integer.value[0] = wl1273->core->audio_mode; + ucontrol->value.enumerated.item[0] = wl1273->core->audio_mode; return 0; } @@ -233,7 +232,7 @@ static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, dev_dbg(codec->dev, "%s: enter.\n", __func__); - val = ucontrol->value.integer.value[0]; + val = ucontrol->value.enumerated.item[0]; if (wl1273->core->audio_mode == val) return 0; diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 64637d1..a8b3e3f 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -619,7 +619,7 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct arizona *arizona = dev_get_drvdata(codec->dev->parent); - unsigned int v; + unsigned int v = 0; int ret; switch (event) { @@ -654,7 +654,7 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w, break; } - return wm_adsp2_early_event(w, kcontrol, event); + return wm_adsp2_early_event(w, kcontrol, event, v); } static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, @@ -1408,7 +1408,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), -WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev), +WM_ADSP2("DSP1", 0, wm5102_adsp_power_ev), SND_SOC_DAPM_OUTPUT("HPOUT1L"), SND_SOC_DAPM_OUTPUT("HPOUT1R"), @@ -1599,6 +1599,9 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "Slim2 Capture", NULL, "SYSCLK" }, { "Slim3 Capture", NULL, "SYSCLK" }, + { "Audio Trace DSP", NULL, "DSP1" }, + { "Audio Trace DSP", NULL, "SYSCLK" }, + { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -1735,7 +1738,7 @@ static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source, } } -#define WM5102_RATES SNDRV_PCM_RATE_8000_192000 +#define WM5102_RATES SNDRV_PCM_RATE_KNOT #define WM5102_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) @@ -1864,14 +1867,67 @@ static struct snd_soc_dai_driver wm5102_dai[] = { }, .ops = &arizona_simple_dai_ops, }, + { + .name = "wm5102-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .compress_new = snd_soc_new_compress, + }, + { + .name = "wm5102-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 4, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + }, }; +static int wm5102_open(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct wm5102_priv *priv = snd_soc_codec_get_drvdata(rtd->codec); + + return wm_adsp_compr_open(&priv->core.adsp[0], stream); +} + +static irqreturn_t wm5102_adsp2_irq(int irq, void *data) +{ + struct wm5102_priv *priv = data; + struct arizona *arizona = priv->core.arizona; + int ret; + + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[0]); + if (ret == -ENODEV) { + dev_err(arizona->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + static int wm5102_codec_probe(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->core.arizona; int ret; + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", wm5102_adsp2_irq, + priv); + if (ret != 0) { + dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret); + return ret; + } + ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec); if (ret) return ret; @@ -1946,6 +2002,20 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5102 = { .num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes), }; +static struct snd_compr_ops wm5102_compr_ops = { + .open = wm5102_open, + .free = wm_adsp_compr_free, + .set_params = wm_adsp_compr_set_params, + .get_caps = wm_adsp_compr_get_caps, + .trigger = wm_adsp_compr_trigger, + .pointer = wm_adsp_compr_pointer, + .copy = wm_adsp_compr_copy, +}; + +static struct snd_soc_platform_driver wm5102_compr_platform = { + .compr_ops = &wm5102_compr_ops, +}; + static int wm5102_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -2005,12 +2075,25 @@ static int wm5102_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); - return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102, + ret = snd_soc_register_platform(&pdev->dev, &wm5102_compr_platform); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); + return ret; + } + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102, wm5102_dai, ARRAY_SIZE(wm5102_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); + snd_soc_unregister_platform(&pdev->dev); + } + + return ret; } static int wm5102_remove(struct platform_device *pdev) { + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 6088d30..83ba70f 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -191,6 +191,25 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } +static int wm5110_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + unsigned int v; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); + if (ret != 0) { + dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret); + return ret; + } + + v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; + + return wm_adsp2_early_event(w, kcontrol, event, v); +} + static const struct reg_sequence wm5110_no_dre_left_enable[] = { { 0x3024, 0xE410 }, { 0x3025, 0x0056 }, @@ -1179,10 +1198,10 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, NULL, 0), -WM_ADSP2("DSP1", 0), -WM_ADSP2("DSP2", 1), -WM_ADSP2("DSP3", 2), -WM_ADSP2("DSP4", 3), +WM_ADSP2("DSP1", 0, wm5110_adsp_power_ev), +WM_ADSP2("DSP2", 1, wm5110_adsp_power_ev), +WM_ADSP2("DSP3", 2, wm5110_adsp_power_ev), +WM_ADSP2("DSP4", 3, wm5110_adsp_power_ev), SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), @@ -1809,6 +1828,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "Voice Control DSP", NULL, "DSP3" }, { "Voice Control DSP", NULL, "SYSCLK" }, + { "Audio Trace DSP", NULL, "DSP1" }, + { "Audio Trace DSP", NULL, "SYSCLK" }, + { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -2002,7 +2024,7 @@ static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source, } } -#define WM5110_RATES SNDRV_PCM_RATE_8000_192000 +#define WM5110_RATES SNDRV_PCM_RATE_KNOT #define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) @@ -2152,6 +2174,27 @@ static struct snd_soc_dai_driver wm5110_dai[] = { .formats = WM5110_FORMATS, }, }, + { + .name = "wm5110-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .compress_new = snd_soc_new_compress, + }, + { + .name = "wm5110-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 6, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + }, }; static int wm5110_open(struct snd_compr_stream *stream) @@ -2163,6 +2206,8 @@ static int wm5110_open(struct snd_compr_stream *stream) if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) { n_adsp = 2; + } else if (strcmp(rtd->codec_dai->name, "wm5110-dsp-trace") == 0) { + n_adsp = 0; } else { dev_err(arizona->dev, "No suitable compressed stream for DAI '%s'\n", @@ -2175,12 +2220,21 @@ static int wm5110_open(struct snd_compr_stream *stream) static irqreturn_t wm5110_adsp2_irq(int irq, void *data) { - struct wm5110_priv *florida = data; - int ret; + struct wm5110_priv *priv = data; + struct arizona *arizona = priv->core.arizona; + int serviced = 0; + int i, ret; - ret = wm_adsp_compr_handle_irq(&florida->core.adsp[2]); - if (ret == -ENODEV) + for (i = 0; i < WM5110_NUM_ADSP; ++i) { + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]); + if (ret != -ENODEV) + serviced++; + } + + if (!serviced) { + dev_err(arizona->dev, "Spurious compressed data IRQ\n"); return IRQ_NONE; + } return IRQ_HANDLED; } @@ -2366,7 +2420,7 @@ static int wm5110_probe(struct platform_device *pdev) ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform); if (ret < 0) { dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); - goto error; + return ret; } ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110, @@ -2376,12 +2430,12 @@ static int wm5110_probe(struct platform_device *pdev) snd_soc_unregister_platform(&pdev->dev); } -error: return ret; } static int wm5110_remove(struct platform_device *pdev) { + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 61299ca..6f1024f 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -233,7 +233,7 @@ static int wm8753_get_dai(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.integer.value[0] = wm8753->dai_func; + ucontrol->value.enumerated.item[0] = wm8753->dai_func; return 0; } @@ -244,7 +244,7 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); u16 ioctl; - if (wm8753->dai_func == ucontrol->value.integer.value[0]) + if (wm8753->dai_func == ucontrol->value.enumerated.item[0]) return 0; if (snd_soc_codec_is_active(codec)) @@ -252,7 +252,7 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, ioctl = snd_soc_read(codec, WM8753_IOCTL); - wm8753->dai_func = ucontrol->value.integer.value[0]; + wm8753->dai_func = ucontrol->value.enumerated.item[0]; if (((ioctl >> 2) & 0x3) == wm8753->dai_func) return 1; diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 8172e49..edd7a77 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -396,7 +396,7 @@ static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); struct wm8904_pdata *pdata = wm8904->pdata; - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; if (value >= pdata->num_drc_cfgs) return -EINVAL; @@ -467,7 +467,7 @@ static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); struct wm8904_pdata *pdata = wm8904->pdata; - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; if (value >= pdata->num_retune_mobile_cfgs) return -EINVAL; diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c index c799cca..6b864c0 100644 --- a/sound/soc/codecs/wm8958-dsp2.c +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -459,7 +459,7 @@ static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; int reg; /* Don't allow on the fly reconfiguration */ @@ -549,7 +549,7 @@ static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; int reg; /* Don't allow on the fly reconfiguration */ @@ -582,7 +582,7 @@ static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; int reg; /* Don't allow on the fly reconfiguration */ @@ -749,7 +749,7 @@ static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; int reg; /* Don't allow on the fly reconfiguration */ diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index ff23772..d7f444f 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -240,13 +240,13 @@ SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, 7, 1, 1), -SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv), -SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv), -SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv), -SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume", WM8960_RINPATH, 4, 3, 0, micboost_tlv), @@ -643,29 +643,31 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec) return -EINVAL; } - /* check if the sysclk frequency is available. */ - for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { - if (sysclk_divs[i] == -1) - continue; - sysclk = freq_out / sysclk_divs[i]; - for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { - if (sysclk == dac_divs[j] * lrclk) { + if (wm8960->clk_id != WM8960_SYSCLK_PLL) { + /* check if the sysclk frequency is available. */ + for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + if (sysclk_divs[i] == -1) + continue; + sysclk = freq_out / sysclk_divs[i]; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { + if (sysclk != dac_divs[j] * lrclk) + continue; for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) if (sysclk == bclk * bclk_divs[k] / 10) break; if (k != ARRAY_SIZE(bclk_divs)) break; } + if (j != ARRAY_SIZE(dac_divs)) + break; } - if (j != ARRAY_SIZE(dac_divs)) - break; - } - if (i != ARRAY_SIZE(sysclk_divs)) { - goto configure_clock; - } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { - dev_err(codec->dev, "failed to configure clock\n"); - return -EINVAL; + if (i != ARRAY_SIZE(sysclk_divs)) { + goto configure_clock; + } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { + dev_err(codec->dev, "failed to configure clock\n"); + return -EINVAL; + } } /* get a available pll out frequency and set pll */ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 7350ff6..0c002a5 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -497,9 +497,9 @@ static int eqmode_get(struct snd_kcontrol *kcontrol, reg = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF); if (reg & WM8983_EQ3DMODE) - ucontrol->value.integer.value[0] = 1; + ucontrol->value.enumerated.item[0] = 1; else - ucontrol->value.integer.value[0] = 0; + ucontrol->value.enumerated.item[0] = 0; return 0; } @@ -511,18 +511,18 @@ static int eqmode_put(struct snd_kcontrol *kcontrol, unsigned int regpwr2, regpwr3; unsigned int reg_eq; - if (ucontrol->value.integer.value[0] != 0 - && ucontrol->value.integer.value[0] != 1) + if (ucontrol->value.enumerated.item[0] != 0 + && ucontrol->value.enumerated.item[0] != 1) return -EINVAL; reg_eq = snd_soc_read(codec, WM8983_EQ1_LOW_SHELF); switch ((reg_eq & WM8983_EQ3DMODE) >> WM8983_EQ3DMODE_SHIFT) { case 0: - if (!ucontrol->value.integer.value[0]) + if (!ucontrol->value.enumerated.item[0]) return 0; break; case 1: - if (ucontrol->value.integer.value[0]) + if (ucontrol->value.enumerated.item[0]) return 0; break; } @@ -537,7 +537,7 @@ static int eqmode_put(struct snd_kcontrol *kcontrol, /* set the desired eqmode */ snd_soc_update_bits(codec, WM8983_EQ1_LOW_SHELF, WM8983_EQ3DMODE_MASK, - ucontrol->value.integer.value[0] + ucontrol->value.enumerated.item[0] << WM8983_EQ3DMODE_SHIFT); /* restore DAC/ADC configuration */ snd_soc_write(codec, WM8983_POWER_MANAGEMENT_2, regpwr2); diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 9918152..6ac76fe 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -531,9 +531,9 @@ static int eqmode_get(struct snd_kcontrol *kcontrol, reg = snd_soc_read(codec, WM8985_EQ1_LOW_SHELF); if (reg & WM8985_EQ3DMODE) - ucontrol->value.integer.value[0] = 1; + ucontrol->value.enumerated.item[0] = 1; else - ucontrol->value.integer.value[0] = 0; + ucontrol->value.enumerated.item[0] = 0; return 0; } @@ -545,18 +545,18 @@ static int eqmode_put(struct snd_kcontrol *kcontrol, unsigned int regpwr2, regpwr3; unsigned int reg_eq; - if (ucontrol->value.integer.value[0] != 0 - && ucontrol->value.integer.value[0] != 1) + if (ucontrol->value.enumerated.item[0] != 0 + && ucontrol->value.enumerated.item[0] != 1) return -EINVAL; reg_eq = snd_soc_read(codec, WM8985_EQ1_LOW_SHELF); switch ((reg_eq & WM8985_EQ3DMODE) >> WM8985_EQ3DMODE_SHIFT) { case 0: - if (!ucontrol->value.integer.value[0]) + if (!ucontrol->value.enumerated.item[0]) return 0; break; case 1: - if (ucontrol->value.integer.value[0]) + if (ucontrol->value.enumerated.item[0]) return 0; break; } @@ -573,7 +573,7 @@ static int eqmode_put(struct snd_kcontrol *kcontrol, /* set the desired eqmode */ snd_soc_update_bits(codec, WM8985_EQ1_LOW_SHELF, WM8985_EQ3DMODE_MASK, - ucontrol->value.integer.value[0] + ucontrol->value.enumerated.item[0] << WM8985_EQ3DMODE_SHIFT); /* restore DAC/ADC configuration */ snd_soc_write(codec, WM8985_POWER_MANAGEMENT_2, regpwr2); diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 2ccbb32..a18aecb 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -362,7 +362,7 @@ static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol, struct wm8994 *control = wm8994->wm8994; struct wm8994_pdata *pdata = &control->pdata; int drc = wm8994_get_drc(kcontrol->id.name); - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; if (drc < 0) return drc; @@ -469,7 +469,7 @@ static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct wm8994 *control = wm8994->wm8994; struct wm8994_pdata *pdata = &control->pdata; int block = wm8994_get_retune_mobile_block(kcontrol->id.name); - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; if (block < 0) return block; diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 8d7d6c0..f99b34f 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -416,7 +416,7 @@ static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); struct wm8996_pdata *pdata = &wm8996->pdata; int block = wm8996_get_retune_mobile_block(kcontrol->id.name); - int value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.enumerated.item[0]; if (block < 0) return block; diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index b4dba3a..52d766e 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -943,7 +943,7 @@ static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source, } } -#define WM8997_RATES SNDRV_PCM_RATE_8000_192000 +#define WM8997_RATES SNDRV_PCM_RATE_KNOT #define WM8997_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 7719bc5..0123960 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -1170,7 +1170,7 @@ static const struct snd_soc_dapm_route wm8998_dapm_routes[] = { { "DRC1 Signal Activity", NULL, "DRC1R" }, }; -#define WM8998_RATES SNDRV_PCM_RATE_8000_192000 +#define WM8998_RATES SNDRV_PCM_RATE_KNOT #define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index ccb3b15..363b3b6 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -344,9 +344,9 @@ static int speaker_mode_get(struct snd_kcontrol *kcontrol, reg = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2); if (reg & WM9081_SPK_MODE) - ucontrol->value.integer.value[0] = 1; + ucontrol->value.enumerated.item[0] = 1; else - ucontrol->value.integer.value[0] = 0; + ucontrol->value.enumerated.item[0] = 0; return 0; } @@ -365,7 +365,7 @@ static int speaker_mode_put(struct snd_kcontrol *kcontrol, unsigned int reg2 = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2); /* Are we changing anything? */ - if (ucontrol->value.integer.value[0] == + if (ucontrol->value.enumerated.item[0] == ((reg2 & WM9081_SPK_MODE) != 0)) return 0; @@ -373,7 +373,7 @@ static int speaker_mode_put(struct snd_kcontrol *kcontrol, if (reg_pwr & WM9081_SPK_ENA) return -EINVAL; - if (ucontrol->value.integer.value[0]) { + if (ucontrol->value.enumerated.item[0]) { /* Class AB */ reg2 &= ~(WM9081_SPK_INV_MUTE | WM9081_OUT_SPK_CTRL); reg2 |= WM9081_SPK_MODE; diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 79e1436..9849643 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1212,7 +1212,7 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) if (IS_ERR(wm9713->ac97)) return PTR_ERR(wm9713->ac97); - regmap = devm_regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); + regmap = regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); if (IS_ERR(regmap)) { snd_soc_free_ac97_codec(wm9713->ac97); return PTR_ERR(regmap); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 33806d4..d3b1cb1 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -32,9 +32,6 @@ #include <sound/initval.h> #include <sound/tlv.h> -#include <linux/mfd/arizona/registers.h> - -#include "arizona.h" #include "wm_adsp.h" #define adsp_crit(_dsp, fmt, ...) \ @@ -295,6 +292,8 @@ struct wm_adsp_compr { u32 *raw_buf; unsigned int copied_total; + + unsigned int sample_rate; }; #define WM_ADSP_DATA_WORD_SIZE 3 @@ -328,7 +327,7 @@ struct wm_adsp_buffer_region_def { unsigned int size_offset; }; -static struct wm_adsp_buffer_region_def ez2control_regions[] = { +static const struct wm_adsp_buffer_region_def default_regions[] = { { .mem_type = WMFW_ADSP2_XM, .base_offset = HOST_BUFFER_FIELD(X_buf_base), @@ -350,10 +349,10 @@ struct wm_adsp_fw_caps { u32 id; struct snd_codec_desc desc; int num_regions; - struct wm_adsp_buffer_region_def *region_defs; + const struct wm_adsp_buffer_region_def *region_defs; }; -static const struct wm_adsp_fw_caps ez2control_caps[] = { +static const struct wm_adsp_fw_caps ctrl_caps[] = { { .id = SND_AUDIOCODEC_BESPOKE, .desc = { @@ -362,8 +361,26 @@ static const struct wm_adsp_fw_caps ez2control_caps[] = { .num_sample_rates = 1, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, - .num_regions = ARRAY_SIZE(ez2control_regions), - .region_defs = ez2control_regions, + .num_regions = ARRAY_SIZE(default_regions), + .region_defs = default_regions, + }, +}; + +static const struct wm_adsp_fw_caps trace_caps[] = { + { + .id = SND_AUDIOCODEC_BESPOKE, + .desc = { + .max_ch = 8, + .sample_rates = { + 4000, 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000, 64000, 88200, + 96000, 176400, 192000 + }, + .num_sample_rates = 15, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .num_regions = ARRAY_SIZE(default_regions), + .region_defs = default_regions, }, }; @@ -382,11 +399,16 @@ static const struct { [WM_ADSP_FW_CTRL] = { .file = "ctrl", .compr_direction = SND_COMPRESS_CAPTURE, - .num_caps = ARRAY_SIZE(ez2control_caps), - .caps = ez2control_caps, + .num_caps = ARRAY_SIZE(ctrl_caps), + .caps = ctrl_caps, }, [WM_ADSP_FW_ASR] = { .file = "asr" }, - [WM_ADSP_FW_TRACE] = { .file = "trace" }, + [WM_ADSP_FW_TRACE] = { + .file = "trace", + .compr_direction = SND_COMPRESS_CAPTURE, + .num_caps = ARRAY_SIZE(trace_caps), + .caps = trace_caps, + }, [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, [WM_ADSP_FW_MISC] = { .file = "misc" }, }; @@ -586,7 +608,7 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); - ucontrol->value.integer.value[0] = dsp[e->shift_l].fw; + ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; return 0; } @@ -599,10 +621,10 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); int ret = 0; - if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw) + if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) return 0; - if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) + if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) return -EINVAL; mutex_lock(&dsp[e->shift_l].pwr_lock); @@ -610,7 +632,7 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, if (dsp[e->shift_l].running || dsp[e->shift_l].compr) ret = -EBUSY; else - dsp[e->shift_l].fw = ucontrol->value.integer.value[0]; + dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; mutex_unlock(&dsp[e->shift_l].pwr_lock); @@ -719,19 +741,19 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, reg = ctl->alg_region.base + ctl->offset; reg = wm_adsp_region_to_reg(mem, reg); - scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); + scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; ret = regmap_raw_write(dsp->regmap, reg, scratch, - ctl->len); + len); if (ret) { adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", - ctl->len, reg, ret); + len, reg, ret); kfree(scratch); return ret; } - adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg); + adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); kfree(scratch); @@ -778,20 +800,20 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, reg = ctl->alg_region.base + ctl->offset; reg = wm_adsp_region_to_reg(mem, reg); - scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); + scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); if (!scratch) return -ENOMEM; - ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len); + ret = regmap_raw_read(dsp->regmap, reg, scratch, len); if (ret) { adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", - ctl->len, reg, ret); + len, reg, ret); kfree(scratch); return ret; } - adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg); + adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); - memcpy(buf, scratch, ctl->len); + memcpy(buf, scratch, len); kfree(scratch); return 0; @@ -855,17 +877,18 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ; if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; + } else { + kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; } - ret = snd_soc_add_card_controls(dsp->card, - kcontrol, 1); + ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); if (ret < 0) goto err_kcontrol; kfree(kcontrol); - ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, - ctl->name); + ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name); return 0; @@ -885,9 +908,7 @@ static int wm_coeff_init_control_caches(struct wm_adsp *dsp) if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) continue; - ret = wm_coeff_read_control(ctl, - ctl->cache, - ctl->len); + ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); if (ret < 0) return ret; } @@ -904,9 +925,7 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp) if (!ctl->enabled) continue; if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { - ret = wm_coeff_write_control(ctl, - ctl->cache, - ctl->len); + ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); if (ret < 0) return ret; } @@ -1502,8 +1521,7 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm list: %d\n", - ret); + adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); kfree(alg); return ERR_PTR(ret); } @@ -2002,8 +2020,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, goto err_mutex; } - val = (val & dsp->sysclk_mask) - >> dsp->sysclk_shift; + val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_31, @@ -2096,8 +2113,7 @@ static int wm_adsp2_ena(struct wm_adsp *dsp) /* Wait for the RAM to start, should be near instantaneous */ for (count = 0; count < 10; ++count) { - ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, - &val); + ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); if (ret != 0) return ret; @@ -2123,30 +2139,9 @@ static void wm_adsp2_boot_work(struct work_struct *work) struct wm_adsp, boot_work); int ret; - unsigned int val; mutex_lock(&dsp->pwr_lock); - /* - * For simplicity set the DSP clock rate to be the - * SYSCLK rate rather than making it configurable. - */ - ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); - if (ret != 0) { - adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); - goto err_mutex; - } - val = (val & ARIZONA_SYSCLK_FREQ_MASK) - >> ARIZONA_SYSCLK_FREQ_SHIFT; - - ret = regmap_update_bits_async(dsp->regmap, - dsp->base + ADSP2_CLOCKING, - ADSP2_CLK_SEL_MASK, val); - if (ret != 0) { - adsp_err(dsp, "Failed to set clock rate: %d\n", ret); - goto err_mutex; - } - ret = wm_adsp2_ena(dsp); if (ret != 0) goto err_mutex; @@ -2186,8 +2181,21 @@ err_mutex: mutex_unlock(&dsp->pwr_lock); } +static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq) +{ + int ret; + + ret = regmap_update_bits_async(dsp->regmap, + dsp->base + ADSP2_CLOCKING, + ADSP2_CLK_SEL_MASK, + freq << ADSP2_CLK_SEL_SHIFT); + if (ret != 0) + adsp_err(dsp, "Failed to set clock rate: %d\n", ret); +} + int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) + struct snd_kcontrol *kcontrol, int event, + unsigned int freq) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); @@ -2197,6 +2205,7 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + wm_adsp2_set_dspclk(dsp, freq); queue_work(system_unbound_wq, &dsp->boot_work); break; default: @@ -2471,6 +2480,8 @@ int wm_adsp_compr_set_params(struct snd_compr_stream *stream, if (!compr->raw_buf) return -ENOMEM; + compr->sample_rate = params->codec.sample_rate; + return 0; } EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); @@ -2810,7 +2821,6 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) mutex_lock(&dsp->pwr_lock); if (!buf) { - adsp_err(dsp, "Spurious buffer IRQ\n"); ret = -ENODEV; goto out; } @@ -2841,7 +2851,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) goto out; } - if (compr->stream) + if (compr && compr->stream) snd_compr_fragment_elapsed(compr->stream); out: @@ -2911,6 +2921,7 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, tstamp->copied_total = compr->copied_total; tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; + tstamp->sampling_rate = compr->sample_rate; out: mutex_unlock(&dsp->pwr_lock); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 1a928ec..b61cb57 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -80,7 +80,7 @@ struct wm_adsp { SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \ wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) -#define WM_ADSP2_E(wname, num, event_fn) \ +#define WM_ADSP2(wname, num, event_fn) \ { .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \ .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \ @@ -88,9 +88,6 @@ struct wm_adsp { .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } -#define WM_ADSP2(wname, num) \ - WM_ADSP2_E(wname, num, wm_adsp2_early_event) - extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; int wm_adsp1_init(struct wm_adsp *dsp); @@ -100,7 +97,8 @@ int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event); + struct snd_kcontrol *kcontrol, int event, + unsigned int freq); int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 3736d9a..50ca291 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -5,7 +5,7 @@ config SND_DAVINCI_SOC config SND_EDMA_SOC tristate "SoC Audio for Texas Instruments chips using eDMA" - depends on SOC_AM33XX || SOC_AM43XX || ARCH_DAVINCI + depends on TI_EDMA select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M here if you want audio support for TI SoC which uses eDMA. @@ -13,6 +13,7 @@ config SND_EDMA_SOC - daVinci devices - AM335x - AM437x/AM438x + - DRA7xx family config SND_DAVINCI_SOC_I2S tristate diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 2ccb8bc..e132498 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -77,6 +77,7 @@ struct davinci_mcasp { u32 fifo_base; struct device *dev; struct snd_pcm_substream *substreams[2]; + unsigned int dai_fmt; /* McASP specific data */ int tdm_slots; @@ -398,6 +399,9 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, bool fs_pol_rising; bool inv_fs = false; + if (!fmt) + return 0; + pm_runtime_get_sync(mcasp->dev); switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_A: @@ -529,6 +533,8 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); } + + mcasp->dai_fmt = fmt; out: pm_runtime_put(mcasp->dev); return ret; @@ -1026,6 +1032,10 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, int period_size = params_period_size(params); int ret; + ret = davinci_mcasp_set_dai_fmt(cpu_dai, mcasp->dai_fmt); + if (ret) + return ret; + /* * If mcasp is BCLK master, and a BCLK divider was not provided by * the machine driver, we need to calculate the ratio. @@ -1517,6 +1527,8 @@ static int mcasp_reparent_fck(struct platform_device *pdev) if (!parent_name) return 0; + dev_warn(&pdev->dev, "Update the bindings to use assigned-clocks!\n"); + gfclk = clk_get(&pdev->dev, "fck"); if (IS_ERR(gfclk)) { dev_err(&pdev->dev, "failed to get fck\n"); diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index ce664c2..bff258d 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -645,6 +645,8 @@ static int dw_i2s_probe(struct platform_device *pdev) dev->dev = &pdev->dev; + dev->i2s_reg_comp1 = I2S_COMP_PARAM_1; + dev->i2s_reg_comp2 = I2S_COMP_PARAM_2; if (pdata) { dev->capability = pdata->cap; clk_id = NULL; @@ -652,9 +654,6 @@ static int dw_i2s_probe(struct platform_device *pdev) if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) { dev->i2s_reg_comp1 = pdata->i2s_reg_comp1; dev->i2s_reg_comp2 = pdata->i2s_reg_comp2; - } else { - dev->i2s_reg_comp1 = I2S_COMP_PARAM_1; - dev->i2s_reg_comp2 = I2S_COMP_PARAM_2; } ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata); } else { diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 14dfdee..35aabf9 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -292,8 +292,8 @@ config SND_SOC_FSL_ASOC_CARD select SND_SOC_FSL_SSI help ALSA SoC Audio support with ASRC feature for Freescale SoCs that have - ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888 - and SGTL5000. + ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888, + CS4271, CS4272 and SGTL5000. Say Y if you want to add support for Freescale Generic ASoC Sound Card. endif # SND_IMX_SOC diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 562b3bd..dffd549 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -28,6 +28,8 @@ #include "../codecs/wm8962.h" #include "../codecs/wm8960.h" +#define CS427x_SYSCLK_MCLK 0 + #define RX 0 #define TX 1 @@ -99,19 +101,26 @@ struct fsl_asoc_card_priv { /** * This dapm route map exsits for DPCM link only. * The other routes shall go through Device Tree. + * + * Note: keep all ASRC routes in the second half + * to drop them easily for non-ASRC cases. */ static const struct snd_soc_dapm_route audio_map[] = { - {"CPU-Playback", NULL, "ASRC-Playback"}, + /* 1st half -- Normal DAPM routes */ {"Playback", NULL, "CPU-Playback"}, - {"ASRC-Capture", NULL, "CPU-Capture"}, {"CPU-Capture", NULL, "Capture"}, + /* 2nd half -- ASRC DAPM routes */ + {"CPU-Playback", NULL, "ASRC-Playback"}, + {"ASRC-Capture", NULL, "CPU-Capture"}, }; static const struct snd_soc_dapm_route audio_map_ac97[] = { - {"AC97 Playback", NULL, "ASRC-Playback"}, + /* 1st half -- Normal DAPM routes */ {"Playback", NULL, "AC97 Playback"}, - {"ASRC-Capture", NULL, "AC97 Capture"}, {"AC97 Capture", NULL, "Capture"}, + /* 2nd half -- ASRC DAPM routes */ + {"AC97 Playback", NULL, "ASRC-Playback"}, + {"ASRC-Capture", NULL, "AC97 Capture"}, }; /* Add all possible widgets into here without being redundant */ @@ -528,6 +537,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; priv->cpu_priv.slot_width = 32; priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { + codec_dai_name = "cs4271-hifi"; + priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK; + priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { codec_dai_name = "sgtl5000"; priv->codec_priv.mclk_id = SGTL5000_SYSCLK; @@ -593,6 +606,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets; priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets); + /* Drop the second half of DAPM routes -- ASRC */ + if (!asrc_pdev) + priv->card.num_dapm_routes /= 2; + memcpy(priv->dai_link, fsl_asoc_card_dai, sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); @@ -681,6 +698,7 @@ fail: static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-ac97", }, { .compatible = "fsl,imx-audio-cs42888", }, + { .compatible = "fsl,imx-audio-cs427x", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, { .compatible = "fsl,imx-audio-wm8960", }, diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index fef264d..0754df7 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -17,6 +17,7 @@ #include <linux/of_address.h> #include <linux/regmap.h> #include <linux/slab.h> +#include <linux/time.h> #include <sound/core.h> #include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> @@ -919,7 +920,7 @@ static int fsl_sai_resume(struct device *dev) regcache_cache_only(sai->regmap, false); regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR); regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR); - msleep(1); + usleep_range(1000, 2000); regmap_write(sai->regmap, FSL_SAI_TCSR, 0); regmap_write(sai->regmap, FSL_SAI_RCSR, 0); return regcache_sync(sai->regmap); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 40dfd8a..ed8de10 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -112,20 +112,6 @@ struct fsl_ssi_rxtx_reg_val { struct fsl_ssi_reg_val tx; }; -static const struct reg_default fsl_ssi_reg_defaults[] = { - {CCSR_SSI_SCR, 0x00000000}, - {CCSR_SSI_SIER, 0x00003003}, - {CCSR_SSI_STCR, 0x00000200}, - {CCSR_SSI_SRCR, 0x00000200}, - {CCSR_SSI_STCCR, 0x00040000}, - {CCSR_SSI_SRCCR, 0x00040000}, - {CCSR_SSI_SACNT, 0x00000000}, - {CCSR_SSI_STMSK, 0x00000000}, - {CCSR_SSI_SRMSK, 0x00000000}, - {CCSR_SSI_SACCEN, 0x00000000}, - {CCSR_SSI_SACCDIS, 0x00000000}, -}; - static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -190,8 +176,7 @@ static const struct regmap_config fsl_ssi_regconfig = { .val_bits = 32, .reg_stride = 4, .val_format_endian = REGMAP_ENDIAN_NATIVE, - .reg_defaults = fsl_ssi_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults), + .num_reg_defaults_raw = CCSR_SSI_SACCDIS / sizeof(uint32_t) + 1, .readable_reg = fsl_ssi_readable_reg, .volatile_reg = fsl_ssi_volatile_reg, .precious_reg = fsl_ssi_precious_reg, @@ -201,6 +186,7 @@ static const struct regmap_config fsl_ssi_regconfig = { struct fsl_ssi_soc_data { bool imx; + bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */ bool offline_config; u32 sisr_write_mask; }; @@ -303,6 +289,7 @@ static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = { static struct fsl_ssi_soc_data fsl_ssi_imx21 = { .imx = true, + .imx21regs = true, .offline_config = true, .sisr_write_mask = 0, }; @@ -586,8 +573,12 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private) */ regmap_write(regs, CCSR_SSI_SACNT, CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV); - regmap_write(regs, CCSR_SSI_SACCDIS, 0xff); - regmap_write(regs, CCSR_SSI_SACCEN, 0x300); + + /* no SACC{ST,EN,DIS} regs on imx21-class SSI */ + if (!ssi_private->soc->imx21regs) { + regmap_write(regs, CCSR_SSI_SACCDIS, 0xff); + regmap_write(regs, CCSR_SSI_SACCEN, 0x300); + } /* * Enable SSI, Transmit and Receive. AC97 has to communicate with the @@ -1397,6 +1388,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) struct resource *res; void __iomem *iomem; char name[64]; + struct regmap_config regconfig = fsl_ssi_regconfig; of_id = of_match_device(fsl_ssi_ids, &pdev->dev); if (!of_id || !of_id->data) @@ -1444,15 +1436,25 @@ static int fsl_ssi_probe(struct platform_device *pdev) return PTR_ERR(iomem); ssi_private->ssi_phys = res->start; + if (ssi_private->soc->imx21regs) { + /* + * According to datasheet imx21-class SSI + * don't have SACC{ST,EN,DIS} regs. + */ + regconfig.max_register = CCSR_SSI_SRMSK; + regconfig.num_reg_defaults_raw = + CCSR_SSI_SRMSK / sizeof(uint32_t) + 1; + } + ret = of_property_match_string(np, "clock-names", "ipg"); if (ret < 0) { ssi_private->has_ipg_clk_name = false; ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem, - &fsl_ssi_regconfig); + ®config); } else { ssi_private->has_ipg_clk_name = true; ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev, - "ipg", iomem, &fsl_ssi_regconfig); + "ipg", iomem, ®config); } if (IS_ERR(ssi_private->regs)) { dev_err(&pdev->dev, "Failed to init register map\n"); diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index a407e83..fb896b2 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -72,8 +72,6 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) goto end; } - platform_set_drvdata(pdev, data); - end: of_node_put(spdif_np); diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 0bab760..243700c 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -13,6 +13,7 @@ #include <linux/of_device.h> #include <linux/of_platform.h> #include <linux/delay.h> +#include <linux/time.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -127,7 +128,7 @@ static void psc_ac97_cold_reset(struct snd_ac97 *ac97) mutex_unlock(&psc_dma->mutex); - msleep(1); + usleep_range(1000, 2000); psc_ac97_warm_reset(ac97); } diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 1ded881..2389ab4 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -99,7 +99,7 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, if (ret && ret != -ENOTSUPP) goto err; } - + return 0; err: return ret; } diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 803f95e..b3e6c23 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -30,11 +30,15 @@ config SND_SST_IPC_ACPI config SND_SOC_INTEL_SST tristate select SND_SOC_INTEL_SST_ACPI if ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI depends on (X86 || COMPILE_TEST) config SND_SOC_INTEL_SST_ACPI tristate +config SND_SOC_INTEL_SST_MATCH + tristate + config SND_SOC_INTEL_HASWELL tristate @@ -57,7 +61,7 @@ config SND_SOC_INTEL_HASWELL_MACH config SND_SOC_INTEL_BYT_RT5640_MACH tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" depends on X86_INTEL_LPSS && I2C - depends on DW_DMAC_CORE=y && (SND_SOC_INTEL_BYTCR_RT5640_MACH = n) + depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n) select SND_SOC_INTEL_SST select SND_SOC_INTEL_BAYTRAIL select SND_SOC_RT5640 @@ -69,7 +73,7 @@ config SND_SOC_INTEL_BYT_RT5640_MACH config SND_SOC_INTEL_BYT_MAX98090_MACH tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec" depends on X86_INTEL_LPSS && I2C - depends on DW_DMAC_CORE=y + depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n) select SND_SOC_INTEL_SST select SND_SOC_INTEL_BAYTRAIL select SND_SOC_MAX98090 @@ -97,6 +101,7 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH select SND_SOC_RT5640 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR platforms with RT5640 audio codec. @@ -109,6 +114,7 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH select SND_SOC_RT5651 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR platforms with RT5651 audio codec. @@ -121,6 +127,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH select SND_SOC_RT5670 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with RT5672 audio codec. @@ -133,6 +140,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH select SND_SOC_RT5645 select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with RT5645/5650 audio codec. @@ -145,6 +153,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH select SND_SOC_TS3A227E select SND_SST_MFLD_PLATFORM select SND_SST_IPC_ACPI + select SND_SOC_INTEL_SST_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with MAX98090 audio codec it also can support TI jack chip as aux device. @@ -154,6 +163,7 @@ config SND_SOC_INTEL_SKYLAKE tristate select SND_HDA_EXT_CORE select SND_SOC_TOPOLOGY + select SND_HDA_I915 select SND_SOC_INTEL_SST config SND_SOC_INTEL_SKL_RT286_MACH @@ -163,6 +173,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH select SND_SOC_INTEL_SKYLAKE select SND_SOC_RT286 select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI help This adds support for ASoC machine driver for Skylake platforms with RT286 I2S audio codec. @@ -177,6 +188,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH select SND_SOC_NAU8825 select SND_SOC_SSM4567 select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for NAU88L25 + SSM4567. @@ -191,6 +203,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH select SND_SOC_NAU8825 select SND_SOC_MAX98357A select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI help This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for NAU88L25 + MAX98357A. diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 55c33dc..52ed434 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -528,6 +528,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { .ops = &sst_compr_dai_ops, .playback = { .stream_name = "Compress Playback", + .channels_min = 1, }, }, /* BE CPU Dais */ diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index 4fce03f..3bc4b63 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -342,6 +342,10 @@ static struct sst_acpi_mach sst_acpi_chv[] = { &chv_platform_data }, {"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL, &chv_platform_data }, + /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ + {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL, + &chv_platform_data }, + {}, }; diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 3dc7358..8afa6fe 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -318,7 +318,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx, union ipc_header_high msg_high; u32 msg_low; struct ipc_dsp_hdr *dsp_hdr; - unsigned int cmd_id; msg_high = msg->mrfld_header.p.header_high; msg_low = msg->mrfld_header.p.header_low_payload; @@ -357,7 +356,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx, return; /* Copy command id so that we can use to put sst to reset */ dsp_hdr = (struct ipc_dsp_hdr *)data; - cmd_id = dsp_hdr->cmd_id; dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id); if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result, msg_high.part.drv_id, diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 9a1752d..032a2e7 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -32,6 +32,18 @@ #include "../atom/sst-atom-controls.h" #include "../common/sst-acpi.h" +enum { + BYT_RT5640_DMIC1_MAP, + BYT_RT5640_DMIC2_MAP, + BYT_RT5640_IN1_MAP, +}; + +#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) +#define BYT_RT5640_DMIC_EN BIT(16) + +static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | + BYT_RT5640_DMIC_EN; + static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -70,18 +82,6 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = { {"IN1P", NULL, "Internal Mic"}, }; -enum { - BYT_RT5640_DMIC1_MAP, - BYT_RT5640_DMIC2_MAP, - BYT_RT5640_IN1_MAP, -}; - -#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) -#define BYT_RT5640_DMIC_EN BIT(16) - -static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | - BYT_RT5640_DMIC_EN; - static const struct snd_kcontrol_new byt_rt5640_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -174,7 +174,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) return ret; } - dmi_check_system(byt_rt5640_quirk_table); switch (BYT_RT5640_MAP(byt_rt5640_quirk)) { case BYT_RT5640_IN1_MAP: custom_map = byt_rt5640_intmic_in1_map; @@ -341,15 +340,34 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { int ret_val = 0; struct sst_acpi_mach *mach; + const char *i2c_name = NULL; + int i; + int dai_index; /* register the soc card */ byt_rt5640_card.dev = &pdev->dev; mach = byt_rt5640_card.dev->platform_data; + /* fix index of codec dai */ + dai_index = MERR_DPCM_COMPR + 1; + for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) { + if (!strcmp(byt_rt5640_dais[i].codec_name, "i2c-10EC5640:00")) { + dai_index = i; + break; + } + } + /* fixup codec name based on HID */ - snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name), - "%s%s%s", "i2c-", mach->id, ":00"); - byt_rt5640_dais[MERR_DPCM_COMPR+1].codec_name = byt_rt5640_codec_name; + i2c_name = sst_acpi_find_name_from_hid(mach->id); + if (i2c_name != NULL) { + snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name), + "%s%s", "i2c-", i2c_name); + + byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name; + } + + /* check quirks before creating card */ + dmi_check_system(byt_rt5640_quirk_table); ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 90588d6..e609f08 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -287,33 +287,20 @@ static struct snd_soc_card snd_soc_card_cht = { .num_controls = ARRAY_SIZE(cht_mc_controls), }; -static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - *(bool *)context = true; - return AE_OK; -} - static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; - bool found = false; struct cht_mc_private *drv; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); if (!drv) return -ENOMEM; - if (ACPI_SUCCESS(acpi_get_devices( - "104C227E", - snd_acpi_codec_match, - &found, NULL)) && found) { - drv->ts3a227e_present = true; - } else { + drv->ts3a227e_present = acpi_dev_present("104C227E"); + if (!drv->ts3a227e_present) { /* no need probe TI jack detection chip */ snd_soc_card_cht.aux_dev = NULL; snd_soc_card_cht.num_aux_devs = 0; - drv->ts3a227e_present = false; } /* register the soc card */ diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 2d3afdd..2a6f808 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -147,6 +147,17 @@ static const struct snd_kcontrol_new cht_mc_controls[] = { SOC_DAPM_PIN_SWITCH("Ext Spk"), }; +static struct snd_soc_jack_pin cht_bsw_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int cht_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -202,9 +213,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) else jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; - ret = snd_soc_card_jack_new(runtime->card, "Headset Jack", + ret = snd_soc_card_jack_new(runtime->card, "Headset", jack_type, &ctx->jack, - NULL, 0); + cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins)); if (ret) { dev_err(runtime->dev, "Headset jack creation failed %d\n", ret); return ret; @@ -333,20 +344,12 @@ static struct cht_acpi_card snd_soc_cards[] = { {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650}, }; -static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - *(bool *)context = true; - return AE_OK; -} - static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; int i; struct cht_mc_private *drv; struct snd_soc_card *card = snd_soc_cards[0].soc_card; - bool found = false; char codec_name[16]; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); @@ -354,10 +357,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) { - if (ACPI_SUCCESS(acpi_get_devices( - snd_soc_cards[i].codec_id, - snd_acpi_codec_match, - &found, NULL)) && found) { + if (acpi_dev_present(snd_soc_cards[i].codec_id)) { dev_dbg(&pdev->dev, "found codec %s\n", snd_soc_cards[i].codec_id); card = snd_soc_cards[i].soc_card; @@ -367,8 +367,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } card->dev = &pdev->dev; sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id); + /* set correct codec name */ - strcpy((char *)card->dai_link[2].codec_name, codec_name); + for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) + if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) + card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL); + snd_soc_card_set_drvdata(card, drv); ret_val = devm_snd_soc_register_card(&pdev->dev, card); if (ret_val) { diff --git a/sound/soc/intel/boards/mfld_machine.c b/sound/soc/intel/boards/mfld_machine.c index 49c09a0..34f46c7 100644 --- a/sound/soc/intel/boards/mfld_machine.c +++ b/sound/soc/intel/boards/mfld_machine.c @@ -94,7 +94,7 @@ static const struct soc_enum lo_enum = static int headset_get_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = hs_switch; + ucontrol->value.enumerated.item[0] = hs_switch; return 0; } @@ -104,12 +104,12 @@ static int headset_set_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_context *dapm = &card->dapm; - if (ucontrol->value.integer.value[0] == hs_switch) + if (ucontrol->value.enumerated.item[0] == hs_switch) return 0; snd_soc_dapm_mutex_lock(dapm); - if (ucontrol->value.integer.value[0]) { + if (ucontrol->value.enumerated.item[0]) { pr_debug("hs_set HS path\n"); snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones"); snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); @@ -123,7 +123,7 @@ static int headset_set_switch(struct snd_kcontrol *kcontrol, snd_soc_dapm_mutex_unlock(dapm); - hs_switch = ucontrol->value.integer.value[0]; + hs_switch = ucontrol->value.enumerated.item[0]; return 0; } @@ -148,7 +148,7 @@ static void lo_enable_out_pins(struct snd_soc_dapm_context *dapm) static int lo_get_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = lo_dac; + ucontrol->value.enumerated.item[0] = lo_dac; return 0; } @@ -158,7 +158,7 @@ static int lo_set_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_context *dapm = &card->dapm; - if (ucontrol->value.integer.value[0] == lo_dac) + if (ucontrol->value.enumerated.item[0] == lo_dac) return 0; snd_soc_dapm_mutex_lock(dapm); @@ -168,7 +168,7 @@ static int lo_set_switch(struct snd_kcontrol *kcontrol, */ lo_enable_out_pins(dapm); - switch (ucontrol->value.integer.value[0]) { + switch (ucontrol->value.enumerated.item[0]) { case 0: pr_debug("set vibra path\n"); snd_soc_dapm_disable_pin_unlocked(dapm, "VIB1OUT"); @@ -202,7 +202,7 @@ static int lo_set_switch(struct snd_kcontrol *kcontrol, snd_soc_dapm_mutex_unlock(dapm); - lo_dac = ucontrol->value.integer.value[0]; + lo_dac = ucontrol->value.enumerated.item[0]; return 0; } diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index ab7da9c..72176b7 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -22,6 +22,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include "../../codecs/nau8825.h" +#include "../../codecs/hdac_hdmi.h" #define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" #define SKL_MAXIM_CODEC_DAI "HiFi" @@ -29,6 +30,16 @@ static struct snd_soc_jack skylake_headset; static struct snd_soc_card skylake_audio_card; +enum { + SKL_DPCM_AUDIO_PB = 0, + SKL_DPCM_AUDIO_CP, + SKL_DPCM_AUDIO_REF_CP, + SKL_DPCM_AUDIO_DMIC_CP, + SKL_DPCM_AUDIO_HDMI1_PB, + SKL_DPCM_AUDIO_HDMI2_PB, + SKL_DPCM_AUDIO_HDMI3_PB, +}; + static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -87,7 +98,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Spk", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), - SND_SOC_DAPM_SINK("WoV Sink"), SND_SOC_DAPM_SPK("DP", NULL), SND_SOC_DAPM_SPK("HDMI", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, @@ -107,7 +117,6 @@ static const struct snd_soc_dapm_route skylake_map[] = { { "MIC", NULL, "Headset Mic" }, { "DMic", NULL, "SoC DMIC" }, - {"WoV Sink", NULL, "hwd_in sink"}, {"HDMI", NULL, "hif5 Output"}, {"DP", NULL, "hif6 Output"}, @@ -124,8 +133,14 @@ static const struct snd_soc_dapm_route skylake_map[] = { /* DMIC */ { "dmic01_hifi", NULL, "DMIC01 Rx" }, { "DMIC01 Rx", NULL, "DMIC AIF" }, - { "hifi1", NULL, "iDisp Tx"}, - { "iDisp Tx", NULL, "iDisp_out"}, + + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, }; @@ -171,11 +186,31 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) nau8825_enable_jack_detect(codec, &skylake_headset); snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); - snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink"); return ret; } +static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB); +} + +static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB); +} + +static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB); +} + static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; @@ -318,7 +353,7 @@ static struct snd_soc_ops skylaye_refcap_ops = { /* skylake digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link skylake_dais[] = { /* Front End DAI links */ - { + [SKL_DPCM_AUDIO_PB] = { .name = "Skl Audio Port", .stream_name = "Audio", .cpu_dai_name = "System Pin", @@ -333,7 +368,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_playback = 1, .ops = &skylake_nau8825_fe_ops, }, - { + [SKL_DPCM_AUDIO_CP] = { .name = "Skl Audio Capture Port", .stream_name = "Audio Record", .cpu_dai_name = "System Pin", @@ -347,7 +382,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_capture = 1, .ops = &skylake_nau8825_fe_ops, }, - { + [SKL_DPCM_AUDIO_REF_CP] = { .name = "Skl Audio Reference cap", .stream_name = "Wake on Voice", .cpu_dai_name = "Reference Pin", @@ -361,7 +396,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dynamic = 1, .ops = &skylaye_refcap_ops, }, - { + [SKL_DPCM_AUDIO_DMIC_CP] = { .name = "Skl Audio DMIC cap", .stream_name = "dmiccap", .cpu_dai_name = "DMIC Pin", @@ -374,15 +409,45 @@ static struct snd_soc_dai_link skylake_dais[] = { .dynamic = 1, .ops = &skylake_dmic_ops, }, - { - .name = "Skl HDMI Port", - .stream_name = "Hdmi", - .cpu_dai_name = "HDMI Pin", + [SKL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Skl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Skl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", .platform_name = "0000:00:1f.3", .dpcm_playback = 1, .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Skl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, .nonatomic = 1, .dynamic = 1, }, @@ -407,7 +472,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP1 - Codec */ .name = "SSP1-Codec", - .be_id = 0, + .be_id = 1, .cpu_dai_name = "SSP1 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -424,7 +489,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "dmic01", - .be_id = 1, + .be_id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -435,13 +500,36 @@ static struct snd_soc_dai_link skylake_dais[] = { .no_pcm = 1, }, { - .name = "iDisp", + .name = "iDisp1", .be_id = 3, - .cpu_dai_name = "iDisp Pin", + .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", .platform_name = "0000:00:1f.3", .dpcm_playback = 1, + .init = skylake_hdmi1_init, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .be_id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi2_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .be_id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi3_init, + .dpcm_playback = 1, .no_pcm = 1, }, }; diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index c071812..5f1ca99 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -26,6 +26,7 @@ #include <sound/jack.h> #include <sound/pcm_params.h> #include "../../codecs/nau8825.h" +#include "../../codecs/hdac_hdmi.h" #define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" #define SKL_SSM_CODEC_DAI "ssm4567-hifi" @@ -33,6 +34,16 @@ static struct snd_soc_jack skylake_headset; static struct snd_soc_card skylake_audio_card; +enum { + SKL_DPCM_AUDIO_PB = 0, + SKL_DPCM_AUDIO_CP, + SKL_DPCM_AUDIO_REF_CP, + SKL_DPCM_AUDIO_DMIC_CP, + SKL_DPCM_AUDIO_HDMI1_PB, + SKL_DPCM_AUDIO_HDMI2_PB, + SKL_DPCM_AUDIO_HDMI3_PB, +}; + static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -92,7 +103,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = { SND_SOC_DAPM_SPK("Left Speaker", NULL), SND_SOC_DAPM_SPK("Right Speaker", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), - SND_SOC_DAPM_SINK("WoV Sink"), SND_SOC_DAPM_SPK("DP", NULL), SND_SOC_DAPM_SPK("HDMI", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, @@ -113,8 +123,6 @@ static const struct snd_soc_dapm_route skylake_map[] = { {"MIC", NULL, "Headset Mic"}, {"DMic", NULL, "SoC DMIC"}, - {"WoV Sink", NULL, "hwd_in sink"}, - {"HDMI", NULL, "hif5 Output"}, {"DP", NULL, "hif6 Output"}, /* CODEC BE connections */ @@ -122,6 +130,11 @@ static const struct snd_soc_dapm_route skylake_map[] = { { "Right Playback", NULL, "ssp0 Tx"}, { "ssp0 Tx", NULL, "codec0_out"}, + /* IV feedback path */ + { "codec0_lp_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "Left Capture Sense" }, + { "ssp0 Rx", NULL, "Right Capture Sense" }, + { "Playback", NULL, "ssp1 Tx"}, { "ssp1 Tx", NULL, "codec1_out"}, @@ -131,8 +144,14 @@ static const struct snd_soc_dapm_route skylake_map[] = { /* DMIC */ { "dmic01_hifi", NULL, "DMIC01 Rx" }, { "DMIC01 Rx", NULL, "DMIC AIF" }, - { "hifi1", NULL, "iDisp Tx"}, - { "iDisp Tx", NULL, "iDisp_out"}, + + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, }; @@ -197,11 +216,32 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) nau8825_enable_jack_detect(codec, &skylake_headset); snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); - snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink"); return ret; } +static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB); +} + +static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB); +} + + +static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB); +} + static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm; @@ -362,7 +402,7 @@ static struct snd_soc_ops skylaye_refcap_ops = { /* skylake digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link skylake_dais[] = { /* Front End DAI links */ - { + [SKL_DPCM_AUDIO_PB] = { .name = "Skl Audio Port", .stream_name = "Audio", .cpu_dai_name = "System Pin", @@ -377,7 +417,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_playback = 1, .ops = &skylake_nau8825_fe_ops, }, - { + [SKL_DPCM_AUDIO_CP] = { .name = "Skl Audio Capture Port", .stream_name = "Audio Record", .cpu_dai_name = "System Pin", @@ -391,7 +431,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_capture = 1, .ops = &skylake_nau8825_fe_ops, }, - { + [SKL_DPCM_AUDIO_REF_CP] = { .name = "Skl Audio Reference cap", .stream_name = "Wake on Voice", .cpu_dai_name = "Reference Pin", @@ -405,7 +445,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dynamic = 1, .ops = &skylaye_refcap_ops, }, - { + [SKL_DPCM_AUDIO_DMIC_CP] = { .name = "Skl Audio DMIC cap", .stream_name = "dmiccap", .cpu_dai_name = "DMIC Pin", @@ -418,13 +458,43 @@ static struct snd_soc_dai_link skylake_dais[] = { .dynamic = 1, .ops = &skylake_dmic_ops, }, - { - .name = "Skl HDMI Port", - .stream_name = "Hdmi", - .cpu_dai_name = "HDMI Pin", + [SKL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Skl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Skl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Skl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", .platform_name = "0000:00:1f.3", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, .init = NULL, .nonatomic = 1, @@ -448,11 +518,12 @@ static struct snd_soc_dai_link skylake_dais[] = { .ignore_pmdown_time = 1, .be_hw_params_fixup = skylake_ssp_fixup, .dpcm_playback = 1, + .dpcm_capture = 1, }, { /* SSP1 - Codec */ .name = "SSP1-Codec", - .be_id = 0, + .be_id = 1, .cpu_dai_name = "SSP1 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -469,7 +540,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "dmic01", - .be_id = 1, + .be_id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -480,13 +551,36 @@ static struct snd_soc_dai_link skylake_dais[] = { .no_pcm = 1, }, { - .name = "iDisp", + .name = "iDisp1", .be_id = 3, - .cpu_dai_name = "iDisp Pin", + .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", .platform_name = "0000:00:1f.3", .dpcm_playback = 1, + .init = skylake_hdmi1_init, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .be_id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi2_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .be_id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi3_init, + .dpcm_playback = 1, .no_pcm = 1, }, }; diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 7396ddb..2016397a 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -26,8 +26,20 @@ #include <sound/jack.h> #include <sound/pcm_params.h> #include "../../codecs/rt286.h" +#include "../../codecs/hdac_hdmi.h" static struct snd_soc_jack skylake_headset; + +enum { + SKL_DPCM_AUDIO_PB = 0, + SKL_DPCM_AUDIO_CP, + SKL_DPCM_AUDIO_REF_CP, + SKL_DPCM_AUDIO_DMIC_CP, + SKL_DPCM_AUDIO_HDMI1_PB, + SKL_DPCM_AUDIO_HDMI2_PB, + SKL_DPCM_AUDIO_HDMI3_PB, +}; + /* Headset jack detection DAPM pins */ static struct snd_soc_jack_pin skylake_headset_pins[] = { { @@ -52,7 +64,9 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("DMIC2", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), - SND_SOC_DAPM_SINK("WoV Sink"), + SND_SOC_DAPM_SPK("HDMI1", NULL), + SND_SOC_DAPM_SPK("HDMI2", NULL), + SND_SOC_DAPM_SPK("HDMI3", NULL), }; static const struct snd_soc_dapm_route skylake_rt286_map[] = { @@ -70,7 +84,9 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = { {"DMIC1 Pin", NULL, "DMIC2"}, {"DMic", NULL, "SoC DMIC"}, - {"WoV Sink", NULL, "hwd_in sink"}, + {"HDMI1", NULL, "hif5 Output"}, + {"HDMI2", NULL, "hif6 Output"}, + {"HDMI3", NULL, "hif7 Output"}, /* CODEC BE connections */ { "AIF1 Playback", NULL, "ssp0 Tx"}, @@ -84,8 +100,12 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = { { "dmic01_hifi", NULL, "DMIC01 Rx" }, { "DMIC01 Rx", NULL, "DMIC AIF" }, - { "hif1", NULL, "iDisp Tx"}, - { "iDisp Tx", NULL, "iDisp_out"}, + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, }; @@ -116,11 +136,17 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) rt286_mic_detect(codec, &skylake_headset); snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); - snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink"); return 0; } +static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id); +} + static unsigned int rates[] = { 48000, }; @@ -212,7 +238,10 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = 4; + if (params_channels(params) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; return 0; } @@ -246,7 +275,7 @@ static struct snd_soc_ops skylake_dmic_ops = { /* skylake digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link skylake_rt286_dais[] = { /* Front End DAI links */ - { + [SKL_DPCM_AUDIO_PB] = { .name = "Skl Audio Port", .stream_name = "Audio", .cpu_dai_name = "System Pin", @@ -263,7 +292,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dpcm_playback = 1, .ops = &skylake_rt286_fe_ops, }, - { + [SKL_DPCM_AUDIO_CP] = { .name = "Skl Audio Capture Port", .stream_name = "Audio Record", .cpu_dai_name = "System Pin", @@ -279,7 +308,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dpcm_capture = 1, .ops = &skylake_rt286_fe_ops, }, - { + [SKL_DPCM_AUDIO_REF_CP] = { .name = "Skl Audio Reference cap", .stream_name = "refcap", .cpu_dai_name = "Reference Pin", @@ -292,7 +321,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .nonatomic = 1, .dynamic = 1, }, - { + [SKL_DPCM_AUDIO_DMIC_CP] = { .name = "Skl Audio DMIC cap", .stream_name = "dmiccap", .cpu_dai_name = "DMIC Pin", @@ -305,6 +334,42 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dynamic = 1, .ops = &skylake_dmic_ops, }, + [SKL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Skl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Skl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Skl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, /* Back End DAI links */ { @@ -338,6 +403,39 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .dpcm_capture = 1, .no_pcm = 1, }, + { + .name = "iDisp1", + .be_id = 2, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .be_id = 3, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .be_id = 4, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = skylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, }; /* skylake audio machine driver for SPT + RT286S */ diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 668fdee..fbbb25c 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,13 +1,10 @@ snd-soc-sst-dsp-objs := sst-dsp.o -ifneq ($(CONFIG_SND_SST_IPC_ACPI),) -snd-soc-sst-acpi-objs := sst-match-acpi.o -else -snd-soc-sst-acpi-objs := sst-acpi.o sst-match-acpi.o -endif - +snd-soc-sst-acpi-objs := sst-acpi.o +snd-soc-sst-match-objs := sst-match-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o +obj-$(CONFIG_SND_SOC_INTEL_SST_MATCH) += snd-soc-sst-match.o diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 7a85c57..2c5eda1 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -215,6 +215,7 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = { .dma_size = SST_LPT_DSP_DMA_SIZE, }; +#if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI) static struct sst_acpi_mach baytrail_machines[] = { { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, @@ -231,11 +232,14 @@ static struct sst_acpi_desc sst_acpi_baytrail_desc = { .sst_id = SST_DEV_ID_BYT, .resindex_dma_base = -1, }; +#endif static const struct acpi_device_id sst_acpi_match[] = { { "INT33C8", (unsigned long)&sst_acpi_haswell_desc }, { "INT3438", (unsigned long)&sst_acpi_broadwell_desc }, +#if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI) { "80860F28", (unsigned long)&sst_acpi_baytrail_desc }, +#endif { } }; MODULE_DEVICE_TABLE(acpi, sst_acpi_match); diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h index 3ee3b7a..4dcfb7e 100644 --- a/sound/soc/intel/common/sst-acpi.h +++ b/sound/soc/intel/common/sst-acpi.h @@ -14,6 +14,9 @@ #include <linux/acpi.h> +/* translation fron HID to I2C name, needed for DAI codec_name */ +const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); + /* acpi match */ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 81aa1ed..97dc1ae 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -317,6 +317,7 @@ struct sst_dsp { struct skl_cl_dev cl_dev; u32 intr_status; const struct firmware *fw; + struct snd_dma_buffer dmab; }; /* Size optimised DRAM/IRAM memcpy */ diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c index dd077e1..7898433 100644 --- a/sound/soc/intel/common/sst-match-acpi.c +++ b/sound/soc/intel/common/sst-match-acpi.c @@ -13,17 +13,53 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ -#include <linux/acpi.h> -#include <linux/device.h> -#include <linux/module.h> -#include <linux/platform_device.h> #include "sst-acpi.h" +static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level, + void *context, void **ret) +{ + struct acpi_device *adev; + const char *name = NULL; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (adev->status.present && adev->status.functional) { + name = acpi_dev_name(adev); + *(const char **)ret = name; + return AE_CTRL_TERMINATE; + } + + return AE_OK; +} + +const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) +{ + const char *name = NULL; + acpi_status status; + + status = acpi_get_devices(hid, sst_acpi_find_name, NULL, + (void **)&name); + + if (ACPI_FAILURE(status) || name[0] == '\0') + return NULL; + + return name; +} +EXPORT_SYMBOL_GPL(sst_acpi_find_name_from_hid); + static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, void *context, void **ret) { + unsigned long long sta; + acpi_status status; + *(bool *)context = true; + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) + *(bool *)context = false; + return AE_OK; } @@ -37,7 +73,9 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) sst_acpi_mach_match, &found, NULL)) && found) return mach; - return NULL; } EXPORT_SYMBOL_GPL(sst_acpi_find_machine); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index de6dac4..79c5089 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -72,17 +72,47 @@ static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable) skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask); } +static struct skl_dsp_loader_ops skl_get_loader_ops(void) +{ + struct skl_dsp_loader_ops loader_ops; + + memset(&loader_ops, 0, sizeof(struct skl_dsp_loader_ops)); + + loader_ops.alloc_dma_buf = skl_alloc_dma_buf; + loader_ops.free_dma_buf = skl_free_dma_buf; + + return loader_ops; +}; + +static const struct skl_dsp_ops dsp_ops[] = { + { + .id = 0x9d70, + .loader_ops = skl_get_loader_ops, + .init = skl_sst_dsp_init, + .cleanup = skl_sst_dsp_cleanup + }, +}; + +static int skl_get_dsp_ops(int pci_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) { + if (dsp_ops[i].id == pci_id) + return i; + } + + return -EINVAL; +} + int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_bus *bus = ebus_to_hbus(ebus); - int irq = bus->irq; struct skl_dsp_loader_ops loader_ops; - int ret; - - loader_ops.alloc_dma_buf = skl_alloc_dma_buf; - loader_ops.free_dma_buf = skl_free_dma_buf; + int irq = bus->irq; + int ret, index; /* enable ppcap interrupt */ snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true); @@ -95,8 +125,14 @@ int skl_init_dsp(struct skl *skl) return -ENXIO; } - ret = skl_sst_dsp_init(bus->dev, mmio_base, irq, + index = skl_get_dsp_ops(skl->pci->device); + if (index < 0) + return -EINVAL; + + loader_ops = dsp_ops[index].loader_ops(); + ret = dsp_ops[index].init(bus->dev, mmio_base, irq, skl->fw_name, loader_ops, &skl->skl_sst); + if (ret < 0) return ret; @@ -106,18 +142,26 @@ int skl_init_dsp(struct skl *skl) return ret; } -void skl_free_dsp(struct skl *skl) +int skl_free_dsp(struct skl *skl) { struct hdac_ext_bus *ebus = &skl->ebus; struct hdac_bus *bus = ebus_to_hbus(ebus); - struct skl_sst *ctx = skl->skl_sst; + struct skl_sst *ctx = skl->skl_sst; + int index; /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false); - skl_sst_dsp_cleanup(bus->dev, ctx); + index = skl_get_dsp_ops(skl->pci->device); + if (index < 0) + return -EIO; + + dsp_ops[index].cleanup(bus->dev, ctx); + if (ctx->dsp->addr.lpe) iounmap(ctx->dsp->addr.lpe); + + return 0; } int skl_suspend_dsp(struct skl *skl) @@ -238,9 +282,8 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig, * Calculate the gatewat settings required for copier module, type of * gateway and index of gateway to use */ -static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, - struct skl_module_cfg *mconfig, - struct skl_cpr_cfg *cpr_mconfig) +static u32 skl_get_node_id(struct skl_sst *ctx, + struct skl_module_cfg *mconfig) { union skl_connector_node_id node_id = {0}; union skl_ssp_dma_node ssp_node = {0}; @@ -289,13 +332,24 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, break; default: - cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID; + node_id.val = 0xFFFFFFFF; + break; + } + + return node_id.val; +} + +static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_cpr_cfg *cpr_mconfig) +{ + cpr_mconfig->gtw_cfg.node_id = skl_get_node_id(ctx, mconfig); + + if (cpr_mconfig->gtw_cfg.node_id == SKL_NON_GATEWAY_CPR_NODE_ID) { cpr_mconfig->cpr_feature_mask = 0; return; } - cpr_mconfig->gtw_cfg.node_id = node_id.val; - if (SKL_CONN_SOURCE == mconfig->hw_conn_type) cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->obs; else @@ -307,6 +361,46 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx, skl_copy_copier_caps(mconfig, cpr_mconfig); } +#define DMA_CONTROL_ID 5 + +int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig) +{ + struct skl_dma_control *dma_ctrl; + struct skl_i2s_config_blob config_blob; + struct skl_ipc_large_config_msg msg = {0}; + int err = 0; + + + /* + * if blob size is same as capablity size, then no dma control + * present so return + */ + if (mconfig->formats_config.caps_size == sizeof(config_blob)) + return 0; + + msg.large_param_id = DMA_CONTROL_ID; + msg.param_data_size = sizeof(struct skl_dma_control) + + mconfig->formats_config.caps_size; + + dma_ctrl = kzalloc(msg.param_data_size, GFP_KERNEL); + if (dma_ctrl == NULL) + return -ENOMEM; + + dma_ctrl->node_id = skl_get_node_id(ctx, mconfig); + + /* size in dwords */ + dma_ctrl->config_length = sizeof(config_blob) / 4; + + memcpy(dma_ctrl->config_data, mconfig->formats_config.caps, + mconfig->formats_config.caps_size); + + err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl); + + kfree(dma_ctrl); + + return err; +} + static void skl_setup_out_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_audio_data_format *out_fmt) @@ -688,14 +782,14 @@ int skl_unbind_modules(struct skl_sst *ctx, /* get src queue index */ src_index = skl_get_queue_index(src_mcfg->m_out_pin, dst_id, out_max); if (src_index < 0) - return -EINVAL; + return 0; msg.src_queue = src_index; /* get dst queue index */ dst_index = skl_get_queue_index(dst_mcfg->m_in_pin, src_id, in_max); if (dst_index < 0) - return -EINVAL; + return 0; msg.dst_queue = dst_index; @@ -747,7 +841,7 @@ int skl_bind_modules(struct skl_sst *ctx, skl_dump_bind_info(ctx, src_mcfg, dst_mcfg); - if (src_mcfg->m_state < SKL_MODULE_INIT_DONE && + if (src_mcfg->m_state < SKL_MODULE_INIT_DONE || dst_mcfg->m_state < SKL_MODULE_INIT_DONE) return 0; diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 6e4b21c..14d1916e 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -145,3 +145,37 @@ struct nhlt_specific_cfg return NULL; } + +static void skl_nhlt_trim_space(struct skl *skl) +{ + char *s = skl->tplg_name; + int cnt; + int i; + + cnt = 0; + for (i = 0; s[i]; i++) { + if (!isspace(s[i])) + s[cnt++] = s[i]; + } + + s[cnt] = '\0'; +} + +int skl_nhlt_update_topology_bin(struct skl *skl) +{ + struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; + struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); + struct device *dev = bus->dev; + + dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n", + nhlt->header.oem_id, nhlt->header.oem_table_id, + nhlt->header.oem_revision); + + snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s", + skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id, + nhlt->header.oem_revision, "-tplg.bin"); + + skl_nhlt_trim_space(skl); + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index f355325..dab0900 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -206,6 +206,23 @@ static int skl_get_format(struct snd_pcm_substream *substream, return format_val; } +static int skl_be_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_sst *ctx = skl->skl_sst; + struct skl_module_cfg *mconfig; + + if ((dai->playback_active > 1) || (dai->capture_active > 1)) + return 0; + + mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream); + if (mconfig == NULL) + return -EINVAL; + + return skl_dsp_set_dma_control(ctx, mconfig); +} + static int skl_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -458,7 +475,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *link_dev; struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); - struct skl_dma_params *dma_params; + struct hdac_ext_dma_params *dma_params; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct skl_pipe_params p_params = {0}; @@ -470,11 +487,9 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); /* set the stream tag in the codec dai dma params */ - dma_params = (struct skl_dma_params *) - snd_soc_dai_get_dma_data(codec_dai, substream); + dma_params = snd_soc_dai_get_dma_data(codec_dai, substream); if (dma_params) dma_params->stream_tag = hdac_stream(link_dev)->stream_tag; - snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -588,6 +603,7 @@ static struct snd_soc_dai_ops skl_dmic_dai_ops = { static struct snd_soc_dai_ops skl_be_ssp_dai_ops = { .hw_params = skl_be_hw_params, + .prepare = skl_be_prepare, }; static struct snd_soc_dai_ops skl_link_dai_ops = { @@ -660,6 +676,51 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, }, +{ + .name = "HDMI1 Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "HDMI1 Playback", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +{ + .name = "HDMI2 Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "HDMI2 Playback", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +{ + .name = "HDMI3 Pin", + .ops = &skl_pcm_dai_ops, + .playback = { + .stream_name = "HDMI3 Playback", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, /* BE CPU Dais */ { @@ -699,14 +760,41 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { - .name = "iDisp Pin", + .name = "iDisp1 Pin", .ops = &skl_link_dai_ops, .playback = { - .stream_name = "iDisp Tx", + .stream_name = "iDisp1 Tx", .channels_min = HDA_STEREO, .channels_max = HDA_STEREO, .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "iDisp2 Pin", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "iDisp2 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000| + SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "iDisp3 Pin", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "iDisp3 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000| + SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE, }, }, { @@ -864,6 +952,9 @@ static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus, delay += hstream->bufsize; } + if (hstream->bufsize == delay) + delay = 0; + if (delay >= hstream->period_bytes) { dev_info(bus->dev, "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 1bfb7f6..a5267e8 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -34,7 +34,7 @@ void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state) mutex_unlock(&ctx->mutex); } -static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx) +static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx) { int ret; @@ -60,7 +60,7 @@ static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx) return ret; } -static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) +static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) { int ret; @@ -87,7 +87,7 @@ static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx) return ret; } -static bool is_skl_dsp_core_enable(struct sst_dsp *ctx) +static bool is_skl_dsp_core_enable(struct sst_dsp *ctx) { int val; bool is_enable; @@ -140,7 +140,7 @@ static int skl_dsp_start_core(struct sst_dsp *ctx) return ret; } -static int skl_dsp_core_power_up(struct sst_dsp *ctx) +static int skl_dsp_core_power_up(struct sst_dsp *ctx) { int ret; @@ -166,7 +166,7 @@ static int skl_dsp_core_power_up(struct sst_dsp *ctx) return ret; } -static int skl_dsp_core_power_down(struct sst_dsp *ctx) +static int skl_dsp_core_power_down(struct sst_dsp *ctx) { /* update bits */ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, @@ -181,7 +181,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx) "Power down"); } -static int skl_dsp_enable_core(struct sst_dsp *ctx) +int skl_dsp_enable_core(struct sst_dsp *ctx) { int ret; @@ -195,7 +195,7 @@ static int skl_dsp_enable_core(struct sst_dsp *ctx) return skl_dsp_start_core(ctx); } -int skl_dsp_disable_core(struct sst_dsp *ctx) +int skl_dsp_disable_core(struct sst_dsp *ctx) { int ret; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index cbb4075..b6e310d 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -53,6 +53,10 @@ struct sst_dsp_device; /* HIPCT */ #define SKL_ADSP_REG_HIPCT_BUSY BIT(31) +/* FW base IDs */ +#define SKL_INSTANCE_ID 0 +#define SKL_BASE_FW_MODULE_ID 0 + /* Intel HD Audio SRAM Window 1 */ #define SKL_ADSP_SRAM1_BASE 0xA000 @@ -144,7 +148,8 @@ int skl_cldma_prepare(struct sst_dsp *ctx); void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state); struct sst_dsp *skl_dsp_ctx_init(struct device *dev, struct sst_dsp_device *sst_dev, int irq); -int skl_dsp_disable_core(struct sst_dsp *ctx); +int skl_dsp_enable_core(struct sst_dsp *ctx); +int skl_dsp_disable_core(struct sst_dsp *ctx); bool is_skl_dsp_running(struct sst_dsp *ctx); irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id); int skl_dsp_wake(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index e26f474..348a734 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -35,9 +35,6 @@ #define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE #define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4) -#define SKL_INSTANCE_ID 0 -#define SKL_BASE_FW_MODULE_ID 0 - #define SKL_NUM_MODULES 1 static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 4624556..545b4e7 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -54,12 +54,9 @@ static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w) /* * Each pipelines needs memory to be allocated. Check if we have free memory - * from available pool. Then only add this to pool - * This is freed when pipe is deleted - * Note: DSP does actual memory management we only keep track for complete - * pool + * from available pool. */ -static bool skl_tplg_alloc_pipe_mem(struct skl *skl, +static bool skl_is_pipe_mem_avail(struct skl *skl, struct skl_module_cfg *mconfig) { struct skl_sst *ctx = skl->skl_sst; @@ -74,10 +71,20 @@ static bool skl_tplg_alloc_pipe_mem(struct skl *skl, "exceeds ppl memory available %d mem %d\n", skl->resource.max_mem, skl->resource.mem); return false; + } else { + return true; } +} +/* + * Add the mem to the mem pool. This is freed when pipe is deleted. + * Note: DSP does actual memory management we only keep track for complete + * pool + */ +static void skl_tplg_alloc_pipe_mem(struct skl *skl, + struct skl_module_cfg *mconfig) +{ skl->resource.mem += mconfig->pipe->memory_pages; - return true; } /* @@ -85,10 +92,10 @@ static bool skl_tplg_alloc_pipe_mem(struct skl *skl, * quantified in MCPS (Million Clocks Per Second) required for module/pipe * * Each pipelines needs mcps to be allocated. Check if we have mcps for this - * pipe. This adds the mcps to driver counter - * This is removed on pipeline delete + * pipe. */ -static bool skl_tplg_alloc_pipe_mcps(struct skl *skl, + +static bool skl_is_pipe_mcps_avail(struct skl *skl, struct skl_module_cfg *mconfig) { struct skl_sst *ctx = skl->skl_sst; @@ -98,13 +105,18 @@ static bool skl_tplg_alloc_pipe_mcps(struct skl *skl, "%s: module_id %d instance %d\n", __func__, mconfig->id.module_id, mconfig->id.instance_id); dev_err(ctx->dev, - "exceeds ppl memory available %d > mem %d\n", + "exceeds ppl mcps available %d > mem %d\n", skl->resource.max_mcps, skl->resource.mcps); return false; + } else { + return true; } +} +static void skl_tplg_alloc_pipe_mcps(struct skl *skl, + struct skl_module_cfg *mconfig) +{ skl->resource.mcps += mconfig->mcps; - return true; } /* @@ -248,6 +260,65 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx, multiplier; } +static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx) +{ + struct skl_module_cfg *m_cfg = w->priv; + int link_type, dir; + u32 ch, s_freq, s_fmt; + struct nhlt_specific_cfg *cfg; + struct skl *skl = get_skl_ctx(ctx->dev); + + /* check if we already have blob */ + if (m_cfg->formats_config.caps_size > 0) + return 0; + + dev_dbg(ctx->dev, "Applying default cfg blob\n"); + switch (m_cfg->dev_type) { + case SKL_DEVICE_DMIC: + link_type = NHLT_LINK_DMIC; + dir = SNDRV_PCM_STREAM_CAPTURE; + s_freq = m_cfg->in_fmt[0].s_freq; + s_fmt = m_cfg->in_fmt[0].bit_depth; + ch = m_cfg->in_fmt[0].channels; + break; + + case SKL_DEVICE_I2S: + link_type = NHLT_LINK_SSP; + if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) { + dir = SNDRV_PCM_STREAM_PLAYBACK; + s_freq = m_cfg->out_fmt[0].s_freq; + s_fmt = m_cfg->out_fmt[0].bit_depth; + ch = m_cfg->out_fmt[0].channels; + } else { + dir = SNDRV_PCM_STREAM_CAPTURE; + s_freq = m_cfg->in_fmt[0].s_freq; + s_fmt = m_cfg->in_fmt[0].bit_depth; + ch = m_cfg->in_fmt[0].channels; + } + break; + + default: + return -EINVAL; + } + + /* update the blob based on virtual bus_id and default params */ + cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type, + s_fmt, ch, s_freq, dir); + if (cfg) { + m_cfg->formats_config.caps_size = cfg->size; + m_cfg->formats_config.caps = (u32 *) &cfg->caps; + } else { + dev_err(ctx->dev, "Blob NULL for id %x type %d dirn %d\n", + m_cfg->vbus_id, link_type, dir); + dev_err(ctx->dev, "PCM: ch %d, freq %d, fmt %d\n", + ch, s_freq, s_fmt); + return -EIO; + } + + return 0; +} + static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, struct skl_sst *ctx) { @@ -411,7 +482,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) mconfig = w->priv; /* check resource available */ - if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + if (!skl_is_pipe_mcps_avail(skl, mconfig)) return -ENOMEM; if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) { @@ -421,6 +492,9 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) return ret; } + /* update blob if blob is null for be with default value */ + skl_tplg_update_be_blob(w, ctx); + /* * apply fix/conversion to module params based on * FE/BE params @@ -435,6 +509,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe) ret = skl_tplg_set_module_params(w, ctx); if (ret < 0) return ret; + skl_tplg_alloc_pipe_mcps(skl, mconfig); } return 0; @@ -477,10 +552,10 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, struct skl_sst *ctx = skl->skl_sst; /* check resource available */ - if (!skl_tplg_alloc_pipe_mcps(skl, mconfig)) + if (!skl_is_pipe_mcps_avail(skl, mconfig)) return -EBUSY; - if (!skl_tplg_alloc_pipe_mem(skl, mconfig)) + if (!skl_is_pipe_mem_avail(skl, mconfig)) return -ENOMEM; /* @@ -526,11 +601,75 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, src_module = dst_module; } + skl_tplg_alloc_pipe_mem(skl, mconfig); + skl_tplg_alloc_pipe_mcps(skl, mconfig); + + return 0; +} + +/* + * Some modules require params to be set after the module is bound to + * all pins connected. + * + * The module provider initializes set_param flag for such modules and we + * send params after binding + */ +static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w, + struct skl_module_cfg *mcfg, struct skl_sst *ctx) +{ + int i, ret; + struct skl_module_cfg *mconfig = w->priv; + const struct snd_kcontrol_new *k; + struct soc_bytes_ext *sb; + struct skl_algo_data *bc; + struct skl_specific_cfg *sp_cfg; + + /* + * check all out/in pins are in bind state. + * if so set the module param + */ + for (i = 0; i < mcfg->max_out_queue; i++) { + if (mcfg->m_out_pin[i].pin_state != SKL_PIN_BIND_DONE) + return 0; + } + + for (i = 0; i < mcfg->max_in_queue; i++) { + if (mcfg->m_in_pin[i].pin_state != SKL_PIN_BIND_DONE) + return 0; + } + + if (mconfig->formats_config.caps_size > 0 && + mconfig->formats_config.set_params == SKL_PARAM_BIND) { + sp_cfg = &mconfig->formats_config; + ret = skl_set_module_params(ctx, sp_cfg->caps, + sp_cfg->caps_size, + sp_cfg->param_id, mconfig); + if (ret < 0) + return ret; + } + + for (i = 0; i < w->num_kcontrols; i++) { + k = &w->kcontrol_news[i]; + if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + sb = (void *) k->private_value; + bc = (struct skl_algo_data *)sb->dobj.private; + + if (bc->set_params == SKL_PARAM_BIND) { + ret = skl_set_module_params(ctx, + (u32 *)bc->params, bc->max, + bc->param_id, mconfig); + if (ret < 0) + return ret; + } + } + } + return 0; } static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, struct skl *skl, + struct snd_soc_dapm_widget *src_w, struct skl_module_cfg *src_mconfig) { struct snd_soc_dapm_path *p; @@ -547,6 +686,10 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name); next_sink = p->sink; + + if (!is_skl_dsp_widget_type(p->sink)) + return skl_tplg_bind_sinks(p->sink, skl, src_w, src_mconfig); + /* * here we will check widgets in sink pipelines, so that * can be any widgets type and we are only interested if @@ -558,11 +701,19 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, sink = p->sink; sink_mconfig = sink->priv; + if (src_mconfig->m_state == SKL_MODULE_UNINIT || + sink_mconfig->m_state == SKL_MODULE_UNINIT) + continue; + /* Bind source to sink, mixin is always source */ ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig); if (ret) return ret; + /* set module params after bind */ + skl_tplg_set_module_bind_params(src_w, src_mconfig, ctx); + skl_tplg_set_module_bind_params(sink, sink_mconfig, ctx); + /* Start sinks pipe first */ if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) { if (sink_mconfig->pipe->conn_type != @@ -576,7 +727,7 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w, } if (!sink) - return skl_tplg_bind_sinks(next_sink, skl, src_mconfig); + return skl_tplg_bind_sinks(next_sink, skl, src_w, src_mconfig); return 0; } @@ -605,7 +756,7 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, * if sink is not started, start sink pipe first, then start * this pipe */ - ret = skl_tplg_bind_sinks(w, skl, src_mconfig); + ret = skl_tplg_bind_sinks(w, skl, w, src_mconfig); if (ret) return ret; @@ -693,6 +844,10 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, if (ret) return ret; + /* set module params after bind */ + skl_tplg_set_module_bind_params(source, src_mconfig, ctx); + skl_tplg_set_module_bind_params(sink, sink_mconfig, ctx); + if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE) ret = skl_run_pipe(ctx, sink_mconfig->pipe); } @@ -773,10 +928,7 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, continue; } - ret = skl_unbind_modules(ctx, src_module, dst_module); - if (ret < 0) - return ret; - + skl_unbind_modules(ctx, src_module, dst_module); src_module = dst_module; } @@ -814,9 +966,6 @@ static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, * This is a connecter and if path is found that means * unbind between source and sink has not happened yet */ - ret = skl_stop_pipe(ctx, sink_mconfig->pipe); - if (ret < 0) - return ret; ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig); } @@ -842,6 +991,12 @@ static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w, case SND_SOC_DAPM_PRE_PMU: return skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + case SND_SOC_DAPM_POST_PMU: + return skl_tplg_mixer_dapm_post_pmu_event(w, skl); + + case SND_SOC_DAPM_PRE_PMD: + return skl_tplg_mixer_dapm_pre_pmd_event(w, skl); + case SND_SOC_DAPM_POST_PMD: return skl_tplg_mixer_dapm_post_pmd_event(w, skl); } @@ -916,6 +1071,13 @@ static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol, skl_get_module_params(skl->skl_sst, (u32 *)bc->params, bc->max, bc->param_id, mconfig); + /* decrement size for TLV header */ + size -= 2 * sizeof(u32); + + /* check size as we don't want to send kernel data */ + if (size > bc->max) + size = bc->max; + if (bc->params) { if (copy_to_user(data, &bc->param_id, sizeof(u32))) return -EFAULT; @@ -950,7 +1112,7 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol, return -EFAULT; } else { if (copy_from_user(ac->params, - data + 2 * sizeof(u32), size)) + data + 2, size)) return -EFAULT; } @@ -1063,6 +1225,66 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) return NULL; } +static struct skl_module_cfg *skl_get_mconfig_pb_cpr( + struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + struct skl_module_cfg *mconfig = NULL; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + if (w->endpoints[SND_SOC_DAPM_DIR_OUT] > 0) { + if (p->connect && + (p->sink->id == snd_soc_dapm_aif_out) && + p->source->priv) { + mconfig = p->source->priv; + return mconfig; + } + mconfig = skl_get_mconfig_pb_cpr(dai, p->source); + if (mconfig) + return mconfig; + } + } + return mconfig; +} + +static struct skl_module_cfg *skl_get_mconfig_cap_cpr( + struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p; + struct skl_module_cfg *mconfig = NULL; + + snd_soc_dapm_widget_for_each_sink_path(w, p) { + if (w->endpoints[SND_SOC_DAPM_DIR_IN] > 0) { + if (p->connect && + (p->source->id == snd_soc_dapm_aif_in) && + p->sink->priv) { + mconfig = p->sink->priv; + return mconfig; + } + mconfig = skl_get_mconfig_cap_cpr(dai, p->sink); + if (mconfig) + return mconfig; + } + } + return mconfig; +} + +struct skl_module_cfg * +skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, int stream) +{ + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + mconfig = skl_get_mconfig_pb_cpr(dai, w); + } else { + w = dai->capture_widget; + mconfig = skl_get_mconfig_cap_cpr(dai, w); + } + return mconfig; +} + static u8 skl_tplg_be_link_type(int dev_type) { int ret; @@ -1436,8 +1658,7 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be, if (!ac->params) return -ENOMEM; - if (dfw_ac->params) - memcpy(ac->params, dfw_ac->params, ac->max); + memcpy(ac->params, dfw_ac->params, ac->max); } be->dobj.private = ac; @@ -1495,11 +1716,16 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) struct hdac_bus *bus = ebus_to_hbus(ebus); struct skl *skl = ebus_to_skl(ebus); - ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); + ret = request_firmware(&fw, skl->tplg_name, bus->dev); if (ret < 0) { dev_err(bus->dev, "tplg fw %s load failed with %d\n", - "dfw_sst.bin", ret); - return ret; + skl->tplg_name, ret); + ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); + if (ret < 0) { + dev_err(bus->dev, "Fallback tplg fw %s load failed with %d\n", + "dfw_sst.bin", ret); + return ret; + } } /* @@ -1510,6 +1736,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) &skl_tplg_ops, fw, 0); if (ret < 0) { dev_err(bus->dev, "tplg component load failed%d\n", ret); + release_firmware(fw); return -EINVAL; } diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 9aa2a2b..de3c401 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -113,6 +113,29 @@ struct skl_cpr_gtw_cfg { u32 config_data[1]; } __packed; +struct skl_i2s_config_blob { + u32 gateway_attrib; + u32 tdm_ts_group[8]; + u32 ssc0; + u32 ssc1; + u32 sscto; + u32 sspsp; + u32 sstsa; + u32 ssrsa; + u32 ssc2; + u32 sspsp2; + u32 ssc3; + u32 ssioc; + u32 mdivc; + u32 mdivr; +} __packed; + +struct skl_dma_control { + u32 node_id; + u32 config_length; + u32 config_data[1]; +} __packed; + struct skl_cpr_cfg { struct skl_base_cfg base_cfg; struct skl_audio_data_format out_fmt; @@ -313,6 +336,8 @@ static inline struct skl *get_skl_ctx(struct device *dev) int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); +int skl_dsp_set_dma_control(struct skl_sst *ctx, + struct skl_module_cfg *mconfig); void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, struct skl_pipe_params *params, int stream); int skl_tplg_init(struct snd_soc_platform *platform, @@ -345,5 +370,7 @@ int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size, int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, u32 param_id, struct skl_module_cfg *mcfg); +struct skl_module_cfg *skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, + int stream); enum skl_bitdepth skl_get_bit_depth(int params); #endif diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index c9ae010..1db88a6 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -144,7 +144,8 @@ enum module_pin_type { enum skl_module_param_type { SKL_PARAM_DEFAULT = 0, SKL_PARAM_INIT, - SKL_PARAM_SET + SKL_PARAM_SET, + SKL_PARAM_BIND }; struct skl_dfw_module_pin { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 443a15d..ab5e25a 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -28,6 +28,9 @@ #include <linux/firmware.h> #include <sound/pcm.h> #include "../common/sst-acpi.h" +#include <sound/hda_register.h> +#include <sound/hdaudio.h> +#include <sound/hda_i915.h> #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -243,6 +246,16 @@ static int skl_resume(struct device *dev) struct hdac_bus *bus = ebus_to_hbus(ebus); int ret; + /* Turned OFF in HDMI codec driver after codec reconfiguration */ + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + ret = snd_hdac_display_power(bus, true); + if (ret < 0) { + dev_err(bus->dev, + "Cannot turn on display power on i915\n"); + return ret; + } + } + /* * resume only when we are not in suspend active, otherwise need to * restore the device @@ -481,6 +494,27 @@ static int skl_create(struct pci_dev *pci, return 0; } +static int skl_i915_init(struct hdac_bus *bus) +{ + int err; + + /* + * The HDMI codec is in GPU so we need to ensure that it is powered + * up and ready for probe + */ + err = snd_hdac_i915_init(bus); + if (err < 0) + return err; + + err = snd_hdac_display_power(bus, true); + if (err < 0) { + dev_err(bus->dev, "Cannot turn on display power on i915\n"); + return err; + } + + return err; +} + static int skl_first_init(struct hdac_ext_bus *ebus) { struct skl *skl = ebus_to_skl(ebus); @@ -543,6 +577,12 @@ static int skl_first_init(struct hdac_ext_bus *ebus) /* initialize chip */ skl_init_pci(skl); + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + err = skl_i915_init(bus); + if (err < 0) + return err; + } + skl_init_chip(bus, true); /* codec detection */ @@ -573,11 +613,15 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_free; + skl->pci_id = pci->device; + skl->nhlt = skl_nhlt_init(bus->dev); if (skl->nhlt == NULL) goto out_free; + skl_nhlt_update_topology_bin(skl); + pci_set_drvdata(skl->pci, ebus); /* check if dsp is there */ @@ -613,9 +657,15 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_unregister; + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { + err = snd_hdac_display_power(bus, false); + if (err < 0) { + dev_err(bus->dev, "Cannot turn off display power on i915\n"); + return err; + } + } + /*configure PM */ - pm_runtime_set_autosuspend_delay(bus->dev, SKL_SUSPEND_DELAY); - pm_runtime_use_autosuspend(bus->dev); pm_runtime_put_noidle(bus->dev); pm_runtime_allow(bus->dev); @@ -636,6 +686,31 @@ out_free: return err; } +static void skl_shutdown(struct pci_dev *pci) +{ + struct hdac_ext_bus *ebus = pci_get_drvdata(pci); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_stream *s; + struct hdac_ext_stream *stream; + struct skl *skl; + + if (ebus == NULL) + return; + + skl = ebus_to_skl(ebus); + + if (skl->init_failed) + return; + + snd_hdac_ext_stop_streams(ebus); + list_for_each_entry(s, &bus->stream_list, list) { + stream = stream_to_hdac_ext_stream(s); + snd_hdac_ext_stream_decouple(ebus, stream, false); + } + + snd_hdac_bus_stop_chip(bus); +} + static void skl_remove(struct pci_dev *pci) { struct hdac_ext_bus *ebus = pci_get_drvdata(pci); @@ -644,6 +719,9 @@ static void skl_remove(struct pci_dev *pci) if (skl->tplg) release_firmware(skl->tplg); + if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) + snd_hdac_i915_exit(&ebus->bus); + if (pci_dev_run_wake(pci)) pm_runtime_get_noresume(&pci->dev); pci_dev_put(pci); @@ -664,11 +742,18 @@ static struct sst_acpi_mach sst_skl_devdata[] = { {} }; +static struct sst_acpi_mach sst_bxtp_devdata[] = { + { "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL }, +}; + /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), .driver_data = (unsigned long)&sst_skl_devdata}, + /* BXT-P */ + { PCI_DEVICE(0x8086, 0x5a98), + .driver_data = (unsigned long)&sst_bxtp_devdata}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); @@ -679,6 +764,7 @@ static struct pci_driver skl_driver = { .id_table = skl_ids, .probe = skl_probe, .remove = skl_remove, + .shutdown = skl_shutdown, .driver = { .pm = &skl_pm, }, diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4d18293..39e16fa 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -73,6 +73,8 @@ struct skl { struct list_head ppl_list; const char *fw_name; + char tplg_name[64]; + unsigned short pci_id; const struct firmware *tplg; int supend_active; @@ -88,6 +90,16 @@ struct skl_dma_params { u8 stream_tag; }; +struct skl_dsp_ops { + int id; + struct skl_dsp_loader_ops (*loader_ops)(void); + int (*init)(struct device *dev, void __iomem *mmio_base, + int irq, const char *fw_name, + struct skl_dsp_loader_ops loader_ops, + struct skl_sst **skl_sst); + void (*cleanup)(struct device *dev, struct skl_sst *ctx); +}; + int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); @@ -96,8 +108,9 @@ void skl_nhlt_free(void *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); +int skl_nhlt_update_topology_bin(struct skl *skl); int skl_init_dsp(struct skl *skl); -void skl_free_dsp(struct skl *skl); +int skl_free_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); #endif /* __SOUND_SOC_SKL_H */ diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 15c04e2..f7e789e 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -9,7 +9,7 @@ config SND_SOC_MEDIATEK config SND_SOC_MT8173_MAX98090 tristate "ASoC Audio driver for MT8173 with MAX98090 codec" - depends on SND_SOC_MEDIATEK + depends on SND_SOC_MEDIATEK && I2C select SND_SOC_MAX98090 help This adds ASoC driver for Mediatek MT8173 boards @@ -17,9 +17,30 @@ config SND_SOC_MT8173_MAX98090 Select Y if you have such device. If unsure select "N". +config SND_SOC_MT8173_RT5650 + tristate "ASoC Audio driver for MT8173 with RT5650 codec" + depends on SND_SOC_MEDIATEK && I2C + select SND_SOC_RT5645 + help + This adds ASoC driver for Mediatek MT8173 boards + with the RT5650 audio codec. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MT8173_RT5650_RT5514 + tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs" + depends on SND_SOC_MEDIATEK && I2C + select SND_SOC_RT5645 + select SND_SOC_RT5514 + help + This adds ASoC driver for Mediatek MT8173 boards + with the RT5650 and RT5514 codecs. + Select Y if you have such device. + If unsure select "N". + config SND_SOC_MT8173_RT5650_RT5676 tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs" - depends on SND_SOC_MEDIATEK + depends on SND_SOC_MEDIATEK && I2C select SND_SOC_RT5645 select SND_SOC_RT5677 help @@ -27,4 +48,3 @@ config SND_SOC_MT8173_RT5650_RT5676 with the RT5650 and RT5676 codecs. Select Y if you have such device. If unsure select "N". - diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 75effbe..d486860 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -2,4 +2,6 @@ obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o # Machine support obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o +obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173-rt5650-rt5514.c new file mode 100644 index 0000000..58e0836 --- /dev/null +++ b/sound/soc/mediatek/mt8173-rt5650-rt5514.c @@ -0,0 +1,258 @@ +/* + * 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> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../codecs/rt5645.h" + +#define MCLK_FOR_CODECS 12288000 + +static const struct snd_soc_dapm_widget mt8173_rt5650_rt5514_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_rt5650_rt5514_routes[] = { + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + {"Sub DMIC1L", NULL, "Int Mic"}, + {"Sub DMIC1R", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headset Mic", NULL, "micbias1"}, + {"Headset Mic", NULL, "micbias2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new mt8173_rt5650_rt5514_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + /* pll from mclk 12.288M */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, + params_rate(params) * 512); + if (ret) + return ret; + + /* sysclk from pll */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + return 0; +} + +static struct snd_soc_ops mt8173_rt5650_rt5514_ops = { + .hw_params = mt8173_rt5650_rt5514_hw_params, +}; + +static struct snd_soc_jack mt8173_rt5650_rt5514_jack; + +static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; + int ret; + + rt5645_sel_asrc_clk_src(codec, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5514_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new Headset Jack %d\n", ret); + return ret; + } + + return rt5645_set_jack_detect(codec, + &mt8173_rt5650_rt5514_jack, + &mt8173_rt5650_rt5514_jack, + &mt8173_rt5650_rt5514_jack); +} + +static struct snd_soc_dai_link_component mt8173_rt5650_rt5514_codecs[] = { + { + .dai_name = "rt5645-aif1", + }, + { + .dai_name = "rt5514-aif1", + }, +}; + +enum { + DAI_LINK_PLAYBACK, + DAI_LINK_CAPTURE, + DAI_LINK_CODEC_I2S, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_rt5650_rt5514_dais[] = { + /* Front End DAI links */ + [DAI_LINK_PLAYBACK] = { + .name = "rt5650_rt5514 Playback", + .stream_name = "rt5650_rt5514 Playback", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + [DAI_LINK_CAPTURE] = { + .name = "rt5650_rt5514 Capture", + .stream_name = "rt5650_rt5514 Capture", + .cpu_dai_name = "VUL", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + /* Back End DAI links */ + [DAI_LINK_CODEC_I2S] = { + .name = "Codec", + .cpu_dai_name = "I2S", + .no_pcm = 1, + .codecs = mt8173_rt5650_rt5514_codecs, + .num_codecs = 2, + .init = mt8173_rt5650_rt5514_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &mt8173_rt5650_rt5514_ops, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_codec_conf mt8173_rt5650_rt5514_codec_conf[] = { + { + .name_prefix = "Sub", + }, +}; + +static struct snd_soc_card mt8173_rt5650_rt5514_card = { + .name = "mtk-rt5650-rt5514", + .owner = THIS_MODULE, + .dai_link = mt8173_rt5650_rt5514_dais, + .num_links = ARRAY_SIZE(mt8173_rt5650_rt5514_dais), + .codec_conf = mt8173_rt5650_rt5514_codec_conf, + .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5514_codec_conf), + .controls = mt8173_rt5650_rt5514_controls, + .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5514_controls), + .dapm_widgets = mt8173_rt5650_rt5514_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5514_widgets), + .dapm_routes = mt8173_rt5650_rt5514_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5514_routes), +}; + +static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_rt5650_rt5514_card; + struct device_node *platform_node; + int i, ret; + + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + + for (i = 0; i < card->num_links; i++) { + if (mt8173_rt5650_rt5514_dais[i].platform_name) + continue; + mt8173_rt5650_rt5514_dais[i].platform_of_node = platform_node; + } + + mt8173_rt5650_rt5514_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); + if (!mt8173_rt5650_rt5514_codecs[0].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5514_codecs[1].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); + if (!mt8173_rt5650_rt5514_codecs[1].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + mt8173_rt5650_rt5514_codec_conf[0].of_node = + mt8173_rt5650_rt5514_codecs[1].of_node; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + 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; +} + +static const struct of_device_id mt8173_rt5650_rt5514_dt_match[] = { + { .compatible = "mediatek,mt8173-rt5650-rt5514", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5514_dt_match); + +static struct platform_driver mt8173_rt5650_rt5514_driver = { + .driver = { + .name = "mtk-rt5650-rt5514", + .of_match_table = mt8173_rt5650_rt5514_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_rt5650_rt5514_dev_probe, +}; + +module_platform_driver(mt8173_rt5650_rt5514_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 RT5650 and RT5514 SoC machine driver"); +MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-rt5650-rt5514"); + diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c index 50ba538..5c4c58c 100644 --- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c @@ -131,10 +131,17 @@ static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = { }, }; +enum { + DAI_LINK_PLAYBACK, + DAI_LINK_CAPTURE, + DAI_LINK_CODEC_I2S, + DAI_LINK_INTERCODEC +}; + /* Digital audio interface glue - connects codec <---> CPU */ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { /* Front End DAI links */ - { + [DAI_LINK_PLAYBACK] = { .name = "rt5650_rt5676 Playback", .stream_name = "rt5650_rt5676 Playback", .cpu_dai_name = "DL1", @@ -144,7 +151,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dynamic = 1, .dpcm_playback = 1, }, - { + [DAI_LINK_CAPTURE] = { .name = "rt5650_rt5676 Capture", .stream_name = "rt5650_rt5676 Capture", .cpu_dai_name = "VUL", @@ -156,7 +163,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { }, /* Back End DAI links */ - { + [DAI_LINK_CODEC_I2S] = { .name = "Codec", .cpu_dai_name = "I2S", .no_pcm = 1, @@ -170,7 +177,8 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, }, - { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ + /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */ + [DAI_LINK_INTERCODEC] = { .name = "rt5650_rt5676 intercodec", .stream_name = "rt5650_rt5676 intercodec", .cpu_dai_name = "snd-soc-dummy-dai", @@ -240,7 +248,7 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) mt8173_rt5650_rt5676_codec_conf[0].of_node = mt8173_rt5650_rt5676_codecs[1].of_node; - mt8173_rt5650_rt5676_dais[3].codec_of_node = + mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node = mt8173_rt5650_rt5676_codecs[1].of_node; card->dev = &pdev->dev; diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173-rt5650.c new file mode 100644 index 0000000..bb09bb1 --- /dev/null +++ b/sound/soc/mediatek/mt8173-rt5650.c @@ -0,0 +1,236 @@ +/* + * 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> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../codecs/rt5645.h" + +#define MCLK_FOR_CODECS 12288000 + +static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = { + {"Speaker", NULL, "SPOL"}, + {"Speaker", NULL, "SPOR"}, + {"DMIC L1", NULL, "Int Mic"}, + {"DMIC R1", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Headset Mic", NULL, "micbias1"}, + {"Headset Mic", NULL, "micbias2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, +}; + +static const struct snd_kcontrol_new mt8173_rt5650_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int i, ret; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + + /* pll from mclk 12.288M */ + ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, + params_rate(params) * 512); + if (ret) + return ret; + + /* sysclk from pll */ + ret = snd_soc_dai_set_sysclk(codec_dai, 1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + return 0; +} + +static struct snd_soc_ops mt8173_rt5650_ops = { + .hw_params = mt8173_rt5650_hw_params, +}; + +static struct snd_soc_jack mt8173_rt5650_jack; + +static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec_dais[0]->codec; + int ret; + + rt5645_sel_asrc_clk_src(codec, + RT5645_DA_STEREO_FILTER | + RT5645_AD_STEREO_FILTER, + RT5645_CLK_SEL_I2S1_ASRC); + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Can't new Headset Jack %d\n", ret); + return ret; + } + + return rt5645_set_jack_detect(codec, + &mt8173_rt5650_jack, + &mt8173_rt5650_jack, + &mt8173_rt5650_jack); +} + +static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = { + { + .dai_name = "rt5645-aif1", + }, +}; + +enum { + DAI_LINK_PLAYBACK, + DAI_LINK_CAPTURE, + DAI_LINK_CODEC_I2S, +}; + +/* Digital audio interface glue - connects codec <---> CPU */ +static struct snd_soc_dai_link mt8173_rt5650_dais[] = { + /* Front End DAI links */ + [DAI_LINK_PLAYBACK] = { + .name = "rt5650 Playback", + .stream_name = "rt5650 Playback", + .cpu_dai_name = "DL1", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + }, + [DAI_LINK_CAPTURE] = { + .name = "rt5650 Capture", + .stream_name = "rt5650 Capture", + .cpu_dai_name = "VUL", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + }, + /* Back End DAI links */ + [DAI_LINK_CODEC_I2S] = { + .name = "Codec", + .cpu_dai_name = "I2S", + .no_pcm = 1, + .codecs = mt8173_rt5650_codecs, + .num_codecs = 1, + .init = mt8173_rt5650_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &mt8173_rt5650_ops, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static struct snd_soc_card mt8173_rt5650_card = { + .name = "mtk-rt5650", + .owner = THIS_MODULE, + .dai_link = mt8173_rt5650_dais, + .num_links = ARRAY_SIZE(mt8173_rt5650_dais), + .controls = mt8173_rt5650_controls, + .num_controls = ARRAY_SIZE(mt8173_rt5650_controls), + .dapm_widgets = mt8173_rt5650_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets), + .dapm_routes = mt8173_rt5650_routes, + .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes), +}; + +static int mt8173_rt5650_dev_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt8173_rt5650_card; + struct device_node *platform_node; + int i, ret; + + platform_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,platform", 0); + if (!platform_node) { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + + for (i = 0; i < card->num_links; i++) { + if (mt8173_rt5650_dais[i].platform_name) + continue; + mt8173_rt5650_dais[i].platform_of_node = platform_node; + } + + mt8173_rt5650_codecs[0].of_node = + of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0); + if (!mt8173_rt5650_codecs[0].of_node) { + dev_err(&pdev->dev, + "Property 'audio-codec' missing or invalid\n"); + return -EINVAL; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + 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; +} + +static const struct of_device_id mt8173_rt5650_dt_match[] = { + { .compatible = "mediatek,mt8173-rt5650", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match); + +static struct platform_driver mt8173_rt5650_driver = { + .driver = { + .name = "mtk-rt5650", + .of_match_table = mt8173_rt5650_dt_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, + .probe = mt8173_rt5650_dev_probe, +}; + +module_platform_driver(mt8173_rt5650_driver); + +/* Module information */ +MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver"); +MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtk-rt5650"); + diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h index 9b1af1a..f341f62 100644 --- a/sound/soc/mediatek/mtk-afe-common.h +++ b/sound/soc/mediatek/mtk-afe-common.h @@ -87,6 +87,7 @@ struct mtk_afe_memif_data { int irq_en_shift; int irq_fs_shift; int irq_clr_shift; + int msb_shift; }; struct mtk_afe_memif { diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c index 08af9f5..f1c58a2 100644 --- a/sound/soc/mediatek/mtk-afe-pcm.c +++ b/sound/soc/mediatek/mtk-afe-pcm.c @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/dma-mapping.h> #include <linux/pm_runtime.h> #include <sound/soc.h> #include "mtk-afe-common.h" @@ -35,9 +36,11 @@ #define AFE_I2S_CON1 0x0034 #define AFE_I2S_CON2 0x0038 #define AFE_CONN_24BIT 0x006c +#define AFE_MEMIF_MSB 0x00cc #define AFE_CONN1 0x0024 #define AFE_CONN2 0x0028 +#define AFE_CONN3 0x002c #define AFE_CONN7 0x0460 #define AFE_CONN8 0x0464 #define AFE_HDMI_CONN0 0x0390 @@ -61,6 +64,7 @@ #define AFE_HDMI_OUT_CUR 0x0378 #define AFE_HDMI_OUT_END 0x037c +#define AFE_ADDA_TOP_CON0 0x0120 #define AFE_ADDA2_TOP_CON0 0x0600 #define AFE_HDMI_OUT_CON0 0x0370 @@ -257,6 +261,7 @@ static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate) return -EINVAL; /* from external ADC */ + regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x1); regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1); /* set input */ @@ -281,20 +286,13 @@ static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable) regmap_read(afe->regmap, AFE_I2S_CON2, &val); if (!!(val & AFE_I2S_CON2_EN) == enable) - return; /* must skip soft reset */ - - /* I2S soft reset begin */ - regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0x4); + return; /* input */ regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable); /* output */ regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable); - - /* I2S soft reset end */ - udelay(1); - regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0); } static int mtk_afe_dais_enable_clks(struct mtk_afe *afe, @@ -363,6 +361,7 @@ static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream, return 0; mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL); regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0); return 0; @@ -382,6 +381,7 @@ static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream, AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M); mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL); + mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL); } static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream, @@ -395,6 +395,9 @@ static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream, mtk_afe_dais_set_clks(afe, afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256, NULL, 0); + mtk_afe_dais_set_clks(afe, + afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256, + NULL, 0); /* config I2S */ ret = mtk_afe_set_i2s(afe, substream->runtime->rate); if (ret) @@ -592,6 +595,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform); struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id]; + int msb_at_bit33 = 0; int ret; dev_dbg(afe->dev, @@ -603,7 +607,8 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - memif->phys_buf_addr = substream->runtime->dma_addr; + msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0; + memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr); memif->buffer_size = substream->runtime->dma_bytes; /* start */ @@ -614,6 +619,11 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream, memif->data->reg_ofs_base + AFE_BASE_END_OFFSET, memif->phys_buf_addr + memif->buffer_size - 1); + /* set MSB to 33-bit */ + regmap_update_bits(afe->regmap, AFE_MEMIF_MSB, + 1 << memif->data->msb_shift, + msb_at_bit33 << memif->data->msb_shift); + /* set channel */ if (memif->data->mono_shift >= 0) { unsigned int mono = (params_channels(params) == 1) ? 1 : 0; @@ -894,15 +904,19 @@ static const struct snd_kcontrol_new mtk_afe_o04_mix[] = { }; static const struct snd_kcontrol_new mtk_afe_o09_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0), }; static const struct snd_kcontrol_new mtk_afe_o10_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0), }; static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = { /* inter-connections */ + SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -925,12 +939,16 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = { {"I2S Playback", NULL, "O04"}, {"VUL", NULL, "O09"}, {"VUL", NULL, "O10"}, + {"I03", NULL, "I2S Capture"}, + {"I04", NULL, "I2S Capture"}, {"I17", NULL, "I2S Capture"}, {"I18", NULL, "I2S Capture"}, { "O03", "I05 Switch", "I05" }, { "O04", "I06 Switch", "I06" }, { "O09", "I17 Switch", "I17" }, + { "O09", "I03 Switch", "I03" }, { "O10", "I18 Switch", "I18" }, + { "O10", "I04 Switch", "I04" }, }; static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = { @@ -978,6 +996,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 0, .irq_fs_shift = 4, .irq_clr_shift = 0, + .msb_shift = 0, }, { .name = "DL2", .id = MTK_AFE_MEMIF_DL2, @@ -991,6 +1010,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 2, .irq_fs_shift = 16, .irq_clr_shift = 2, + .msb_shift = 1, }, { .name = "VUL", .id = MTK_AFE_MEMIF_VUL, @@ -1004,6 +1024,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 1, .irq_fs_shift = 8, .irq_clr_shift = 1, + .msb_shift = 6, }, { .name = "DAI", .id = MTK_AFE_MEMIF_DAI, @@ -1017,6 +1038,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 3, .irq_fs_shift = 20, .irq_clr_shift = 3, + .msb_shift = 5, }, { .name = "AWB", .id = MTK_AFE_MEMIF_AWB, @@ -1030,6 +1052,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 14, .irq_fs_shift = 24, .irq_clr_shift = 6, + .msb_shift = 3, }, { .name = "MOD_DAI", .id = MTK_AFE_MEMIF_MOD_DAI, @@ -1043,6 +1066,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 3, .irq_fs_shift = 20, .irq_clr_shift = 3, + .msb_shift = 4, }, { .name = "HDMI", .id = MTK_AFE_MEMIF_HDMI, @@ -1056,6 +1080,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = { .irq_en_shift = 12, .irq_fs_shift = -1, .irq_clr_shift = 4, + .msb_shift = 8, }, }; @@ -1189,6 +1214,10 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev) struct mtk_afe *afe; struct resource *res; + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); + if (ret) + return ret; + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); if (!afe) return -ENOMEM; diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index c866ade..1363100 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -381,9 +381,19 @@ static int mxs_saif_startup(struct snd_pcm_substream *substream, __raw_writel(BM_SAIF_CTRL_CLKGATE, saif->base + SAIF_CTRL + MXS_CLR_ADDR); + clk_prepare(saif->clk); + return 0; } +static void mxs_saif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); + + clk_unprepare(saif->clk); +} + /* * Should only be called when port is inactive. * although can be called multiple times by upper layers. @@ -408,7 +418,7 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream, } stat = __raw_readl(saif->base + SAIF_STAT); - if (stat & BM_SAIF_STAT_BUSY) { + if (!saif->mclk_in_use && (stat & BM_SAIF_STAT_BUSY)) { dev_err(cpu_dai->dev, "error: busy\n"); return -EBUSY; } @@ -424,8 +434,6 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream, return ret; } - /* prepare clk in hw_param, enable in trigger */ - clk_prepare(saif->clk); if (saif != master_saif) { /* * Set an initial clock rate for the saif internal logic to work @@ -611,6 +619,7 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, static const struct snd_soc_dai_ops mxs_saif_dai_ops = { .startup = mxs_saif_startup, + .shutdown = mxs_saif_shutdown, .trigger = mxs_saif_trigger, .prepare = mxs_saif_prepare, .hw_params = mxs_saif_hw_params, diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 190f868..fdecb70 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -133,7 +133,7 @@ static struct snd_soc_ops n810_ops = { static int n810_get_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = n810_spk_func; + ucontrol->value.enumerated.item[0] = n810_spk_func; return 0; } @@ -143,10 +143,10 @@ static int n810_set_spk(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (n810_spk_func == ucontrol->value.integer.value[0]) + if (n810_spk_func == ucontrol->value.enumerated.item[0]) return 0; - n810_spk_func = ucontrol->value.integer.value[0]; + n810_spk_func = ucontrol->value.enumerated.item[0]; n810_ext_control(&card->dapm); return 1; @@ -155,7 +155,7 @@ static int n810_set_spk(struct snd_kcontrol *kcontrol, static int n810_get_jack(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = n810_jack_func; + ucontrol->value.enumerated.item[0] = n810_jack_func; return 0; } @@ -165,10 +165,10 @@ static int n810_set_jack(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (n810_jack_func == ucontrol->value.integer.value[0]) + if (n810_jack_func == ucontrol->value.enumerated.item[0]) return 0; - n810_jack_func = ucontrol->value.integer.value[0]; + n810_jack_func = ucontrol->value.enumerated.item[0]; n810_ext_control(&card->dapm); return 1; @@ -177,7 +177,7 @@ static int n810_set_jack(struct snd_kcontrol *kcontrol, static int n810_get_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = n810_dmic_func; + ucontrol->value.enumerated.item[0] = n810_dmic_func; return 0; } @@ -187,10 +187,10 @@ static int n810_set_input(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (n810_dmic_func == ucontrol->value.integer.value[0]) + if (n810_dmic_func == ucontrol->value.enumerated.item[0]) return 0; - n810_dmic_func = ucontrol->value.integer.value[0]; + n810_dmic_func = ucontrol->value.enumerated.item[0]; n810_ext_control(&card->dapm); return 1; diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index f83cc2b..64425d3 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -345,6 +345,7 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) dai_drv = &omap4_hdmi_dai; break; case OMAPDSS_VER_OMAP5: + case OMAPDSS_VER_DRA7xx: dai_drv = &omap5_hdmi_dai; break; default: diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 5e21f08..5494924 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -132,7 +132,7 @@ static struct snd_soc_ops rx51_ops = { static int rx51_get_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = rx51_spk_func; + ucontrol->value.enumerated.item[0] = rx51_spk_func; return 0; } @@ -142,10 +142,10 @@ static int rx51_set_spk(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (rx51_spk_func == ucontrol->value.integer.value[0]) + if (rx51_spk_func == ucontrol->value.enumerated.item[0]) return 0; - rx51_spk_func = ucontrol->value.integer.value[0]; + rx51_spk_func = ucontrol->value.enumerated.item[0]; rx51_ext_control(&card->dapm); return 1; @@ -180,7 +180,7 @@ static int rx51_hp_event(struct snd_soc_dapm_widget *w, static int rx51_get_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = rx51_dmic_func; + ucontrol->value.enumerated.item[0] = rx51_dmic_func; return 0; } @@ -190,10 +190,10 @@ static int rx51_set_input(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (rx51_dmic_func == ucontrol->value.integer.value[0]) + if (rx51_dmic_func == ucontrol->value.enumerated.item[0]) return 0; - rx51_dmic_func = ucontrol->value.integer.value[0]; + rx51_dmic_func = ucontrol->value.enumerated.item[0]; rx51_ext_control(&card->dapm); return 1; @@ -202,7 +202,7 @@ static int rx51_set_input(struct snd_kcontrol *kcontrol, static int rx51_get_jack(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = rx51_jack_func; + ucontrol->value.enumerated.item[0] = rx51_jack_func; return 0; } @@ -212,10 +212,10 @@ static int rx51_set_jack(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (rx51_jack_func == ucontrol->value.integer.value[0]) + if (rx51_jack_func == ucontrol->value.enumerated.item[0]) return 0; - rx51_jack_func = ucontrol->value.integer.value[0]; + rx51_jack_func = ucontrol->value.enumerated.item[0]; rx51_ext_control(&card->dapm); return 1; diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 416ea64..ec522e9 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -52,7 +52,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int freq_out, sspa_mclk, sysclk; - int sspa_div; if (params_rate(params) > 11025) { freq_out = params_rate(params) * 512; @@ -63,7 +62,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream, sysclk = params_rate(params) * 512; sspa_mclk = params_rate(params) * 64; } - sspa_div = freq_out / sspa_mclk; snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0); snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk); diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index c97dc13..dcbb7aa 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -163,7 +163,7 @@ static struct snd_soc_ops corgi_ops = { static int corgi_get_jack(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = corgi_jack_func; + ucontrol->value.enumerated.item[0] = corgi_jack_func; return 0; } @@ -172,10 +172,10 @@ static int corgi_set_jack(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (corgi_jack_func == ucontrol->value.integer.value[0]) + if (corgi_jack_func == ucontrol->value.enumerated.item[0]) return 0; - corgi_jack_func = ucontrol->value.integer.value[0]; + corgi_jack_func = ucontrol->value.enumerated.item[0]; corgi_ext_control(&card->dapm); return 1; } @@ -183,7 +183,7 @@ static int corgi_set_jack(struct snd_kcontrol *kcontrol, static int corgi_get_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = corgi_spk_func; + ucontrol->value.enumerated.item[0] = corgi_spk_func; return 0; } @@ -192,10 +192,10 @@ static int corgi_set_spk(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (corgi_spk_func == ucontrol->value.integer.value[0]) + if (corgi_spk_func == ucontrol->value.enumerated.item[0]) return 0; - corgi_spk_func = ucontrol->value.integer.value[0]; + corgi_spk_func = ucontrol->value.enumerated.item[0]; corgi_ext_control(&card->dapm); return 1; } diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 241d0be..62b8377 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -308,17 +308,17 @@ static int magician_set_spk(struct snd_kcontrol *kcontrol, static int magician_get_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = magician_in_sel; + ucontrol->value.enumerated.item[0] = magician_in_sel; return 0; } static int magician_set_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - if (magician_in_sel == ucontrol->value.integer.value[0]) + if (magician_in_sel == ucontrol->value.enumerated.item[0]) return 0; - magician_in_sel = ucontrol->value.integer.value[0]; + magician_in_sel = ucontrol->value.enumerated.item[0]; switch (magician_in_sel) { case MAGICIAN_MIC: diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 84d0e2e..4b3b714 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -138,7 +138,7 @@ static struct snd_soc_ops poodle_ops = { static int poodle_get_jack(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = poodle_jack_func; + ucontrol->value.enumerated.item[0] = poodle_jack_func; return 0; } @@ -147,10 +147,10 @@ static int poodle_set_jack(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (poodle_jack_func == ucontrol->value.integer.value[0]) + if (poodle_jack_func == ucontrol->value.enumerated.item[0]) return 0; - poodle_jack_func = ucontrol->value.integer.value[0]; + poodle_jack_func = ucontrol->value.enumerated.item[0]; poodle_ext_control(&card->dapm); return 1; } @@ -158,7 +158,7 @@ static int poodle_set_jack(struct snd_kcontrol *kcontrol, static int poodle_get_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = poodle_spk_func; + ucontrol->value.enumerated.item[0] = poodle_spk_func; return 0; } @@ -167,10 +167,10 @@ static int poodle_set_spk(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (poodle_spk_func == ucontrol->value.integer.value[0]) + if (poodle_spk_func == ucontrol->value.enumerated.item[0]) return 0; - poodle_spk_func = ucontrol->value.integer.value[0]; + poodle_spk_func = ucontrol->value.enumerated.item[0]; poodle_ext_control(&card->dapm); return 1; } diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index b002226..0e02634 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -164,7 +164,7 @@ static struct snd_soc_ops spitz_ops = { static int spitz_get_jack(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = spitz_jack_func; + ucontrol->value.enumerated.item[0] = spitz_jack_func; return 0; } @@ -173,10 +173,10 @@ static int spitz_set_jack(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (spitz_jack_func == ucontrol->value.integer.value[0]) + if (spitz_jack_func == ucontrol->value.enumerated.item[0]) return 0; - spitz_jack_func = ucontrol->value.integer.value[0]; + spitz_jack_func = ucontrol->value.enumerated.item[0]; spitz_ext_control(&card->dapm); return 1; } @@ -184,7 +184,7 @@ static int spitz_set_jack(struct snd_kcontrol *kcontrol, static int spitz_get_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = spitz_spk_func; + ucontrol->value.enumerated.item[0] = spitz_spk_func; return 0; } @@ -193,10 +193,10 @@ static int spitz_set_spk(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (spitz_spk_func == ucontrol->value.integer.value[0]) + if (spitz_spk_func == ucontrol->value.enumerated.item[0]) return 0; - spitz_spk_func = ucontrol->value.integer.value[0]; + spitz_spk_func = ucontrol->value.enumerated.item[0]; spitz_ext_control(&card->dapm); return 1; } diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 49518dd..c508f02 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -95,7 +95,7 @@ static struct snd_soc_ops tosa_ops = { static int tosa_get_jack(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = tosa_jack_func; + ucontrol->value.enumerated.item[0] = tosa_jack_func; return 0; } @@ -104,10 +104,10 @@ static int tosa_set_jack(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (tosa_jack_func == ucontrol->value.integer.value[0]) + if (tosa_jack_func == ucontrol->value.enumerated.item[0]) return 0; - tosa_jack_func = ucontrol->value.integer.value[0]; + tosa_jack_func = ucontrol->value.enumerated.item[0]; tosa_ext_control(&card->dapm); return 1; } @@ -115,7 +115,7 @@ static int tosa_set_jack(struct snd_kcontrol *kcontrol, static int tosa_get_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - ucontrol->value.integer.value[0] = tosa_spk_func; + ucontrol->value.enumerated.item[0] = tosa_spk_func; return 0; } @@ -124,10 +124,10 @@ static int tosa_set_spk(struct snd_kcontrol *kcontrol, { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - if (tosa_spk_func == ucontrol->value.integer.value[0]) + if (tosa_spk_func == ucontrol->value.enumerated.item[0]) return 0; - tosa_spk_func = ucontrol->value.integer.value[0]; + tosa_spk_func = ucontrol->value.enumerated.item[0]; tosa_ext_control(&card->dapm); return 1; } diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 3cc252e..8ec9a07 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -11,21 +11,24 @@ 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 + depends on SND_SOC_QCOM && HAS_DMA select SND_SOC_LPASS_IPQ806X select SND_SOC_MAX98357A help @@ -34,7 +37,7 @@ config SND_SOC_STORM config SND_SOC_APQ8016_SBC tristate "SoC Audio support for APQ8016 SBC platforms" - depends on SND_SOC_QCOM + depends on SND_SOC_QCOM && HAS_DMA select SND_SOC_LPASS_APQ8016 help Support for Qualcomm Technologies LPASS audio block in diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index 1efdf00..1289543 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -30,6 +30,7 @@ struct apq8016_sbc_data { struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ }; +#define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21) #define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17) #define MIC_CTRL_TLMM_SCLK_EN BIT(1) #define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16)) @@ -53,6 +54,12 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) MIC_CTRL_TLMM_SCLK_EN, pdata->mic_iomux); break; + case MI2S_TERTIARY: + writel(readl(pdata->mic_iomux) | MIC_CTRL_TER_WS_SLAVE_SEL | + MIC_CTRL_TLMM_SCLK_EN, + pdata->mic_iomux); + + break; default: dev_err(card->dev, "unsupported cpu dai configuration\n"); @@ -126,9 +133,6 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) } link->platform_of_node = link->cpu_of_node; - /* For now we only support playback */ - link->playback_only = true; - ret = of_property_read_string(np, "link-name", &link->name); if (ret) { dev_err(card->dev, "error getting codec dai_link name\n"); diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index 94efc01..3eef0c3 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -133,23 +133,36 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { }, }; -static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata) +static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, + int direction) { struct lpass_variant *v = drvdata->variant; - int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map, + int chan = 0; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, v->rdma_channels); - if (chan >= v->rdma_channels) - return -EBUSY; + if (chan >= v->rdma_channels) + return -EBUSY; + } else { + chan = find_next_zero_bit(&drvdata->dma_ch_bit_map, + v->wrdma_channel_start + + v->wrdma_channels, + v->wrdma_channel_start); + + if (chan >= v->wrdma_channel_start + v->wrdma_channels) + return -EBUSY; + } - set_bit(chan, &drvdata->rdma_ch_bit_map); + set_bit(chan, &drvdata->dma_ch_bit_map); return chan; } static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) { - clear_bit(chan, &drvdata->rdma_ch_bit_map); + clear_bit(chan, &drvdata->dma_ch_bit_map); return 0; } @@ -212,7 +225,11 @@ static struct lpass_variant apq8016_data = { .rdma_reg_base = 0x8400, .rdma_reg_stride = 0x1000, .rdma_channels = 2, - .rdmactl_audif_start = 1, + .dmactl_audif_start = 1, + .wrdma_reg_base = 0xB000, + .wrdma_reg_stride = 0x1000, + .wrdma_channel_start = 5, + .wrdma_channels = 2, .dai_driver = apq8016_lpass_cpu_dai_driver, .num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver), .init = apq8016_lpass_init, diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 00b6c9d..3cde9fb 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -120,31 +120,60 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - switch (channels) { - case 1: - regval |= LPAIF_I2SCTL_SPKMODE_SD0; - regval |= LPAIF_I2SCTL_SPKMONO_MONO; - break; - case 2: - regval |= LPAIF_I2SCTL_SPKMODE_SD0; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 4: - regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 6: - regval |= LPAIF_I2SCTL_SPKMODE_6CH; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 8: - regval |= LPAIF_I2SCTL_SPKMODE_8CH; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - default: - dev_err(dai->dev, "%s() invalid channels given: %u\n", - __func__, channels); - return -EINVAL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (channels) { + case 1: + regval |= LPAIF_I2SCTL_SPKMODE_SD0; + regval |= LPAIF_I2SCTL_SPKMONO_MONO; + break; + case 2: + regval |= LPAIF_I2SCTL_SPKMODE_SD0; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 4: + regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 6: + regval |= LPAIF_I2SCTL_SPKMODE_6CH; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 8: + regval |= LPAIF_I2SCTL_SPKMODE_8CH; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + default: + dev_err(dai->dev, "%s() invalid channels given: %u\n", + __func__, channels); + return -EINVAL; + } + } else { + switch (channels) { + case 1: + regval |= LPAIF_I2SCTL_MICMODE_SD0; + regval |= LPAIF_I2SCTL_MICMONO_MONO; + break; + case 2: + regval |= LPAIF_I2SCTL_MICMODE_SD0; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + case 4: + regval |= LPAIF_I2SCTL_MICMODE_QUAD01; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + case 6: + regval |= LPAIF_I2SCTL_MICMODE_6CH; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + case 8: + regval |= LPAIF_I2SCTL_MICMODE_8CH; + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + break; + default: + dev_err(dai->dev, "%s() invalid channels given: %u\n", + __func__, channels); + return -EINVAL; + } } ret = regmap_write(drvdata->lpaif_map, @@ -188,10 +217,19 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; + unsigned int val, mask; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = LPAIF_I2SCTL_SPKEN_ENABLE; + mask = LPAIF_I2SCTL_SPKEN_MASK; + } else { + val = LPAIF_I2SCTL_MICEN_ENABLE; + mask = LPAIF_I2SCTL_MICEN_MASK; + } ret = regmap_update_bits(drvdata->lpaif_map, LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); + mask, val); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); @@ -204,16 +242,24 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret = -EINVAL; + unsigned int val, mask; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = LPAIF_I2SCTL_SPKEN_ENABLE; + mask = LPAIF_I2SCTL_SPKEN_MASK; + } else { + val = LPAIF_I2SCTL_MICEN_ENABLE; + mask = LPAIF_I2SCTL_MICEN_MASK; + } + ret = regmap_update_bits(drvdata->lpaif_map, LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - LPAIF_I2SCTL_SPKEN_MASK, - LPAIF_I2SCTL_SPKEN_ENABLE); + mask, val); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); @@ -221,11 +267,18 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = LPAIF_I2SCTL_SPKEN_DISABLE; + mask = LPAIF_I2SCTL_SPKEN_MASK; + } else { + val = LPAIF_I2SCTL_MICEN_DISABLE; + mask = LPAIF_I2SCTL_MICEN_MASK; + } + ret = regmap_update_bits(drvdata->lpaif_map, LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - LPAIF_I2SCTL_SPKEN_MASK, - LPAIF_I2SCTL_SPKEN_DISABLE); + mask, val); if (ret) dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", __func__, ret); @@ -294,6 +347,17 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) return true; } + for (i = 0; i < v->wrdma_channels; ++i) { + if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) + return true; + } + return false; } @@ -327,6 +391,19 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) return true; } + for (i = 0; i < v->wrdma_channels; ++i) { + if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) + return true; + if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) + return true; + } + return false; } @@ -344,6 +421,10 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) if (reg == LPAIF_RDMACURR_REG(v, i)) return true; + for (i = 0; i < v->wrdma_channels; ++i) + if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) + return true; + return false; } @@ -355,7 +436,6 @@ static struct regmap_config lpass_cpu_regmap_config = { .readable_reg = lpass_cpu_regmap_readable, .volatile_reg = lpass_cpu_regmap_volatile, .cache_type = REGCACHE_FLAT, - .val_format_endian = REGMAP_ENDIAN_LITTLE, }; int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) @@ -399,8 +479,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) return PTR_ERR((void const __force *)drvdata->lpaif); } - lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant, - variant->rdma_channels); + lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant, + variant->wrdma_channels + + variant->wrdma_channel_start); drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, &lpass_cpu_regmap_config); diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index 7a41679..608c1a9 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -63,9 +63,12 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { .ops = &asoc_qcom_lpass_cpu_dai_ops, }; -static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata) +static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) { - return IPQ806X_LPAIF_RDMA_CHAN_MI2S; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return IPQ806X_LPAIF_RDMA_CHAN_MI2S; + else /* Capture currently not implemented */ + return -EINVAL; } static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) @@ -83,6 +86,10 @@ static struct lpass_variant ipq806x_data = { .rdma_reg_base = 0x6000, .rdma_reg_stride = 0x1000, .rdma_channels = 4, + .wrdma_reg_base = 0xB000, + .wrdma_reg_stride = 0x1000, + .wrdma_channel_start = 5, + .wrdma_channels = 4, .dai_driver = &ipq806x_lpass_cpu_dai_driver, .num_dai = 1, .alloc_dma_channel = ipq806x_lpass_alloc_dma_channel, diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h index 95e22f1..2240bc6 100644 --- a/sound/soc/qcom/lpass-lpaif-reg.h +++ b/sound/soc/qcom/lpass-lpaif-reg.h @@ -47,6 +47,28 @@ #define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT) #define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT) +#define LPAIF_I2SCTL_MICEN_MASK GENMASK(8, 8) +#define LPAIF_I2SCTL_MICEN_SHIFT 8 +#define LPAIF_I2SCTL_MICEN_DISABLE (0 << LPAIF_I2SCTL_MICEN_SHIFT) +#define LPAIF_I2SCTL_MICEN_ENABLE (1 << LPAIF_I2SCTL_MICEN_SHIFT) + +#define LPAIF_I2SCTL_MICMODE_MASK GENMASK(7, 4) +#define LPAIF_I2SCTL_MICMODE_SHIFT 4 +#define LPAIF_I2SCTL_MICMODE_NONE (0 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD0 (1 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD1 (2 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD2 (3 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_SD3 (4 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_QUAD01 (5 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_QUAD23 (6 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_6CH (7 << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE_8CH (8 << LPAIF_I2SCTL_MICMODE_SHIFT) + +#define LPAIF_I2SCTL_MIMONO_MASK GENMASK(3, 3) +#define LPAIF_I2SCTL_MICMONO_SHIFT 3 +#define LPAIF_I2SCTL_MICMONO_STEREO (0 << LPAIF_I2SCTL_MICMONO_SHIFT) +#define LPAIF_I2SCTL_MICMONO_MONO (1 << LPAIF_I2SCTL_MICMONO_SHIFT) + #define LPAIF_I2SCTL_WSSRC_MASK 0x0004 #define LPAIF_I2SCTL_WSSRC_SHIFT 2 #define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT) @@ -90,37 +112,65 @@ #define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan)) #define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan)) -#define LPAIF_RDMACTL_BURSTEN_MASK 0x800 -#define LPAIF_RDMACTL_BURSTEN_SHIFT 11 -#define LPAIF_RDMACTL_BURSTEN_SINGLE (0 << LPAIF_RDMACTL_BURSTEN_SHIFT) -#define LPAIF_RDMACTL_BURSTEN_INCR4 (1 << LPAIF_RDMACTL_BURSTEN_SHIFT) - -#define LPAIF_RDMACTL_WPSCNT_MASK 0x700 -#define LPAIF_RDMACTL_WPSCNT_SHIFT 8 -#define LPAIF_RDMACTL_WPSCNT_ONE (0 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_TWO (1 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_THREE (2 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_FOUR (3 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_SIX (5 << LPAIF_RDMACTL_WPSCNT_SHIFT) -#define LPAIF_RDMACTL_WPSCNT_EIGHT (7 << LPAIF_RDMACTL_WPSCNT_SHIFT) - -#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0 -#define LPAIF_RDMACTL_AUDINTF_SHIFT 4 - -#define LPAIF_RDMACTL_FIFOWM_MASK 0x00E -#define LPAIF_RDMACTL_FIFOWM_SHIFT 1 -#define LPAIF_RDMACTL_FIFOWM_1 (0 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_2 (1 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_3 (2 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_4 (3 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_5 (4 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_6 (5 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_7 (6 << LPAIF_RDMACTL_FIFOWM_SHIFT) -#define LPAIF_RDMACTL_FIFOWM_8 (7 << LPAIF_RDMACTL_FIFOWM_SHIFT) - -#define LPAIF_RDMACTL_ENABLE_MASK 0x1 -#define LPAIF_RDMACTL_ENABLE_SHIFT 0 -#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT) -#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT) - +#define LPAIF_WRDMA_REG_ADDR(v, addr, chan) \ + (v->wrdma_reg_base + (addr) + \ + v->wrdma_reg_stride * (chan - v->wrdma_channel_start)) + +#define LPAIF_WRDMACTL_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x00, (chan)) +#define LPAIF_WRDMABASE_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x04, (chan)) +#define LPAIF_WRDMABUFF_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x08, (chan)) +#define LPAIF_WRDMACURR_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x0C, (chan)) +#define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan)) +#define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan)) + +#define __LPAIF_DMA_REG(v, chan, dir, reg) \ + (dir == SNDRV_PCM_STREAM_PLAYBACK) ? \ + LPAIF_RDMA##reg##_REG(v, chan) : \ + LPAIF_WRDMA##reg##_REG(v, chan) + +#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL) +#define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE) +#define LPAIF_DMABUFF_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BUFF) +#define LPAIF_DMACURR_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CURR) +#define LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER) +#define LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT) + +#define LPAIF_DMACTL_BURSTEN_MASK 0x800 +#define LPAIF_DMACTL_BURSTEN_SHIFT 11 +#define LPAIF_DMACTL_BURSTEN_SINGLE (0 << LPAIF_DMACTL_BURSTEN_SHIFT) +#define LPAIF_DMACTL_BURSTEN_INCR4 (1 << LPAIF_DMACTL_BURSTEN_SHIFT) + +#define LPAIF_DMACTL_WPSCNT_MASK 0x700 +#define LPAIF_DMACTL_WPSCNT_SHIFT 8 +#define LPAIF_DMACTL_WPSCNT_ONE (0 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_TWO (1 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_THREE (2 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_FOUR (3 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_SIX (5 << LPAIF_DMACTL_WPSCNT_SHIFT) +#define LPAIF_DMACTL_WPSCNT_EIGHT (7 << LPAIF_DMACTL_WPSCNT_SHIFT) + +#define LPAIF_DMACTL_AUDINTF_MASK 0x0F0 +#define LPAIF_DMACTL_AUDINTF_SHIFT 4 +#define LPAIF_DMACTL_AUDINTF(id) (id << LPAIF_DMACTL_AUDINTF_SHIFT) + +#define LPAIF_DMACTL_FIFOWM_MASK 0x00E +#define LPAIF_DMACTL_FIFOWM_SHIFT 1 +#define LPAIF_DMACTL_FIFOWM_1 (0 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_2 (1 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_3 (2 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_4 (3 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_5 (4 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_6 (5 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_7 (6 << LPAIF_DMACTL_FIFOWM_SHIFT) +#define LPAIF_DMACTL_FIFOWM_8 (7 << LPAIF_DMACTL_FIFOWM_SHIFT) + +#define LPAIF_DMACTL_ENABLE_MASK 0x1 +#define LPAIF_DMACTL_ENABLE_SHIFT 0 +#define LPAIF_DMACTL_ENABLE_OFF (0 << LPAIF_DMACTL_ENABLE_SHIFT) +#define LPAIF_DMACTL_ENABLE_ON (1 << LPAIF_DMACTL_ENABLE_SHIFT) + +#define LPAIF_DMACTL_DYNCLK_MASK BIT(12) +#define LPAIF_DMACTL_DYNCLK_SHIFT 12 +#define LPAIF_DMACTL_DYNCLK_OFF (0 << LPAIF_DMACTL_DYNCLK_SHIFT) +#define LPAIF_DMACTL_DYNCLK_ON (1 << LPAIF_DMACTL_DYNCLK_SHIFT) #endif /* __LPASS_LPAIF_REG_H__ */ diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 79688aa..6e86654 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -26,6 +26,7 @@ struct lpass_pcm_data { int rdma_ch; + int wrdma_ch; int i2s_port; }; @@ -90,8 +91,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int regval; + int ch, dir = substream->stream; int bitwidth; - int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start; + int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; bitwidth = snd_pcm_format_width(format); if (bitwidth < 0) { @@ -100,25 +107,25 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, return bitwidth; } - regval = LPAIF_RDMACTL_BURSTEN_INCR4 | - LPAIF_RDMACTL_AUDINTF(rdma_port) | - LPAIF_RDMACTL_FIFOWM_8; + regval = LPAIF_DMACTL_BURSTEN_INCR4 | + LPAIF_DMACTL_AUDINTF(dma_port) | + LPAIF_DMACTL_FIFOWM_8; switch (bitwidth) { case 16: switch (channels) { case 1: case 2: - regval |= LPAIF_RDMACTL_WPSCNT_ONE; + regval |= LPAIF_DMACTL_WPSCNT_ONE; break; case 4: - regval |= LPAIF_RDMACTL_WPSCNT_TWO; + regval |= LPAIF_DMACTL_WPSCNT_TWO; break; case 6: - regval |= LPAIF_RDMACTL_WPSCNT_THREE; + regval |= LPAIF_DMACTL_WPSCNT_THREE; break; case 8: - regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + regval |= LPAIF_DMACTL_WPSCNT_FOUR; break; default: dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", @@ -130,19 +137,19 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, case 32: switch (channels) { case 1: - regval |= LPAIF_RDMACTL_WPSCNT_ONE; + regval |= LPAIF_DMACTL_WPSCNT_ONE; break; case 2: - regval |= LPAIF_RDMACTL_WPSCNT_TWO; + regval |= LPAIF_DMACTL_WPSCNT_TWO; break; case 4: - regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + regval |= LPAIF_DMACTL_WPSCNT_FOUR; break; case 6: - regval |= LPAIF_RDMACTL_WPSCNT_SIX; + regval |= LPAIF_DMACTL_WPSCNT_SIX; break; case 8: - regval |= LPAIF_RDMACTL_WPSCNT_EIGHT; + regval |= LPAIF_DMACTL_WPSCNT_EIGHT; break; default: dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", @@ -157,7 +164,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval); + LPAIF_DMACTL_REG(v, ch, dir), regval); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -174,10 +181,15 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; + unsigned int reg; int ret; - ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch); + else + reg = LPAIF_WRDMACTL_REG(v, pcm_data->wrdma_ch); + + ret = regmap_write(drvdata->lpaif_map, reg, 0); if (ret) dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -193,10 +205,15 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; - int ret, ch = pcm_data->rdma_ch; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMABASE_REG(v, ch), + LPAIF_DMABASE_REG(v, ch, dir), runtime->dma_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", @@ -205,7 +222,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMABUFF_REG(v, ch), + LPAIF_DMABUFF_REG(v, ch, dir), (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", @@ -214,7 +231,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMAPER_REG(v, ch), + LPAIF_DMAPER_REG(v, ch, dir), (snd_pcm_lib_period_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", @@ -223,8 +240,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -242,7 +259,12 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; - int ret, ch = pcm_data->rdma_ch; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -269,9 +291,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, - LPAIF_RDMACTL_ENABLE_ON); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, + LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -282,9 +304,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, - LPAIF_RDMACTL_ENABLE_OFF); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, + LPAIF_DMACTL_ENABLE_OFF); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -314,10 +336,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; unsigned int base_addr, curr_addr; - int ret, ch = pcm_data->rdma_ch; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; ret = regmap_read(drvdata->lpaif_map, - LPAIF_RDMABASE_REG(v, ch), &base_addr); + LPAIF_DMABASE_REG(v, ch, dir), &base_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", __func__, ret); @@ -325,7 +352,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( } ret = regmap_read(drvdata->lpaif_map, - LPAIF_RDMACURR_REG(v, ch), &curr_addr); + LPAIF_DMACURR_REG(v, ch, dir), &curr_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", __func__, ret); @@ -439,104 +466,124 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) return IRQ_HANDLED; } -static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, - struct snd_soc_pcm_runtime *soc_runtime) -{ - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = soc_runtime->dev; - buf->private_data = NULL; - buf->area = dma_alloc_coherent(soc_runtime->dev, size, &buf->addr, - GFP_KERNEL); - if (!buf->area) { - dev_err(soc_runtime->dev, "%s: Could not allocate DMA buffer\n", - __func__); - return -ENOMEM; - } - buf->bytes = size; - - return 0; -} - -static void lpass_platform_free_buffer(struct snd_pcm_substream *substream, - struct snd_soc_pcm_runtime *soc_runtime) -{ - struct snd_dma_buffer *buf = &substream->dma_buffer; - - if (buf->area) { - dma_free_coherent(soc_runtime->dev, buf->bytes, buf->area, - buf->addr); - } - buf->area = NULL; -} - static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) { struct snd_pcm *pcm = soc_runtime->pcm; - struct snd_pcm_substream *substream = - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_pcm_substream *psubstream, *csubstream; struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; int ret; struct lpass_pcm_data *data; + size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - if (v->alloc_dma_channel) - data->rdma_ch = v->alloc_dma_channel(drvdata); - - if (IS_ERR_VALUE(data->rdma_ch)) - return data->rdma_ch; - - drvdata->substream[data->rdma_ch] = substream; data->i2s_port = cpu_dai->driver->id; - snd_soc_pcm_set_drvdata(soc_runtime, data); - soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32); - soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask; + psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (psubstream) { + if (v->alloc_dma_channel) + data->rdma_ch = v->alloc_dma_channel(drvdata, + SNDRV_PCM_STREAM_PLAYBACK); - ret = lpass_platform_alloc_buffer(substream, soc_runtime); - if (ret) - return ret; + if (IS_ERR_VALUE(data->rdma_ch)) + return data->rdma_ch; - ret = regmap_write(drvdata->lpaif_map, + drvdata->substream[data->rdma_ch] = psubstream; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + soc_runtime->platform->dev, + size, &psubstream->dma_buffer); + if (ret) + goto playback_alloc_err; + + ret = regmap_write(drvdata->lpaif_map, LPAIF_RDMACTL_REG(v, data->rdma_ch), 0); - if (ret) { - dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + if (ret) { + dev_err(soc_runtime->dev, + "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + goto capture_alloc_err; + } + } + + csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (csubstream) { + if (v->alloc_dma_channel) + data->wrdma_ch = v->alloc_dma_channel(drvdata, + SNDRV_PCM_STREAM_CAPTURE); + + if (IS_ERR_VALUE(data->wrdma_ch)) + goto capture_alloc_err; + + drvdata->substream[data->wrdma_ch] = csubstream; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + soc_runtime->platform->dev, + size, &csubstream->dma_buffer); + if (ret) + goto capture_alloc_err; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0); + if (ret) { + dev_err(soc_runtime->dev, + "%s() error writing to wrdmactl reg: %d\n", __func__, ret); - goto err_buf; + goto capture_reg_err; + } } return 0; -err_buf: - lpass_platform_free_buffer(substream, soc_runtime); +capture_reg_err: + if (csubstream) + snd_dma_free_pages(&csubstream->dma_buffer); + +capture_alloc_err: + if (psubstream) + snd_dma_free_pages(&psubstream->dma_buffer); + + playback_alloc_err: + dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); + return ret; } static void lpass_platform_pcm_free(struct snd_pcm *pcm) { - struct snd_pcm_substream *substream = - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct lpass_data *drvdata = - snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime); - struct lpass_variant *v = drvdata->variant; - - drvdata->substream[data->rdma_ch] = NULL; - - if (v->free_dma_channel) - v->free_dma_channel(drvdata, data->rdma_ch); - - lpass_platform_free_buffer(substream, soc_runtime); + struct snd_soc_pcm_runtime *rt; + struct lpass_data *drvdata; + struct lpass_pcm_data *data; + struct lpass_variant *v; + struct snd_pcm_substream *substream; + int ch, i; + + for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { + substream = pcm->streams[i].substream; + if (substream) { + rt = substream->private_data; + data = snd_soc_pcm_get_drvdata(rt); + drvdata = snd_soc_platform_get_drvdata(rt->platform); + + ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ? data->rdma_ch + : data->wrdma_ch; + v = drvdata->variant; + drvdata->substream[ch] = NULL; + if (v->free_dma_channel) + v->free_dma_channel(drvdata, ch); + + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } + } } static struct snd_soc_platform_driver lpass_platform_driver = { diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 0b63e2e..30714ad 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -50,7 +50,7 @@ struct lpass_data { struct lpass_variant *variant; /* bit map to keep track of static channel allocations */ - unsigned long rdma_ch_bit_map; + unsigned long dma_ch_bit_map; /* used it for handling interrupt per dma channel */ struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS]; @@ -71,16 +71,20 @@ struct lpass_variant { u32 rdma_reg_base; u32 rdma_reg_stride; u32 rdma_channels; + u32 wrdma_reg_base; + u32 wrdma_reg_stride; + u32 wrdma_channels; /** * on SOCs like APQ8016 the channel control bits start * at different offset to ipq806x **/ - u32 rdmactl_audif_start; + u32 dmactl_audif_start; + u32 wrdma_channel_start; /* SOC specific intialization like clocks */ int (*init)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev); - int (*alloc_dma_channel)(struct lpass_data *data); + int (*alloc_dma_channel)(struct lpass_data *data, int direction); int (*free_dma_channel)(struct lpass_data *data, int ch); /* SOC specific dais */ diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 6561c4c..2f8e204 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -440,11 +440,21 @@ static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg) } } +static const struct reg_default rockchip_i2s_reg_defaults[] = { + {0x00, 0x0000000f}, + {0x04, 0x0000000f}, + {0x08, 0x00071f1f}, + {0x10, 0x001f0000}, + {0x14, 0x01f00000}, +}; + static const struct regmap_config rockchip_i2s_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = I2S_RXDR, + .reg_defaults = rockchip_i2s_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_reg_defaults), .writeable_reg = rockchip_i2s_wr_reg, .readable_reg = rockchip_i2s_rd_reg, .volatile_reg = rockchip_i2s_volatile_reg, @@ -575,6 +585,9 @@ static int rockchip_i2s_remove(struct platform_device *pdev) static const struct of_device_id rockchip_i2s_match[] = { { .compatible = "rockchip,rk3066-i2s", }, + { .compatible = "rockchip,rk3188-i2s", }, + { .compatible = "rockchip,rk3288-i2s", }, + { .compatible = "rockchip,rk3399-i2s", }, {}, }; diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 5a806da..100781e 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -28,6 +28,7 @@ enum rk_spdif_type { RK_SPDIF_RK3066, RK_SPDIF_RK3188, RK_SPDIF_RK3288, + RK_SPDIF_RK3366, }; #define RK3288_GRF_SOC_CON2 0x24c @@ -45,16 +46,22 @@ struct rk_spdif_dev { static const struct of_device_id rk_spdif_match[] = { { .compatible = "rockchip,rk3066-spdif", - .data = (void *) RK_SPDIF_RK3066 }, + .data = (void *)RK_SPDIF_RK3066 }, { .compatible = "rockchip,rk3188-spdif", - .data = (void *) RK_SPDIF_RK3188 }, + .data = (void *)RK_SPDIF_RK3188 }, { .compatible = "rockchip,rk3288-spdif", - .data = (void *) RK_SPDIF_RK3288 }, + .data = (void *)RK_SPDIF_RK3288 }, + { .compatible = "rockchip,rk3366-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3368-spdif", + .data = (void *)RK_SPDIF_RK3366 }, + { .compatible = "rockchip,rk3399-spdif", + .data = (void *)RK_SPDIF_RK3366 }, {}, }; MODULE_DEVICE_TABLE(of, rk_spdif_match); -static int rk_spdif_runtime_suspend(struct device *dev) +static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); @@ -64,7 +71,7 @@ static int rk_spdif_runtime_suspend(struct device *dev) return 0; } -static int rk_spdif_runtime_resume(struct device *dev) +static int __maybe_unused rk_spdif_runtime_resume(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); int ret; diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 84d9e77..70a2559 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -481,10 +481,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; u32 mod, mask, val = 0; + unsigned long flags; - spin_lock(i2s->lock); + spin_lock_irqsave(i2s->lock, flags); mod = readl(i2s->addr + I2SMOD); - spin_unlock(i2s->lock); + spin_unlock_irqrestore(i2s->lock, flags); switch (clk_id) { case SAMSUNG_I2S_OPCLK: @@ -575,11 +576,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, return -EINVAL; } - spin_lock(i2s->lock); + spin_lock_irqsave(i2s->lock, flags); mod = readl(i2s->addr + I2SMOD); mod = (mod & ~mask) | val; writel(mod, i2s->addr + I2SMOD); - spin_unlock(i2s->lock); + spin_unlock_irqrestore(i2s->lock, flags); return 0; } @@ -590,6 +591,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, struct i2s_dai *i2s = to_info(dai); int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; u32 mod, tmp = 0; + unsigned long flags; lrp_shift = i2s->variant_regs->lrp_off; sdf_shift = i2s->variant_regs->sdf_off; @@ -649,7 +651,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; } - spin_lock(i2s->lock); + spin_lock_irqsave(i2s->lock, flags); mod = readl(i2s->addr + I2SMOD); /* * Don't change the I2S mode if any controller is active on this @@ -657,7 +659,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, */ if (any_active(i2s) && ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { - spin_unlock(i2s->lock); + spin_unlock_irqrestore(i2s->lock, flags); dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; @@ -666,7 +668,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, mod &= ~(sdf_mask | lrp_rlow | mod_slave); mod |= tmp; writel(mod, i2s->addr + I2SMOD); - spin_unlock(i2s->lock); + spin_unlock_irqrestore(i2s->lock, flags); return 0; } @@ -676,6 +678,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, { struct i2s_dai *i2s = to_info(dai); u32 mod, mask = 0, val = 0; + unsigned long flags; if (!is_secondary(i2s)) mask |= (MOD_DC2_EN | MOD_DC1_EN); @@ -744,11 +747,11 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - spin_lock(i2s->lock); + spin_lock_irqsave(i2s->lock, flags); mod = readl(i2s->addr + I2SMOD); mod = (mod & ~mask) | val; writel(mod, i2s->addr + I2SMOD); - spin_unlock(i2s->lock); + spin_unlock_irqrestore(i2s->lock, flags); samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 6d3ef36..606399d 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -90,6 +90,108 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) return (0x6 + ws) << 8; } +static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + unsigned int target_rate, + unsigned int *target_val, + unsigned int *target_en) +{ + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int idx, sel, div, step; + unsigned int val, en; + unsigned int min, diff; + unsigned int sel_rate[] = { + clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ + clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ + clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ + adg->rbga_rate_for_441khz, /* 0011: RBGA */ + adg->rbgb_rate_for_48khz, /* 0100: RBGB */ + }; + + min = ~0; + val = 0; + en = 0; + for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { + idx = 0; + step = 2; + + if (!sel_rate[sel]) + continue; + + for (div = 2; div <= 98304; div += step) { + diff = abs(target_rate - sel_rate[sel] / div); + if (min > diff) { + val = (sel << 8) | idx; + min = diff; + en = 1 << (sel + 1); /* fixme */ + } + + /* + * step of 0_0000 / 0_0001 / 0_1101 + * are out of order + */ + if ((idx > 2) && (idx % 2)) + step *= 2; + if (idx == 0x1c) { + div += step; + step *= 2; + } + idx++; + } + } + + if (min == ~0) { + dev_err(dev, "no Input clock\n"); + return; + } + + *target_val = val; + if (target_en) + *target_en = en; +} + +static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate, + u32 *in, u32 *out, u32 *en) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + unsigned int target_rate; + u32 *target_val; + u32 _in; + u32 _out; + u32 _en; + + /* default = SSI WS */ + _in = + _out = rsnd_adg_ssi_ws_timing_gen2(io); + + target_rate = 0; + target_val = NULL; + _en = 0; + if (runtime->rate != in_rate) { + target_rate = out_rate; + target_val = &_out; + } else if (runtime->rate != out_rate) { + target_rate = in_rate; + target_val = &_in; + } + + if (target_rate) + __rsnd_adg_get_timesel_ratio(priv, io, + target_rate, + target_val, &_en); + + if (in) + *in = _in; + if (out) + *out = _out; + if (en) + *en = _en; +} + int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, struct rsnd_dai_stream *io) { @@ -100,7 +202,10 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, int shift = (id % 2) ? 16 : 0; u32 mask, val; - val = rsnd_adg_ssi_ws_timing_gen2(io); + rsnd_adg_get_timesel_ratio(priv, io, + rsnd_src_get_in_rate(priv, io), + rsnd_src_get_out_rate(priv, io), + NULL, &val, NULL); val = val << shift; mask = 0xffff << shift; @@ -110,25 +215,24 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, return 0; } -static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io, - u32 timsel) +int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, + struct rsnd_dai_stream *io, + unsigned int in_rate, + unsigned int out_rate) { struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - int is_play = rsnd_io_is_play(io); + u32 in, out; + u32 mask, en; int id = rsnd_mod_id(src_mod); int shift = (id % 2) ? 16 : 0; - u32 mask, ws; - u32 in, out; rsnd_mod_confirm_src(src_mod); - ws = rsnd_adg_ssi_ws_timing_gen2(io); - - in = (is_play) ? timsel : ws; - out = (is_play) ? ws : timsel; + rsnd_adg_get_timesel_ratio(priv, io, + in_rate, out_rate, + &in, &out, &en); in = in << shift; out = out << shift; @@ -157,91 +261,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, break; } - return 0; -} - -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io, - unsigned int src_rate, - unsigned int dst_rate) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); - struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct rsnd_mod *adg_mod = rsnd_mod_get(adg); - struct device *dev = rsnd_priv_to_dev(priv); - int idx, sel, div, step, ret; - u32 val, en; - unsigned int min, diff; - unsigned int sel_rate [] = { - clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ - clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ - clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ - adg->rbga_rate_for_441khz, /* 0011: RBGA */ - adg->rbgb_rate_for_48khz, /* 0100: RBGB */ - }; - - rsnd_mod_confirm_src(src_mod); - - min = ~0; - val = 0; - en = 0; - for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { - idx = 0; - step = 2; - - if (!sel_rate[sel]) - continue; - - for (div = 2; div <= 98304; div += step) { - diff = abs(src_rate - sel_rate[sel] / div); - if (min > diff) { - val = (sel << 8) | idx; - min = diff; - en = 1 << (sel + 1); /* fixme */ - } - - /* - * step of 0_0000 / 0_0001 / 0_1101 - * are out of order - */ - if ((idx > 2) && (idx % 2)) - step *= 2; - if (idx == 0x1c) { - div += step; - step *= 2; - } - idx++; - } - } - - if (min == ~0) { - dev_err(dev, "no Input clock\n"); - return -EIO; - } - - ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val); - if (ret < 0) { - dev_err(dev, "timsel error\n"); - return ret; - } - - rsnd_mod_bset(adg_mod, DIV_EN, en, en); - - dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); + if (en) + rsnd_mod_bset(adg_mod, DIV_EN, en, en); return 0; } -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod, - struct rsnd_dai_stream *io) -{ - u32 val = rsnd_adg_ssi_ws_timing_gen2(io); - - rsnd_mod_confirm_src(src_mod); - - return rsnd_adg_set_src_timsel_gen2(src_mod, io, val); -} - static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) { struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); @@ -518,13 +543,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv) return -ENOMEM; } - /* - * ADG is special module. - * Use ADG mod without rsnd_mod_init() to make debug easy - * for rsnd_write/rsnd_read - */ - adg->mod.ops = &adg_ops; - adg->mod.priv = priv; + rsnd_mod_init(priv, &adg->mod, &adg_ops, + NULL, NULL, 0, 0); rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkout(priv, adg); diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index cd1f064..abb5eaa 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -29,7 +29,6 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, { struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); - struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct device *dev = rsnd_priv_to_dev(priv); u32 data; @@ -38,6 +37,8 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, if (mix) { struct rsnd_dai *rdai; + struct rsnd_mod *src; + struct rsnd_dai_stream *tio; int i; u32 path[] = { [0] = 0, @@ -55,16 +56,20 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, */ data = 0; for_each_rsnd_dai(rdai, priv, i) { - io = &rdai->playback; - if (mix == rsnd_io_to_mod_mix(io)) + tio = &rdai->playback; + src = rsnd_io_to_mod_src(tio); + if (mix == rsnd_io_to_mod_mix(tio)) data |= path[rsnd_mod_id(src)]; - io = &rdai->capture; - if (mix == rsnd_io_to_mod_mix(io)) + tio = &rdai->capture; + src = rsnd_io_to_mod_src(tio); + if (mix == rsnd_io_to_mod_mix(tio)) data |= path[rsnd_mod_id(src)]; } } else { + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + u32 path[] = { [0] = 0x30000, [1] = 0x30001, @@ -152,7 +157,8 @@ int rsnd_cmd_probe(struct rsnd_priv *priv) for_each_rsnd_cmd(cmd, priv, i) { ret = rsnd_mod_init(priv, rsnd_mod_get(cmd), - &rsnd_cmd_ops, NULL, RSND_MOD_CMD, i); + &rsnd_cmd_ops, NULL, + rsnd_mod_get_status, RSND_MOD_CMD, i); if (ret) return ret; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 02b4b08..3351a70 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -138,12 +138,22 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, return mod->ops->dma_req(io, mod); } +u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type) +{ + return &mod->status; +} + int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, - struct rsnd_mod_ops *ops, - struct clk *clk, - enum rsnd_mod_type type, - int id) + struct rsnd_mod_ops *ops, + struct clk *clk, + u32* (*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type), + enum rsnd_mod_type type, + int id) { int ret = clk_prepare(clk); @@ -155,6 +165,7 @@ int rsnd_mod_init(struct rsnd_priv *priv, mod->type = type; mod->clk = clk; mod->priv = priv; + mod->get_status = get_status; return ret; } @@ -163,6 +174,7 @@ void rsnd_mod_quit(struct rsnd_mod *mod) { if (mod->clk) clk_unprepare(mod->clk); + mod->clk = NULL; } void rsnd_mod_interrupt(struct rsnd_mod *mod, @@ -212,13 +224,36 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io) return rdai->slots_num; } -int rsnd_get_slot_width(struct rsnd_dai_stream *io) +int rsnd_runtime_channel_original(struct rsnd_dai_stream *io) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - int chan = runtime->channels; - /* Multi channel Mode */ - if (rsnd_ssi_multi_slaves(io)) + return runtime->channels; +} + +int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io) +{ + int chan = rsnd_runtime_channel_original(io); + struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io); + + if (ctu_mod) { + u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod); + + if (converted_chan) + return converted_chan; + } + + return chan; +} + +int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io) +{ + int chan = rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io); + + /* Use Multi SSI */ + if (rsnd_runtime_is_ssi_multi(io)) chan /= rsnd_get_slot_num(io); /* TDM Extend Mode needs 8ch */ @@ -228,6 +263,21 @@ int rsnd_get_slot_width(struct rsnd_dai_stream *io) return chan; } +int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io) +{ + int slots = rsnd_get_slot_num(io); + int chan = rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io); + + return (chan >= 6) && (slots > 1); +} + +int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io) +{ + return rsnd_runtime_channel_for_ssi(io) >= 6; +} + /* * ADINR function */ @@ -249,29 +299,6 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io) return 0; } -u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct device *dev = rsnd_priv_to_dev(priv); - u32 chan = runtime->channels; - - switch (chan) { - case 1: - case 2: - case 4: - case 6: - case 8: - break; - default: - dev_warn(dev, "not supported channel\n"); - chan = 0; - break; - } - - return chan; -} - /* * DALIGN function */ @@ -324,31 +351,73 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io) struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct rsnd_mod *mod = (io)->mod[idx]; \ struct device *dev = rsnd_priv_to_dev(priv); \ - u32 *status = (io)->mod_status + idx; \ + u32 *status = mod->get_status(io, mod, idx); \ u32 mask = 0xF << __rsnd_mod_shift_##func; \ u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \ u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \ int ret = 0; \ int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \ - *status = (*status & ~mask) + \ - (add << __rsnd_mod_shift_##func); \ + if (add == 0xF) \ + call = 0; \ + else \ + *status = (*status & ~mask) + \ + (add << __rsnd_mod_shift_##func); \ dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \ rsnd_mod_name(mod), rsnd_mod_id(mod), \ *status, call ? #func : ""); \ if (call) \ ret = (mod)->ops->func(mod, io, param); \ + if (ret) \ + dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n", \ + rsnd_mod_name(mod), rsnd_mod_id(mod), ret); \ ret; \ }) +static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = { + { + /* CAPTURE */ + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + RSND_MOD_SSIU, + RSND_MOD_SSIM3, + RSND_MOD_SSIM2, + RSND_MOD_SSIM1, + RSND_MOD_SSIP, + RSND_MOD_SSI, + }, { + /* PLAYBACK */ + RSND_MOD_AUDMAPP, + RSND_MOD_AUDMA, + RSND_MOD_SSIM3, + RSND_MOD_SSIM2, + RSND_MOD_SSIM1, + RSND_MOD_SSIP, + RSND_MOD_SSI, + RSND_MOD_SSIU, + RSND_MOD_DVC, + RSND_MOD_MIX, + RSND_MOD_CTU, + RSND_MOD_CMD, + RSND_MOD_SRC, + }, +}; + #define rsnd_dai_call(fn, io, param...) \ ({ \ struct rsnd_mod *mod; \ + int type, is_play = rsnd_io_is_play(io); \ int ret = 0, i; \ for (i = 0; i < RSND_MOD_MAX; i++) { \ - mod = (io)->mod[i]; \ + type = rsnd_mod_sequence[is_play][i]; \ + mod = (io)->mod[type]; \ if (!mod) \ continue; \ - ret |= rsnd_mod_call(i, io, fn, param); \ + ret |= rsnd_mod_call(type, io, fn, param); \ } \ ret; \ }) @@ -363,6 +432,9 @@ int rsnd_dai_connect(struct rsnd_mod *mod, if (!mod) return -EIO; + if (io->mod[type] == mod) + return 0; + if (io->mod[type]) return -EINVAL; @@ -511,9 +583,16 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, ret = rsnd_dai_call(start, io, priv); if (ret < 0) goto dai_trigger_end; + + ret = rsnd_dai_call(irq, io, priv, 1); + if (ret < 0) + goto dai_trigger_end; + break; case SNDRV_PCM_TRIGGER_STOP: - ret = rsnd_dai_call(stop, io, priv); + ret = rsnd_dai_call(irq, io, priv, 0); + + ret |= rsnd_dai_call(stop, io, priv); ret |= rsnd_dai_call(quit, io, priv); @@ -863,7 +942,7 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl, } } - if (change) + if (change && cfg->update) cfg->update(cfg->io, mod); return change; @@ -923,7 +1002,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod, int ch_size, u32 max) { - if (ch_size > RSND_DVC_CHANNELS) + if (ch_size > RSND_MAX_CHANNELS) return -EINVAL; _cfg->cfg.max = max; @@ -1055,7 +1134,6 @@ static int rsnd_probe(struct platform_device *pdev) struct rsnd_priv *priv; struct device *dev = &pdev->dev; struct rsnd_dai *rdai; - const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev); int (*probe_func[])(struct rsnd_priv *priv) = { rsnd_gen_probe, rsnd_dma_probe, @@ -1081,7 +1159,7 @@ static int rsnd_probe(struct platform_device *pdev) } priv->pdev = pdev; - priv->flags = (unsigned long)of_id->data; + priv->flags = (unsigned long)of_device_get_match_data(dev); spin_lock_init(&priv->lock); /* diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index d53a225..9dcc1f9 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -12,8 +12,75 @@ #define CTU_NAME_SIZE 16 #define CTU_NAME "ctu" +/* + * User needs to setup CTU by amixer, and its settings are + * based on below registers + * + * CTUn_CPMDR : amixser set "CTU Pass" + * CTUn_SV0xR : amixser set "CTU SV0" + * CTUn_SV1xR : amixser set "CTU SV1" + * CTUn_SV2xR : amixser set "CTU SV2" + * CTUn_SV3xR : amixser set "CTU SV3" + * + * [CTU Pass] + * 0000: default + * 0001: Connect input data of channel 0 + * 0010: Connect input data of channel 1 + * 0011: Connect input data of channel 2 + * 0100: Connect input data of channel 3 + * 0101: Connect input data of channel 4 + * 0110: Connect input data of channel 5 + * 0111: Connect input data of channel 6 + * 1000: Connect input data of channel 7 + * 1001: Connect calculated data by scale values of matrix row 0 + * 1010: Connect calculated data by scale values of matrix row 1 + * 1011: Connect calculated data by scale values of matrix row 2 + * 1100: Connect calculated data by scale values of matrix row 3 + * + * [CTU SVx] + * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07] + * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17] + * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27] + * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37] + * [Output4] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output5] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output6] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * [Output7] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] + * + * [SVxx] + * Plus Minus + * value time dB value time dB + * ----------------------------------------------------------------------- + * H'7F_FFFF 2 6 H'80_0000 2 6 + * ... + * H'40_0000 1 0 H'C0_0000 1 0 + * ... + * H'00_0001 2.38 x 10^-7 -132 + * H'00_0000 0 Mute H'FF_FFFF 2.38 x 10^-7 -132 + * + * + * Ex) Input ch -> Output ch + * 1ch -> 0ch + * 0ch -> 1ch + * + * amixer set "CTU Reset" on + * amixer set "CTU Pass" 9,10 + * amixer set "CTU SV0" 0,4194304 + * amixer set "CTU SV1" 4194304,0 + * or + * amixer set "CTU Reset" on + * amixer set "CTU Pass" 2,1 + */ + struct rsnd_ctu { struct rsnd_mod mod; + struct rsnd_kctrl_cfg_m pass; + struct rsnd_kctrl_cfg_m sv0; + struct rsnd_kctrl_cfg_m sv1; + struct rsnd_kctrl_cfg_m sv2; + struct rsnd_kctrl_cfg_m sv3; + struct rsnd_kctrl_cfg_s reset; + int channels; }; #define rsnd_ctu_nr(priv) ((priv)->ctu_nr) @@ -23,12 +90,28 @@ struct rsnd_ctu { ((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \ i++) +#define rsnd_mod_to_ctu(_mod) \ + container_of((_mod), struct rsnd_ctu, mod) + #define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id) -#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1) -#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0) -static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable) + +static void rsnd_ctu_activation(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, CTU_SWRSR, 0); + rsnd_mod_write(mod, CTU_SWRSR, 1); +} + +static void rsnd_ctu_halt(struct rsnd_mod *mod) +{ + rsnd_mod_write(mod, CTU_CTUIR, 1); + rsnd_mod_write(mod, CTU_SWRSR, 0); +} + +int rsnd_ctu_converted_channel(struct rsnd_mod *mod) { - rsnd_mod_write(mod, CTU_CTUIR, enable); + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + + return ctu->channels; } static int rsnd_ctu_probe_(struct rsnd_mod *mod, @@ -38,17 +121,103 @@ static int rsnd_ctu_probe_(struct rsnd_mod *mod, return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4); } +static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + u32 cpmdr = 0; + u32 scmdr = 0; + int i; + + for (i = 0; i < RSND_MAX_CHANNELS; i++) { + u32 val = ctu->pass.val[i]; + + cpmdr |= val << (28 - (i * 4)); + + if ((val > 0x8) && (scmdr < (val - 0x8))) + scmdr = val - 0x8; + } + + rsnd_mod_write(mod, CTU_CTUIR, 1); + + rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io)); + + rsnd_mod_write(mod, CTU_CPMDR, cpmdr); + + rsnd_mod_write(mod, CTU_SCMDR, scmdr); + + if (scmdr > 0) { + rsnd_mod_write(mod, CTU_SV00R, ctu->sv0.val[0]); + rsnd_mod_write(mod, CTU_SV01R, ctu->sv0.val[1]); + rsnd_mod_write(mod, CTU_SV02R, ctu->sv0.val[2]); + rsnd_mod_write(mod, CTU_SV03R, ctu->sv0.val[3]); + rsnd_mod_write(mod, CTU_SV04R, ctu->sv0.val[4]); + rsnd_mod_write(mod, CTU_SV05R, ctu->sv0.val[5]); + rsnd_mod_write(mod, CTU_SV06R, ctu->sv0.val[6]); + rsnd_mod_write(mod, CTU_SV07R, ctu->sv0.val[7]); + } + if (scmdr > 1) { + rsnd_mod_write(mod, CTU_SV10R, ctu->sv1.val[0]); + rsnd_mod_write(mod, CTU_SV11R, ctu->sv1.val[1]); + rsnd_mod_write(mod, CTU_SV12R, ctu->sv1.val[2]); + rsnd_mod_write(mod, CTU_SV13R, ctu->sv1.val[3]); + rsnd_mod_write(mod, CTU_SV14R, ctu->sv1.val[4]); + rsnd_mod_write(mod, CTU_SV15R, ctu->sv1.val[5]); + rsnd_mod_write(mod, CTU_SV16R, ctu->sv1.val[6]); + rsnd_mod_write(mod, CTU_SV17R, ctu->sv1.val[7]); + } + if (scmdr > 2) { + rsnd_mod_write(mod, CTU_SV20R, ctu->sv2.val[0]); + rsnd_mod_write(mod, CTU_SV21R, ctu->sv2.val[1]); + rsnd_mod_write(mod, CTU_SV22R, ctu->sv2.val[2]); + rsnd_mod_write(mod, CTU_SV23R, ctu->sv2.val[3]); + rsnd_mod_write(mod, CTU_SV24R, ctu->sv2.val[4]); + rsnd_mod_write(mod, CTU_SV25R, ctu->sv2.val[5]); + rsnd_mod_write(mod, CTU_SV26R, ctu->sv2.val[6]); + rsnd_mod_write(mod, CTU_SV27R, ctu->sv2.val[7]); + } + if (scmdr > 3) { + rsnd_mod_write(mod, CTU_SV30R, ctu->sv3.val[0]); + rsnd_mod_write(mod, CTU_SV31R, ctu->sv3.val[1]); + rsnd_mod_write(mod, CTU_SV32R, ctu->sv3.val[2]); + rsnd_mod_write(mod, CTU_SV33R, ctu->sv3.val[3]); + rsnd_mod_write(mod, CTU_SV34R, ctu->sv3.val[4]); + rsnd_mod_write(mod, CTU_SV35R, ctu->sv3.val[5]); + rsnd_mod_write(mod, CTU_SV36R, ctu->sv3.val[6]); + rsnd_mod_write(mod, CTU_SV37R, ctu->sv3.val[7]); + } + + rsnd_mod_write(mod, CTU_CTUIR, 0); +} + +static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + int i; + + if (!ctu->reset.val) + return; + + for (i = 0; i < RSND_MAX_CHANNELS; i++) { + ctu->pass.val[i] = 0; + ctu->sv0.val[i] = 0; + ctu->sv1.val[i] = 0; + ctu->sv2.val[i] = 0; + ctu->sv3.val[i] = 0; + } + ctu->reset.val = 0; +} + static int rsnd_ctu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { rsnd_mod_power_on(mod); - rsnd_ctu_initialize_lock(mod); - - rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io)); + rsnd_ctu_activation(mod); - rsnd_ctu_initialize_unlock(mod); + rsnd_ctu_value_init(io, mod); return 0; } @@ -57,16 +226,110 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { + rsnd_ctu_halt(mod); + rsnd_mod_power_off(mod); return 0; } +static int rsnd_ctu_hw_params(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *fe_params) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + struct snd_soc_pcm_runtime *fe = substream->private_data; + + /* + * CTU assumes that it is used under DPCM if user want to use + * channel transfer. Then, CTU should be FE. + * And then, this function will be called *after* BE settings. + * this means, each BE already has fixuped hw_params. + * see + * dpcm_fe_dai_hw_params() + * dpcm_be_dai_hw_params() + */ + ctu->channels = 0; + if (fe->dai_link->dynamic) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_soc_dpcm *dpcm; + struct snd_pcm_hw_params *be_params; + int stream = substream->stream; + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + be_params = &dpcm->hw_params; + if (params_channels(fe_params) != params_channels(be_params)) + ctu->channels = params_channels(be_params); + } + + dev_dbg(dev, "CTU convert channels %d\n", ctu->channels); + } + + return 0; +} + +static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); + int ret; + + /* CTU Pass */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", + NULL, + &ctu->pass, RSND_MAX_CHANNELS, + 0xC); + + /* ROW0 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", + NULL, + &ctu->sv0, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW1 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", + NULL, + &ctu->sv1, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW2 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", + NULL, + &ctu->sv2, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* ROW3 */ + ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", + NULL, + &ctu->sv3, RSND_MAX_CHANNELS, + 0x00FFFFFF); + if (ret < 0) + return ret; + + /* Reset */ + ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset", + rsnd_ctu_value_reset, + &ctu->reset, 1); + + return ret; +} + static struct rsnd_mod_ops rsnd_ctu_ops = { .name = CTU_NAME, .probe = rsnd_ctu_probe_, .init = rsnd_ctu_init, .quit = rsnd_ctu_quit, + .hw_params = rsnd_ctu_hw_params, + .pcm_new = rsnd_ctu_pcm_new, }; struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) @@ -129,7 +392,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, - clk, RSND_MOD_CTU, i); + clk, rsnd_mod_get_status, RSND_MOD_CTU, i); if (ret) goto rsnd_ctu_probe_done; diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 418e6fd..7658e8f 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -622,15 +622,13 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, } } -struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, int id) +int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + struct rsnd_mod **dma_mod, int id) { - struct rsnd_mod *dma_mod; struct rsnd_mod *mod_from = NULL; struct rsnd_mod *mod_to = NULL; struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); - struct rsnd_dma *dma; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_mod_ops *ops; enum rsnd_mod_type type; @@ -646,17 +644,10 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, * rsnd_rdai_continuance_probe() */ if (!dmac) - return ERR_PTR(-EAGAIN); - - dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); - if (!dma) - return ERR_PTR(-ENOMEM); + return -EAGAIN; rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to); - dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); - dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); - /* for Gen2 */ if (mod_from && mod_to) { ops = &rsnd_dmapp_ops; @@ -678,27 +669,38 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, type = RSND_MOD_AUDMA; } - dma_mod = rsnd_mod_get(dma); + if (!(*dma_mod)) { + struct rsnd_dma *dma; - ret = rsnd_mod_init(priv, dma_mod, - ops, NULL, type, dma_id); - if (ret < 0) - return ERR_PTR(ret); + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; - dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", - rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod), - rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), - rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + *dma_mod = rsnd_mod_get(dma); - ret = attach(io, dma, id, mod_from, mod_to); - if (ret < 0) - return ERR_PTR(ret); + dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); + dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); + + ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, + rsnd_mod_get_status, type, dma_id); + if (ret < 0) + return ret; - ret = rsnd_dai_connect(dma_mod, io, type); + dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", + rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), + rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), + rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + + ret = attach(io, dma, id, mod_from, mod_to); + if (ret < 0) + return ret; + } + + ret = rsnd_dai_connect(*dma_mod, io, type); if (ret < 0) - return ERR_PTR(ret); + return ret; - return rsnd_mod_get(dma); + return 0; } int rsnd_dma_probe(struct rsnd_priv *priv) diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index d45ffe4..02d971f 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -8,6 +8,29 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +/* + * Playback Volume + * amixer set "DVC Out" 100% + * + * Capture Volume + * amixer set "DVC In" 100% + * + * Playback Mute + * amixer set "DVC Out Mute" on + * + * Capture Mute + * amixer set "DVC In Mute" on + * + * Volume Ramp + * amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps" + * amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps" + * amixer set "DVC Out Ramp" on + * aplay xxx.wav & + * amixer set "DVC Out" 80% // Volume Down + * amixer set "DVC Out" 100% // Volume Up + */ + #include "rsnd.h" #define RSND_DVC_NAME_SIZE 16 @@ -83,15 +106,15 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); - u32 val[RSND_DVC_CHANNELS]; + u32 val[RSND_MAX_CHANNELS]; int i; /* Enable Ramp */ if (dvc->ren.val) - for (i = 0; i < RSND_DVC_CHANNELS; i++) + for (i = 0; i < RSND_MAX_CHANNELS; i++) val[i] = dvc->volume.cfg.max; else - for (i = 0; i < RSND_DVC_CHANNELS; i++) + for (i = 0; i < RSND_MAX_CHANNELS; i++) val[i] = dvc->volume.val[i]; /* Enable Digital Volume */ @@ -116,7 +139,7 @@ static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, u32 vrdbr = 0; adinr = rsnd_get_adinr_bit(mod, io) | - rsnd_get_adinr_chan(mod, io); + rsnd_runtime_channel_after_ctu(io); /* Enable Digital Volume, Zero Cross Mute Mode */ dvucr |= 0x101; @@ -373,7 +396,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, - clk, RSND_MOD_DVC, i); + clk, rsnd_mod_get_status, RSND_MOD_DVC, i); if (ret) goto rsnd_dvc_probe_done; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ea24247..46c0ba7 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -104,23 +104,6 @@ void rsnd_write(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); - - dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), - rsnd_reg_name(gen, reg), reg, data); -} - -void rsnd_force_write(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - - if (!rsnd_is_accessible_reg(priv, gen, reg)) - return; - regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data); dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n", @@ -137,8 +120,8 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), - mask, data); + regmap_fields_force_update_bits(gen->regs[reg], + rsnd_mod_id(mod), mask, data); dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n", rsnd_mod_name(mod), rsnd_mod_id(mod), @@ -260,8 +243,43 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40), RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40), RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40), + RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100), RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100), RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100), + RSND_GEN_M_REG(CTU_CPMDR, 0x510, 0x100), + RSND_GEN_M_REG(CTU_SCMDR, 0x514, 0x100), + RSND_GEN_M_REG(CTU_SV00R, 0x518, 0x100), + RSND_GEN_M_REG(CTU_SV01R, 0x51c, 0x100), + RSND_GEN_M_REG(CTU_SV02R, 0x520, 0x100), + RSND_GEN_M_REG(CTU_SV03R, 0x524, 0x100), + RSND_GEN_M_REG(CTU_SV04R, 0x528, 0x100), + RSND_GEN_M_REG(CTU_SV05R, 0x52c, 0x100), + RSND_GEN_M_REG(CTU_SV06R, 0x530, 0x100), + RSND_GEN_M_REG(CTU_SV07R, 0x534, 0x100), + RSND_GEN_M_REG(CTU_SV10R, 0x538, 0x100), + RSND_GEN_M_REG(CTU_SV11R, 0x53c, 0x100), + RSND_GEN_M_REG(CTU_SV12R, 0x540, 0x100), + RSND_GEN_M_REG(CTU_SV13R, 0x544, 0x100), + RSND_GEN_M_REG(CTU_SV14R, 0x548, 0x100), + RSND_GEN_M_REG(CTU_SV15R, 0x54c, 0x100), + RSND_GEN_M_REG(CTU_SV16R, 0x550, 0x100), + RSND_GEN_M_REG(CTU_SV17R, 0x554, 0x100), + RSND_GEN_M_REG(CTU_SV20R, 0x558, 0x100), + RSND_GEN_M_REG(CTU_SV21R, 0x55c, 0x100), + RSND_GEN_M_REG(CTU_SV22R, 0x560, 0x100), + RSND_GEN_M_REG(CTU_SV23R, 0x564, 0x100), + RSND_GEN_M_REG(CTU_SV24R, 0x568, 0x100), + RSND_GEN_M_REG(CTU_SV25R, 0x56c, 0x100), + RSND_GEN_M_REG(CTU_SV26R, 0x570, 0x100), + RSND_GEN_M_REG(CTU_SV27R, 0x574, 0x100), + RSND_GEN_M_REG(CTU_SV30R, 0x578, 0x100), + RSND_GEN_M_REG(CTU_SV31R, 0x57c, 0x100), + RSND_GEN_M_REG(CTU_SV32R, 0x580, 0x100), + RSND_GEN_M_REG(CTU_SV33R, 0x584, 0x100), + RSND_GEN_M_REG(CTU_SV34R, 0x588, 0x100), + RSND_GEN_M_REG(CTU_SV35R, 0x58c, 0x100), + RSND_GEN_M_REG(CTU_SV36R, 0x590, 0x100), + RSND_GEN_M_REG(CTU_SV37R, 0x594, 0x100), RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40), RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40), RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40), diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 65542b6..195fc7b 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -51,7 +51,7 @@ static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, rsnd_mod_write(mod, MIX_MIXIR, 1); /* General Information */ - rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io)); + rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); /* volume step */ rsnd_mod_write(mod, MIX_MIXMR, 0); @@ -172,7 +172,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, - clk, RSND_MOD_MIX, i); + clk, rsnd_mod_get_status, RSND_MOD_MIX, i); if (ret) goto rsnd_mix_probe_done; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 317dd79..fc89a67 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -86,8 +86,43 @@ enum rsnd_reg { RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */ RSND_REG_CMD_ROUTE_SLCT, RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */ + RSND_REG_CTU_SWRSR, RSND_REG_CTU_CTUIR, RSND_REG_CTU_ADINR, + RSND_REG_CTU_CPMDR, + RSND_REG_CTU_SCMDR, + RSND_REG_CTU_SV00R, + RSND_REG_CTU_SV01R, + RSND_REG_CTU_SV02R, + RSND_REG_CTU_SV03R, + RSND_REG_CTU_SV04R, + RSND_REG_CTU_SV05R, + RSND_REG_CTU_SV06R, + RSND_REG_CTU_SV07R, + RSND_REG_CTU_SV10R, + RSND_REG_CTU_SV11R, + RSND_REG_CTU_SV12R, + RSND_REG_CTU_SV13R, + RSND_REG_CTU_SV14R, + RSND_REG_CTU_SV15R, + RSND_REG_CTU_SV16R, + RSND_REG_CTU_SV17R, + RSND_REG_CTU_SV20R, + RSND_REG_CTU_SV21R, + RSND_REG_CTU_SV22R, + RSND_REG_CTU_SV23R, + RSND_REG_CTU_SV24R, + RSND_REG_CTU_SV25R, + RSND_REG_CTU_SV26R, + RSND_REG_CTU_SV27R, + RSND_REG_CTU_SV30R, + RSND_REG_CTU_SV31R, + RSND_REG_CTU_SV32R, + RSND_REG_CTU_SV33R, + RSND_REG_CTU_SV34R, + RSND_REG_CTU_SV35R, + RSND_REG_CTU_SV36R, + RSND_REG_CTU_SV37R, RSND_REG_MIX_SWRSR, RSND_REG_MIX_MIXIR, RSND_REG_MIX_ADINR, @@ -147,8 +182,6 @@ struct rsnd_dai_stream; rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) #define rsnd_mod_write(m, r, d) \ rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) -#define rsnd_mod_force_write(m, r, d) \ - rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) #define rsnd_mod_bset(m, r, s, d) \ rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) @@ -160,14 +193,13 @@ void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod, void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); -u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io); u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); /* * R-Car DMA */ -struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, int id); +int rsnd_dma_attach(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id); int rsnd_dma_probe(struct rsnd_priv *priv); struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, struct rsnd_mod *mod, char *name); @@ -214,6 +246,9 @@ struct rsnd_mod_ops { int (*stop)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv); + int (*irq)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, int enable); int (*pcm_new)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd); @@ -233,47 +268,54 @@ struct rsnd_mod { struct rsnd_mod_ops *ops; struct rsnd_priv *priv; struct clk *clk; + u32 *(*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type); + u32 status; }; /* * status * - * 0xH0000CBA + * 0xH0000CB0 * - * A 0: probe 1: remove * B 0: init 1: quit * C 0: start 1: stop * * H is always called (see __rsnd_mod_call) + * H 0: probe 1: remove * H 0: pcm_new * H 0: fallback * H 0: hw_params */ -#define __rsnd_mod_shift_probe 0 -#define __rsnd_mod_shift_remove 0 #define __rsnd_mod_shift_init 4 #define __rsnd_mod_shift_quit 4 #define __rsnd_mod_shift_start 8 #define __rsnd_mod_shift_stop 8 +#define __rsnd_mod_shift_probe 28 /* always called */ +#define __rsnd_mod_shift_remove 28 /* always called */ +#define __rsnd_mod_shift_irq 28 /* always called */ #define __rsnd_mod_shift_pcm_new 28 /* always called */ #define __rsnd_mod_shift_fallback 28 /* always called */ #define __rsnd_mod_shift_hw_params 28 /* always called */ -#define __rsnd_mod_add_probe 1 -#define __rsnd_mod_add_remove -1 +#define __rsnd_mod_add_probe 0 +#define __rsnd_mod_add_remove 0 #define __rsnd_mod_add_init 1 #define __rsnd_mod_add_quit -1 #define __rsnd_mod_add_start 1 #define __rsnd_mod_add_stop -1 +#define __rsnd_mod_add_irq 0 #define __rsnd_mod_add_pcm_new 0 #define __rsnd_mod_add_fallback 0 #define __rsnd_mod_add_hw_params 0 #define __rsnd_mod_call_probe 0 -#define __rsnd_mod_call_remove 1 +#define __rsnd_mod_call_remove 0 #define __rsnd_mod_call_init 0 #define __rsnd_mod_call_quit 1 #define __rsnd_mod_call_start 0 #define __rsnd_mod_call_stop 1 +#define __rsnd_mod_call_irq 0 #define __rsnd_mod_call_pcm_new 0 #define __rsnd_mod_call_fallback 0 #define __rsnd_mod_call_hw_params 0 @@ -286,10 +328,13 @@ struct rsnd_mod { int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, - struct rsnd_mod_ops *ops, - struct clk *clk, - enum rsnd_mod_type type, - int id); + struct rsnd_mod_ops *ops, + struct clk *clk, + u32* (*get_status)(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type), + enum rsnd_mod_type type, + int id); void rsnd_mod_quit(struct rsnd_mod *mod); char *rsnd_mod_name(struct rsnd_mod *mod); struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, @@ -297,6 +342,10 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, void rsnd_mod_interrupt(struct rsnd_mod *mod, void (*callback)(struct rsnd_mod *mod, struct rsnd_dai_stream *io)); +u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type); + void rsnd_parse_connect_common(struct rsnd_dai *rdai, struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), struct device_node *node, @@ -306,9 +355,14 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, void rsnd_set_slot(struct rsnd_dai *rdai, int slots, int slots_total); int rsnd_get_slot(struct rsnd_dai_stream *io); -int rsnd_get_slot_width(struct rsnd_dai_stream *io); int rsnd_get_slot_num(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_original(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io); +int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io); + /* * R-Car sound DAI */ @@ -319,7 +373,7 @@ struct rsnd_dai_stream { struct rsnd_mod *mod[RSND_MOD_MAX]; struct rsnd_dai_path_info *info; /* rcar_snd.h */ struct rsnd_dai *rdai; - u32 mod_status[RSND_MOD_MAX]; + u32 parent_ssi_status; int byte_pos; int period_pos; int byte_per_period; @@ -392,12 +446,10 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); int rsnd_adg_probe(struct rsnd_priv *priv); void rsnd_adg_remove(struct rsnd_priv *priv); -int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, +int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, struct rsnd_dai_stream *io, - unsigned int src_rate, - unsigned int dst_rate); -int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, - struct rsnd_dai_stream *io); + unsigned int in_rate, + unsigned int out_rate); int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io); @@ -498,10 +550,10 @@ struct rsnd_kctrl_cfg { struct snd_kcontrol *kctrl; }; -#define RSND_DVC_CHANNELS 8 +#define RSND_MAX_CHANNELS 8 struct rsnd_kctrl_cfg_m { struct rsnd_kctrl_cfg cfg; - u32 val[RSND_DVC_CHANNELS]; + u32 val[RSND_MAX_CHANNELS]; }; struct rsnd_kctrl_cfg_s { @@ -547,7 +599,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); -u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io); +u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); #define rsnd_ssi_is_pin_sharing(io) \ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) @@ -573,9 +625,13 @@ void rsnd_ssiu_remove(struct rsnd_priv *priv); int rsnd_src_probe(struct rsnd_priv *priv); void rsnd_src_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); -unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct snd_pcm_runtime *runtime); + +#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1) +#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0) +unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + int is_in); + #define rsnd_src_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") #define rsnd_parse_connect_src(rdai, playback, capture) \ @@ -588,6 +644,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, */ int rsnd_ctu_probe(struct rsnd_priv *priv); void rsnd_ctu_remove(struct rsnd_priv *priv); +int rsnd_ctu_converted_channel(struct rsnd_mod *mod); struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); #define rsnd_ctu_of_node(priv) \ of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu") diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c index 8a357fd..1bc7ecf 100644 --- a/sound/soc/sh/rcar/rsrc-card.c +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -66,12 +66,12 @@ struct rsrc_card_priv { struct snd_soc_dai_link *dai_link; int dai_num; u32 convert_rate; + u32 convert_channels; }; #define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) #define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) #define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i)) -#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data) static int rsrc_card_startup(struct snd_pcm_substream *substream) { @@ -145,11 +145,16 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); - if (!priv->convert_rate) - return 0; + if (priv->convert_rate) + rate->min = + rate->max = priv->convert_rate; - rate->min = rate->max = priv->convert_rate; + if (priv->convert_channels) + channels->min = + channels->max = priv->convert_channels; return 0; } @@ -246,7 +251,7 @@ static int rsrc_card_parse_links(struct device_node *np, struct device *dev = rsrc_priv_to_dev(priv); const struct rsrc_card_of_data *of_data; - of_data = rsrc_dev_to_of_data(dev); + of_data = of_device_get_match_data(dev); /* FE is dummy */ dai_link->cpu_of_node = NULL; @@ -396,7 +401,7 @@ static int rsrc_card_parse_of(struct device_node *node, struct rsrc_card_priv *priv, struct device *dev) { - const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); + const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev); struct rsrc_card_dai *props; struct snd_soc_dai_link *links; int ret; @@ -437,9 +442,13 @@ static int rsrc_card_parse_of(struct device_node *node, /* sampling rate convert */ of_property_read_u32(node, "convert-rate", &priv->convert_rate); - dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n", - priv->snd_card.name ? priv->snd_card.name : "", - priv->convert_rate); + /* channels transfer */ + of_property_read_u32(node, "convert-channels", &priv->convert_channels); + + dev_dbg(dev, "New rsrc-audio-card: %s\n", + priv->snd_card.name ? priv->snd_card.name : ""); + dev_dbg(dev, "SRC : convert_rate %d\n", priv->convert_rate); + dev_dbg(dev, "CTU : convert_channels %d\n", priv->convert_channels); ret = rsrc_card_dai_link_of(node, priv); if (ret < 0) diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 5eda056..15d6ffe 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -25,7 +25,6 @@ struct rsnd_src { struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ u32 convert_rate; /* sampling rate convert */ - int err; int irq; }; @@ -34,7 +33,7 @@ struct rsnd_src { #define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) #define rsnd_src_to_dma(src) ((src)->dma) #define rsnd_src_nr(priv) ((priv)->src_nr) -#define rsnd_enable_sync_convert(src) ((src)->sen.val) +#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val) #define rsnd_mod_to_src(_mod) \ container_of((_mod), struct rsnd_src, mod) @@ -94,15 +93,16 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, } static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, - struct rsnd_src *src) + struct rsnd_mod *mod) { struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate; if (!runtime) return 0; - if (!rsnd_enable_sync_convert(src)) + if (!rsnd_src_sync_is_enabled(mod)) return src->convert_rate; convert_rate = src->sync.val; @@ -116,23 +116,33 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, return convert_rate; } -unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct snd_pcm_runtime *runtime) +unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + int is_in) { struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); - struct rsnd_src *src; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); unsigned int rate = 0; + int is_play = rsnd_io_is_play(io); - if (src_mod) { - src = rsnd_mod_to_src(src_mod); + /* + * + * Playback + * runtime_rate -> [SRC] -> convert_rate + * + * Capture + * convert_rate -> [SRC] -> runtime_rate + */ - /* - * return convert rate if SRC is used, - * otherwise, return runtime->rate as usual - */ - rate = rsnd_src_convert_rate(io, src); - } + if (is_play == is_in) + return runtime->rate; + + /* + * return convert rate if SRC is used, + * otherwise, return runtime->rate as usual + */ + if (src_mod) + rate = rsnd_src_convert_rate(io, src_mod); if (!rate) rate = runtime->rate; @@ -179,8 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_src *src = rsnd_mod_to_src(mod); - u32 convert_rate = rsnd_src_convert_rate(io, src); + u32 fin, fout; u32 ifscr, fsrate, adinr; u32 cr, route; u32 bsdsr, bsisr; @@ -189,13 +198,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, if (!runtime) return; + fin = rsnd_src_get_in_rate(priv, io); + fout = rsnd_src_get_out_rate(priv, io); + /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ - if (!convert_rate) + if (fin == fout) ratio = 0; - else if (convert_rate > runtime->rate) - ratio = 100 * convert_rate / runtime->rate; + else if (fin > fout) + ratio = 100 * fin / fout; else - ratio = 100 * runtime->rate / convert_rate; + ratio = 100 * fout / fin; if (ratio > 600) { dev_err(dev, "FSO/FSI ratio error\n"); @@ -206,16 +218,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, * SRC_ADINR */ adinr = rsnd_get_adinr_bit(mod, io) | - rsnd_get_adinr_chan(mod, io); + rsnd_runtime_channel_original(io); /* * SRC_IFSCR / SRC_IFSVR */ ifscr = 0; fsrate = 0; - if (convert_rate) { + if (fin != fout) { ifscr = 1; - fsrate = 0x0400000 / convert_rate * runtime->rate; + fsrate = 0x0400000 / fout * fin; } /* @@ -223,10 +235,10 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, */ cr = 0x00011110; route = 0x0; - if (convert_rate) { + if (fin != fout) { route = 0x1; - if (rsnd_enable_sync_convert(src)) { + if (rsnd_src_sync_is_enabled(mod)) { cr |= 0x1; route |= rsnd_io_is_play(io) ? (0x1 << 24) : (0x1 << 25); @@ -250,6 +262,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, break; } + rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); + rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ rsnd_mod_write(mod, SRC_ADINR, adinr); rsnd_mod_write(mod, SRC_IFSCR, ifscr); @@ -259,22 +273,17 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_BSISR, bsisr); rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ - rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1); rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); - if (convert_rate) - rsnd_adg_set_convert_clk_gen2(mod, io, - runtime->rate, - convert_rate); - else - rsnd_adg_set_convert_timing_gen2(mod, io); + rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); } -#define rsnd_src_irq_enable(mod) rsnd_src_irq_ctrol(mod, 1) -#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0) -static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable) +static int rsnd_src_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) { struct rsnd_src *src = rsnd_mod_to_src(mod); u32 sys_int_val, int_val, sys_int_mask; @@ -298,14 +307,16 @@ static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable) /* * WORKAROUND * - * ignore over flow error when rsnd_enable_sync_convert() + * ignore over flow error when rsnd_src_sync_is_enabled() */ - if (rsnd_enable_sync_convert(src)) + if (rsnd_src_sync_is_enabled(mod)) sys_int_val = sys_int_val & 0xffff; rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); + + return 0; } static void rsnd_src_status_clear(struct rsnd_mod *mod) @@ -316,9 +327,8 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod) rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val); } -static bool rsnd_src_record_error(struct rsnd_mod *mod) +static bool rsnd_src_error_occurred(struct rsnd_mod *mod) { - struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val0, val1; bool ret = false; @@ -327,18 +337,14 @@ static bool rsnd_src_record_error(struct rsnd_mod *mod) /* * WORKAROUND * - * ignore over flow error when rsnd_enable_sync_convert() + * ignore over flow error when rsnd_src_sync_is_enabled() */ - if (rsnd_enable_sync_convert(src)) + if (rsnd_src_sync_is_enabled(mod)) val0 = val0 & 0xffff; if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) || - (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) { - struct rsnd_src *src = rsnd_mod_to_src(mod); - - src->err++; + (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) ret = true; - } return ret; } @@ -347,7 +353,6 @@ static int rsnd_src_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_src *src = rsnd_mod_to_src(mod); u32 val; /* @@ -355,7 +360,7 @@ static int rsnd_src_start(struct rsnd_mod *mod, * * Enable SRC output if you want to use sync convert together with DVC */ - val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ? + val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ? 0x01 : 0x11; rsnd_mod_write(mod, SRC_CTRL, val); @@ -367,11 +372,7 @@ static int rsnd_src_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - /* - * stop SRC output only - * see rsnd_src_quit - */ - rsnd_mod_write(mod, SRC_CTRL, 0x01); + rsnd_mod_write(mod, SRC_CTRL, 0); return 0; } @@ -390,10 +391,6 @@ static int rsnd_src_init(struct rsnd_mod *mod, rsnd_src_status_clear(mod); - rsnd_src_irq_enable(mod); - - src->err = 0; - /* reset sync convert_rate */ src->sync.val = 0; @@ -405,21 +402,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - rsnd_src_irq_disable(mod); - - /* stop both out/in */ - rsnd_mod_write(mod, SRC_CTRL, 0); rsnd_src_halt(mod); rsnd_mod_power_off(mod); - if (src->err) - dev_warn(dev, "%s[%d] under/over flow err = %d\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); - src->convert_rate = 0; /* reset sync convert_rate */ @@ -432,8 +419,7 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_src *src = rsnd_mod_to_src(mod); - struct device *dev = rsnd_priv_to_dev(priv); + bool stop = false; spin_lock(&priv->lock); @@ -441,26 +427,16 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod, if (!rsnd_io_is_working(io)) goto rsnd_src_interrupt_out; - if (rsnd_src_record_error(mod)) { - - dev_dbg(dev, "%s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - rsnd_src_stop(mod, io, priv); - rsnd_src_start(mod, io, priv); - } - - if (src->err > 1024) { - rsnd_src_irq_disable(mod); - - dev_warn(dev, "no more %s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - } + if (rsnd_src_error_occurred(mod)) + stop = true; rsnd_src_status_clear(mod); rsnd_src_interrupt_out: spin_unlock(&priv->lock); + + if (stop) + snd_pcm_stop_xrun(io->substream); } static irqreturn_t rsnd_src_interrupt(int irq, void *data) @@ -485,7 +461,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod, /* * IRQ is not supported on non-DT * see - * rsnd_src_irq_enable() + * rsnd_src_irq() */ ret = devm_request_irq(dev, irq, rsnd_src_interrupt, @@ -495,9 +471,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod, return ret; } - src->dma = rsnd_dma_attach(io, mod, 0); - if (IS_ERR(src->dma)) - return PTR_ERR(src->dma); + ret = rsnd_dma_attach(io, mod, &src->dma, 0); return ret; } @@ -506,8 +480,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) { - struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); struct rsnd_src *src = rsnd_mod_to_src(mod); int ret; @@ -516,15 +488,10 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, */ /* - * SRC sync convert needs clock master - */ - if (!rsnd_rdai_is_clk_master(rdai)) - return 0; - - /* - * SRC In doesn't work if DVC was enabled + * It can't use SRC Synchronous convert + * when Capture if it uses CMD */ - if (dvc && !rsnd_io_is_play(io)) + if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io)) return 0; /* @@ -557,6 +524,7 @@ static struct rsnd_mod_ops rsnd_src_ops = { .quit = rsnd_src_quit, .start = rsnd_src_start, .stop = rsnd_src_stop, + .irq = rsnd_src_irq, .hw_params = rsnd_src_hw_params, .pcm_new = rsnd_src_pcm_new, }; @@ -622,7 +590,8 @@ int rsnd_src_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(src), - &rsnd_src_ops, clk, RSND_MOD_SRC, i); + &rsnd_src_ops, clk, rsnd_mod_get_status, + RSND_MOD_SRC, i); if (ret) goto rsnd_src_probe_done; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 7ee89da..5f848f0 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -64,7 +64,6 @@ #define SSI_NAME "ssi" struct rsnd_ssi { - struct rsnd_ssi *parent; struct rsnd_mod mod; struct rsnd_mod *dma; @@ -75,7 +74,6 @@ struct rsnd_ssi { u32 wsr; int chan; int rate; - int err; int irq; unsigned int usrcnt; }; @@ -96,7 +94,10 @@ struct rsnd_ssi { #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) #define rsnd_ssi_mode_flags(p) ((p)->flags) #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) -#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io)) +#define rsnd_ssi_is_multi_slave(mod, io) \ + (rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod))) +#define rsnd_ssi_is_run_mods(mod, io) \ + (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { @@ -141,43 +142,13 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, udelay(50); } - dev_warn(dev, "status check failed\n"); -} - -static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - - if (rsnd_is_gen1(priv)) - return 0; - - /* enable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, - rsnd_ssi_is_dma_mode(ssi_mod) ? - 0x0e000000 : 0x0f000000); - - return 0; -} - -static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); - - if (rsnd_is_gen1(priv)) - return 0; - - /* disable SSI interrupt if Gen2 */ - rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000); - - return 0; + dev_warn(dev, "%s[%d] status check failed\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); } -u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) +static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) { struct rsnd_mod *mod; - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct device *dev = rsnd_priv_to_dev(priv); enum rsnd_mod_type types[] = { RSND_MOD_SSIM1, RSND_MOD_SSIM2, @@ -185,16 +156,6 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) }; int i, mask; - switch (runtime->channels) { - case 2: /* Multi channel is not needed for Stereo */ - return 0; - case 6: - break; - default: - dev_err(dev, "unsupported channel\n"); - return 0; - } - mask = 0; for (i = 0; i < ARRAY_SIZE(types); i++) { mod = rsnd_io_to_mod(io, types[i]); @@ -207,22 +168,41 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) return mask; } -static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, +static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) +{ + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); + + return rsnd_ssi_multi_slaves_runtime(io) | + 1 << rsnd_mod_id(ssi_mod) | + 1 << rsnd_mod_id(ssi_parent_mod); +} + +u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) +{ + if (rsnd_runtime_is_ssi_multi(io)) + return rsnd_ssi_multi_slaves(io); + + return 0; +} + +static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); - int slots = rsnd_get_slot_width(io); + int chan = rsnd_runtime_channel_for_ssi(io); int j, ret; int ssi_clk_mul_table[] = { 1, 2, 4, 8, 16, 6, 12, }; unsigned int main_rate; - unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime); + unsigned int rate = rsnd_io_is_play(io) ? + rsnd_src_get_out_rate(priv, io) : + rsnd_src_get_in_rate(priv, io); if (!rsnd_rdai_is_clk_master(rdai)) return 0; @@ -249,10 +229,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, /* * this driver is assuming that - * system word is 32bit x slots + * system word is 32bit x chan * see rsnd_ssi_init() */ - main_rate = rate * 32 * slots * ssi_clk_mul_table[j]; + main_rate = rate * 32 * chan * ssi_clk_mul_table[j]; ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); if (0 == ret) { @@ -274,11 +254,11 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, return -EIO; } -static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi, +static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - struct rsnd_mod *mod = rsnd_mod_get(ssi); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); if (!rsnd_rdai_is_clk_master(rdai)) @@ -296,17 +276,18 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi, rsnd_adg_ssi_clk_stop(mod); } -static int rsnd_ssi_config_init(struct rsnd_ssi *ssi, +static void rsnd_ssi_config_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr_own; u32 cr_mode; u32 wsr; int is_tdm; - is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0; + is_tdm = rsnd_runtime_is_ssi_tdm(io); /* * always use 32bit system word. @@ -332,11 +313,9 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi, case 32: cr_own |= DWL_24; break; - default: - return -EINVAL; } - if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) { + if (rsnd_ssi_is_dma_mode(mod)) { cr_mode = UIEN | OIEN | /* over/under run */ DMEN; /* DMA : enable DMA */ } else { @@ -357,8 +336,16 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi, ssi->cr_own = cr_own; ssi->cr_mode = cr_mode; ssi->wsr = wsr; +} - return 0; +static void rsnd_ssi_register_setup(struct rsnd_mod *mod) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + rsnd_mod_write(mod, SSIWSR, ssi->wsr); + rsnd_mod_write(mod, SSICR, ssi->cr_own | + ssi->cr_clk | + ssi->cr_mode); /* without EN */ } /* @@ -371,28 +358,25 @@ static int rsnd_ssi_init(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); int ret; + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + ssi->usrcnt++; rsnd_mod_power_on(mod); - ret = rsnd_ssi_master_clk_start(ssi, io); + ret = rsnd_ssi_master_clk_start(mod, io); if (ret < 0) return ret; - if (rsnd_ssi_is_parent(mod, io)) - return 0; - - ret = rsnd_ssi_config_init(ssi, io); - if (ret < 0) - return ret; + if (!rsnd_ssi_is_parent(mod, io)) + rsnd_ssi_config_init(mod, io); - ssi->err = -1; /* ignore 1st error */ + rsnd_ssi_register_setup(mod); /* clear error status */ rsnd_ssi_status_clear(mod); - rsnd_ssi_irq_enable(mod); - return 0; } @@ -403,25 +387,19 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct device *dev = rsnd_priv_to_dev(priv); + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + if (!ssi->usrcnt) { dev_err(dev, "%s[%d] usrcnt error\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); return -EIO; } - if (!rsnd_ssi_is_parent(mod, io)) { - if (ssi->err > 0) - dev_warn(dev, "%s[%d] under/over flow err = %d\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), - ssi->err); - + if (!rsnd_ssi_is_parent(mod, io)) ssi->cr_own = 0; - ssi->err = 0; - rsnd_ssi_irq_disable(mod); - } - - rsnd_ssi_master_clk_stop(ssi, io); + rsnd_ssi_master_clk_stop(mod, io); rsnd_mod_power_off(mod); @@ -456,61 +434,43 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod, return 0; } -static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi) -{ - struct rsnd_mod *mod = rsnd_mod_get(ssi); - u32 status = rsnd_ssi_status_get(mod); - - /* under/over flow error */ - if (status & (UIRQ | OIRQ)) - ssi->err++; - - return status; -} - -static int __rsnd_ssi_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static int rsnd_ssi_start(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 cr; - - cr = ssi->cr_own | - ssi->cr_clk | - ssi->cr_mode; + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; /* * EN will be set via SSIU :: SSI_CONTROL * if Multi channel mode */ - if (!rsnd_ssi_multi_slaves(io)) - cr |= EN; + if (rsnd_ssi_multi_slaves_runtime(io)) + return 0; - rsnd_mod_write(mod, SSICR, cr); - rsnd_mod_write(mod, SSIWSR, ssi->wsr); + rsnd_mod_bset(mod, SSICR, EN, EN); return 0; } -static int rsnd_ssi_start(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static int rsnd_ssi_stop(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv) { + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 cr; + + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + /* - * no limit to start + * don't stop if not last user * see also - * rsnd_ssi_stop + * rsnd_ssi_start * rsnd_ssi_interrupt */ - return __rsnd_ssi_start(mod, io, priv); -} - -static int __rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) -{ - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - u32 cr; + if (ssi->usrcnt > 1) + return 0; /* * disable all IRQ, @@ -532,33 +492,38 @@ static int __rsnd_ssi_stop(struct rsnd_mod *mod, return 0; } -static int rsnd_ssi_stop(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) +static int rsnd_ssi_irq(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct rsnd_priv *priv, + int enable) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + u32 val = 0; - /* - * don't stop if not last user - * see also - * rsnd_ssi_start - * rsnd_ssi_interrupt - */ - if (ssi->usrcnt > 1) + if (rsnd_is_gen1(priv)) return 0; - return __rsnd_ssi_stop(mod, io, priv); + if (rsnd_ssi_is_parent(mod, io)) + return 0; + + if (!rsnd_ssi_is_run_mods(mod, io)) + return 0; + + if (enable) + val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000; + + rsnd_mod_write(mod, SSI_INT_ENABLE, val); + + return 0; } static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); int is_dma = rsnd_ssi_is_dma_mode(mod); u32 status; bool elapsed = false; + bool stop = false; spin_lock(&priv->lock); @@ -566,7 +531,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, if (!rsnd_io_is_working(io)) goto rsnd_ssi_interrupt_out; - status = rsnd_ssi_record_error(ssi); + status = rsnd_ssi_status_get(mod); /* PIO only */ if (!is_dma && (status & DIRQ)) { @@ -588,23 +553,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, } /* DMA only */ - if (is_dma && (status & (UIRQ | OIRQ))) { - /* - * restart SSI - */ - dev_dbg(dev, "%s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - __rsnd_ssi_stop(mod, io, priv); - __rsnd_ssi_start(mod, io, priv); - } - - if (ssi->err > 1024) { - rsnd_ssi_irq_disable(mod); - - dev_warn(dev, "no more %s[%d] restart\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - } + if (is_dma && (status & (UIRQ | OIRQ))) + stop = true; rsnd_ssi_status_clear(mod); rsnd_ssi_interrupt_out: @@ -612,6 +562,10 @@ rsnd_ssi_interrupt_out: if (elapsed) rsnd_dai_period_elapsed(io); + + if (stop) + snd_pcm_stop_xrun(io->substream); + } static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) @@ -627,12 +581,17 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) * SSI PIO */ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct rsnd_priv *priv) + struct rsnd_dai_stream *io) { + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + if (!__rsnd_ssi_is_pin_sharing(mod)) return; + if (!rsnd_rdai_is_clk_master(rdai)) + return; + switch (rsnd_mod_id(mod)) { case 1: case 2: @@ -647,6 +606,20 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, } } +static int rsnd_ssi_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + /* + * rsnd_rdai_is_clk_master() will be enabled after set_fmt, + * and, pcm_new will be called after it. + * This function reuse pcm_new at this point. + */ + rsnd_ssi_parent_attach(mod, io); + + return 0; +} + static int rsnd_ssi_common_probe(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -662,7 +635,10 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, if (rsnd_ssi_is_multi_slave(mod, io)) return 0; - rsnd_ssi_parent_attach(mod, io, priv); + /* + * It can't judge ssi parent at this point + * see rsnd_ssi_pcm_new() + */ ret = rsnd_ssiu_attach(io, mod); if (ret < 0) @@ -683,6 +659,8 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, .hw_params = rsnd_ssi_hw_params, }; @@ -705,9 +683,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, if (ret) return ret; - ssi->dma = rsnd_dma_attach(io, mod, dma_id); - if (IS_ERR(ssi->dma)) - return PTR_ERR(ssi->dma); + /* SSI probe might be called many times in MUX multi path */ + ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id); return ret; } @@ -772,6 +749,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .quit = rsnd_ssi_quit, .start = rsnd_ssi_start, .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, .fallback = rsnd_ssi_fallback, .hw_params = rsnd_ssi_hw_params, }; @@ -858,6 +837,41 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); } +static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, + struct rsnd_mod *mod, + enum rsnd_mod_type type) +{ + /* + * SSIP (= SSI parent) needs to be special, otherwise, + * 2nd SSI might doesn't start. see also rsnd_mod_call() + * + * We can't include parent SSI status on SSI, because we don't know + * how many SSI requests parent SSI. Thus, it is localed on "io" now. + * ex) trouble case + * Playback: SSI0 + * Capture : SSI1 (needs SSI0) + * + * 1) start Capture -> SSI0/SSI1 are started. + * 2) start Playback -> SSI0 doesn't work, because it is already + * marked as "started" on 1) + * + * OTOH, using each mod's status is good for MUX case. + * It doesn't need to start in 2nd start + * ex) + * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 + * | + * IO-1: SRC1 -> CTU2 -+ + * + * 1) start IO-0 -> start SSI0 + * 2) start IO-1 -> SSI0 doesn't need to start, because it is + * already started on 1) + */ + if (type == RSND_MOD_SSIP) + return &io->parent_ssi_status; + + return rsnd_mod_get_status(io, mod, type); +} + int rsnd_ssi_probe(struct rsnd_priv *priv) { struct device_node *node; @@ -920,7 +934,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ops = &rsnd_ssi_dma_ops; ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, - RSND_MOD_SSI, i); + rsnd_ssi_get_status, RSND_MOD_SSI, i); if (ret) goto rsnd_ssi_probe_done; diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 06d7282..6f9b388 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -27,7 +27,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); - u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io); + u32 multi_ssi_slaves = rsnd_ssi_multi_slaves_runtime(io); int use_busif = rsnd_ssi_use_busif(io); int id = rsnd_mod_id(mod); u32 mask1, val1; @@ -105,7 +105,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, if (ret < 0) return ret; - if (rsnd_get_slot_width(io) >= 6) { + if (rsnd_runtime_is_ssi_tdm(io)) { /* * TDM Extend Mode * see @@ -115,13 +115,14 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, } if (rsnd_ssi_use_busif(io)) { - u32 val = rsnd_get_dalign(mod, io); - rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr_bit(mod, io) | - rsnd_get_adinr_chan(mod, io)); + (rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io))); rsnd_mod_write(mod, SSI_BUSIF_MODE, 1); - rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val); + rsnd_mod_write(mod, SSI_BUSIF_DALIGN, + rsnd_get_dalign(mod, io)); } return 0; @@ -136,7 +137,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod, rsnd_mod_write(mod, SSI_CTRL, 0x1); - if (rsnd_ssi_multi_slaves(io)) + if (rsnd_ssi_multi_slaves_runtime(io)) rsnd_mod_write(mod, SSI_CONTROL, 0x1); return 0; @@ -151,7 +152,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, rsnd_mod_write(mod, SSI_CTRL, 0); - if (rsnd_ssi_multi_slaves(io)) + if (rsnd_ssi_multi_slaves_runtime(io)) rsnd_mod_write(mod, SSI_CONTROL, 0); return 0; @@ -206,7 +207,8 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) for_each_rsnd_ssiu(ssiu, priv, i) { ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu), - ops, NULL, RSND_MOD_SSIU, i); + ops, NULL, rsnd_mod_get_status, + RSND_MOD_SSIU, i); if (ret) return ret; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 790ee2b..d2e62b15 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -986,16 +986,16 @@ static int soc_bind_dai_link(struct snd_soc_card *card, dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); - rtd = soc_new_pcm_runtime(card, dai_link); - if (!rtd) - return -ENOMEM; - if (soc_is_dai_link_bound(card, dai_link)) { dev_dbg(card->dev, "ASoC: dai link %s already bound\n", dai_link->name); return 0; } + rtd = soc_new_pcm_runtime(card, dai_link); + if (!rtd) + return -ENOMEM; + cpu_dai_component.name = dai_link->cpu_name; cpu_dai_component.of_node = dai_link->cpu_of_node; cpu_dai_component.dai_name = dai_link->cpu_dai_name; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5a2812f..801ae1a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -310,7 +310,7 @@ struct dapm_kcontrol_data { }; static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol) + struct snd_kcontrol *kcontrol, const char *ctrl_name) { struct dapm_kcontrol_data *data; struct soc_mixer_control *mc; @@ -333,7 +333,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, if (mc->autodisable) { struct snd_soc_dapm_widget template; - name = kasprintf(GFP_KERNEL, "%s %s", kcontrol->id.name, + name = kasprintf(GFP_KERNEL, "%s %s", ctrl_name, "Autodisable"); if (!name) { ret = -ENOMEM; @@ -371,7 +371,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, if (e->autodisable) { struct snd_soc_dapm_widget template; - name = kasprintf(GFP_KERNEL, "%s %s", kcontrol->id.name, + name = kasprintf(GFP_KERNEL, "%s %s", ctrl_name, "Autodisable"); if (!name) { ret = -ENOMEM; @@ -871,7 +871,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, kcontrol->private_free = dapm_kcontrol_free; - ret = dapm_kcontrol_data_alloc(w, kcontrol); + ret = dapm_kcontrol_data_alloc(w, kcontrol, name); if (ret) { snd_ctl_free_one(kcontrol); goto exit_free; @@ -2805,7 +2805,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num) { - int i, ret = 0; + int i; mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { @@ -2814,7 +2814,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, } mutex_unlock(&dapm->card->dapm_mutex); - return ret; + return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes); @@ -3573,7 +3573,7 @@ static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, { struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = w->params_select; + ucontrol->value.enumerated.item[0] = w->params_select; return 0; } @@ -3587,13 +3587,13 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, if (w->power) return -EBUSY; - if (ucontrol->value.integer.value[0] == w->params_select) + if (ucontrol->value.enumerated.item[0] == w->params_select) return 0; - if (ucontrol->value.integer.value[0] >= w->num_params) + if (ucontrol->value.enumerated.item[0] >= w->num_params) return -EINVAL; - w->params_select = ucontrol->value.integer.value[0]; + w->params_select = ucontrol->value.enumerated.item[0]; return 0; } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index e898b42..aa99dac 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1810,7 +1810,8 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) continue; dev_dbg(be->dev, "ASoC: hw_free BE %s\n", @@ -1866,18 +1867,6 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) if (!snd_soc_dpcm_be_can_update(fe, be, stream)) continue; - /* only allow hw_params() if no connected FEs are running */ - if (!snd_soc_dpcm_can_be_params(fe, be, stream)) - continue; - - if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && - (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE)) - continue; - - dev_dbg(be->dev, "ASoC: hw_params BE %s\n", - dpcm->fe->dai_link->name); - /* copy params for each dpcm */ memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params, sizeof(struct snd_pcm_hw_params)); @@ -1894,6 +1883,18 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) } } + /* only allow hw_params() if no connected FEs are running */ + if (!snd_soc_dpcm_can_be_params(fe, be, stream)) + continue; + + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE)) + continue; + + dev_dbg(be->dev, "ASoC: hw_params BE %s\n", + dpcm->fe->dai_link->name); + ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); if (ret < 0) { dev_err(dpcm->be->dev, diff --git a/sound/sparc/Kconfig b/sound/sparc/Kconfig index d75deba..dfcd386 100644 --- a/sound/sparc/Kconfig +++ b/sound/sparc/Kconfig @@ -22,6 +22,7 @@ config SND_SUN_AMD7930 config SND_SUN_CS4231 tristate "Sun CS4231" select SND_PCM + select SND_TIMER help Say Y here to include support for CS4231 sound device on Sun. diff --git a/sound/usb/midi.c b/sound/usb/midi.c index cc39f63..007cf58 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -2455,7 +2455,6 @@ int snd_usbmidi_create(struct snd_card *card, else err = snd_usbmidi_create_endpoints(umidi, endpoints); if (err < 0) { - snd_usbmidi_free(umidi); return err; } diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 23ea6d8..c458d60 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1121,8 +1121,10 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) switch (chip->usb_id) { case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */ case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */ + case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */ case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */ case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */ + case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */ case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */ case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */ @@ -1205,8 +1207,12 @@ void snd_usb_set_interface_quirk(struct usb_device *dev) * "Playback Design" products need a 50ms delay after setting the * USB interface. */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) + switch (le16_to_cpu(dev->descriptor.idVendor)) { + case 0x23ba: /* Playback Design */ + case 0x0644: /* TEAC Corp. */ mdelay(50); + break; + } } void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, @@ -1221,6 +1227,14 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(20); + /* + * "TEAC Corp." products need a 20ms delay after each + * class compliant request + */ + if ((le16_to_cpu(dev->descriptor.idVendor) == 0x0644) && + (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) + mdelay(20); + /* Marantz/Denon devices with USB DAC functionality need a delay * after each class compliant request */ @@ -1269,7 +1283,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, 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(0x22d8, 0x0416): /* OPPO HA-1*/ + case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */ if (fp->altsetting == 2) return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; @@ -1278,6 +1292,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */ case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */ case USB_ID(0x20b1, 0x3023): /* Aune X1S 32BIT/384 DSD DAC */ + case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */ if (fp->altsetting == 3) return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; |