diff options
Diffstat (limited to 'sound')
690 files changed, 27131 insertions, 9806 deletions
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c index 8dfffdc101a2..47c6787158a7 100644 --- a/sound/ac97/bus.c +++ b/sound/ac97/bus.c @@ -387,7 +387,6 @@ void snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl) } EXPORT_SYMBOL_GPL(snd_ac97_controller_unregister); -#ifdef CONFIG_PM static int ac97_pm_runtime_suspend(struct device *dev) { struct ac97_codec_device *codec = to_ac97_device(dev); @@ -419,7 +418,6 @@ static int ac97_pm_runtime_resume(struct device *dev) return pm_generic_runtime_resume(dev); } -#endif /* CONFIG_PM */ static const struct dev_pm_ops ac97_pm = { .suspend = pm_generic_suspend, @@ -428,10 +426,7 @@ static const struct dev_pm_ops ac97_pm = { .thaw = pm_generic_thaw, .poweroff = pm_generic_poweroff, .restore = pm_generic_restore, - SET_RUNTIME_PM_OPS( - ac97_pm_runtime_suspend, - ac97_pm_runtime_resume, - NULL) + RUNTIME_PM_OPS(ac97_pm_runtime_suspend, ac97_pm_runtime_resume, NULL) }; static int ac97_get_enable_clk(struct ac97_codec_device *adev) @@ -535,7 +530,7 @@ const struct bus_type ac97_bus_type = { .name = "ac97bus", .dev_groups = ac97_dev_groups, .match = ac97_bus_match, - .pm = &ac97_pm, + .pm = pm_ptr(&ac97_pm), .probe = ac97_bus_probe, .remove = ac97_bus_remove, }; diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index c3340b8ff3da..243965615ef2 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -1061,7 +1061,7 @@ static void aaci_remove(struct amba_device *dev) } } -static struct amba_id aaci_ids[] = { +static const struct amba_id aaci_ids[] = { { .id = 0x00041041, .mask = 0x000fffff, diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index d8f8e08f1bb7..84e264f335ca 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -817,7 +817,6 @@ err_prepare_enable: return retval; } -#ifdef CONFIG_PM_SLEEP static int atmel_ac97c_suspend(struct device *pdev) { struct snd_card *card = dev_get_drvdata(pdev); @@ -836,11 +835,7 @@ static int atmel_ac97c_resume(struct device *pdev) return ret; } -static SIMPLE_DEV_PM_OPS(atmel_ac97c_pm, atmel_ac97c_suspend, atmel_ac97c_resume); -#define ATMEL_AC97C_PM_OPS &atmel_ac97c_pm -#else -#define ATMEL_AC97C_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(atmel_ac97c_pm, atmel_ac97c_suspend, atmel_ac97c_resume); static void atmel_ac97c_remove(struct platform_device *pdev) { @@ -864,7 +859,7 @@ static struct platform_driver atmel_ac97c_driver = { .remove = atmel_ac97c_remove, .driver = { .name = "atmel_ac97c", - .pm = ATMEL_AC97C_PM_OPS, + .pm = pm_ptr(&atmel_ac97c_pm), .of_match_table = atmel_ac97c_dt_ids, }, }; diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index 147c1fea4708..e9c60dce59fb 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -66,9 +66,8 @@ static int snd_hrtimer_open(struct snd_timer *t) stime = kzalloc(sizeof(*stime), GFP_KERNEL); if (!stime) return -ENOMEM; - hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); stime->timer = t; - stime->hrt.function = snd_hrtimer_callback; + hrtimer_setup(&stime->hrt, snd_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL); t->private_data = stime; return 0; } diff --git a/sound/core/memory.c b/sound/core/memory.c index 2d2d0094c897..d683442b4c97 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -27,38 +27,43 @@ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size if (import_ubuf(ITER_DEST, dst, count, &iter)) return -EFAULT; - return copy_to_iter_fromio(&iter, (const void __iomem *)src, count); + if (copy_to_iter_fromio((const void __iomem *)src, count, &iter) != count) + return -EFAULT; + return 0; } EXPORT_SYMBOL(copy_to_user_fromio); /** * copy_to_iter_fromio - copy data from mmio-space to iov_iter - * @dst: the destination iov_iter * @src: the source pointer on mmio * @count: the data size to copy in bytes + * @dst: the destination iov_iter * * Copies the data from mmio-space to iov_iter. * - * Return: Zero if successful, or non-zero on failure. + * Return: number of bytes to be copied */ -int copy_to_iter_fromio(struct iov_iter *dst, const void __iomem *src, - size_t count) +size_t copy_to_iter_fromio(const void __iomem *src, size_t count, + struct iov_iter *dst) { #if defined(__i386__) || defined(CONFIG_SPARC32) - return copy_to_iter((const void __force *)src, count, dst) == count ? 0 : -EFAULT; + return copy_to_iter((const void __force *)src, count, dst); #else char buf[256]; + size_t res = 0; + while (count) { size_t c = count; if (c > sizeof(buf)) c = sizeof(buf); memcpy_fromio(buf, (void __iomem *)src, c); if (copy_to_iter(buf, c, dst) != c) - return -EFAULT; + return res; count -= c; src += c; + res += c; } - return 0; + return res; #endif } EXPORT_SYMBOL(copy_to_iter_fromio); @@ -79,37 +84,43 @@ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size if (import_ubuf(ITER_SOURCE, (void __user *)src, count, &iter)) return -EFAULT; - return copy_from_iter_toio((void __iomem *)dst, &iter, count); + if (copy_from_iter_toio((void __iomem *)dst, count, &iter) != count) + return -EFAULT; + return 0; } EXPORT_SYMBOL(copy_from_user_toio); /** * copy_from_iter_toio - copy data from iov_iter to mmio-space * @dst: the destination pointer on mmio-space - * @src: the source iov_iter * @count: the data size to copy in bytes + * @src: the source iov_iter * * Copies the data from iov_iter to mmio-space. * - * Return: Zero if successful, or non-zero on failure. + * Return: number of bytes to be copied */ -int copy_from_iter_toio(void __iomem *dst, struct iov_iter *src, size_t count) +size_t copy_from_iter_toio(void __iomem *dst, size_t count, + struct iov_iter *src) { #if defined(__i386__) || defined(CONFIG_SPARC32) - return copy_from_iter((void __force *)dst, count, src) == count ? 0 : -EFAULT; + return copy_from_iter((void __force *)dst, count, src); #else char buf[256]; + size_t res = 0; + while (count) { size_t c = count; if (c > sizeof(buf)) c = sizeof(buf); if (copy_from_iter(buf, c, src) != c) - return -EFAULT; + return res; memcpy_toio(dst, buf, c); count -= c; dst += c; + res += c; } - return 0; + return res; #endif } EXPORT_SYMBOL(copy_from_iter_toio); diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 290690fc2abc..283aac441fa0 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -592,7 +592,6 @@ static const struct attribute_group *pcm_dev_attr_groups[]; * PM callbacks: we need to deal only with suspend here, as the resume is * triggered either from user-space or the driver's resume callback */ -#ifdef CONFIG_PM_SLEEP static int do_pcm_suspend(struct device *dev) { struct snd_pcm_str *pstr = dev_get_drvdata(dev); @@ -601,10 +600,9 @@ static int do_pcm_suspend(struct device *dev) snd_pcm_suspend_all(pstr->pcm); return 0; } -#endif static const struct dev_pm_ops pcm_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL) + SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL) }; /* device type for PCM -- basically only for passing PM callbacks */ diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c index 1cdca4d4fc9c..688eefce82fa 100644 --- a/sound/core/pcm_drm_eld.c +++ b/sound/core/pcm_drm_eld.c @@ -5,8 +5,10 @@ #include <linux/bitfield.h> #include <linux/export.h> #include <linux/hdmi.h> +#include <linux/unaligned.h> #include <drm/drm_edid.h> #include <drm/drm_eld.h> +#include <sound/info.h> #include <sound/pcm.h> #include <sound/pcm_drm_eld.h> @@ -162,3 +164,388 @@ int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld) return ret; } EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld); + +#define SND_PRINT_RATES_ADVISED_BUFSIZE 80 +#define SND_PRINT_BITS_ADVISED_BUFSIZE 16 +#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 + +static const char * const eld_connection_type_names[4] = { + "HDMI", + "DisplayPort", + "2-reserved", + "3-reserved" +}; + +static const char * const cea_audio_coding_type_names[] = { + /* 0 */ "undefined", + /* 1 */ "LPCM", + /* 2 */ "AC-3", + /* 3 */ "MPEG1", + /* 4 */ "MP3", + /* 5 */ "MPEG2", + /* 6 */ "AAC-LC", + /* 7 */ "DTS", + /* 8 */ "ATRAC", + /* 9 */ "DSD (One Bit Audio)", + /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)", + /* 11 */ "DTS-HD", + /* 12 */ "MLP (Dolby TrueHD)", + /* 13 */ "DST", + /* 14 */ "WMAPro", + /* 15 */ "HE-AAC", + /* 16 */ "HE-AACv2", + /* 17 */ "MPEG Surround", +}; + +static const char * const cea_speaker_allocation_names[] = { + /* 0 */ "FL/FR", + /* 1 */ "LFE", + /* 2 */ "FC", + /* 3 */ "RL/RR", + /* 4 */ "RC", + /* 5 */ "FLC/FRC", + /* 6 */ "RLC/RRC", + /* 7 */ "FLW/FRW", + /* 8 */ "FLH/FRH", + /* 9 */ "TC", + /* 10 */ "FCH", +}; + +/* + * SS1:SS0 index => sample size + */ +static const int cea_sample_sizes[4] = { + 0, /* 0: Refer to Stream Header */ + ELD_PCM_BITS_16, /* 1: 16 bits */ + ELD_PCM_BITS_20, /* 2: 20 bits */ + ELD_PCM_BITS_24, /* 3: 24 bits */ +}; + +/* + * SF2:SF1:SF0 index => sampling frequency + */ +static const int cea_sampling_frequencies[8] = { + 0, /* 0: Refer to Stream Header */ + SNDRV_PCM_RATE_32000, /* 1: 32000Hz */ + SNDRV_PCM_RATE_44100, /* 2: 44100Hz */ + SNDRV_PCM_RATE_48000, /* 3: 48000Hz */ + SNDRV_PCM_RATE_88200, /* 4: 88200Hz */ + SNDRV_PCM_RATE_96000, /* 5: 96000Hz */ + SNDRV_PCM_RATE_176400, /* 6: 176400Hz */ + SNDRV_PCM_RATE_192000, /* 7: 192000Hz */ +}; + +#define GRAB_BITS(buf, byte, lowbit, bits) \ +({ \ + BUILD_BUG_ON(lowbit > 7); \ + BUILD_BUG_ON(bits > 8); \ + BUILD_BUG_ON(bits <= 0); \ + \ + (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \ +}) + +static void hdmi_update_short_audio_desc(struct device *dev, + struct snd_cea_sad *a, + const unsigned char *buf) +{ + int i; + int val; + + val = GRAB_BITS(buf, 1, 0, 7); + a->rates = 0; + for (i = 0; i < 7; i++) + if (val & (1 << i)) + a->rates |= cea_sampling_frequencies[i + 1]; + + a->channels = GRAB_BITS(buf, 0, 0, 3); + a->channels++; + + a->sample_bits = 0; + a->max_bitrate = 0; + + a->format = GRAB_BITS(buf, 0, 3, 4); + switch (a->format) { + case AUDIO_CODING_TYPE_REF_STREAM_HEADER: + dev_info(dev, "HDMI: audio coding type 0 not expected\n"); + break; + + case AUDIO_CODING_TYPE_LPCM: + val = GRAB_BITS(buf, 2, 0, 3); + for (i = 0; i < 3; i++) + if (val & (1 << i)) + a->sample_bits |= cea_sample_sizes[i + 1]; + break; + + case AUDIO_CODING_TYPE_AC3: + case AUDIO_CODING_TYPE_MPEG1: + case AUDIO_CODING_TYPE_MP3: + case AUDIO_CODING_TYPE_MPEG2: + case AUDIO_CODING_TYPE_AACLC: + case AUDIO_CODING_TYPE_DTS: + case AUDIO_CODING_TYPE_ATRAC: + a->max_bitrate = GRAB_BITS(buf, 2, 0, 8); + a->max_bitrate *= 8000; + break; + + case AUDIO_CODING_TYPE_SACD: + break; + + case AUDIO_CODING_TYPE_EAC3: + break; + + case AUDIO_CODING_TYPE_DTS_HD: + break; + + case AUDIO_CODING_TYPE_MLP: + break; + + case AUDIO_CODING_TYPE_DST: + break; + + case AUDIO_CODING_TYPE_WMAPRO: + a->profile = GRAB_BITS(buf, 2, 0, 3); + break; + + case AUDIO_CODING_TYPE_REF_CXT: + a->format = GRAB_BITS(buf, 2, 3, 5); + if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT || + a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) { + dev_info(dev, + "HDMI: audio coding xtype %d not expected\n", + a->format); + a->format = 0; + } else + a->format += AUDIO_CODING_TYPE_HE_AAC - + AUDIO_CODING_XTYPE_HE_AAC; + break; + } +} + +/* + * Be careful, ELD buf could be totally rubbish! + */ +int snd_parse_eld(struct device *dev, struct snd_parsed_hdmi_eld *e, + const unsigned char *buf, int size) +{ + int mnl; + int i; + + memset(e, 0, sizeof(*e)); + e->eld_ver = GRAB_BITS(buf, 0, 3, 5); + if (e->eld_ver != ELD_VER_CEA_861D && + e->eld_ver != ELD_VER_PARTIAL) { + dev_info(dev, "HDMI: Unknown ELD version %d\n", e->eld_ver); + goto out_fail; + } + + e->baseline_len = GRAB_BITS(buf, 2, 0, 8); + mnl = GRAB_BITS(buf, 4, 0, 5); + e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3); + + e->support_hdcp = GRAB_BITS(buf, 5, 0, 1); + e->support_ai = GRAB_BITS(buf, 5, 1, 1); + e->conn_type = GRAB_BITS(buf, 5, 2, 2); + e->sad_count = GRAB_BITS(buf, 5, 4, 4); + + e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2; + e->spk_alloc = GRAB_BITS(buf, 7, 0, 7); + + e->port_id = get_unaligned_le64(buf + 8); + + /* not specified, but the spec's tendency is little endian */ + e->manufacture_id = get_unaligned_le16(buf + 16); + e->product_id = get_unaligned_le16(buf + 18); + + if (mnl > ELD_MAX_MNL) { + dev_info(dev, "HDMI: MNL is reserved value %d\n", mnl); + goto out_fail; + } else if (ELD_FIXED_BYTES + mnl > size) { + dev_info(dev, "HDMI: out of range MNL %d\n", mnl); + goto out_fail; + } else + strscpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl + 1); + + for (i = 0; i < e->sad_count; i++) { + if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) { + dev_info(dev, "HDMI: out of range SAD %d\n", i); + goto out_fail; + } + hdmi_update_short_audio_desc(dev, e->sad + i, + buf + ELD_FIXED_BYTES + mnl + 3 * i); + } + + /* + * HDMI sink's ELD info cannot always be retrieved for now, e.g. + * in console or for audio devices. Assume the highest speakers + * configuration, to _not_ prohibit multi-channel audio playback. + */ + if (!e->spk_alloc) + e->spk_alloc = 0xffff; + + return 0; + +out_fail: + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_parse_eld); + +/* + * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with + * hdmi-specific routine. + */ +static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen) +{ + static const unsigned int alsa_rates[] = { + 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000, 384000 + }; + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++) + if (pcm & (1 << i)) + j += scnprintf(buf + j, buflen - j, " %d", + alsa_rates[i]); + + buf[j] = '\0'; /* necessary when j == 0 */ +} + +static void eld_print_pcm_bits(int pcm, char *buf, int buflen) +{ + static const unsigned int bits[] = { 8, 16, 20, 24, 32 }; + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++) + if (pcm & (ELD_PCM_BITS_8 << i)) + j += scnprintf(buf + j, buflen - j, " %d", bits[i]); + + buf[j] = '\0'; /* necessary when j == 0 */ +} + +static void hdmi_show_short_audio_desc(struct device *dev, + struct snd_cea_sad *a) +{ + char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; + char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits ="; + + if (!a->format) + return; + + hdmi_print_pcm_rates(a->rates, buf, sizeof(buf)); + + if (a->format == AUDIO_CODING_TYPE_LPCM) + eld_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2) - 8); + else if (a->max_bitrate) + snprintf(buf2, sizeof(buf2), + ", max bitrate = %d", a->max_bitrate); + else + buf2[0] = '\0'; + + dev_dbg(dev, + "HDMI: supports coding type %s: channels = %d, rates =%s%s\n", + cea_audio_coding_type_names[a->format], + a->channels, buf, buf2); +} + +static void snd_eld_print_channel_allocation(int spk_alloc, char *buf, int buflen) +{ + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { + if (spk_alloc & (1 << i)) + j += scnprintf(buf + j, buflen - j, " %s", + cea_speaker_allocation_names[i]); + } + buf[j] = '\0'; /* necessary when j == 0 */ +} + +void snd_show_eld(struct device *dev, struct snd_parsed_hdmi_eld *e) +{ + int i; + + dev_dbg(dev, "HDMI: detected monitor %s at connection type %s\n", + e->monitor_name, + eld_connection_type_names[e->conn_type]); + + if (e->spk_alloc) { + char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; + + snd_eld_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + dev_dbg(dev, "HDMI: available speakers:%s\n", buf); + } + + for (i = 0; i < e->sad_count; i++) + hdmi_show_short_audio_desc(dev, e->sad + i); +} +EXPORT_SYMBOL_GPL(snd_show_eld); + +#ifdef CONFIG_SND_PROC_FS +static void hdmi_print_sad_info(int i, struct snd_cea_sad *a, + struct snd_info_buffer *buffer) +{ + char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; + + snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n", + i, a->format, cea_audio_coding_type_names[a->format]); + snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels); + + hdmi_print_pcm_rates(a->rates, buf, sizeof(buf)); + snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf); + + if (a->format == AUDIO_CODING_TYPE_LPCM) { + eld_print_pcm_bits(a->sample_bits, buf, sizeof(buf)); + snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n", + i, a->sample_bits, buf); + } + + if (a->max_bitrate) + snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n", + i, a->max_bitrate); + + if (a->profile) + snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile); +} + +void snd_print_eld_info(struct snd_parsed_hdmi_eld *e, + struct snd_info_buffer *buffer) +{ + char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; + int i; + static const char * const eld_version_names[32] = { + "reserved", + "reserved", + "CEA-861D or below", + [3 ... 30] = "reserved", + [31] = "partial" + }; + static const char * const cea_edid_version_names[8] = { + "no CEA EDID Timing Extension block present", + "CEA-861", + "CEA-861-A", + "CEA-861-B, C or D", + [4 ... 7] = "reserved" + }; + + snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); + snd_iprintf(buffer, "connection_type\t\t%s\n", + eld_connection_type_names[e->conn_type]); + snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver, + eld_version_names[e->eld_ver]); + snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver, + cea_edid_version_names[e->cea_edid_ver]); + snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id); + snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id); + snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id); + snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp); + snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); + snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); + + snd_eld_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); + + snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); + + for (i = 0; i < e->sad_count; i++) + hdmi_print_sad_info(i, e->sad + i, buffer); +} +EXPORT_SYMBOL_GPL(snd_print_eld_info); +#endif /* CONFIG_SND_PROC_FS */ diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 4f556211bb56..d3a08e292072 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -432,9 +432,9 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int if (samples == 0) return 0; width = pcm_formats[(INT)format].phys; /* physical width */ - pat = pcm_formats[(INT)format].silence; - if (!width || !pat) + if (!width) return -EINVAL; + pat = pcm_formats[(INT)format].silence; /* signed or 1 byte data */ if (pcm_formats[(INT)format].signd == 1 || width <= 8) { unsigned int bytes = samples * width / 8; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 8a3384342e8d..6c2b6a62d9d2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3245,7 +3245,7 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream, if (copy_from_user(&xfern, _xfern, sizeof(xfern))) return -EFAULT; - bufs = memdup_user(xfern.bufs, sizeof(void *) * runtime->channels); + bufs = memdup_array_user(xfern.bufs, runtime->channels, sizeof(void *)); if (IS_ERR(bufs)) return PTR_ERR(bufs); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 348ce1b7725e..70a958ac1112 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -629,12 +629,15 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream, info->subdevice = substream->number; info->stream = substream->stream; info->flags = rmidi->info_flags; + if (substream->inactive) + info->flags |= SNDRV_RAWMIDI_INFO_STREAM_INACTIVE; strcpy(info->id, rmidi->id); strcpy(info->name, rmidi->name); strcpy(info->subname, substream->name); info->subdevices_count = substream->pstr->substream_count; info->subdevices_avail = (substream->pstr->substream_count - substream->pstr->substream_opened); + info->tied_device = rmidi->tied_device; return 0; } diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig index 0374bbf51cd4..e4f58cb985d4 100644 --- a/sound/core/seq/Kconfig +++ b/sound/core/seq/Kconfig @@ -62,7 +62,7 @@ config SND_SEQ_VIRMIDI config SND_SEQ_UMP bool "Support for UMP events" - default y if SND_SEQ_UMP_CLIENT + default SND_UMP help Say Y here to enable the support for handling UMP (Universal MIDI Packet) events via ALSA sequencer infrastructure, which is an @@ -71,6 +71,6 @@ config SND_SEQ_UMP among legacy and UMP clients. config SND_SEQ_UMP_CLIENT - def_tristate SND_UMP + def_tristate SND_UMP && SND_SEQ_UMP endif # SND_SEQUENCER diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h index 98dd20b42976..6163a00bc8de 100644 --- a/sound/core/seq/oss/seq_oss_device.h +++ b/sound/core/seq/oss/seq_oss_device.h @@ -55,7 +55,6 @@ struct seq_oss_chinfo { struct seq_oss_synthinfo { struct snd_seq_oss_arg arg; struct seq_oss_chinfo *ch; - struct seq_oss_synth_sysex *sysex; int nr_voices; int opened; int is_midi; @@ -157,8 +156,4 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, ev->dest.port = dest_port; } - -/* misc. functions for proc interface */ -char *enabled_str(bool b); - #endif /* __SEQ_OSS_DEVICE_H */ diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c index 7b7c925dd3aa..76fb81077eef 100644 --- a/sound/core/seq/oss/seq_oss_event.c +++ b/sound/core/seq/oss/seq_oss_event.c @@ -290,16 +290,14 @@ note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, st if (note == 255 && info->ch[ch].note >= 0) { /* volume control */ int type; - //if (! vel) - /* set volume to zero -- note off */ - // type = SNDRV_SEQ_EVENT_NOTEOFF; - //else - if (info->ch[ch].vel) + + if (info->ch[ch].vel) /* sample already started -- volume change */ type = SNDRV_SEQ_EVENT_KEYPRESS; else /* sample not started -- start now */ type = SNDRV_SEQ_EVENT_NOTEON; + info->ch[ch].vel = vel; return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev); } else if (note >= 128) diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index 676246fa02f1..e6d7d83ed0e7 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -111,7 +111,7 @@ snd_seq_oss_create_client(void) /* - * receive annoucement from system port, and check the midi device + * receive announcement from system port, and check the midi device */ static int receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop) @@ -449,12 +449,6 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp) /* * misc. functions for proc interface */ -char * -enabled_str(bool b) -{ - return b ? "enabled" : "disabled"; -} - static const char * filemode_str(int val) { diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 51ee4c00a843..9de47e098b29 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -26,13 +26,6 @@ * definition of synth info records */ -/* sysex buffer */ -struct seq_oss_synth_sysex { - int len; - int skip; - unsigned char buf[MAX_SYSEX_BUFLEN]; -}; - /* synth info */ struct seq_oss_synth { int seq_device; @@ -66,7 +59,6 @@ static struct seq_oss_synth midi_synth_dev = { }; static DEFINE_SPINLOCK(register_lock); -static DEFINE_MUTEX(sysex_mutex); /* * prototypes @@ -319,8 +311,6 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp) } snd_use_lock_free(&rec->use_lock); } - kfree(info->sysex); - info->sysex = NULL; kfree(info->ch); info->ch = NULL; } @@ -396,8 +386,6 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev) info = get_synthinfo_nospec(dp, dev); if (!info || !info->opened) return; - if (info->sysex) - info->sysex->len = 0; /* reset sysex */ reset_channels(info); if (info->is_midi) { if (midi_synth_dev.opened <= 0) @@ -409,8 +397,6 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev) dp->file_mode) < 0) { midi_synth_dev.opened--; info->opened = 0; - kfree(info->sysex); - info->sysex = NULL; kfree(info->ch); info->ch = NULL; } @@ -483,64 +469,26 @@ snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev) /* * receive OSS 6 byte sysex packet: - * the full sysex message will be sent if it reaches to the end of data - * (0xff). + * the event is filled and prepared for sending immediately + * (i.e. sysex messages are fragmented) */ int snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf, struct snd_seq_event *ev) { - int i, send; - unsigned char *dest; - struct seq_oss_synth_sysex *sysex; - struct seq_oss_synthinfo *info; + unsigned char *p; + int len = 6; - info = snd_seq_oss_synth_info(dp, dev); - if (!info) - return -ENXIO; + p = memchr(buf, 0xff, 6); + if (p) + len = p - buf + 1; - guard(mutex)(&sysex_mutex); - sysex = info->sysex; - if (sysex == NULL) { - sysex = kzalloc(sizeof(*sysex), GFP_KERNEL); - if (sysex == NULL) - return -ENOMEM; - info->sysex = sysex; - } - - send = 0; - dest = sysex->buf + sysex->len; - /* copy 6 byte packet to the buffer */ - for (i = 0; i < 6; i++) { - if (buf[i] == 0xff) { - send = 1; - break; - } - dest[i] = buf[i]; - sysex->len++; - if (sysex->len >= MAX_SYSEX_BUFLEN) { - sysex->len = 0; - sysex->skip = 1; - break; - } - } - - if (sysex->len && send) { - if (sysex->skip) { - sysex->skip = 0; - sysex->len = 0; - return -EINVAL; /* skip */ - } - /* copy the data to event record and send it */ - ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; - if (snd_seq_oss_synth_addr(dp, dev, ev)) - return -EINVAL; - ev->data.ext.len = sysex->len; - ev->data.ext.ptr = sysex->buf; - sysex->len = 0; - return 0; - } - - return -EINVAL; /* skip */ + /* copy the data to event record and send it */ + if (snd_seq_oss_synth_addr(dp, dev, ev)) + return -EINVAL; + ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; + ev->data.ext.len = len; + ev->data.ext.ptr = buf; + return 0; } /* @@ -660,8 +608,8 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf) rec->synth_type, rec->synth_subtype, rec->nr_voices); snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n", - enabled_str((long)rec->oper.ioctl), - enabled_str((long)rec->oper.load_patch)); + str_enabled_disabled((long)rec->oper.ioctl), + str_enabled_disabled((long)rec->oper.load_patch)); snd_use_lock_free(&rec->use_lock); } } diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 77b6ac9b5c11..198c598a5393 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -106,7 +106,7 @@ static struct snd_seq_client *clientptr(int clientid) return clienttab[clientid]; } -struct snd_seq_client *snd_seq_client_use_ptr(int clientid) +static struct snd_seq_client *client_use_ptr(int clientid, bool load_module) { unsigned long flags; struct snd_seq_client *client; @@ -126,7 +126,7 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid) } spin_unlock_irqrestore(&clients_lock, flags); #ifdef CONFIG_MODULES - if (!in_interrupt()) { + if (load_module) { static DECLARE_BITMAP(client_requested, SNDRV_SEQ_GLOBAL_CLIENTS); static DECLARE_BITMAP(card_requested, SNDRV_CARDS); @@ -168,6 +168,20 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid) return client; } +/* get snd_seq_client object for the given id quickly */ +struct snd_seq_client *snd_seq_client_use_ptr(int clientid) +{ + return client_use_ptr(clientid, false); +} + +/* get snd_seq_client object for the given id; + * if not found, retry after loading the modules + */ +static struct snd_seq_client *client_load_and_use_ptr(int clientid) +{ + return client_use_ptr(clientid, IS_ENABLED(CONFIG_MODULES)); +} + /* Take refcount and perform ioctl_mutex lock on the given client; * used only for OSS sequencer * Unlock via snd_seq_client_ioctl_unlock() below @@ -176,7 +190,7 @@ bool snd_seq_client_ioctl_lock(int clientid) { struct snd_seq_client *client; - client = snd_seq_client_use_ptr(clientid); + client = client_load_and_use_ptr(clientid); if (!client) return false; mutex_lock(&client->ioctl_mutex); @@ -678,12 +692,18 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client, dest_port->time_real); #if IS_ENABLED(CONFIG_SND_SEQ_UMP) - if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT)) { - if (snd_seq_ev_is_ump(event)) { + if (snd_seq_ev_is_ump(event)) { + if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT)) { result = snd_seq_deliver_from_ump(client, dest, dest_port, event, atomic, hop); goto __skip; - } else if (snd_seq_client_is_ump(dest)) { + } else if (dest->type == USER_CLIENT && + !snd_seq_client_is_ump(dest)) { + result = 0; // drop the event + goto __skip; + } + } else if (snd_seq_client_is_ump(dest)) { + if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT)) { result = snd_seq_deliver_to_ump(client, dest, dest_port, event, atomic, hop); goto __skip; @@ -1130,8 +1150,7 @@ static __poll_t snd_seq_poll(struct file *file, poll_table * wait) if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) { /* check if data is available in the pool */ - if (!snd_seq_write_pool_allocated(client) || - snd_seq_pool_poll_wait(client->pool, file, wait)) + if (snd_seq_pool_poll_wait(client->pool, file, wait)) mask |= EPOLLOUT | EPOLLWRNORM; } @@ -1189,7 +1208,7 @@ static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void *arg) int err = 0; /* requested client number */ - cptr = snd_seq_client_use_ptr(info->client); + cptr = client_load_and_use_ptr(info->client); if (cptr == NULL) return -ENOENT; /* don't change !!! */ @@ -1251,7 +1270,7 @@ static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client, struct snd_seq_client *cptr; /* requested client number */ - cptr = snd_seq_client_use_ptr(client_info->client); + cptr = client_load_and_use_ptr(client_info->client); if (cptr == NULL) return -ENOENT; /* don't change !!! */ @@ -1296,6 +1315,10 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client, client->midi_version = client_info->midi_version; memcpy(client->event_filter, client_info->event_filter, 32); client->group_filter = client_info->group_filter; + + /* notify the change */ + snd_seq_system_client_ev_client_change(client->number); + return 0; } @@ -1386,7 +1409,7 @@ static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg) struct snd_seq_client *cptr; struct snd_seq_client_port *port; - cptr = snd_seq_client_use_ptr(info->addr.client); + cptr = client_load_and_use_ptr(info->addr.client); if (cptr == NULL) return -ENXIO; @@ -1419,6 +1442,9 @@ static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg) if (port) { snd_seq_set_port_info(port, info); snd_seq_port_unlock(port); + /* notify the change */ + snd_seq_system_client_ev_port_change(info->addr.client, + info->addr.port); } return 0; } @@ -1475,7 +1501,7 @@ int snd_seq_client_notify_subscription(int client, int port, event.data.connect.dest = info->dest; event.data.connect.sender = info->sender; - return snd_seq_system_notify(client, port, &event); /* non-atomic */ + return snd_seq_system_notify(client, port, &event, false); /* non-atomic */ } @@ -1490,10 +1516,10 @@ static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client, struct snd_seq_client *receiver = NULL, *sender = NULL; struct snd_seq_client_port *sport = NULL, *dport = NULL; - receiver = snd_seq_client_use_ptr(subs->dest.client); + receiver = client_load_and_use_ptr(subs->dest.client); if (!receiver) goto __end; - sender = snd_seq_client_use_ptr(subs->sender.client); + sender = client_load_and_use_ptr(subs->sender.client); if (!sender) goto __end; sport = snd_seq_port_use_ptr(sender, subs->sender.port); @@ -1858,7 +1884,7 @@ static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client, struct snd_seq_client_pool *info = arg; struct snd_seq_client *cptr; - cptr = snd_seq_client_use_ptr(info->client); + cptr = client_load_and_use_ptr(info->client); if (cptr == NULL) return -ENOENT; memset(info, 0, sizeof(*info)); @@ -1962,7 +1988,7 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client, struct snd_seq_client_port *sport = NULL; result = -EINVAL; - sender = snd_seq_client_use_ptr(subs->sender.client); + sender = client_load_and_use_ptr(subs->sender.client); if (!sender) goto __end; sport = snd_seq_port_use_ptr(sender, subs->sender.port); @@ -1993,7 +2019,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) struct list_head *p; int i; - cptr = snd_seq_client_use_ptr(subs->root.client); + cptr = client_load_and_use_ptr(subs->root.client); if (!cptr) goto __end; port = snd_seq_port_use_ptr(cptr, subs->root.port); @@ -2060,7 +2086,7 @@ static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client, if (info->client < 0) info->client = 0; for (; info->client < SNDRV_SEQ_MAX_CLIENTS; info->client++) { - cptr = snd_seq_client_use_ptr(info->client); + cptr = client_load_and_use_ptr(info->client); if (cptr) break; /* found */ } @@ -2083,7 +2109,7 @@ static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client, struct snd_seq_client *cptr; struct snd_seq_client_port *port = NULL; - cptr = snd_seq_client_use_ptr(info->addr.client); + cptr = client_load_and_use_ptr(info->addr.client); if (cptr == NULL) return -ENXIO; @@ -2180,7 +2206,7 @@ static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller, size = sizeof(struct snd_ump_endpoint_info); else size = sizeof(struct snd_ump_block_info); - cptr = snd_seq_client_use_ptr(client); + cptr = client_load_and_use_ptr(client); if (!cptr) return -ENOENT; @@ -2229,6 +2255,16 @@ static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller, error: mutex_unlock(&cptr->ioctl_mutex); snd_seq_client_unlock(cptr); + if (!err && cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO) { + if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) + snd_seq_system_ump_notify(client, 0, + SNDRV_SEQ_EVENT_UMP_EP_CHANGE, + false); + else + snd_seq_system_ump_notify(client, type - 1, + SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE, + false); + } return err; } #endif @@ -2452,7 +2488,7 @@ int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, if (check_event_type_and_length(ev)) return -EINVAL; - cptr = snd_seq_client_use_ptr(client); + cptr = client_load_and_use_ptr(client); if (cptr == NULL) return -EINVAL; @@ -2549,8 +2585,6 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table if (client == NULL) return -ENXIO; - if (! snd_seq_write_pool_allocated(client)) - return 1; if (snd_seq_pool_poll_wait(client->pool, file, wait)) return 1; return 0; @@ -2684,7 +2718,7 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, /* list the client table */ for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) { - client = snd_seq_client_use_ptr(c); + client = client_load_and_use_ptr(c); if (client == NULL) continue; if (client->type == NO_CLIENT) { @@ -2692,6 +2726,7 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, continue; } + mutex_lock(&client->ioctl_mutex); snd_iprintf(buffer, "Client %3d : \"%s\" [%s %s]\n", c, client->name, client->type == USER_CLIENT ? "User" : "Kernel", @@ -2709,6 +2744,7 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, snd_iprintf(buffer, " Input pool :\n"); snd_seq_info_pool(buffer, client->data.user.fifo->pool, " "); } + mutex_unlock(&client->ioctl_mutex); snd_seq_client_unlock(client); } } diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 20155e3e87c6..ccde0ca3d208 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -427,6 +427,7 @@ int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file, poll_table *wait) { poll_wait(file, &pool->output_sleep, wait); + guard(spinlock_irq)(&pool->lock); return snd_seq_output_ok(pool); } diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c index 80267290190d..853920f79016 100644 --- a/sound/core/seq/seq_system.c +++ b/sound/core/seq/seq_system.c @@ -49,12 +49,14 @@ static int sysclient = -1; /* port id numbers for this client */ static int announce_port = -1; +/* number of subscriptions to announce port */ +static int announce_subscribed; /* fill standard header data, source port & channel are filled in */ static int setheader(struct snd_seq_event * ev, int client, int port) { - if (announce_port < 0) + if (announce_port < 0 || !announce_subscribed) return -ENODEV; memset(ev, 0, sizeof(struct snd_seq_event)); @@ -76,26 +78,27 @@ static int setheader(struct snd_seq_event * ev, int client, int port) /* entry points for broadcasting system events */ -void snd_seq_system_broadcast(int client, int port, int type) +void snd_seq_system_broadcast(int client, int port, int type, bool atomic) { struct snd_seq_event ev; if (setheader(&ev, client, port) < 0) return; ev.type = type; - snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0); + snd_seq_kernel_client_dispatch(sysclient, &ev, atomic, 0); } EXPORT_SYMBOL_GPL(snd_seq_system_broadcast); /* entry points for broadcasting system events */ -int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev) +int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev, + bool atomic) { ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED; ev->source.client = sysclient; ev->source.port = announce_port; ev->dest.client = client; ev->dest.port = port; - return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0); + return snd_seq_kernel_client_dispatch(sysclient, ev, atomic, 0); } /* call-back handler for timer events */ @@ -104,6 +107,22 @@ static int event_input_timer(struct snd_seq_event * ev, int direct, void *privat return snd_seq_control_queue(ev, atomic, hop); } +static int sys_announce_subscribe(void *private_data, + struct snd_seq_port_subscribe *info) +{ + announce_subscribed++; + return 0; +} + +static int sys_announce_unsubscribe(void *private_data, + struct snd_seq_port_subscribe *info) +{ + if (snd_BUG_ON(!announce_subscribed)) + return 0; + announce_subscribed--; + return 0; +} + /* register our internal client */ int __init snd_seq_system_client_init(void) { @@ -143,7 +162,10 @@ int __init snd_seq_system_client_init(void) /* register announcement port */ strcpy(port->name, "Announce"); port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */ - port->kernel = NULL; + pcallbacks.event_input = NULL; + pcallbacks.subscribe = sys_announce_subscribe; + pcallbacks.unsubscribe = sys_announce_unsubscribe; + port->kernel = &pcallbacks; port->type = 0; port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; port->addr.client = sysclient; diff --git a/sound/core/seq/seq_system.h b/sound/core/seq/seq_system.h index 4fe88ad40346..62e513f74871 100644 --- a/sound/core/seq/seq_system.h +++ b/sound/core/seq/seq_system.h @@ -10,16 +10,31 @@ /* entry points for broadcasting system events */ -void snd_seq_system_broadcast(int client, int port, int type); - -#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START) -#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT) -#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE) -#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START) -#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT) -#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE) - -int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev); +void snd_seq_system_broadcast(int client, int port, int type, bool atomic); + +/* normal system notification event broadcast */ +#define notify_event(client, port, type) \ + snd_seq_system_broadcast(client, port, type, false) + +/* notify UMP EP/FB change event */ +static inline void snd_seq_system_ump_notify(int client, int block, int type, + bool atomic) +{ + /* reuse the existing snd_seq_system_broadcast(): + * struct snd_seq_ev_ump_notify is compatible with struct snd_seq_addr + */ + snd_seq_system_broadcast(client, block, type, atomic); +} + +#define snd_seq_system_client_ev_client_start(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_START) +#define snd_seq_system_client_ev_client_exit(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT) +#define snd_seq_system_client_ev_client_change(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE) +#define snd_seq_system_client_ev_port_start(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_START) +#define snd_seq_system_client_ev_port_exit(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_EXIT) +#define snd_seq_system_client_ev_port_change(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE) + +int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev, + bool atomic); /* register our internal client */ int snd_seq_system_client_init(void); diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index e956f17f3792..1255351b59ce 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -272,8 +272,6 @@ static void update_port_infos(struct seq_ump_client *client) new); if (err < 0) continue; - /* notify to system port */ - snd_seq_system_client_ev_port_change(client->seq_client, i); } } @@ -390,6 +388,33 @@ static void handle_group_notify(struct work_struct *work) setup_client_group_filter(client); } +/* UMP EP change notification */ +static int seq_ump_notify_ep_change(struct snd_ump_endpoint *ump) +{ + struct seq_ump_client *client = ump->seq_client; + struct snd_seq_client *cptr; + int client_id; + + if (!client) + return -ENODEV; + client_id = client->seq_client; + cptr = snd_seq_kernel_client_get(client_id); + if (!cptr) + return -ENODEV; + + snd_seq_system_ump_notify(client_id, 0, SNDRV_SEQ_EVENT_UMP_EP_CHANGE, + true); + + /* update sequencer client name if needed */ + if (*ump->core.name && strcmp(ump->core.name, cptr->name)) { + strscpy(cptr->name, ump->core.name, sizeof(cptr->name)); + snd_seq_system_client_ev_client_change(client_id); + } + + snd_seq_kernel_client_put(cptr); + return 0; +} + /* UMP FB change notification */ static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump, struct snd_ump_block *fb) @@ -399,20 +424,29 @@ static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump, if (!client) return -ENODEV; schedule_work(&client->group_notify_work); + snd_seq_system_ump_notify(client->seq_client, fb->info.block_id, + SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE, + true); return 0; } /* UMP protocol change notification; just update the midi_version field */ static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump) { - if (!ump->seq_client) + struct seq_ump_client *client = ump->seq_client; + + if (!client) return -ENODEV; - setup_client_midi_version(ump->seq_client); + setup_client_midi_version(client); + snd_seq_system_ump_notify(client->seq_client, 0, + SNDRV_SEQ_EVENT_UMP_EP_CHANGE, + true); return 0; } static const struct snd_seq_ump_ops seq_ump_ops = { .input_receive = seq_ump_input_receive, + .notify_ep_change = seq_ump_notify_ep_change, .notify_fb_change = seq_ump_notify_fb_change, .switch_protocol = seq_ump_switch_protocol, }; diff --git a/sound/core/timer.c b/sound/core/timer.c index fbada79380f9..d774b9b71ce2 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1515,91 +1515,97 @@ static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *ti id->subdevice = timer->tmr_subdevice; } -static int snd_timer_user_next_device(struct snd_timer_id __user *_tid) +static void get_next_device(struct snd_timer_id *id) { - struct snd_timer_id id; struct snd_timer *timer; struct list_head *p; - if (copy_from_user(&id, _tid, sizeof(id))) - return -EFAULT; - guard(mutex)(®ister_mutex); - if (id.dev_class < 0) { /* first item */ + if (id->dev_class < 0) { /* first item */ if (list_empty(&snd_timer_list)) - snd_timer_user_zero_id(&id); + snd_timer_user_zero_id(id); else { timer = list_entry(snd_timer_list.next, struct snd_timer, device_list); - snd_timer_user_copy_id(&id, timer); + snd_timer_user_copy_id(id, timer); } } else { - switch (id.dev_class) { + switch (id->dev_class) { case SNDRV_TIMER_CLASS_GLOBAL: - id.device = id.device < 0 ? 0 : id.device + 1; + id->device = id->device < 0 ? 0 : id->device + 1; list_for_each(p, &snd_timer_list) { timer = list_entry(p, struct snd_timer, device_list); if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { - snd_timer_user_copy_id(&id, timer); + snd_timer_user_copy_id(id, timer); break; } - if (timer->tmr_device >= id.device) { - snd_timer_user_copy_id(&id, timer); + if (timer->tmr_device >= id->device) { + snd_timer_user_copy_id(id, timer); break; } } if (p == &snd_timer_list) - snd_timer_user_zero_id(&id); + snd_timer_user_zero_id(id); break; case SNDRV_TIMER_CLASS_CARD: case SNDRV_TIMER_CLASS_PCM: - if (id.card < 0) { - id.card = 0; + if (id->card < 0) { + id->card = 0; } else { - if (id.device < 0) { - id.device = 0; + if (id->device < 0) { + id->device = 0; } else { - if (id.subdevice < 0) - id.subdevice = 0; - else if (id.subdevice < INT_MAX) - id.subdevice++; + if (id->subdevice < 0) + id->subdevice = 0; + else if (id->subdevice < INT_MAX) + id->subdevice++; } } list_for_each(p, &snd_timer_list) { timer = list_entry(p, struct snd_timer, device_list); - if (timer->tmr_class > id.dev_class) { - snd_timer_user_copy_id(&id, timer); + if (timer->tmr_class > id->dev_class) { + snd_timer_user_copy_id(id, timer); break; } - if (timer->tmr_class < id.dev_class) + if (timer->tmr_class < id->dev_class) continue; - if (timer->card->number > id.card) { - snd_timer_user_copy_id(&id, timer); + if (timer->card->number > id->card) { + snd_timer_user_copy_id(id, timer); break; } - if (timer->card->number < id.card) + if (timer->card->number < id->card) continue; - if (timer->tmr_device > id.device) { - snd_timer_user_copy_id(&id, timer); + if (timer->tmr_device > id->device) { + snd_timer_user_copy_id(id, timer); break; } - if (timer->tmr_device < id.device) + if (timer->tmr_device < id->device) continue; - if (timer->tmr_subdevice > id.subdevice) { - snd_timer_user_copy_id(&id, timer); + if (timer->tmr_subdevice > id->subdevice) { + snd_timer_user_copy_id(id, timer); break; } - if (timer->tmr_subdevice < id.subdevice) + if (timer->tmr_subdevice < id->subdevice) continue; - snd_timer_user_copy_id(&id, timer); + snd_timer_user_copy_id(id, timer); break; } if (p == &snd_timer_list) - snd_timer_user_zero_id(&id); + snd_timer_user_zero_id(id); break; default: - snd_timer_user_zero_id(&id); + snd_timer_user_zero_id(id); } } +} + +static int snd_timer_user_next_device(struct snd_timer_id __user *_tid) +{ + struct snd_timer_id id; + + if (copy_from_user(&id, _tid, sizeof(id))) + return -EFAULT; + scoped_guard(mutex, ®ister_mutex) + get_next_device(&id); if (copy_to_user(_tid, &id, sizeof(*_tid))) return -EFAULT; return 0; @@ -1620,23 +1626,24 @@ static int snd_timer_user_ginfo(struct file *file, tid = ginfo->tid; memset(ginfo, 0, sizeof(*ginfo)); ginfo->tid = tid; - guard(mutex)(®ister_mutex); - t = snd_timer_find(&tid); - if (!t) - return -ENODEV; - ginfo->card = t->card ? t->card->number : -1; - if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) - ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; - strscpy(ginfo->id, t->id, sizeof(ginfo->id)); - strscpy(ginfo->name, t->name, sizeof(ginfo->name)); - scoped_guard(spinlock_irq, &t->lock) - ginfo->resolution = snd_timer_hw_resolution(t); - if (t->hw.resolution_min > 0) { - ginfo->resolution_min = t->hw.resolution_min; - ginfo->resolution_max = t->hw.resolution_max; - } - list_for_each(p, &t->open_list_head) { - ginfo->clients++; + scoped_guard(mutex, ®ister_mutex) { + t = snd_timer_find(&tid); + if (!t) + return -ENODEV; + ginfo->card = t->card ? t->card->number : -1; + if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) + ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; + strscpy(ginfo->id, t->id, sizeof(ginfo->id)); + strscpy(ginfo->name, t->name, sizeof(ginfo->name)); + scoped_guard(spinlock_irq, &t->lock) + ginfo->resolution = snd_timer_hw_resolution(t); + if (t->hw.resolution_min > 0) { + ginfo->resolution_min = t->hw.resolution_min; + ginfo->resolution_max = t->hw.resolution_max; + } + list_for_each(p, &t->open_list_head) { + ginfo->clients++; + } } if (copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) return -EFAULT; @@ -1674,31 +1681,31 @@ static int snd_timer_user_gstatus(struct file *file, struct snd_timer_gstatus gstatus; struct snd_timer_id tid; struct snd_timer *t; - int err = 0; if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus))) return -EFAULT; tid = gstatus.tid; memset(&gstatus, 0, sizeof(gstatus)); gstatus.tid = tid; - guard(mutex)(®ister_mutex); - t = snd_timer_find(&tid); - if (t != NULL) { - guard(spinlock_irq)(&t->lock); - gstatus.resolution = snd_timer_hw_resolution(t); - if (t->hw.precise_resolution) { - t->hw.precise_resolution(t, &gstatus.resolution_num, - &gstatus.resolution_den); + scoped_guard(mutex, ®ister_mutex) { + t = snd_timer_find(&tid); + if (t != NULL) { + guard(spinlock_irq)(&t->lock); + gstatus.resolution = snd_timer_hw_resolution(t); + if (t->hw.precise_resolution) { + t->hw.precise_resolution(t, &gstatus.resolution_num, + &gstatus.resolution_den); + } else { + gstatus.resolution_num = gstatus.resolution; + gstatus.resolution_den = 1000000000uL; + } } else { - gstatus.resolution_num = gstatus.resolution; - gstatus.resolution_den = 1000000000uL; + return -ENODEV; } - } else { - err = -ENODEV; } - if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) - err = -EFAULT; - return err; + if (copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) + return -EFAULT; + return 0; } static int snd_timer_user_tselect(struct file *file, diff --git a/sound/core/ump.c b/sound/core/ump.c index 9198bff4768c..8d8681a42ca5 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -37,6 +37,7 @@ static int process_legacy_output(struct snd_ump_endpoint *ump, u32 *buffer, int count); static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src, int words); +static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump); static void update_legacy_names(struct snd_ump_endpoint *ump); #else static inline int process_legacy_output(struct snd_ump_endpoint *ump, @@ -48,11 +49,42 @@ static inline void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src, int words) { } +static inline void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump) +{ +} static inline void update_legacy_names(struct snd_ump_endpoint *ump) { } #endif +/* copy a string safely with stripping non-printable letters */ +static void safe_copy_string(void *dst, size_t max_dst_size, + const void *src, size_t max_src_size) +{ + const unsigned char *s = src; + unsigned char *d = dst; + + if (!max_dst_size--) + return; + for (s = src; max_dst_size && *s && max_src_size--; s++) { + if (!isascii(*s) || !isprint(*s)) + continue; + *d++ = *s; + max_dst_size--; + } + *d = 0; +} + +/* append a string safely with stripping non-printable letters */ +static void safe_append_string(void *dst, size_t max_dst_size, + const void *src, size_t max_src_size) +{ + unsigned char *d = dst; + size_t len = strlen(d); + + safe_copy_string(d + len, max_dst_size - len, src, max_src_size); +} + static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = { .dev_register = snd_ump_dev_register, .dev_unregister = snd_ump_dev_unregister, @@ -565,16 +597,10 @@ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump) } if (!*fb->info.name) continue; - if (!*group->name) { - /* store the first matching name */ - strscpy(group->name, fb->info.name, - sizeof(group->name)); - } else { - /* when overlapping, concat names */ + if (*group->name) strlcat(group->name, ", ", sizeof(group->name)); - strlcat(group->name, fb->info.name, - sizeof(group->name)); - } + safe_append_string(group->name, sizeof(group->name), + fb->info.name, sizeof(fb->info.name)); } } } @@ -669,6 +695,15 @@ static void choose_default_protocol(struct snd_ump_endpoint *ump) ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI1; } +/* notify the EP info/name change to sequencer */ +static void seq_notify_ep_change(struct snd_ump_endpoint *ump) +{ +#if IS_ENABLED(CONFIG_SND_SEQUENCER) + if (ump->parsed && ump->seq_ops && ump->seq_ops->notify_ep_change) + ump->seq_ops->notify_ep_change(ump); +#endif +} + /* handle EP info stream message; update the UMP attributes */ static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump, const union snd_ump_stream_msg *buf) @@ -693,6 +728,7 @@ static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump, ump->info.protocol &= ump->info.protocol_caps; choose_default_protocol(ump); + seq_notify_ep_change(ump); return 1; /* finished */ } @@ -715,24 +751,46 @@ static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump, ump->info.family_id, ump->info.model_id, ump->info.sw_revision); + seq_notify_ep_change(ump); return 1; /* finished */ } +/* set up the core rawmidi name from UMP EP name string */ +static void ump_set_rawmidi_name(struct snd_ump_endpoint *ump) +{ + safe_copy_string(ump->core.name, sizeof(ump->core.name), + ump->info.name, sizeof(ump->info.name)); +} + /* handle EP name stream message; update the UMP name string */ static int ump_handle_ep_name_msg(struct snd_ump_endpoint *ump, const union snd_ump_stream_msg *buf) { - return ump_append_string(ump, ump->info.name, sizeof(ump->info.name), - buf->raw, 2); + int ret; + + ret = ump_append_string(ump, ump->info.name, sizeof(ump->info.name), + buf->raw, 2); + if (ret && ump->parsed) { + ump_set_rawmidi_name(ump); + ump_legacy_set_rawmidi_name(ump); + seq_notify_ep_change(ump); + } + + return ret; } /* handle EP product id stream message; update the UMP product_id string */ static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump, const union snd_ump_stream_msg *buf) { - return ump_append_string(ump, ump->info.product_id, - sizeof(ump->info.product_id), - buf->raw, 2); + int ret; + + ret = ump_append_string(ump, ump->info.product_id, + sizeof(ump->info.product_id), + buf->raw, 2); + if (ret) + seq_notify_ep_change(ump); + return ret; } /* notify the protocol change to sequencer */ @@ -1045,6 +1103,8 @@ int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump) if (err < 0) ump_dbg(ump, "Unable to get UMP EP name string\n"); + ump_set_rawmidi_name(ump); + /* Request Endpoint Product ID */ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_PRODUCT_ID, UMP_STREAM_MSG_STATUS_PRODUCT_ID); @@ -1250,8 +1310,8 @@ static int fill_legacy_mapping(struct snd_ump_endpoint *ump) return num; } -static void fill_substream_names(struct snd_ump_endpoint *ump, - struct snd_rawmidi *rmidi, int dir) +static void update_legacy_substreams(struct snd_ump_endpoint *ump, + struct snd_rawmidi *rmidi, int dir) { struct snd_rawmidi_substream *s; const char *name; @@ -1261,10 +1321,11 @@ static void fill_substream_names(struct snd_ump_endpoint *ump, idx = ump->legacy_mapping[s->number]; name = ump->groups[idx].name; if (!*name) - name = ump->info.name; + name = ump->core.name; scnprintf(s->name, sizeof(s->name), "Group %d (%.16s)%s", idx + 1, name, ump->groups[idx].active ? "" : " [Inactive]"); + s->inactive = !ump->groups[idx].active; } } @@ -1272,8 +1333,16 @@ static void update_legacy_names(struct snd_ump_endpoint *ump) { struct snd_rawmidi *rmidi = ump->legacy_rmidi; - fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT); - fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT); + update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT); + update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT); +} + +static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump) +{ + struct snd_rawmidi *rmidi = ump->legacy_rmidi; + + snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)", + ump->core.name); } int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump, @@ -1306,14 +1375,15 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump, if (output) snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ump_legacy_output_ops); - snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)", - ump->info.name); rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP; rmidi->ops = &snd_ump_legacy_ops; rmidi->private_data = ump; ump->legacy_rmidi = rmidi; + ump_legacy_set_rawmidi_name(ump); update_legacy_names(ump); + snd_rawmidi_tie_devices(rmidi, &ump->core); + ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id); return 0; } diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 8f5df9b3aaaa..c1a3efb633c5 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -457,8 +457,7 @@ static int dummy_hrtimer_create(struct snd_pcm_substream *substream) if (!dpcm) return -ENOMEM; substream->runtime->private_data = dpcm; - hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - dpcm->timer.function = dummy_hrtimer_callback; + hrtimer_setup(&dpcm->timer, dummy_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); dpcm->substream = substream; atomic_set(&dpcm->running, 0); return 0; diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c index 78c9b1c7590f..e8482c2290c3 100644 --- a/sound/drivers/pcsp/pcsp.c +++ b/sound/drivers/pcsp/pcsp.c @@ -103,8 +103,7 @@ static int snd_card_pcsp_probe(int devnum, struct device *dev) if (devnum != 0) return -EINVAL; - hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - pcsp_chip.timer.function = pcsp_do_timer; + hrtimer_setup(&pcsp_chip.timer, pcsp_do_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); err = snd_devm_card_new(dev, index, id, THIS_MODULE, 0, &card); if (err < 0) diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index efd59e9d9935..0907d0a2296f 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -135,13 +135,13 @@ static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer) snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", (data & 0x00000020) ? "Professional" : "Consumer", - (data & 0x00000040) ? "on" : "off"); + str_on_off(data & 0x00000040)); snd_iprintf(buffer, "Optical output interface format: %s\n", (data & 0x00000100) ? "S/PDIF" : "ADAT"); snd_iprintf(buffer, "Word output single speed: %s\n", - (data & 0x00002000) ? "on" : "off"); + str_on_off(data & 0x00002000)); snd_iprintf(buffer, "S/PDIF input interface: %s\n", (data & 0x00000200) ? "Optical" : "Coaxial"); diff --git a/sound/hda/hdac_component.c b/sound/hda/hdac_component.c index bb37e7e0bd79..9c82a2864a2f 100644 --- a/sound/hda/hdac_component.c +++ b/sound/hda/hdac_component.c @@ -5,6 +5,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/component.h> +#include <linux/string_choices.h> #include <sound/core.h> #include <sound/hdaudio.h> #include <sound/hda_component.h> @@ -42,8 +43,7 @@ int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable) if (!acomp->ops->codec_wake_override) return 0; - dev_dbg(bus->dev, "%s codec wakeup\n", - enable ? "enable" : "disable"); + dev_dbg(bus->dev, "%s codec wakeup\n", str_enable_disable(enable)); acomp->ops->codec_wake_override(acomp->dev, enable); @@ -67,8 +67,7 @@ void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable) { struct drm_audio_component *acomp = bus->audio_component; - dev_dbg(bus->dev, "display power %s\n", - enable ? "enable" : "disable"); + dev_dbg(bus->dev, "display power %s\n", str_enable_disable(enable)); mutex_lock(&bus->lock); if (enable) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 2670792f43b4..4e85a838ad7e 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -492,32 +492,21 @@ static int setup_bdle(struct hdac_bus *bus, } /** - * snd_hdac_stream_setup_periods - set up BDL entries + * snd_hdac_stream_setup_bdle - set up BDL entries * @azx_dev: HD-audio core stream to set up + * @dmab: allocated DMA buffer + * @runtime: substream runtime, optional * * Set up the buffer descriptor table of the given stream based on the * period and buffer sizes of the assigned PCM substream. */ -int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) +static int snd_hdac_stream_setup_bdle(struct hdac_stream *azx_dev, struct snd_dma_buffer *dmab, + struct snd_pcm_runtime *runtime) { struct hdac_bus *bus = azx_dev->bus; - struct snd_pcm_substream *substream = azx_dev->substream; - struct snd_compr_stream *cstream = azx_dev->cstream; - struct snd_pcm_runtime *runtime = NULL; - struct snd_dma_buffer *dmab; - __le32 *bdl; int i, ofs, periods, period_bytes; int pos_adj, pos_align; - - if (substream) { - runtime = substream->runtime; - dmab = snd_pcm_get_dma_buf(substream); - } else if (cstream) { - dmab = snd_pcm_get_dma_buf(cstream); - } else { - WARN(1, "No substream or cstream assigned\n"); - return -EINVAL; - } + __le32 *bdl; /* reset BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); @@ -571,6 +560,33 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) azx_dev->bufsize, period_bytes); return -EINVAL; } + +/** + * snd_hdac_stream_setup_periods - set up BDL entries + * @azx_dev: HD-audio core stream to set up + * + * Set up the buffer descriptor table of the given stream based on the + * period and buffer sizes of the assigned PCM substream. + */ +int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) +{ + struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_compr_stream *cstream = azx_dev->cstream; + struct snd_pcm_runtime *runtime = NULL; + struct snd_dma_buffer *dmab; + + if (substream) { + runtime = substream->runtime; + dmab = snd_pcm_get_dma_buf(substream); + } else if (cstream) { + dmab = snd_pcm_get_dma_buf(cstream); + } else { + WARN(1, "No substream or cstream assigned\n"); + return -EINVAL; + } + + return snd_hdac_stream_setup_bdle(azx_dev, dmab, runtime); +} EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods); /** @@ -923,7 +939,6 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, unsigned int byte_size, struct snd_dma_buffer *bufp) { struct hdac_bus *bus = azx_dev->bus; - __le32 *bdl; int err; snd_hdac_dsp_lock(azx_dev); @@ -943,18 +958,14 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, azx_dev->substream = NULL; azx_dev->bufsize = byte_size; - azx_dev->period_bytes = byte_size; + /* It is recommended to transfer the firmware in two or more chunks. */ + azx_dev->period_bytes = byte_size / 2; azx_dev->format_val = format; + azx_dev->no_period_wakeup = 1; snd_hdac_stream_reset(azx_dev); - /* reset BDL address */ - snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); - snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); - - azx_dev->frags = 0; - bdl = (__le32 *)azx_dev->bdl.area; - err = setup_bdle(bus, bufp, azx_dev, &bdl, 0, byte_size, 0); + err = snd_hdac_stream_setup_bdle(azx_dev, bufp, NULL); if (err < 0) goto error; diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index f564ec7af194..5103e37be428 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -108,6 +108,10 @@ static const struct config_entry config_table[] = { {} } }, + { + .flags = FLAG_SST, + .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M, + }, #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) { @@ -539,6 +543,11 @@ static const struct config_entry config_table[] = { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = PCI_DEVICE_ID_INTEL_HDA_PTL, }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = PCI_DEVICE_ID_INTEL_HDA_PTL_H, + }, + #endif }; diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index 59c784a70ac1..e35c727a52fa 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -1735,27 +1735,27 @@ static int snd_es18xx_new_device(struct snd_card *card, chip->active = 0; if (!devm_request_region(card->dev, port, 16, "ES18xx")) { - dev_err(card->dev, "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1); + dev_err(card->dev, "unable to grab ports 0x%lx-0x%lx\n", port, port + 16 - 1); return -EBUSY; } if (devm_request_irq(card->dev, irq, snd_es18xx_interrupt, 0, "ES18xx", (void *) card)) { - dev_err(card->dev, "unable to grap IRQ %d\n", irq); + dev_err(card->dev, "unable to grab IRQ %d\n", irq); return -EBUSY; } chip->irq = irq; card->sync_irq = chip->irq; if (snd_devm_request_dma(card->dev, dma1, "ES18xx DMA 1")) { - dev_err(card->dev, "unable to grap DMA1 %d\n", dma1); + dev_err(card->dev, "unable to grab DMA1 %d\n", dma1); return -EBUSY; } chip->dma1 = dma1; if (dma2 != dma1 && snd_devm_request_dma(card->dev, dma2, "ES18xx DMA 2")) { - dev_err(card->dev, "unable to grap DMA2 %d\n", dma2); + dev_err(card->dev, "unable to grab DMA2 %d\n", dma2); return -EBUSY; } chip->dma2 = dma2; diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index 220ea1952c1e..a07a665d93dc 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -486,7 +486,7 @@ __skip_base: #endif /* CS4231 || OPTi93X */ #ifndef OPTi93X - outb(irq_bits << 3 | dma_bits, chip->wss_base); + outb(irq_bits << 3 | dma_bits, chip->wss_base); #else /* OPTi93X */ snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits)); #endif /* OPTi93X */ diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index 071ba85f76bb..7034072c80d4 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/string_choices.h> #include <sound/core.h> #include <sound/control.h> #include <sound/info.h> @@ -1157,8 +1158,8 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff ((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : "")); } if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) { - snd_iprintf(buffer, "QSound decoder %sabled\n", - p->q_enabled ? "en" : "dis"); + snd_iprintf(buffer, "QSound decoder %s\n", + str_enabled_disabled(p->q_enabled)); } else { snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n", p->acc_format, diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 25f93e56cfc7..6e710dce5c60 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1864,7 +1864,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m strcat(name, " "); strcat(name, pid->name); if (pid->mask != 0xffffffff) - sprintf(name + strlen(name), " rev %d", id & ~pid->mask); + sprintf(name + strlen(name), " rev %u", id & ~pid->mask); if (ac97 && pid->patch) { if ((modem && (pid->flags & AC97_MODEM_PATCH)) || (! modem && ! (pid->flags & AC97_MODEM_PATCH))) diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 5426f7bc9884..518834964b7b 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -161,12 +161,12 @@ static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffe "Mic select : %s\n" "ADC/DAC loopback : %s\n", val & 0x8000 ? "post" : "pre", - val & 0x4000 ? "on" : "off", - val & 0x2000 ? "on" : "off", - val & 0x1000 ? "on" : "off", + str_on_off(val & 0x4000), + str_on_off(val & 0x2000), + str_on_off(val & 0x1000), val & 0x0200 ? "Mic" : "MIX", val & 0x0100 ? "Mic2" : "Mic1", - val & 0x0080 ? "on" : "off"); + str_on_off(val & 0x0080)); if (ac97->ext_id & AC97_EI_DRA) snd_iprintf(buffer, "Double rate slots: %s\n", double_rate_slots[(val >> 10) & 3]); diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 50e30704bf6f..9ed778b6b03c 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -626,7 +626,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe reg = ad1889_readw(chip, AD_DS_WSMC); snd_iprintf(buffer, "Wave output: %s\n", - (reg & AD_DS_WSMC_WAEN) ? "enabled" : "disabled"); + str_enabled_disabled(reg & AD_DS_WSMC_WAEN)); snd_iprintf(buffer, "Wave Channels: %s\n", (reg & AD_DS_WSMC_WAST) ? "stereo" : "mono"); snd_iprintf(buffer, "Wave Quality: %d-bit linear\n", @@ -642,7 +642,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe snd_iprintf(buffer, "Synthesis output: %s\n", - reg & AD_DS_WSMC_SYEN ? "enabled" : "disabled"); + str_enabled_disabled(reg & AD_DS_WSMC_SYEN)); /* SYRQ is at offset 4 */ tmp = (reg & AD_DS_WSMC_SYRQ) ? @@ -654,7 +654,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe reg = ad1889_readw(chip, AD_DS_RAMC); snd_iprintf(buffer, "ADC input: %s\n", - (reg & AD_DS_RAMC_ADEN) ? "enabled" : "disabled"); + str_enabled_disabled(reg & AD_DS_RAMC_ADEN)); snd_iprintf(buffer, "ADC Channels: %s\n", (reg & AD_DS_RAMC_ADST) ? "stereo" : "mono"); snd_iprintf(buffer, "ADC Quality: %d-bit linear\n", @@ -669,7 +669,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe (reg & AD_DS_RAMC_ADST) ? "stereo" : "mono"); snd_iprintf(buffer, "Resampler input: %s\n", - reg & AD_DS_RAMC_REEN ? "enabled" : "disabled"); + str_enabled_disabled(reg & AD_DS_RAMC_REEN)); /* RERQ is at offset 12 */ tmp = (reg & AD_DS_RAMC_RERQ) ? diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index e3cac73517d6..cb8593c376ee 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -3084,7 +3084,7 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci, } } } - sprintf(card->shortname, "C-Media CMI%d", val); + sprintf(card->shortname, "C-Media CMI%u", val); if (cm->chip_version < 68) scnprintf(modelstr, sizeof(modelstr), " (model %d)", cm->chip_version); diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 83aaf9441ef3..806c4d754387 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -159,7 +159,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input) struct daio *daio = &dao->daio; int i; - entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL); + entry = kcalloc(daio->rscl.msr, sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -188,7 +188,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input) struct daio *daio = &dao->daio; int i; - entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL); + entry = kcalloc(daio->rscr.msr, sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -211,52 +211,30 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input) return 0; } -static int dao_clear_left_input(struct dao *dao) +static int dao_clear_input(struct dao *dao, unsigned int start, unsigned int end) { - struct imapper *entry; - struct daio *daio = &dao->daio; - int i; + unsigned int i; - if (!dao->imappers[0]) + if (!dao->imappers[start]) return 0; - - entry = dao->imappers[0]; - dao->mgr->imap_delete(dao->mgr, entry); - /* Program conjugate resources */ - for (i = 1; i < daio->rscl.msr; i++) { - entry = dao->imappers[i]; - dao->mgr->imap_delete(dao->mgr, entry); + for (i = start; i < end; i++) { + dao->mgr->imap_delete(dao->mgr, dao->imappers[i]); dao->imappers[i] = NULL; } - kfree(dao->imappers[0]); - dao->imappers[0] = NULL; - return 0; } -static int dao_clear_right_input(struct dao *dao) -{ - struct imapper *entry; - struct daio *daio = &dao->daio; - int i; - if (!dao->imappers[daio->rscl.msr]) - return 0; - - entry = dao->imappers[daio->rscl.msr]; - dao->mgr->imap_delete(dao->mgr, entry); - /* Program conjugate resources */ - for (i = 1; i < daio->rscr.msr; i++) { - entry = dao->imappers[daio->rscl.msr + i]; - dao->mgr->imap_delete(dao->mgr, entry); - dao->imappers[daio->rscl.msr + i] = NULL; - } - - kfree(dao->imappers[daio->rscl.msr]); - dao->imappers[daio->rscl.msr] = NULL; +static int dao_clear_left_input(struct dao *dao) +{ + return dao_clear_input(dao, 0, dao->daio.rscl.msr); +} - return 0; +static int dao_clear_right_input(struct dao *dao) +{ + return dao_clear_input(dao, dao->daio.rscl.msr, + dao->daio.rscl.msr + dao->daio.rscr.msr); } static const struct dao_rsc_ops dao_ops = { diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 7484de255a3e..4effcc0090ac 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -1966,7 +1966,6 @@ static int __snd_echo_probe(struct pci_dev *pci, struct snd_card *card; struct echoaudio *chip; char *dsp; - __maybe_unused int i; int err; if (dev >= SNDRV_CARDS) @@ -1976,7 +1975,6 @@ static int __snd_echo_probe(struct pci_dev *pci, return -ENOENT; } - i = 0; err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, sizeof(*chip), &card); if (err < 0) @@ -2080,7 +2078,7 @@ static int __snd_echo_probe(struct pci_dev *pci, #ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH /* Creates a list of available digital modes */ chip->num_digital_modes = 0; - for (i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) if (chip->digital_modes & (1 << i)) chip->digital_mode_list[chip->num_digital_modes++] = i; @@ -2092,7 +2090,7 @@ static int __snd_echo_probe(struct pci_dev *pci, #ifdef ECHOCARD_HAS_EXTERNAL_CLOCK /* Creates a list of available clock sources */ chip->num_clock_sources = 0; - for (i = 0; i < 10; i++) + for (int i = 0; i < 10; i++) if (chip->input_clock_types & (1 << i)) chip->clock_source_list[chip->num_clock_sources++] = i; diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 737c28d31b41..bd4734dc04cd 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -11,6 +11,7 @@ #include <linux/slab.h> #include <linux/init.h> +#include <linux/string_choices.h> #include <sound/core.h> #include <sound/emu10k1.h> #include "p16v.h" @@ -32,9 +33,9 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu, snd_iprintf(buffer, "\n%s\n", title); if (status != 0xffffffff) { - snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no"); - snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no"); - snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no"); + snd_iprintf(buffer, "Professional Mode : %s\n", str_yes_no(status & SPCS_PROFESSIONAL)); + snd_iprintf(buffer, "Not Audio Data : %s\n", str_yes_no(status & SPCS_NOTAUDIODATA)); + snd_iprintf(buffer, "Copyright : %s\n", str_yes_no(status & SPCS_COPYRIGHT)); snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]); snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6); snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8); @@ -46,9 +47,9 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu, if (rate_reg > 0) { rate = snd_emu10k1_ptr_read(emu, rate_reg, 0); - snd_iprintf(buffer, "S/PDIF Valid : %s\n", rate & SRCS_SPDIFVALID ? "on" : "off"); - snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off"); - snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off"); + snd_iprintf(buffer, "S/PDIF Valid : %s\n", str_on_off(rate & SRCS_SPDIFVALID)); + snd_iprintf(buffer, "S/PDIF Locked : %s\n", str_on_off(rate & SRCS_SPDIFLOCKED)); + snd_iprintf(buffer, "Rate Locked : %s\n", str_on_off(rate & SRCS_RATELOCKED)); /* From ((Rate * 48000 ) / 262144); */ snd_iprintf(buffer, "Estimated Sample Rate : %d\n", ((rate & 0xFFFFF ) * 375) >> 11); } @@ -208,7 +209,7 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, #if 0 val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0); snd_iprintf(buffer, "\nZoomed Video\n"); - snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off"); + snd_iprintf(buffer, "Rate Locked : %s\n", str_on_off(val & SRCS_RATELOCKED)); snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE); #endif } diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index d29711777161..f6982bc6ff0d 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -441,7 +441,7 @@ snd_emu10k1_synth_free(struct snd_emu10k1 *emu, struct snd_util_memblk *memblk) unmap_memblk(emu, blk); spin_unlock_irqrestore(&emu->memblk_lock, flags); synth_free_pages(emu, blk); - __snd_util_mem_free(hdr, memblk); + __snd_util_mem_free(hdr, memblk); mutex_unlock(&hdr->block_mutex); return 0; } diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 18928b905939..71c89e4e3090 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1850,12 +1850,12 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry, snd_iprintf(buffer, "Ensoniq AudioPCI " CHIP_NAME "\n\n"); snd_iprintf(buffer, "Joystick enable : %s\n", - ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off"); + str_on_off(ensoniq->ctrl & ES_JYSTK_EN)); #ifdef CHIP1370 snd_iprintf(buffer, "MIC +5V bias : %s\n", - ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off"); + str_on_off(ensoniq->ctrl & ES_1370_XCTL1)); snd_iprintf(buffer, "Line In to AOUT : %s\n", - ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off"); + str_on_off(ensoniq->ctrl & ES_1370_XCTL0)); #else snd_iprintf(buffer, "Joystick port : 0x%x\n", (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200); diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 68f1eee9e5c9..fb955a205d50 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -206,8 +206,23 @@ config SND_HDA_SCODEC_TAS2781_I2C comment "Set to Y if you want auto-loading the side codec driver" depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m +config SND_HDA_SCODEC_TAS2781_SPI + tristate "Build TAS2781 HD-audio side codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI + depends on EFI + depends on SND_SOC + select CRC32 + help + Say Y or M here to include TAS2781 SPI HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m + config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" + depends on INPUT select SND_HDA_GENERIC select SND_HDA_GENERIC_LEDS select SND_HDA_SCODEC_COMPONENT @@ -252,6 +267,7 @@ comment "Set to Y if you want auto-loading the codec driver" config SND_HDA_CODEC_HDMI tristate "Build HDMI/DisplayPort HD-audio codec support" select SND_DYNAMIC_MINORS + select SND_PCM_ELD help Say Y or M here to include HDMI and DisplayPort HD-audio codec support in snd-hda-intel driver. This includes all AMD/ATI, diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 80210f845df2..210c406dfbc5 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -40,6 +40,7 @@ snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o snd-hda-cs-dsp-ctls-y := hda_cs_dsp_ctl.o snd-hda-scodec-component-y := hda_component.o snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o +snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o tas2781_spi_fwlib.o # common driver obj-$(CONFIG_SND_HDA) := snd-hda-codec.o @@ -72,6 +73,7 @@ obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o # this must be the last entry after codec drivers; # otherwise the codec patches won't be hooked before the PCI probe diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c index 4ef7878e8fd4..235d22049aa9 100644 --- a/sound/pci/hda/cs35l56_hda.c +++ b/sound/pci/hda/cs35l56_hda.c @@ -546,12 +546,10 @@ static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmw const struct firmware *coeff_firmware, char *coeff_filename) { - if (wmfw_firmware) - release_firmware(wmfw_firmware); + release_firmware(wmfw_firmware); kfree(wmfw_filename); - if (coeff_firmware) - release_firmware(coeff_firmware); + release_firmware(coeff_firmware); kfree(coeff_filename); } diff --git a/sound/pci/hda/cs35l56_hda_spi.c b/sound/pci/hda/cs35l56_hda_spi.c index d4ee5bb7c486..903578466905 100644 --- a/sound/pci/hda/cs35l56_hda_spi.c +++ b/sound/pci/hda/cs35l56_hda_spi.c @@ -22,6 +22,9 @@ static int cs35l56_hda_spi_probe(struct spi_device *spi) return -ENOMEM; cs35l56->base.dev = &spi->dev; + ret = cs35l56_init_config_for_spi(&cs35l56->base, spi); + if (ret) + return ret; #ifdef CS35L56_WAKE_HOLD_TIME_US cs35l56->base.can_hibernate = true; diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 84393f4f429d..8923813ce424 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -80,7 +80,11 @@ static int compare_input_type(const void *ap, const void *bp) /* In case one has boost and the other one has not, pick the one with boost first. */ - return (int)(b->has_boost_on_pin - a->has_boost_on_pin); + if (a->has_boost_on_pin != b->has_boost_on_pin) + return (int)(b->has_boost_on_pin - a->has_boost_on_pin); + + /* Keep the original order */ + return a->order - b->order; } /* Reorder the surround channels @@ -400,6 +404,8 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, reorder_outputs(cfg->speaker_outs, cfg->speaker_pins); /* sort inputs in the order of AUTO_PIN_* type */ + for (i = 0; i < cfg->num_inputs; i++) + cfg->inputs[i].order = i; sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]), compare_input_type, NULL); diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index 579b11beac71..87af3d8c02f7 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -37,6 +37,7 @@ struct auto_pin_cfg_item { unsigned int is_headset_mic:1; unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */ unsigned int has_boost_on_pin:1; + int order; }; struct auto_pin_cfg; diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index e51d47572557..13a7d92e8d8d 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -31,8 +31,9 @@ static void generate_tone(struct hda_beep *beep, int tone) beep->power_hook(beep, true); beep->playing = 1; } - snd_hda_codec_write(codec, beep->nid, 0, - AC_VERB_SET_BEEP_CONTROL, tone); + if (!codec->beep_just_power_on) + snd_hda_codec_write(codec, beep->nid, 0, + AC_VERB_SET_BEEP_CONTROL, tone); if (!tone && beep->playing) { beep->playing = 0; if (beep->power_hook) @@ -212,10 +213,12 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) struct hda_beep *beep; int err; - if (!snd_hda_get_bool_hint(codec, "beep")) - return 0; /* disabled explicitly by hints */ - if (codec->beep_mode == HDA_BEEP_MODE_OFF) - return 0; /* disabled by module option */ + if (!codec->beep_just_power_on) { + if (!snd_hda_get_bool_hint(codec, "beep")) + return 0; /* disabled explicitly by hints */ + if (codec->beep_mode == HDA_BEEP_MODE_OFF) + return 0; /* disabled by module option */ + } beep = kzalloc(sizeof(*beep), GFP_KERNEL); if (beep == NULL) diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index b7ca2a83fbb0..9521e5e0e6e6 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -185,7 +185,7 @@ int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, drv->core.driver.probe = hda_codec_driver_probe; drv->core.driver.remove = hda_codec_driver_remove; drv->core.driver.shutdown = hda_codec_driver_shutdown; - drv->core.driver.pm = &hda_codec_driver_pm; + drv->core.driver.pm = pm_ptr(&hda_codec_driver_pm); drv->core.type = HDA_DEV_LEGACY; drv->core.match = hda_codec_match; drv->core.unsol_event = hda_codec_unsol_event; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 14763c0f31ad..b436d436831b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2470,7 +2470,9 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, break; id = kctl->id; id.index = spdif_index; - snd_ctl_rename_id(codec->card, &kctl->id, &id); + err = snd_ctl_rename_id(codec->card, &kctl->id, &id); + if (err < 0) + return err; } bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI; } @@ -3037,8 +3039,7 @@ const struct dev_pm_ops hda_codec_driver_pm = { .thaw = pm_sleep_ptr(hda_codec_pm_thaw), .poweroff = pm_sleep_ptr(hda_codec_pm_suspend), .restore = pm_sleep_ptr(hda_codec_pm_restore), - .runtime_suspend = pm_ptr(hda_codec_runtime_suspend), - .runtime_resume = pm_ptr(hda_codec_runtime_resume), + RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, NULL) }; /* suspend the codec at shutdown; called from driver's shutdown callback */ diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index 301730432375..d3e87b9c1a4f 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -17,11 +17,6 @@ #include <sound/hda_codec.h> #include "hda_local.h" -enum eld_versions { - ELD_VER_CEA_861D = 2, - ELD_VER_PARTIAL = 31, -}; - enum cea_edid_versions { CEA_EDID_VER_NONE = 0, CEA_EDID_VER_CEA861 = 1, @@ -30,95 +25,12 @@ enum cea_edid_versions { CEA_EDID_VER_RESERVED = 4, }; -static const char * const eld_connection_type_names[4] = { - "HDMI", - "DisplayPort", - "2-reserved", - "3-reserved" -}; - -enum cea_audio_coding_types { - AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0, - AUDIO_CODING_TYPE_LPCM = 1, - AUDIO_CODING_TYPE_AC3 = 2, - AUDIO_CODING_TYPE_MPEG1 = 3, - AUDIO_CODING_TYPE_MP3 = 4, - AUDIO_CODING_TYPE_MPEG2 = 5, - AUDIO_CODING_TYPE_AACLC = 6, - AUDIO_CODING_TYPE_DTS = 7, - AUDIO_CODING_TYPE_ATRAC = 8, - AUDIO_CODING_TYPE_SACD = 9, - AUDIO_CODING_TYPE_EAC3 = 10, - AUDIO_CODING_TYPE_DTS_HD = 11, - AUDIO_CODING_TYPE_MLP = 12, - AUDIO_CODING_TYPE_DST = 13, - AUDIO_CODING_TYPE_WMAPRO = 14, - AUDIO_CODING_TYPE_REF_CXT = 15, - /* also include valid xtypes below */ - AUDIO_CODING_TYPE_HE_AAC = 15, - AUDIO_CODING_TYPE_HE_AAC2 = 16, - AUDIO_CODING_TYPE_MPEG_SURROUND = 17, -}; - -enum cea_audio_coding_xtypes { - AUDIO_CODING_XTYPE_HE_REF_CT = 0, - AUDIO_CODING_XTYPE_HE_AAC = 1, - AUDIO_CODING_XTYPE_HE_AAC2 = 2, - AUDIO_CODING_XTYPE_MPEG_SURROUND = 3, - AUDIO_CODING_XTYPE_FIRST_RESERVED = 4, -}; - -static const char * const cea_audio_coding_type_names[] = { - /* 0 */ "undefined", - /* 1 */ "LPCM", - /* 2 */ "AC-3", - /* 3 */ "MPEG1", - /* 4 */ "MP3", - /* 5 */ "MPEG2", - /* 6 */ "AAC-LC", - /* 7 */ "DTS", - /* 8 */ "ATRAC", - /* 9 */ "DSD (One Bit Audio)", - /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)", - /* 11 */ "DTS-HD", - /* 12 */ "MLP (Dolby TrueHD)", - /* 13 */ "DST", - /* 14 */ "WMAPro", - /* 15 */ "HE-AAC", - /* 16 */ "HE-AACv2", - /* 17 */ "MPEG Surround", -}; - /* * The following two lists are shared between * - HDMI audio InfoFrame (source to sink) * - CEA E-EDID Extension (sink to source) */ -/* - * SS1:SS0 index => sample size - */ -static const int cea_sample_sizes[4] = { - 0, /* 0: Refer to Stream Header */ - AC_SUPPCM_BITS_16, /* 1: 16 bits */ - AC_SUPPCM_BITS_20, /* 2: 20 bits */ - AC_SUPPCM_BITS_24, /* 3: 24 bits */ -}; - -/* - * SF2:SF1:SF0 index => sampling frequency - */ -static const int cea_sampling_frequencies[8] = { - 0, /* 0: Refer to Stream Header */ - SNDRV_PCM_RATE_32000, /* 1: 32000Hz */ - SNDRV_PCM_RATE_44100, /* 2: 44100Hz */ - SNDRV_PCM_RATE_48000, /* 3: 48000Hz */ - SNDRV_PCM_RATE_88200, /* 4: 88200Hz */ - SNDRV_PCM_RATE_96000, /* 5: 96000Hz */ - SNDRV_PCM_RATE_176400, /* 6: 176400Hz */ - SNDRV_PCM_RATE_192000, /* 7: 192000Hz */ -}; - static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid, int byte_index) { @@ -132,159 +44,6 @@ static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid, return val; } -#define GRAB_BITS(buf, byte, lowbit, bits) \ -({ \ - BUILD_BUG_ON(lowbit > 7); \ - BUILD_BUG_ON(bits > 8); \ - BUILD_BUG_ON(bits <= 0); \ - \ - (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \ -}) - -static void hdmi_update_short_audio_desc(struct hda_codec *codec, - struct cea_sad *a, - const unsigned char *buf) -{ - int i; - int val; - - val = GRAB_BITS(buf, 1, 0, 7); - a->rates = 0; - for (i = 0; i < 7; i++) - if (val & (1 << i)) - a->rates |= cea_sampling_frequencies[i + 1]; - - a->channels = GRAB_BITS(buf, 0, 0, 3); - a->channels++; - - a->sample_bits = 0; - a->max_bitrate = 0; - - a->format = GRAB_BITS(buf, 0, 3, 4); - switch (a->format) { - case AUDIO_CODING_TYPE_REF_STREAM_HEADER: - codec_info(codec, "HDMI: audio coding type 0 not expected\n"); - break; - - case AUDIO_CODING_TYPE_LPCM: - val = GRAB_BITS(buf, 2, 0, 3); - for (i = 0; i < 3; i++) - if (val & (1 << i)) - a->sample_bits |= cea_sample_sizes[i + 1]; - break; - - case AUDIO_CODING_TYPE_AC3: - case AUDIO_CODING_TYPE_MPEG1: - case AUDIO_CODING_TYPE_MP3: - case AUDIO_CODING_TYPE_MPEG2: - case AUDIO_CODING_TYPE_AACLC: - case AUDIO_CODING_TYPE_DTS: - case AUDIO_CODING_TYPE_ATRAC: - a->max_bitrate = GRAB_BITS(buf, 2, 0, 8); - a->max_bitrate *= 8000; - break; - - case AUDIO_CODING_TYPE_SACD: - break; - - case AUDIO_CODING_TYPE_EAC3: - break; - - case AUDIO_CODING_TYPE_DTS_HD: - break; - - case AUDIO_CODING_TYPE_MLP: - break; - - case AUDIO_CODING_TYPE_DST: - break; - - case AUDIO_CODING_TYPE_WMAPRO: - a->profile = GRAB_BITS(buf, 2, 0, 3); - break; - - case AUDIO_CODING_TYPE_REF_CXT: - a->format = GRAB_BITS(buf, 2, 3, 5); - if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT || - a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) { - codec_info(codec, - "HDMI: audio coding xtype %d not expected\n", - a->format); - a->format = 0; - } else - a->format += AUDIO_CODING_TYPE_HE_AAC - - AUDIO_CODING_XTYPE_HE_AAC; - break; - } -} - -/* - * Be careful, ELD buf could be totally rubbish! - */ -int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e, - const unsigned char *buf, int size) -{ - int mnl; - int i; - - memset(e, 0, sizeof(*e)); - e->eld_ver = GRAB_BITS(buf, 0, 3, 5); - if (e->eld_ver != ELD_VER_CEA_861D && - e->eld_ver != ELD_VER_PARTIAL) { - codec_info(codec, "HDMI: Unknown ELD version %d\n", e->eld_ver); - goto out_fail; - } - - e->baseline_len = GRAB_BITS(buf, 2, 0, 8); - mnl = GRAB_BITS(buf, 4, 0, 5); - e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3); - - e->support_hdcp = GRAB_BITS(buf, 5, 0, 1); - e->support_ai = GRAB_BITS(buf, 5, 1, 1); - e->conn_type = GRAB_BITS(buf, 5, 2, 2); - e->sad_count = GRAB_BITS(buf, 5, 4, 4); - - e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2; - e->spk_alloc = GRAB_BITS(buf, 7, 0, 7); - - e->port_id = get_unaligned_le64(buf + 8); - - /* not specified, but the spec's tendency is little endian */ - e->manufacture_id = get_unaligned_le16(buf + 16); - e->product_id = get_unaligned_le16(buf + 18); - - if (mnl > ELD_MAX_MNL) { - codec_info(codec, "HDMI: MNL is reserved value %d\n", mnl); - goto out_fail; - } else if (ELD_FIXED_BYTES + mnl > size) { - codec_info(codec, "HDMI: out of range MNL %d\n", mnl); - goto out_fail; - } else - strscpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl + 1); - - for (i = 0; i < e->sad_count; i++) { - if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) { - codec_info(codec, "HDMI: out of range SAD %d\n", i); - goto out_fail; - } - hdmi_update_short_audio_desc(codec, e->sad + i, - buf + ELD_FIXED_BYTES + mnl + 3 * i); - } - - /* - * HDMI sink's ELD info cannot always be retrieved for now, e.g. - * in console or for audio devices. Assume the highest speakers - * configuration, to _not_ prohibit multi-channel audio playback. - */ - if (!e->spk_alloc) - e->spk_alloc = 0xffff; - - return 0; - -out_fail: - return -EINVAL; -} - int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid) { return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE, @@ -346,155 +105,27 @@ error: return ret; } -/* - * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with - * hdmi-specific routine. - */ -static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen) -{ - static const unsigned int alsa_rates[] = { - 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, - 88200, 96000, 176400, 192000, 384000 - }; - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++) - if (pcm & (1 << i)) - j += scnprintf(buf + j, buflen - j, " %d", - alsa_rates[i]); - - buf[j] = '\0'; /* necessary when j == 0 */ -} - -#define SND_PRINT_RATES_ADVISED_BUFSIZE 80 - -static void hdmi_show_short_audio_desc(struct hda_codec *codec, - struct cea_sad *a) -{ - char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; - char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits ="; - - if (!a->format) - return; - - hdmi_print_pcm_rates(a->rates, buf, sizeof(buf)); - - if (a->format == AUDIO_CODING_TYPE_LPCM) - snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2) - 8); - else if (a->max_bitrate) - snprintf(buf2, sizeof(buf2), - ", max bitrate = %d", a->max_bitrate); - else - buf2[0] = '\0'; - - codec_dbg(codec, - "HDMI: supports coding type %s: channels = %d, rates =%s%s\n", - cea_audio_coding_type_names[a->format], - a->channels, buf, buf2); -} - -void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) -{ - int i; - - codec_dbg(codec, "HDMI: detected monitor %s at connection type %s\n", - e->monitor_name, - eld_connection_type_names[e->conn_type]); - - if (e->spk_alloc) { - char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); - codec_dbg(codec, "HDMI: available speakers:%s\n", buf); - } - - for (i = 0; i < e->sad_count; i++) - hdmi_show_short_audio_desc(codec, e->sad + i); -} - #ifdef CONFIG_SND_PROC_FS - -static void hdmi_print_sad_info(int i, struct cea_sad *a, - struct snd_info_buffer *buffer) -{ - char buf[SND_PRINT_RATES_ADVISED_BUFSIZE]; - - snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n", - i, a->format, cea_audio_coding_type_names[a->format]); - snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels); - - hdmi_print_pcm_rates(a->rates, buf, sizeof(buf)); - snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf); - - if (a->format == AUDIO_CODING_TYPE_LPCM) { - snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf)); - snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n", - i, a->sample_bits, buf); - } - - if (a->max_bitrate) - snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n", - i, a->max_bitrate); - - if (a->profile) - snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile); -} - void snd_hdmi_print_eld_info(struct hdmi_eld *eld, struct snd_info_buffer *buffer, hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid) { - struct parsed_hdmi_eld *e = &eld->info; - char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - int i; - static const char * const eld_version_names[32] = { - "reserved", - "reserved", - "CEA-861D or below", - [3 ... 30] = "reserved", - [31] = "partial" - }; - static const char * const cea_edid_version_names[8] = { - "no CEA EDID Timing Extension block present", - "CEA-861", - "CEA-861-A", - "CEA-861-B, C or D", - [4 ... 7] = "reserved" - }; - snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present); snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid); snd_iprintf(buffer, "codec_pin_nid\t\t0x%x\n", pin_nid); snd_iprintf(buffer, "codec_dev_id\t\t0x%x\n", dev_id); snd_iprintf(buffer, "codec_cvt_nid\t\t0x%x\n", cvt_nid); + if (!eld->eld_valid) return; - snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); - snd_iprintf(buffer, "connection_type\t\t%s\n", - eld_connection_type_names[e->conn_type]); - snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver, - eld_version_names[e->eld_ver]); - snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver, - cea_edid_version_names[e->cea_edid_ver]); - snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id); - snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id); - snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id); - snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp); - snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); - snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); - - snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); - snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); - - snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); - - for (i = 0; i < e->sad_count; i++) - hdmi_print_sad_info(i, e->sad + i, buffer); + + snd_print_eld_info(&eld->info, buffer); } void snd_hdmi_write_eld_info(struct hdmi_eld *eld, struct snd_info_buffer *buffer) { - struct parsed_hdmi_eld *e = &eld->info; + struct snd_parsed_hdmi_eld *e = &eld->info; char line[64]; char name[64]; char *sname; @@ -556,7 +187,7 @@ void snd_hdmi_write_eld_info(struct hdmi_eld *eld, #endif /* CONFIG_SND_PROC_FS */ /* update PCM info based on ELD */ -void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, +void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e, struct hda_pcm_stream *hinfo) { u32 rates; @@ -574,17 +205,17 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, maxbps = 16; channels_max = 2; for (i = 0; i < e->sad_count; i++) { - struct cea_sad *a = &e->sad[i]; + struct snd_cea_sad *a = &e->sad[i]; rates |= a->rates; if (a->channels > channels_max) channels_max = a->channels; if (a->format == AUDIO_CODING_TYPE_LPCM) { - if (a->sample_bits & AC_SUPPCM_BITS_20) { + if (a->sample_bits & ELD_PCM_BITS_20) { formats |= SNDRV_PCM_FMTBIT_S32_LE; if (maxbps < 20) maxbps = 20; } - if (a->sample_bits & AC_SUPPCM_BITS_24) { + if (a->sample_bits & ELD_PCM_BITS_24) { formats |= SNDRV_PCM_FMTBIT_S32_LE; if (maxbps < 24) maxbps = 24; diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 727f39acedfc..9325e5c3cbe6 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -84,10 +84,8 @@ static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) { -#ifndef CONFIG_SND_DEBUG_VERBOSE if (!capable(CAP_SYS_RAWIO)) return -EACCES; -#endif return 0; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4a62440adfaf..512fb22f5e5e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -37,6 +37,7 @@ #include <linux/completion.h> #include <linux/acpi.h> #include <linux/pgtable.h> +#include <linux/dmi.h> #ifdef CONFIG_X86 /* for snoop control */ @@ -1050,7 +1051,7 @@ static int azx_suspend(struct device *dev) return 0; } -static int __maybe_unused azx_resume(struct device *dev) +static int azx_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; @@ -1097,7 +1098,7 @@ static int azx_thaw_noirq(struct device *dev) return 0; } -static int __maybe_unused azx_runtime_suspend(struct device *dev) +static int azx_runtime_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; @@ -1114,7 +1115,7 @@ static int __maybe_unused azx_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused azx_runtime_resume(struct device *dev) +static int azx_runtime_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; @@ -1131,7 +1132,7 @@ static int __maybe_unused azx_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused azx_runtime_idle(struct device *dev) +static int azx_runtime_idle(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; @@ -1162,7 +1163,7 @@ static const struct dev_pm_ops azx_pm = { .complete = pm_sleep_ptr(azx_complete), .freeze_noirq = pm_sleep_ptr(azx_freeze_noirq), .thaw_noirq = pm_sleep_ptr(azx_thaw_noirq), - SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle) + RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle) }; @@ -1352,8 +1353,21 @@ static void azx_free(struct azx *chip) if (use_vga_switcheroo(hda)) { if (chip->disabled && hda->probe_continued) snd_hda_unlock_devices(&chip->bus); - if (hda->vga_switcheroo_registered) + if (hda->vga_switcheroo_registered) { vga_switcheroo_unregister_client(chip->pci); + + /* Some GPUs don't have sound, and azx_first_init fails, + * leaving the device probed but non-functional. As long + * as it's probed, the PCI subsystem keeps its runtime + * PM status as active. Force it to suspended (as we + * actually stop the chip) to allow GPU to suspend via + * vga_switcheroo, and print a warning. + */ + dev_warn(&pci->dev, "GPU sound probed, but not operational: please add a quirk to driver_denylist\n"); + pm_runtime_disable(&pci->dev); + pm_runtime_set_suspended(&pci->dev); + pm_runtime_enable(&pci->dev); + } } if (bus->chip_init) { @@ -2061,6 +2075,27 @@ static const struct pci_device_id driver_denylist[] = { {} }; +static struct pci_device_id driver_denylist_ideapad_z570[] = { + { PCI_DEVICE_SUB(0x10de, 0x0bea, 0x0000, 0x0000) }, /* NVIDIA GF108 HDA */ + {} +}; + +/* DMI-based denylist, to be used when: + * - PCI subsystem IDs are zero, impossible to distinguish from valid sound cards. + * - Different modifications of the same laptop use different GPU models. + */ +static const struct dmi_system_id driver_denylist_dmi[] = { + { + /* No HDA in NVIDIA DGPU. BIOS disables it, but quirk_nvidia_hda() reenables. */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"), + }, + .driver_data = &driver_denylist_ideapad_z570, + }, + {} +}; + static const struct hda_controller_ops pci_hda_ops = { .disable_msi_reset_irq = disable_msi_reset_irq, .position_check = azx_position_check, @@ -2071,6 +2106,7 @@ static DECLARE_BITMAP(probed_devs, SNDRV_CARDS); static int azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { + const struct dmi_system_id *dmi; struct snd_card *card; struct hda_intel *hda; struct azx *chip; @@ -2083,6 +2119,12 @@ static int azx_probe(struct pci_dev *pci, return -ENODEV; } + dmi = dmi_first_match(driver_denylist_dmi); + if (dmi && pci_match_id(dmi->driver_data, pci)) { + dev_info(&pci->dev, "Skipping the device on the DMI denylist\n"); + return -ENODEV; + } + dev = find_first_zero_bit(probed_devs, SNDRV_CARDS); if (dev >= SNDRV_CARDS) return -ENODEV; @@ -2232,6 +2274,8 @@ static const struct snd_pci_quirk power_save_denylist[] = { SND_PCI_QUIRK(0x1631, 0xe017, "Packard Bell NEC IMEDIA 5204", 0), /* KONTRON SinglePC may cause a stall at runtime resume */ SND_PCI_QUIRK(0x1734, 0x1232, "KONTRON SinglePC", 0), + /* Dell ALC3271 */ + SND_PCI_QUIRK(0x1028, 0x0962, "Dell ALC3271", 0), {} }; @@ -2496,6 +2540,8 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_ARL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) }, /* Panther Lake */ { PCI_DEVICE_DATA(INTEL, HDA_PTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, + /* Panther Lake-H */ + { PCI_DEVICE_DATA(INTEL, HDA_PTL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) }, /* Apollolake (Broxton-P) */ { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) }, /* Gemini-Lake */ @@ -2738,9 +2784,9 @@ static const struct pci_device_id azx_ids[] = { { PCI_VDEVICE(ZHAOXIN, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN }, /* Loongson HDAudio*/ { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA), - .driver_data = AZX_DRIVER_LOONGSON }, + .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL }, { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI), - .driver_data = AZX_DRIVER_LOONGSON }, + .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL }, { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); @@ -2753,7 +2799,7 @@ static struct pci_driver azx_driver = { .remove = azx_remove, .shutdown = azx_shutdown, .driver = { - .pm = &azx_pm, + .pm = pm_ptr(&azx_pm), }, }; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 763f79f6f32e..4714057dba85 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -10,6 +10,8 @@ #ifndef __SOUND_HDA_LOCAL_H #define __SOUND_HDA_LOCAL_H +#include <sound/pcm_drm_eld.h> + /* We abuse kcontrol_new.subdev field to pass the NID corresponding to * the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG, * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID. @@ -675,61 +677,18 @@ int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, #define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \ snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL) -/* - * CEA Short Audio Descriptor data - */ -struct cea_sad { - int channels; - int format; /* (format == 0) indicates invalid SAD */ - int rates; - int sample_bits; /* for LPCM */ - int max_bitrate; /* for AC3...ATRAC */ - int profile; /* for WMAPRO */ -}; - -#define ELD_FIXED_BYTES 20 -#define ELD_MAX_SIZE 256 -#define ELD_MAX_MNL 16 -#define ELD_MAX_SAD 16 - -/* - * ELD: EDID Like Data - */ -struct parsed_hdmi_eld { - /* - * all fields will be cleared before updating ELD - */ - int baseline_len; - int eld_ver; - int cea_edid_ver; - char monitor_name[ELD_MAX_MNL + 1]; - int manufacture_id; - int product_id; - u64 port_id; - int support_hdcp; - int support_ai; - int conn_type; - int aud_synch_delay; - int spk_alloc; - int sad_count; - struct cea_sad sad[ELD_MAX_SAD]; -}; - struct hdmi_eld { bool monitor_present; bool eld_valid; int eld_size; char eld_buffer[ELD_MAX_SIZE]; - struct parsed_hdmi_eld info; + struct snd_parsed_hdmi_eld info; }; int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid, unsigned char *buf, int *eld_size); -int snd_hdmi_parse_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e, - const unsigned char *buf, int size); -void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e); -void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, +void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e, struct hda_pcm_stream *hinfo); int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index 265fd4737893..140e24bf4d7f 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -648,7 +648,7 @@ static const struct hda_patch_item patch_items[NUM_LINE_MODES] = { }, }; -/* check the line starting with '[' -- change the parser mode accodingly */ +/* check the line starting with '[' -- change the parser mode accordingly */ static int parse_line_mode(char *buf, struct hda_bus *bus) { int i; diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index b1e30a83dfb0..a590d431c5ff 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -125,7 +125,7 @@ static void hda_tegra_init(struct hda_tegra *hda) /* * power management */ -static int __maybe_unused hda_tegra_suspend(struct device *dev) +static int hda_tegra_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); int rc; @@ -138,7 +138,7 @@ static int __maybe_unused hda_tegra_suspend(struct device *dev) return 0; } -static int __maybe_unused hda_tegra_resume(struct device *dev) +static int hda_tegra_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); int rc; @@ -151,7 +151,7 @@ static int __maybe_unused hda_tegra_resume(struct device *dev) return 0; } -static int __maybe_unused hda_tegra_runtime_suspend(struct device *dev) +static int hda_tegra_runtime_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; @@ -170,7 +170,7 @@ static int __maybe_unused hda_tegra_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused hda_tegra_runtime_resume(struct device *dev) +static int hda_tegra_runtime_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; @@ -204,10 +204,8 @@ static int __maybe_unused hda_tegra_runtime_resume(struct device *dev) } static const struct dev_pm_ops hda_tegra_pm = { - SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume) - SET_RUNTIME_PM_OPS(hda_tegra_runtime_suspend, - hda_tegra_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume) + RUNTIME_PM_OPS(hda_tegra_runtime_suspend, hda_tegra_runtime_resume, NULL) }; static int hda_tegra_dev_disconnect(struct snd_device *device) @@ -602,7 +600,7 @@ static void hda_tegra_shutdown(struct platform_device *pdev) static struct platform_driver tegra_platform_hda = { .driver = { .name = "tegra-hda", - .pm = &hda_tegra_pm, + .pm = pm_ptr(&hda_tegra_pm), .of_match_table = hda_tegra_match, }, .probe = hda_tegra_probe, diff --git a/sound/pci/hda/ideapad_hotkey_led_helper.c b/sound/pci/hda/ideapad_hotkey_led_helper.c new file mode 100644 index 000000000000..c10d97964d49 --- /dev/null +++ b/sound/pci/hda/ideapad_hotkey_led_helper.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Ideapad helper functions for Lenovo Ideapad LED control, + * It should be included from codec driver. + */ + +#if IS_ENABLED(CONFIG_IDEAPAD_LAPTOP) + +#include <linux/acpi.h> +#include <linux/leds.h> + +static bool is_ideapad(struct hda_codec *codec) +{ + return (codec->core.subsystem_id >> 16 == 0x17aa) && + (acpi_dev_found("LHK2019") || acpi_dev_found("VPC2004")); +} + +static void hda_fixup_ideapad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + if (!is_ideapad(codec)) + return; + snd_hda_gen_add_mute_led_cdev(codec, NULL); + snd_hda_gen_add_micmute_led_cdev(codec, NULL); + } +} + +#else /* CONFIG_IDEAPAD_LAPTOP */ + +static void hda_fixup_ideapad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ +} + +#endif /* CONFIG_IDEAPAD_LAPTOP */ diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 538c37a78a56..34874039ad45 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -291,6 +291,7 @@ enum { CXT_FIXUP_GPIO1, CXT_FIXUP_ASPIRE_DMIC, CXT_FIXUP_THINKPAD_ACPI, + CXT_FIXUP_LENOVO_XPAD_ACPI, CXT_FIXUP_OLPC_XO, CXT_FIXUP_CAP_MIX_AMP, CXT_FIXUP_TOSHIBA_P105, @@ -313,6 +314,9 @@ enum { /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" +/* for hda_fixup_ideapad_acpi() */ +#include "ideapad_hotkey_led_helper.c" + static void cxt_fixup_stereo_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -928,6 +932,12 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = hda_fixup_thinkpad_acpi, }, + [CXT_FIXUP_LENOVO_XPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = hda_fixup_ideapad_acpi, + .chained = true, + .chain_id = CXT_FIXUP_THINKPAD_ACPI, + }, [CXT_FIXUP_OLPC_XO] = { .type = HDA_FIXUP_FUNC, .v.func = cxt_fixup_olpc_xo, @@ -1080,6 +1090,7 @@ static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE), SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO), + SND_PCI_QUIRK(0x103c, 0x8231, "HP ProBook 450 G4", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE), @@ -1119,7 +1130,7 @@ static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI), + SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad/Ideapad", CXT_FIXUP_LENOVO_XPAD_ACPI), SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004), SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205), HDA_CODEC_QUIRK(0x2782, 0x12c3, "Sirius Gen1", CXT_PINCFG_TOP_SPEAKER), @@ -1133,6 +1144,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = { { .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" }, { .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" }, { .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" }, + { .id = CXT_FIXUP_LENOVO_XPAD_ACPI, .name = "thinkpad-ideapad" }, { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" }, { .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" }, { .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" }, diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c index 759f48038273..09240138e087 100644 --- a/sound/pci/hda/patch_cs8409-tables.c +++ b/sound/pci/hda/patch_cs8409-tables.c @@ -121,7 +121,7 @@ static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = { { CS42L42_MIXER_CHA_VOL, 0x3F }, { CS42L42_MIXER_CHB_VOL, 0x3F }, { CS42L42_MIXER_ADC_VOL, 0x3f }, - { CS42L42_HP_CTL, 0x03 }, + { CS42L42_HP_CTL, 0x0D }, { CS42L42_MIC_DET_CTL1, 0xB6 }, { CS42L42_TIPSENSE_CTL, 0xC2 }, { CS42L42_HS_CLAMP_DISABLE, 0x01 }, @@ -131,7 +131,7 @@ static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = { { CS42L42_RSENSE_CTL3, 0x00 }, { CS42L42_TSENSE_CTL, 0x80 }, { CS42L42_HS_BIAS_CTL, 0xC0 }, - { CS42L42_PWR_CTL1, 0x02 }, + { CS42L42_PWR_CTL1, 0x02, 10000 }, { CS42L42_ADC_OVFL_INT_MASK, 0xff }, { CS42L42_MIXER_INT_MASK, 0xff }, { CS42L42_SRC_INT_MASK, 0xff }, @@ -315,7 +315,7 @@ static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = { { CS42L42_ASP_TX_SZ_EN, 0x01 }, { CS42L42_PWR_CTL1, 0x0A }, { CS42L42_PWR_CTL2, 0x84 }, - { CS42L42_HP_CTL, 0x03 }, + { CS42L42_HP_CTL, 0x0D }, { CS42L42_MIXER_CHA_VOL, 0x3F }, { CS42L42_MIXER_CHB_VOL, 0x3F }, { CS42L42_MIXER_ADC_VOL, 0x3f }, @@ -328,7 +328,7 @@ static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = { { CS42L42_RSENSE_CTL3, 0x00 }, { CS42L42_TSENSE_CTL, 0x80 }, { CS42L42_HS_BIAS_CTL, 0xC0 }, - { CS42L42_PWR_CTL1, 0x02 }, + { CS42L42_PWR_CTL1, 0x02, 10000 }, { CS42L42_ADC_OVFL_INT_MASK, 0xff }, { CS42L42_MIXER_INT_MASK, 0xff }, { CS42L42_SRC_INT_MASK, 0xff }, @@ -371,7 +371,7 @@ static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = { { CS42L42_ASP_TX_SZ_EN, 0x00 }, { CS42L42_PWR_CTL1, 0x0E }, { CS42L42_PWR_CTL2, 0x84 }, - { CS42L42_HP_CTL, 0x01 }, + { CS42L42_HP_CTL, 0x0D }, { CS42L42_MIXER_CHA_VOL, 0x3F }, { CS42L42_MIXER_CHB_VOL, 0x3F }, { CS42L42_MIXER_ADC_VOL, 0x3f }, @@ -384,7 +384,7 @@ static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = { { CS42L42_RSENSE_CTL3, 0x00 }, { CS42L42_TSENSE_CTL, 0x80 }, { CS42L42_HS_BIAS_CTL, 0xC0 }, - { CS42L42_PWR_CTL1, 0x06 }, + { CS42L42_PWR_CTL1, 0x06, 10000 }, { CS42L42_ADC_OVFL_INT_MASK, 0xff }, { CS42L42_MIXER_INT_MASK, 0xff }, { CS42L42_SRC_INT_MASK, 0xff }, diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c index 614327218634..e50006757a2c 100644 --- a/sound/pci/hda/patch_cs8409.c +++ b/sound/pci/hda/patch_cs8409.c @@ -346,6 +346,11 @@ static int cs8409_i2c_bulk_write(struct sub_codec *scodec, const struct cs8409_i if (cs8409_i2c_wait_complete(codec) < 0) goto error; + /* Certain use cases may require a delay + * after a write operation before proceeding. + */ + if (seq[i].delay) + fsleep(seq[i].delay); } mutex_unlock(&spec->i2c_mux); @@ -876,7 +881,7 @@ static void cs42l42_resume(struct sub_codec *cs42l42) { CS42L42_DET_INT_STATUS2, 0x00 }, { CS42L42_TSRS_PLUG_STATUS, 0x00 }, }; - int fsv_old, fsv_new; + unsigned int fsv; /* Bring CS42L42 out of Reset */ spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0); @@ -888,18 +893,19 @@ static void cs42l42_resume(struct sub_codec *cs42l42) /* Initialize CS42L42 companion codec */ cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num); - msleep(CS42L42_INIT_TIMEOUT_MS); /* Clear interrupts, by reading interrupt status registers */ cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs)); - fsv_old = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL); - if (cs42l42->full_scale_vol == CS42L42_FULL_SCALE_VOL_0DB) - fsv_new = fsv_old & ~CS42L42_FULL_SCALE_VOL_MASK; - else - fsv_new = fsv_old & CS42L42_FULL_SCALE_VOL_MASK; - if (fsv_new != fsv_old) - cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv_new); + fsv = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL); + if (cs42l42->full_scale_vol) { + // Set the full scale volume bit + fsv |= CS42L42_FULL_SCALE_VOL_MASK; + cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv); + } + // Unmute analog channels A and B + fsv = (fsv & ~CS42L42_ANA_MUTE_AB); + cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv); /* we have to explicitly allow unsol event handling even during the * resume phase so that the jack event is processed properly @@ -920,7 +926,7 @@ static void cs42l42_suspend(struct sub_codec *cs42l42) { CS42L42_MIXER_CHA_VOL, 0x3F }, { CS42L42_MIXER_ADC_VOL, 0x3F }, { CS42L42_MIXER_CHB_VOL, 0x3F }, - { CS42L42_HP_CTL, 0x0F }, + { CS42L42_HP_CTL, 0x0D }, { CS42L42_ASP_RX_DAI0_EN, 0x00 }, { CS42L42_ASP_CLK_CFG, 0x00 }, { CS42L42_PWR_CTL1, 0xFE }, diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h index 5e48115caf09..e4bd2e12110b 100644 --- a/sound/pci/hda/patch_cs8409.h +++ b/sound/pci/hda/patch_cs8409.h @@ -229,10 +229,10 @@ enum cs8409_coefficient_index_registers { #define CS42L42_I2C_SLEEP_US (2000) #define CS42L42_PDN_TIMEOUT_US (250000) #define CS42L42_PDN_SLEEP_US (2000) -#define CS42L42_INIT_TIMEOUT_MS (45) +#define CS42L42_ANA_MUTE_AB (0x0C) #define CS42L42_FULL_SCALE_VOL_MASK (2) -#define CS42L42_FULL_SCALE_VOL_0DB (1) -#define CS42L42_FULL_SCALE_VOL_MINUS6DB (0) +#define CS42L42_FULL_SCALE_VOL_0DB (0) +#define CS42L42_FULL_SCALE_VOL_MINUS6DB (1) /* Dell BULLSEYE / WARLOCK / CYBORG Specific Definitions */ @@ -290,6 +290,7 @@ enum { struct cs8409_i2c_param { unsigned int addr; unsigned int value; + unsigned int delay; }; struct cs8409_cir_param { diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 643e0496b093..7167989a8d86 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1511,8 +1511,8 @@ static void update_eld(struct hda_codec *codec, if (eld->eld_valid) { if (eld->eld_size <= 0 || - snd_hdmi_parse_eld(codec, &eld->info, eld->eld_buffer, - eld->eld_size) < 0) { + snd_parse_eld(hda_codec_dev(codec), &eld->info, + eld->eld_buffer, eld->eld_size) < 0) { eld->eld_valid = false; if (repoll) { schedule_delayed_work(&per_pin->work, @@ -1555,7 +1555,7 @@ static void update_eld(struct hda_codec *codec, pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); if (eld->eld_valid) - snd_hdmi_show_eld(codec, &eld->info); + snd_show_eld(hda_codec_dev(codec), &eld->info); eld_changed = (pin_eld->eld_valid != eld->eld_valid); eld_changed |= (pin_eld->monitor_present != eld->monitor_present); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ad66378d7321..b4fe681ec3cb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -28,6 +28,7 @@ #include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" +#include "hda_beep.h" #include "hda_jack.h" #include "hda_generic.h" #include "hda_component.h" @@ -586,6 +587,9 @@ static void alc_shutup_pins(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; + if (spec->no_shutup_pins) + return; + switch (codec->core.vendor_id) { case 0x10ec0236: case 0x10ec0256: @@ -601,8 +605,7 @@ static void alc_shutup_pins(struct hda_codec *codec) alc_headset_mic_no_shutup(codec); break; default: - if (!spec->no_shutup_pins) - snd_hda_shutup_pins(codec); + snd_hda_shutup_pins(codec); break; } } @@ -890,9 +893,7 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) } } -/* - */ - +/* inverted digital-mic */ static void alc_fixup_inv_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -3790,6 +3791,7 @@ static void alc225_init(struct hda_codec *codec) AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); msleep(75); + alc_update_coef_idx(codec, 0x4a, 3 << 10, 0); alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ } } @@ -3844,6 +3846,79 @@ static void alc225_shutup(struct hda_codec *codec) } } +static void alc222_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp1_pin_sense, hp2_pin_sense; + + if (!hp_pin) + return; + + msleep(30); + + hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); + hp2_pin_sense = snd_hda_jack_detect(codec, 0x14); + + if (hp1_pin_sense || hp2_pin_sense) { + msleep(2); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + msleep(75); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); + + msleep(75); + } +} + +static void alc222_shutup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = alc_get_hp_pin(spec); + bool hp1_pin_sense, hp2_pin_sense; + + if (!hp_pin) + hp_pin = 0x21; + + hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin); + hp2_pin_sense = snd_hda_jack_detect(codec, 0x14); + + if (hp1_pin_sense || hp2_pin_sense) { + msleep(2); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + msleep(75); + + if (hp1_pin_sense) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (hp2_pin_sense) + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + msleep(75); + } + alc_auto_setup_eapd(codec, false); + alc_shutup_pins(codec); +} + static void alc_default_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -4718,6 +4793,21 @@ static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec, } } +static void alc295_fixup_hp_mute_led_coefbit11(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->mute_led_polarity = 0; + spec->mute_led_coef.idx = 0xb; + spec->mute_led_coef.mask = 3 << 3; + spec->mute_led_coef.on = 1 << 3; + spec->mute_led_coef.off = 1 << 4; + snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set); + } +} + static void alc285_fixup_hp_mute_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -4928,7 +5018,6 @@ static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec, alc298_samsung_v2_init_amps(codec, 4); } -#if IS_REACHABLE(CONFIG_INPUT) static void gpio2_mic_hotkey_event(struct hda_codec *codec, struct hda_jack_callback *event) { @@ -5037,10 +5126,6 @@ static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec, spec->kb_dev = NULL; } } -#else /* INPUT */ -#define alc280_fixup_hp_gpio2_mic_hotkey NULL -#define alc233_fixup_lenovo_line2_mic_hotkey NULL -#endif /* INPUT */ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -5054,6 +5139,16 @@ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec, } } +static void alc233_fixup_lenovo_low_en_micmute_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->micmute_led_polarity = 1; + alc233_fixup_lenovo_line2_mic_hotkey(codec, fix, action); +} + static void alc_hp_mute_disable(struct hda_codec *codec, unsigned int delay) { if (delay <= 0) @@ -5902,7 +5997,7 @@ static void alc_determine_headset_type(struct hda_codec *codec) } codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n", - is_ctia ? "yes" : "no"); + str_yes_no(is_ctia)); spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP; } @@ -6924,6 +7019,30 @@ static void alc285_fixup_hp_envy_x360(struct hda_codec *codec, } } +static void alc285_fixup_hp_beep(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + codec->beep_just_power_on = true; + } else if (action == HDA_FIXUP_ACT_INIT) { +#ifdef CONFIG_SND_HDA_INPUT_BEEP + /* + * Just enable loopback to internal speaker and headphone jack. + * Disable amplification to get about the same beep volume as + * was on pure BIOS setup before loading the driver. + */ + alc_update_coef_idx(codec, 0x36, 0x7070, BIT(13)); + + snd_hda_enable_beep_device(codec, 1); + +#if !IS_ENABLED(CONFIG_INPUT_PCSPKR) + dev_warn_once(hda_codec_dev(codec), + "enable CONFIG_INPUT_PCSPKR to get PC beeps\n"); +#endif +#endif + } +} + /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" @@ -6934,6 +7053,15 @@ static void alc_fixup_thinkpad_acpi(struct hda_codec *codec, hda_fixup_thinkpad_acpi(codec, fix, action); } +/* for hda_fixup_ideapad_acpi() */ +#include "ideapad_hotkey_led_helper.c" + +static void alc_fixup_ideapad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + hda_fixup_ideapad_acpi(codec, fix, action); +} + /* Fixup for Lenovo Legion 15IMHg05 speaker output on headset removal. */ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, const struct hda_fixup *fix, @@ -7128,6 +7256,11 @@ static void tas2781_fixup_i2c(struct hda_codec *cdc, comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1); } +static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2); +} + static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { @@ -7485,6 +7618,16 @@ static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec, spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook; } +/* + * Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly + * at PM resume + */ +static void alc283_fixup_dell_hp_resume(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_INIT) + alc_write_coef_idx(codec, 0xd, 0x2800); +} enum { ALC269_FIXUP_GPIO2, @@ -7555,7 +7698,9 @@ enum { ALC290_FIXUP_MONO_SPEAKERS_HSJACK, ALC290_FIXUP_SUBWOOFER, ALC290_FIXUP_SUBWOOFER_HSJACK, + ALC295_FIXUP_HP_MUTE_LED_COEFBIT11, ALC269_FIXUP_THINKPAD_ACPI, + ALC269_FIXUP_LENOVO_XPAD_ACPI, ALC269_FIXUP_DMIC_THINKPAD_ACPI, ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, ALC269VC_FIXUP_INFINIX_Y4_MAX, @@ -7597,6 +7742,7 @@ enum { ALC275_FIXUP_DELL_XPS, ALC293_FIXUP_LENOVO_SPK_NOISE, ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, + ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED, ALC255_FIXUP_DELL_SPK_NOISE, ALC225_FIXUP_DISABLE_MIC_VREF, ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, @@ -7666,7 +7812,6 @@ enum { ALC285_FIXUP_THINKPAD_X1_GEN7, ALC285_FIXUP_THINKPAD_HEADSET_JACK, ALC294_FIXUP_ASUS_ALLY, - ALC294_FIXUP_ASUS_ALLY_X, ALC294_FIXUP_ASUS_ALLY_PINS, ALC294_FIXUP_ASUS_ALLY_VERBS, ALC294_FIXUP_ASUS_ALLY_SPEAKER, @@ -7683,6 +7828,7 @@ enum { ALC285_FIXUP_HP_GPIO_LED, ALC285_FIXUP_HP_MUTE_LED, ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED, + ALC285_FIXUP_HP_BEEP_MICMUTE_LED, ALC236_FIXUP_HP_MUTE_LED_COEFBIT2, ALC236_FIXUP_HP_GPIO_LED, ALC236_FIXUP_HP_MUTE_LED, @@ -7762,6 +7908,7 @@ enum { ALC236_FIXUP_DELL_DUAL_CODECS, ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, ALC287_FIXUP_TAS2781_I2C, + ALC245_FIXUP_TAS2781_SPI_2, ALC287_FIXUP_YOGA7_14ARB7_I2C, ALC245_FIXUP_HP_MUTE_LED_COEFBIT, ALC245_FIXUP_HP_X360_MUTE_LEDS, @@ -7785,6 +7932,7 @@ enum { ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, ALC233_FIXUP_MEDION_MTL_SPK, ALC294_FIXUP_BASS_SPEAKER_15, + ALC283_FIXUP_DELL_HP_RESUME, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -8327,6 +8475,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_SKU_IGNORE, }, + [ALC269_FIXUP_LENOVO_XPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_ideapad_acpi, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI, + }, [ALC269_FIXUP_DMIC_THINKPAD_ACPI] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic, @@ -8584,6 +8738,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_fixup_lenovo_line2_mic_hotkey, }, + [ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc233_fixup_lenovo_low_en_micmute_led, + }, [ALC233_FIXUP_INTEL_NUC8_DMIC] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic, @@ -9106,12 +9264,6 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS }, - [ALC294_FIXUP_ASUS_ALLY_X] = { - .type = HDA_FIXUP_FUNC, - .v.func = tas2781_fixup_i2c, - .chained = true, - .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS - }, [ALC294_FIXUP_ASUS_ALLY_PINS] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -9271,6 +9423,12 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_hp_spectre_x360_mute_led, }, + [ALC285_FIXUP_HP_BEEP_MICMUTE_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_hp_beep, + .chained = true, + .chain_id = ALC285_FIXUP_HP_MUTE_LED, + }, [ALC236_FIXUP_HP_MUTE_LED_COEFBIT2] = { .type = HDA_FIXUP_FUNC, .v.func = alc236_fixup_hp_mute_led_coefbit2, @@ -9293,6 +9451,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC283_FIXUP_INT_MIC, }, + [ALC295_FIXUP_HP_MUTE_LED_COEFBIT11] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc295_fixup_hp_mute_led_coefbit11, + }, [ALC298_FIXUP_SAMSUNG_AMP] = { .type = HDA_FIXUP_FUNC, .v.func = alc298_fixup_samsung_amp, @@ -9986,6 +10148,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK, }, + [ALC245_FIXUP_TAS2781_SPI_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = tas2781_fixup_spi, + .chained = true, + .chain_id = ALC285_FIXUP_HP_GPIO_LED, + }, [ALC287_FIXUP_YOGA7_14ARB7_I2C] = { .type = HDA_FIXUP_FUNC, .v.func = yoga7_14arb7_fixup_i2c, @@ -10117,6 +10285,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc294_fixup_bass_speaker_15, }, + [ALC283_FIXUP_DELL_HP_RESUME] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc283_fixup_dell_hp_resume, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -10158,6 +10330,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1308, "Acer Aspire Z24-890", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x132a, "Acer TravelMate B114-21", ALC233_FIXUP_ACER_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1330, "Acer TravelMate X514-51T", ALC255_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1360, "Acer Aspire A115", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x141f, "Acer Spin SP513-54N", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), @@ -10176,6 +10349,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0604, "Dell Venue 11 Pro 7130", ALC283_FIXUP_DELL_HP_RESUME), SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK), SND_PCI_QUIRK(0x1028, 0x062c, "Dell Latitude E5550", ALC292_FIXUP_DELL_E7X), @@ -10331,6 +10505,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360), SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), + SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), @@ -10348,7 +10523,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8730, "HP ProBook 445 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8735, "HP ProBook 435 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT), - SND_PCI_QUIRK(0x103c, 0x8760, "HP", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8760, "HP EliteBook 8{4,5}5 G7", ALC285_FIXUP_HP_BEEP_MICMUTE_LED), SND_PCI_QUIRK(0x103c, 0x876e, "HP ENVY x360 Convertible 13-ay0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED), @@ -10378,6 +10553,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8811, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), SND_PCI_QUIRK(0x103c, 0x8812, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), SND_PCI_QUIRK(0x103c, 0x881d, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x881e, "HP Laptop 15s-du3xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8846, "HP EliteBook 850 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8847, "HP EliteBook x360 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x884b, "HP EliteBook 840 Aero G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED), @@ -10388,6 +10564,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED), @@ -10546,14 +10723,55 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8d01, "HP ZBook Power 14 G12", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d85, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d86, "HP Elite X360 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d8c, "HP EliteBook 13 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d8d, "HP Elite X360 13 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d8e, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d8f, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d90, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8d91, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8da0, "HP 16 Clipper OmniBook 7(X360)", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8da1, "HP 16 Clipper OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8da7, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8da8, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8de9, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2), + SND_PCI_QUIRK(0x103c, 0x8dec, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dee, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8df0, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e14, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e15, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e16, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e17, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8e18, "HP ZBook Firefly 14 G12A", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8e19, "HP ZBook Firefly 14 G12A", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8e1a, "HP ZBook Firefly 14 G12A", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e1b, "HP EliteBook G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e1c, "HP EliteBook G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e2c, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e36, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), + SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x106f, "ASUS VivoBook X515UA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1074, "ASUS G614PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), @@ -10561,21 +10779,25 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x10d3, "ASUS K6500ZC", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x1154, "ASUS TP3607SH", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x1194, "ASUS UM3406KA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1294, "ASUS B3405CVA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x12a3, "Asus N7691ZM", ALC269_FIXUP_ASUS_N7601ZM), SND_PCI_QUIRK(0x1043, 0x12af, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x12b4, "ASUS B3405CCA / P3405CCA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), SND_PCI_QUIRK(0x1043, 0x1433, "ASUS GX650PY/PZ/PV/PU/PYV/PZV/PIV/PVV", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x1460, "Asus VivoBook 15", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1463, "Asus GA402X/GA402N", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604VI/VC/VE/VG/VJ/VQ/VU/VV/VY/VZ", ALC285_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1483, "ASUS GU603VQ/VU/VV/VJ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC), @@ -10597,7 +10819,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1740, "ASUS UX430UA", ALC295_FIXUP_ASUS_DACS), SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK), SND_PCI_QUIRK(0x1043, 0x17f3, "ROG Ally NR2301L/X", ALC294_FIXUP_ASUS_ALLY), - SND_PCI_QUIRK(0x1043, 0x1eb3, "ROG Ally X RC72LA", ALC294_FIXUP_ASUS_ALLY_X), SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS), SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC), @@ -10609,7 +10830,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), - SND_PCI_QUIRK(0x1043, 0x1a30, "ASUS X705UD", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), @@ -10635,6 +10855,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15), + SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), @@ -10652,14 +10873,28 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1f1f, "ASUS H7604JI/JV/J3D", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1f62, "ASUS UX7602ZM", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1f63, "ASUS P5405CSA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1f92, "ASUS ROG Flow X16", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1fb3, "ASUS ROG Flow Z13 GZ302EA", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3011, "ASUS B5605CVA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2), + SND_PCI_QUIRK(0x1043, 0x3061, "ASUS B3405CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3071, "ASUS B5405CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x30c1, "ASUS B3605CCA / P3605CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x30d1, "ASUS B5405CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x30e1, "ASUS B5605CCA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x31d0, "ASUS Zen AIO 27 Z272SD_A272SD", ALC274_FIXUP_ASUS_ZEN_AIO_27), + SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), SND_PCI_QUIRK(0x1043, 0x3a50, "ASUS G834JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), SND_PCI_QUIRK(0x1043, 0x3a60, "ASUS G634JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS), + SND_PCI_QUIRK(0x1043, 0x3d78, "ASUS GA603KH", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3d88, "ASUS GA603KM", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e00, "ASUS G814FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x3e20, "ASUS G814PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x3e30, "ASUS TP3607SA", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), @@ -10667,6 +10902,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), @@ -10865,6 +11102,9 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340), SND_PCI_QUIRK(0x17aa, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x17aa, 0x3384, "ThinkCentre M90a PRO", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), + SND_PCI_QUIRK(0x17aa, 0x3386, "ThinkCentre M90a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), + SND_PCI_QUIRK(0x17aa, 0x3387, "ThinkCentre M70a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED), SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C), @@ -10888,7 +11128,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), - HDA_CODEC_QUIRK(0x17aa, 0x386f, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), + HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), @@ -10963,6 +11203,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), SND_PCI_QUIRK(0x17aa, 0x9e56, "Lenovo ZhaoYang CF4620Z", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1849, 0x0269, "Positivo Master C6400", ALC269VB_FIXUP_ASUS_ZENBOOK), SND_PCI_QUIRK(0x1849, 0x1233, "ASRock NUC Box 1100", ALC233_FIXUP_NO_AUDIO_JACK), SND_PCI_QUIRK(0x1849, 0xa233, "Positivo Master C6300", ALC269_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1854, 0x0440, "LG CQ6", ALC256_FIXUP_HEADPHONE_AMP_VOL), @@ -10998,6 +11239,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), @@ -11069,7 +11311,7 @@ static const struct hda_quirk alc269_fixup_vendor_tbl[] = { SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), - SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI), + SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI), SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED), {} }; @@ -11134,6 +11376,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, .name = "mono-speakers"}, {.id = ALC290_FIXUP_SUBWOOFER_HSJACK, .name = "alc290-subwoofer"}, {.id = ALC269_FIXUP_THINKPAD_ACPI, .name = "thinkpad"}, + {.id = ALC269_FIXUP_LENOVO_XPAD_ACPI, .name = "lenovo-xpad-led"}, {.id = ALC269_FIXUP_DMIC_THINKPAD_ACPI, .name = "dmic-thinkpad"}, {.id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, .name = "alc255-acer"}, {.id = ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc255-asus"}, @@ -11850,8 +12093,11 @@ static int patch_alc269(struct hda_codec *codec) spec->codec_variant = ALC269_TYPE_ALC300; spec->gen.mixer_nid = 0; /* no loopback on ALC300 */ break; + case 0x10ec0222: case 0x10ec0623: spec->codec_variant = ALC269_TYPE_ALC623; + spec->shutup = alc222_shutup; + spec->init_hook = alc222_init; break; case 0x10ec0700: case 0x10ec0701: diff --git a/sound/pci/hda/tas2781-spi.h b/sound/pci/hda/tas2781-spi.h new file mode 100644 index 000000000000..7a0faceeb675 --- /dev/null +++ b/sound/pci/hda/tas2781-spi.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// +// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier +// +// Copyright (C) 2024 Texas Instruments Incorporated +// https://www.ti.com +// +// The TAS2781 driver implements a flexible and configurable +// algo coefficient setting for TAS2781 chips. +// +// Author: Baojun Xu <baojun.xu@ti.com> +// + +#ifndef __TAS2781_SPI_H__ +#define __TAS2781_SPI_H__ + +#define TASDEVICE_RATES \ + (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200) + +#define TASDEVICE_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define TASDEVICE_MAX_BOOK_NUM 256 +#define TASDEVICE_MAX_PAGE 256 + +#define TASDEVICE_MAX_SIZE (TASDEVICE_MAX_BOOK_NUM * TASDEVICE_MAX_PAGE) + +/* PAGE Control Register (available in page0 of each book) */ +#define TASDEVICE_PAGE_SELECT 0x00 +#define TASDEVICE_BOOKCTL_PAGE 0x00 +#define TASDEVICE_BOOKCTL_REG GENMASK(7, 1) +#define TASDEVICE_BOOK_ID(reg) (((reg) & GENMASK(24, 16)) >> 16) +#define TASDEVICE_PAGE_ID(reg) (((reg) & GENMASK(15, 8)) >> 8) +#define TASDEVICE_REG_ID(reg) (((reg) & GENMASK(7, 1)) >> 1) +#define TASDEVICE_PAGE_REG(reg) ((reg) & GENMASK(15, 1)) +#define TASDEVICE_REG(book, page, reg) \ + (((book) << 16) | ((page) << 8) | ((reg) << 1)) + +/* Software Reset */ +#define TAS2781_REG_SWRESET TASDEVICE_REG(0x0, 0x0, 0x01) +#define TAS2781_REG_SWRESET_RESET BIT(0) + +/* System Reset Check Register */ +#define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c) +#define TAS2781_REG_CLK_CONFIG_RESET (0x19) +#define TAS2781_PRE_POST_RESET_CFG 3 + +/* Block Checksum */ +#define TASDEVICE_CHECKSUM TASDEVICE_REG(0x0, 0x0, 0x7e) + +/* Volume control */ +#define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1a) +#define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03) +#define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1) + +#define TASDEVICE_CMD_SING_W 0x1 +#define TASDEVICE_CMD_BURST 0x2 +#define TASDEVICE_CMD_DELAY 0x3 +#define TASDEVICE_CMD_FIELD_W 0x4 + +#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ) + +#define TASDEVICE_CRC8_POLYNOMIAL 0x4d +#define TASDEVICE_SPEAKER_CALIBRATION_SIZE 20 + +/* Flag of calibration registers address. */ +#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7) + +#define TASDEVICE_CALIBRATION_DATA_NAME L"CALI_DATA" +#define TASDEVICE_CALIBRATION_DATA_SIZE 256 + +enum calib_data { + R0_VAL = 0, + INV_R0, + R0LOW, + POWER, + TLIM, + CALIB_MAX +}; + +struct tasdevice_priv { + struct tasdevice_fw *cali_data_fmw; + struct tasdevice_rca rcabin; + struct tasdevice_fw *fmw; + struct gpio_desc *reset; + struct mutex codec_lock; + struct regmap *regmap; + struct device *dev; + + unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE]; + unsigned char coef_binaryname[64]; + unsigned char rca_binaryname[64]; + unsigned char dev_name[32]; + + bool force_fwload_status; + bool playback_started; + bool is_loading; + bool is_loaderr; + unsigned int cali_reg_array[CALIB_MAX]; + unsigned int cali_data[CALIB_MAX]; + unsigned int err_code; + void *codec; + int cur_book; + int cur_prog; + int cur_conf; + int fw_state; + int index; + int irq; + + int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv, + const struct firmware *fmw, + int offset); + int (*fw_parse_program_data)(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset); + int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, + int offset); + int (*tasdevice_load_block)(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block); + + int (*save_calibration)(struct tasdevice_priv *tas_priv); + void (*apply_calibration)(struct tasdevice_priv *tas_priv); +}; + +int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv, + unsigned int reg, unsigned int *value); +int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv, + unsigned int reg, unsigned int value); +int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv, + unsigned int reg, unsigned char *p_data, + unsigned int n_length); +int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv, + unsigned int reg, unsigned char *p_data, + unsigned int n_length); +int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tasdevice, + unsigned int reg, unsigned int mask, + unsigned int value); + +void tasdevice_spi_select_cfg_blk(void *context, int conf_no, + unsigned char block_type); +void tasdevice_spi_config_info_remove(void *context); +int tasdevice_spi_dsp_parser(void *context); +int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw); +void tasdevice_spi_dsp_remove(void *context); +void tasdevice_spi_calbin_remove(void *context); +int tasdevice_spi_select_tuningprm_cfg(void *context, int prm, int cfg_no, + int rca_conf_no); +int tasdevice_spi_prmg_load(void *context, int prm_no); +int tasdevice_spi_prmg_calibdata_load(void *context, int prm_no); +void tasdevice_spi_tuning_switch(void *context, int state); +int tas2781_spi_load_calibration(void *context, char *file_name, + unsigned short i); +#endif /* __TAS2781_SPI_H__ */ diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 0e42b87dadb8..9ed49b0dbe6b 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -45,7 +45,7 @@ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ SNDRV_CTL_ELEM_ACCESS_READWRITE,\ .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw_range, \ + .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = (unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .rreg = xreg, .shift = xshift, \ @@ -594,7 +594,6 @@ static int tas2781_save_calibration(struct tasdevice_priv *tas_priv) efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3); static efi_char16_t efi_name[] = L"CALI_DATA"; - struct tm *tm = &tas_priv->tm; unsigned int attr, crc; unsigned int *tmp_val; efi_status_t status; @@ -629,10 +628,9 @@ static int tas2781_save_calibration(struct tasdevice_priv *tas_priv) crc, tmp_val[21]); if (crc == tmp_val[21]) { - time64_to_tm(tmp_val[20], 0, tm); - dev_dbg(tas_priv->dev, "%4ld-%2d-%2d, %2d:%2d:%2d\n", - tm->tm_year, tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); + time64_t seconds = tmp_val[20]; + + dev_dbg(tas_priv->dev, "%ptTsr\n", &seconds); tasdevice_apply_calibration(tas_priv); } else tas_priv->cali_data.total_sz = 0; @@ -755,8 +753,7 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) out: mutex_unlock(&tas_hda->priv->codec_lock); - if (fmw) - release_firmware(fmw); + release_firmware(fmw); pm_runtime_mark_last_busy(tas_hda->dev); pm_runtime_put_autosuspend(tas_hda->dev); } diff --git a/sound/pci/hda/tas2781_hda_spi.c b/sound/pci/hda/tas2781_hda_spi.c new file mode 100644 index 000000000000..399f2e4c3b62 --- /dev/null +++ b/sound/pci/hda/tas2781_hda_spi.c @@ -0,0 +1,1263 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// TAS2781 HDA SPI driver +// +// Copyright 2024 Texas Instruments, Inc. +// +// Author: Baojun Xu <baojun.xu@ti.com> + +#include <linux/acpi.h> +#include <linux/array_size.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/crc8.h> +#include <linux/crc32.h> +#include <linux/efi.h> +#include <linux/firmware.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <sound/hda_codec.h> +#include <sound/soc.h> +#include <sound/tas2781-dsp.h> +#include <sound/tlv.h> +#include <sound/tas2781-tlv.h> + +#include "tas2781-spi.h" + +#include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_component.h" +#include "hda_jack.h" +#include "hda_generic.h" + +/* + * No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD + * Define two controls, one is Volume control callbacks, the other is + * flag setting control callbacks. + */ + +/* Volume control callbacks for tas2781 */ +#define ACARD_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \ + xhandler_get, xhandler_put, tlv_array) { \ + .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) { \ + .reg = xreg, .rreg = xreg, \ + .shift = xshift, .rshift = xshift,\ + .min = xmin, .max = xmax, .invert = xinvert, \ + } \ +} + +/* Flag control callbacks for tas2781 */ +#define ACARD_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) { \ + .iface = SNDRV_CTL_ELEM_IFACE_CARD, \ + .name = xname, \ + .info = snd_ctl_boolean_mono_info, \ + .get = xhandler_get, \ + .put = xhandler_put, \ + .private_value = xdata, \ +} + +struct tas2781_hda { + struct tasdevice_priv *priv; + struct acpi_device *dacpi; + struct snd_kcontrol *dsp_prog_ctl; + struct snd_kcontrol *dsp_conf_ctl; + struct snd_kcontrol *snd_ctls[3]; + struct snd_kcontrol *prof_ctl; +}; + +static const struct regmap_range_cfg tasdevice_ranges[] = { + { + .range_min = 0, + .range_max = TASDEVICE_MAX_SIZE, + .selector_reg = TASDEVICE_PAGE_SELECT, + .selector_mask = GENMASK(7, 0), + .selector_shift = 0, + .window_start = 0, + .window_len = TASDEVICE_MAX_PAGE, + }, +}; + +static const struct regmap_config tasdevice_regmap = { + .reg_bits = 8, + .val_bits = 8, + .zero_flag_mask = true, + .cache_type = REGCACHE_NONE, + .ranges = tasdevice_ranges, + .num_ranges = ARRAY_SIZE(tasdevice_ranges), + .max_register = TASDEVICE_MAX_SIZE, +}; + +static int tasdevice_spi_switch_book(struct tasdevice_priv *tas_priv, int reg) +{ + struct regmap *map = tas_priv->regmap; + + if (tas_priv->cur_book != TASDEVICE_BOOK_ID(reg)) { + int ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) { + dev_err(tas_priv->dev, "Switch Book E=%d\n", ret); + return ret; + } + tas_priv->cur_book = TASDEVICE_BOOK_ID(reg); + } + return 0; +} + +int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv, + unsigned int reg, + unsigned int *val) +{ + struct regmap *map = tas_priv->regmap; + int ret; + + ret = tasdevice_spi_switch_book(tas_priv, reg); + if (ret < 0) + return ret; + + /* + * In our TAS2781 SPI mode, if read from other book (not book 0), + * or read from page number larger than 1 in book 0, one more byte + * read is needed, and first byte is a dummy byte, need to be ignored. + */ + if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) { + unsigned char data[2]; + + ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1, + data, sizeof(data)); + *val = data[1]; + } else { + ret = regmap_read(map, TASDEVICE_PAGE_REG(reg) | 1, val); + } + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + + return ret; +} + +int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv, + unsigned int reg, + unsigned int value) +{ + struct regmap *map = tas_priv->regmap; + int ret; + + ret = tasdevice_spi_switch_book(tas_priv, reg); + if (ret < 0) + return ret; + + ret = regmap_write(map, TASDEVICE_PAGE_REG(reg), value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + + return ret; +} + +int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv, + unsigned int reg, + unsigned char *data, + unsigned int len) +{ + struct regmap *map = tas_priv->regmap; + int ret; + + ret = tasdevice_spi_switch_book(tas_priv, reg); + if (ret < 0) + return ret; + + ret = regmap_bulk_write(map, TASDEVICE_PAGE_REG(reg), data, len); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + + return ret; +} + +int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv, + unsigned int reg, + unsigned char *data, + unsigned int len) +{ + struct regmap *map = tas_priv->regmap; + int ret; + + ret = tasdevice_spi_switch_book(tas_priv, reg); + if (ret < 0) + return ret; + + if (len > TASDEVICE_MAX_PAGE) + len = TASDEVICE_MAX_PAGE; + /* + * In our TAS2781 SPI mode, if read from other book (not book 0), + * or read from page number larger than 1 in book 0, one more byte + * read is needed, and first byte is a dummy byte, need to be ignored. + */ + if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) { + unsigned char buf[TASDEVICE_MAX_PAGE+1]; + + ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1, buf, + len + 1); + memcpy(data, buf + 1, len); + } else { + ret = regmap_bulk_read(map, TASDEVICE_PAGE_REG(reg) | 1, data, + len); + } + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + + return ret; +} + +int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv, + unsigned int reg, + unsigned int mask, + unsigned int value) +{ + struct regmap *map = tas_priv->regmap; + int ret, val; + + /* + * In our TAS2781 SPI mode, read/write was masked in last bit of + * address, it cause regmap_update_bits() not work as expected. + */ + ret = tasdevice_spi_dev_read(tas_priv, reg, &val); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + return ret; + } + ret = regmap_write(map, TASDEVICE_PAGE_REG(reg), + (val & ~mask) | (mask & value)); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + + return ret; +} + +static void tas2781_spi_reset(struct tasdevice_priv *tas_dev) +{ + int ret; + + if (tas_dev->reset) { + gpiod_set_value_cansleep(tas_dev->reset, 0); + fsleep(800); + gpiod_set_value_cansleep(tas_dev->reset, 1); + } + ret = tasdevice_spi_dev_write(tas_dev, TAS2781_REG_SWRESET, + TAS2781_REG_SWRESET_RESET); + if (ret < 0) + dev_err(tas_dev->dev, "dev sw-reset fail, %d\n", ret); + fsleep(1000); +} + +static int tascodec_spi_init(struct tasdevice_priv *tas_priv, + void *codec, struct module *module, + void (*cont)(const struct firmware *fw, void *context)) +{ + int ret; + + /* + * Codec Lock Hold to ensure that codec_probe and firmware parsing and + * loading do not simultaneously execute. + */ + guard(mutex)(&tas_priv->codec_lock); + + scnprintf(tas_priv->rca_binaryname, + sizeof(tas_priv->rca_binaryname), "%sRCA%d.bin", + tas_priv->dev_name, tas_priv->index); + crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); + tas_priv->codec = codec; + ret = request_firmware_nowait(module, FW_ACTION_UEVENT, + tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv, + cont); + if (ret) + dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", + ret); + + return ret; +} + +static void tasdevice_spi_init(struct tasdevice_priv *tas_priv) +{ + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + + tas_priv->cur_book = -1; + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + + /* Store default registers address for calibration data. */ + tas_priv->cali_reg_array[0] = TASDEVICE_REG(0, 0x17, 0x74); + tas_priv->cali_reg_array[1] = TASDEVICE_REG(0, 0x18, 0x0c); + tas_priv->cali_reg_array[2] = TASDEVICE_REG(0, 0x18, 0x14); + tas_priv->cali_reg_array[3] = TASDEVICE_REG(0, 0x13, 0x70); + tas_priv->cali_reg_array[4] = TASDEVICE_REG(0, 0x18, 0x7c); + + mutex_init(&tas_priv->codec_lock); +} + +static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, + struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask; + int max = mc->max; + int val, ret; + + mask = rounddown_pow_of_two(max); + mask <<= mc->shift; + val = clamp(invert ? max - ucontrol->value.integer.value[0] : + ucontrol->value.integer.value[0], 0, max); + ret = tasdevice_spi_dev_update_bits(tas_priv, + mc->reg, mask, (unsigned int)(val << mc->shift)); + if (ret) + dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", + tas_priv->index); + + return ret; +} + +static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, + struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask = 0; + int max = mc->max; + int ret, val; + + /* Read the primary device */ + ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__); + return ret; + } + + mask = rounddown_pow_of_two(max); + mask <<= mc->shift; + val = (val & mask) >> mc->shift; + val = clamp(invert ? max - val : val, 0, max); + ucontrol->value.integer.value[0] = val; + + return ret; +} + +static int tasdevice_spi_digital_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, + struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int val, ret; + + val = clamp(invert ? max - ucontrol->value.integer.value[0] : + ucontrol->value.integer.value[0], 0, max); + ret = tasdevice_spi_dev_write(tas_priv, mc->reg, (unsigned int)val); + if (ret) + dev_err(tas_priv->dev, "set digital vol err in dev %d\n", + tas_priv->index); + + return ret; +} + +static int tasdevice_spi_digital_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, + struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int ret, val; + + /* Read the primary device as the whole */ + ret = tasdevice_spi_dev_read(tas_priv, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get digital vol err\n", __func__); + return ret; + } + + val = clamp(invert ? max - val : val, 0, max); + ucontrol->value.integer.value[0] = val; + + return ret; +} + +static int tas2781_read_acpi(struct tas2781_hda *tas_hda, + const char *hid, + int id) +{ + struct tasdevice_priv *p = tas_hda->priv; + struct acpi_device *adev; + struct device *physdev; + u32 values[HDA_MAX_COMPONENTS]; + const char *property; + size_t nval; + int ret, i; + + adev = acpi_dev_get_first_match_dev(hid, NULL, -1); + if (!adev) { + dev_err(p->dev, "Failed to find ACPI device: %s\n", hid); + return -ENODEV; + } + + strscpy(p->dev_name, hid, sizeof(p->dev_name)); + tas_hda->dacpi = adev; + physdev = get_device(acpi_get_first_physical_node(adev)); + acpi_dev_put(adev); + + property = "ti,dev-index"; + ret = device_property_count_u32(physdev, property); + if (ret <= 0 || ret > ARRAY_SIZE(values)) { + ret = -EINVAL; + goto err; + } + nval = ret; + + ret = device_property_read_u32_array(physdev, property, values, nval); + if (ret) + goto err; + + p->index = U8_MAX; + for (i = 0; i < nval; i++) { + if (values[i] == id) { + p->index = i; + break; + } + } + if (p->index == U8_MAX) { + dev_dbg(p->dev, "No index found in %s\n", property); + ret = -ENODEV; + goto err; + } + + if (p->index == 0) { + /* All of amps share same RESET pin. */ + p->reset = devm_gpiod_get_index_optional(physdev, "reset", + p->index, GPIOD_OUT_LOW); + if (IS_ERR(p->reset)) { + ret = PTR_ERR(p->reset); + dev_err_probe(p->dev, ret, "Failed on reset GPIO\n"); + goto err; + } + } + put_device(physdev); + + return 0; +err: + dev_err(p->dev, "read acpi error, ret: %d\n", ret); + put_device(physdev); + acpi_dev_put(adev); + + return ret; +} + +static void tas2781_hda_playback_hook(struct device *dev, int action) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + + if (action == HDA_GEN_PCM_ACT_OPEN) { + pm_runtime_get_sync(dev); + guard(mutex)(&tas_hda->priv->codec_lock); + tasdevice_spi_tuning_switch(tas_hda->priv, 0); + } else if (action == HDA_GEN_PCM_ACT_CLOSE) { + guard(mutex)(&tas_hda->priv->codec_lock); + tasdevice_spi_tuning_switch(tas_hda->priv, 1); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } +} + +static int tasdevice_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1; + + return 0; +} + +static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id; + + return 0; +} + +static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + int max = tas_priv->rcabin.ncfgs - 1; + int val; + + val = clamp(ucontrol->value.integer.value[0], 0, max); + if (tas_priv->rcabin.profile_cfg_id != val) { + tas_priv->rcabin.profile_cfg_id = val; + return 1; + } + + return 0; +} + +static int tasdevice_info_programs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->fmw->nr_programs - 1; + + return 0; +} + +static int tasdevice_info_config(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->fmw->nr_configurations - 1; + + return 0; +} + +static int tasdevice_program_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas_priv->cur_prog; + + return 0; +} + +static int tasdevice_program_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + int nr_program = ucontrol->value.integer.value[0]; + int max = tas_priv->fmw->nr_programs - 1; + int val; + + val = clamp(nr_program, 0, max); + + if (tas_priv->cur_prog != val) { + tas_priv->cur_prog = val; + return 1; + } + + return 0; +} + +static int tasdevice_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas_priv->cur_conf; + + return 0; +} + +static int tasdevice_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + int max = tas_priv->fmw->nr_configurations - 1; + int val; + + val = clamp(ucontrol->value.integer.value[0], 0, max); + + if (tas_priv->cur_conf != val) { + tas_priv->cur_conf = val; + return 1; + } + + return 0; +} + +/* + * tas2781_digital_getvol - get the volum control + * @kcontrol: control pointer + * @ucontrol: User data + * + * Customer Kcontrol for tas2781 is primarily for regmap booking, paging + * depends on internal regmap mechanism. + * tas2781 contains book and page two-level register map, especially + * book switching will set the register BXXP00R7F, after switching to the + * correct book, then leverage the mechanism for paging to access the + * register. + * + * Return 0 if succeeded. + */ +static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_spi_digital_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_spi_amp_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + /* The check of the given value is in tasdevice_digital_putvol. */ + return tasdevice_spi_digital_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + /* The check of the given value is in tasdevice_amp_putvol. */ + return tasdevice_spi_amp_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status; + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + str_on_off(tas_priv->force_fwload_status)); + + return 0; +} + +static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol); + bool change, val = (bool)ucontrol->value.integer.value[0]; + + if (tas_priv->force_fwload_status == val) { + change = false; + } else { + change = true; + tas_priv->force_fwload_status = val; + } + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + str_on_off(tas_priv->force_fwload_status)); + + return change; +} + +static const struct snd_kcontrol_new tas2781_snd_controls[] = { + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain 0", TAS2781_AMP_LEVEL, + 1, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, amp_vol_tlv), + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain 0", TAS2781_DVC_LVL, + 0, 0, 200, 1, tas2781_digital_getvol, + tas2781_digital_putvol, dvc_tlv), + ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load 0", 0, + tas2781_force_fwload_get, tas2781_force_fwload_put), + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain 1", TAS2781_AMP_LEVEL, + 1, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, amp_vol_tlv), + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain 1", TAS2781_DVC_LVL, + 0, 0, 200, 1, tas2781_digital_getvol, + tas2781_digital_putvol, dvc_tlv), + ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load 1", 0, + tas2781_force_fwload_get, tas2781_force_fwload_put), +}; + +static const struct snd_kcontrol_new tas2781_prof_ctrl[] = { +{ + .name = "Speaker Profile Id - 0", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_profile, + .get = tasdevice_get_profile_id, + .put = tasdevice_set_profile_id, +}, +{ + .name = "Speaker Profile Id - 1", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_profile, + .get = tasdevice_get_profile_id, + .put = tasdevice_set_profile_id, +}, +}; +static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl[] = { +{ + .name = "Speaker Program Id 0", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_programs, + .get = tasdevice_program_get, + .put = tasdevice_program_put, +}, +{ + .name = "Speaker Program Id 1", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_programs, + .get = tasdevice_program_get, + .put = tasdevice_program_put, +}, +}; + +static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl[] = { +{ + .name = "Speaker Config Id 0", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_config, + .get = tasdevice_config_get, + .put = tasdevice_config_put, +}, +{ + .name = "Speaker Config Id 1", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = tasdevice_info_config, + .get = tasdevice_config_get, + .put = tasdevice_config_put, +}, +}; + +static void tas2781_apply_calib(struct tasdevice_priv *tas_priv) +{ + int i, rc; + + /* + * If no calibration data exist in tasdevice_priv *tas_priv, + * calibration apply will be ignored, and use default values + * in firmware binary, which was loaded during firmware download. + */ + if (tas_priv->cali_data[0] == 0) + return; + /* + * Calibration data was saved in tasdevice_priv *tas_priv as: + * unsigned int cali_data[CALIB_MAX]; + * and every data (in 4 bytes) will be saved in register which in + * book 0, and page number in page_array[], offset was saved in + * rgno_array[]. + */ + for (i = 0; i < CALIB_MAX; i++) { + rc = tasdevice_spi_dev_bulk_write(tas_priv, + tas_priv->cali_reg_array[i], + (unsigned char *)&tas_priv->cali_data[i], 4); + if (rc < 0) + dev_err(tas_priv->dev, + "chn %d calib %d bulk_wr err = %d\n", + tas_priv->index, i, rc); + } +} + +/* + * Update the calibration data, including speaker impedance, f0, etc, + * into algo. Calibrate data is done by manufacturer in the factory. + * These data are used by Algo for calculating the speaker temperature, + * speaker membrane excursion and f0 in real time during playback. + * Calibration data format in EFI is V2, since 2024. + */ +static int tas2781_save_calibration(struct tasdevice_priv *tas_priv) +{ + /* + * GUID was used for data access in BIOS, it was provided by board + * manufactory, like HP: "{02f9af02-7734-4233-b43d-93fe5aa35db3}" + */ + efi_guid_t efi_guid = + EFI_GUID(0x02f9af02, 0x7734, 0x4233, + 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3); + static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME; + unsigned char data[TASDEVICE_CALIBRATION_DATA_SIZE], *buf; + unsigned int attr, crc, offset, *tmp_val; + unsigned long total_sz = 0; + efi_status_t status; + + tas_priv->cali_data[0] = 0; + status = efi.get_variable(efi_name, &efi_guid, &attr, &total_sz, data); + if (status == EFI_BUFFER_TOO_SMALL) { + if (total_sz > TASDEVICE_CALIBRATION_DATA_SIZE) + return -ENOMEM; + /* Get variable contents into buffer */ + status = efi.get_variable(efi_name, &efi_guid, &attr, + &total_sz, data); + } + if (status != EFI_SUCCESS) + return status; + + tmp_val = (unsigned int *)data; + if (tmp_val[0] == 2781) { + /* + * New features were added in calibrated Data V3: + * 1. Added calibration registers address define in + * a node, marked as Device id == 0x80. + * New features were added in calibrated Data V2: + * 1. Added some the fields to store the link_id and + * uniqie_id for multi-link solutions + * 2. Support flexible number of devices instead of + * fixed one in V1. + * Layout of calibrated data V2 in UEFI(total 256 bytes): + * ChipID (2781, 4 bytes) + * Device-Sum (4 bytes) + * TimeStamp of Calibration (4 bytes) + * for (i = 0; i < Device-Sum; i++) { + * Device #i index_info () { + * SDW link id (2bytes) + * SDW unique_id (2bytes) + * } // if Device number is 0x80, mean it's + * calibration registers address. + * Calibrated Data of Device #i (20 bytes) + * } + * CRC (4 bytes) + * Reserved (the rest) + */ + crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0; + + if (crc != tmp_val[3 + tmp_val[1] * 6]) + return 0; + + for (int j = 0; j < tmp_val[1]; j++) { + offset = j * 6 + 3; + if (tmp_val[offset] == tas_priv->index) { + for (int i = 0; i < CALIB_MAX; i++) + tas_priv->cali_data[i] = + tmp_val[offset + i + 1]; + } else if (tmp_val[offset] == + TASDEVICE_CALIBRATION_REG_ADDRESS) { + for (int i = 0; i < CALIB_MAX; i++) { + buf = &data[(offset + i + 1) * 4]; + tas_priv->cali_reg_array[i] = + TASDEVICE_REG(buf[1], buf[2], + buf[3]); + } + } + tas_priv->apply_calibration(tas_priv); + } + } else { + /* + * Calibration data is in V1 format. + * struct cali_data { + * char cali_data[20]; + * } + * + * struct { + * struct cali_data cali_data[4]; + * int TimeStamp of Calibration (4 bytes) + * int CRC (4 bytes) + * } ueft; + */ + crc = crc32(~0, data, 84) ^ ~0; + if (crc == tmp_val[21]) { + for (int i = 0; i < CALIB_MAX; i++) + tas_priv->cali_data[i] = + tmp_val[tas_priv->index * 5 + i]; + tas_priv->apply_calibration(tas_priv); + } + } + + return 0; +} + +static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda) +{ + struct hda_codec *codec = tas_hda->priv->codec; + + snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl); + + snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl); + + for (int i = ARRAY_SIZE(tas_hda->snd_ctls) - 1; i >= 0; i--) + snd_ctl_remove(codec->card, tas_hda->snd_ctls[i]); + + snd_ctl_remove(codec->card, tas_hda->prof_ctl); +} + +static void tasdev_fw_ready(const struct firmware *fmw, void *context) +{ + struct tasdevice_priv *tas_priv = context; + struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); + struct hda_codec *codec = tas_priv->codec; + int i, j, ret, val; + + pm_runtime_get_sync(tas_priv->dev); + guard(mutex)(&tas_priv->codec_lock); + + ret = tasdevice_spi_rca_parser(tas_priv, fmw); + if (ret) + goto out; + + /* Add control one time only. */ + tas_hda->prof_ctl = snd_ctl_new1(&tas2781_prof_ctrl[tas_priv->index], + tas_priv); + ret = snd_ctl_add(codec->card, tas_hda->prof_ctl); + if (ret) { + dev_err(tas_priv->dev, "Failed to add KControl %s = %d\n", + tas2781_prof_ctrl[tas_priv->index].name, ret); + goto out; + } + j = tas_priv->index * ARRAY_SIZE(tas2781_snd_controls) / 2; + for (i = 0; i < 3; i++) { + tas_hda->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_controls[i+j], + tas_priv); + ret = snd_ctl_add(codec->card, tas_hda->snd_ctls[i]); + if (ret) { + dev_err(tas_priv->dev, + "Failed to add KControl %s = %d\n", + tas2781_snd_controls[i+tas_priv->index*3].name, + ret); + goto out; + } + } + + tasdevice_spi_dsp_remove(tas_priv); + + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; + scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%08X-%01d.bin", + codec->core.subsystem_id, tas_priv->index); + ret = tasdevice_spi_dsp_parser(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dspfw load %s error\n", + tas_priv->coef_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + /* Add control one time only. */ + tas_hda->dsp_prog_ctl = + snd_ctl_new1(&tas2781_dsp_prog_ctrl[tas_priv->index], + tas_priv); + ret = snd_ctl_add(codec->card, tas_hda->dsp_prog_ctl); + if (ret) { + dev_err(tas_priv->dev, + "Failed to add KControl %s = %d\n", + tas2781_dsp_prog_ctrl[tas_priv->index].name, ret); + goto out; + } + + tas_hda->dsp_conf_ctl = + snd_ctl_new1(&tas2781_dsp_conf_ctrl[tas_priv->index], + tas_priv); + ret = snd_ctl_add(codec->card, tas_hda->dsp_conf_ctl); + if (ret) { + dev_err(tas_priv->dev, "Failed to add KControl %s = %d\n", + tas2781_dsp_conf_ctrl[tas_priv->index].name, ret); + goto out; + } + + /* Perform AMP reset before firmware download. */ + tas_priv->rcabin.profile_cfg_id = TAS2781_PRE_POST_RESET_CFG; + tas2781_spi_reset(tas_priv); + tas_priv->rcabin.profile_cfg_id = 0; + + tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; + ret = tasdevice_spi_dev_read(tas_priv, TAS2781_REG_CLK_CONFIG, &val); + if (ret < 0) + goto out; + + if (val == TAS2781_REG_CLK_CONFIG_RESET) + ret = tasdevice_spi_prmg_load(tas_priv, 0); + if (ret < 0) { + dev_err(tas_priv->dev, "FW download failed = %d\n", ret); + goto out; + } + if (tas_priv->fmw->nr_programs > 0) + tas_priv->cur_prog = 0; + if (tas_priv->fmw->nr_configurations > 0) + tas_priv->cur_conf = 0; + + /* + * If calibrated data occurs error, dsp will still works with default + * calibrated data inside algo. + */ + +out: + if (fmw) + release_firmware(fmw); + pm_runtime_mark_last_busy(tas_hda->priv->dev); + pm_runtime_put_autosuspend(tas_hda->priv->dev); +} + +static int tas2781_hda_bind(struct device *dev, struct device *master, + void *master_data) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + struct hda_codec *codec; + int ret; + + comp = hda_component_from_index(parent, tas_hda->priv->index); + if (!comp) + return -EINVAL; + + if (comp->dev) + return -EBUSY; + + codec = parent->codec; + + pm_runtime_get_sync(dev); + + comp->dev = dev; + + strscpy(comp->name, dev_name(dev), sizeof(comp->name)); + + ret = tascodec_spi_init(tas_hda->priv, codec, THIS_MODULE, + tasdev_fw_ready); + if (!ret) + comp->playback_hook = tas2781_hda_playback_hook; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static void tas2781_hda_unbind(struct device *dev, struct device *master, + void *master_data) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + + comp = hda_component_from_index(parent, tas_hda->priv->index); + if (comp && (comp->dev == dev)) { + comp->dev = NULL; + memset(comp->name, 0, sizeof(comp->name)); + comp->playback_hook = NULL; + } + + tas2781_hda_remove_controls(tas_hda); + + tasdevice_spi_config_info_remove(tas_hda->priv); + tasdevice_spi_dsp_remove(tas_hda->priv); + + tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING; +} + +static const struct component_ops tas2781_hda_comp_ops = { + .bind = tas2781_hda_bind, + .unbind = tas2781_hda_unbind, +}; + +static void tas2781_hda_remove(struct device *dev) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + + component_del(tas_hda->priv->dev, &tas2781_hda_comp_ops); + + pm_runtime_get_sync(tas_hda->priv->dev); + pm_runtime_disable(tas_hda->priv->dev); + + pm_runtime_put_noidle(tas_hda->priv->dev); + + mutex_destroy(&tas_hda->priv->codec_lock); +} + +static int tas2781_hda_spi_probe(struct spi_device *spi) +{ + struct tasdevice_priv *tas_priv; + struct tas2781_hda *tas_hda; + const char *device_name; + int ret = 0; + + tas_hda = devm_kzalloc(&spi->dev, sizeof(*tas_hda), GFP_KERNEL); + if (!tas_hda) + return -ENOMEM; + + spi->max_speed_hz = TAS2781_SPI_MAX_FREQ; + + tas_priv = devm_kzalloc(&spi->dev, sizeof(*tas_priv), GFP_KERNEL); + if (!tas_priv) + return -ENOMEM; + tas_priv->dev = &spi->dev; + tas_hda->priv = tas_priv; + tas_priv->regmap = devm_regmap_init_spi(spi, &tasdevice_regmap); + if (IS_ERR(tas_priv->regmap)) { + ret = PTR_ERR(tas_priv->regmap); + dev_err(tas_priv->dev, "Failed to allocate regmap: %d\n", + ret); + return ret; + } + if (strstr(dev_name(&spi->dev), "TXNW2781")) { + device_name = "TXNW2781"; + tas_priv->save_calibration = tas2781_save_calibration; + tas_priv->apply_calibration = tas2781_apply_calib; + } else { + dev_err(tas_priv->dev, "Unmatched spi dev %s\n", + dev_name(&spi->dev)); + return -ENODEV; + } + + tas_priv->irq = spi->irq; + dev_set_drvdata(&spi->dev, tas_hda); + ret = tas2781_read_acpi(tas_hda, device_name, + spi_get_chipselect(spi, 0)); + if (ret) + return dev_err_probe(tas_priv->dev, ret, + "Platform not supported\n"); + + tasdevice_spi_init(tas_priv); + + ret = component_add(tas_priv->dev, &tas2781_hda_comp_ops); + if (ret) { + dev_err(tas_priv->dev, "Register component fail: %d\n", ret); + return ret; + } + + pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000); + pm_runtime_use_autosuspend(tas_priv->dev); + pm_runtime_mark_last_busy(tas_priv->dev); + pm_runtime_set_active(tas_priv->dev); + pm_runtime_get_noresume(tas_priv->dev); + pm_runtime_enable(tas_priv->dev); + + pm_runtime_put_autosuspend(tas_priv->dev); + + return 0; +} + +static void tas2781_hda_spi_remove(struct spi_device *spi) +{ + tas2781_hda_remove(&spi->dev); +} + +static int tas2781_runtime_suspend(struct device *dev) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + + guard(mutex)(&tas_hda->priv->codec_lock); + + if (tas_hda->priv->playback_started) + tasdevice_spi_tuning_switch(tas_hda->priv, 1); + + tas_hda->priv->cur_book = -1; + tas_hda->priv->cur_conf = -1; + + return 0; +} + +static int tas2781_runtime_resume(struct device *dev) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + + guard(mutex)(&tas_hda->priv->codec_lock); + + if (tas_hda->priv->playback_started) + tasdevice_spi_tuning_switch(tas_hda->priv, 0); + + return 0; +} + +static int tas2781_system_suspend(struct device *dev) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + /* Shutdown chip before system suspend */ + if (tas_hda->priv->playback_started) + tasdevice_spi_tuning_switch(tas_hda->priv, 1); + + return 0; +} + +static int tas2781_system_resume(struct device *dev) +{ + struct tas2781_hda *tas_hda = dev_get_drvdata(dev); + int ret, val; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + guard(mutex)(&tas_hda->priv->codec_lock); + ret = tasdevice_spi_dev_read(tas_hda->priv, TAS2781_REG_CLK_CONFIG, + &val); + if (ret < 0) + return ret; + + if (val == TAS2781_REG_CLK_CONFIG_RESET) { + tas_hda->priv->cur_book = -1; + tas_hda->priv->cur_conf = -1; + tas_hda->priv->cur_prog = -1; + + ret = tasdevice_spi_prmg_load(tas_hda->priv, 0); + if (ret < 0) { + dev_err(tas_hda->priv->dev, + "FW download failed = %d\n", ret); + return ret; + } + + if (tas_hda->priv->playback_started) + tasdevice_spi_tuning_switch(tas_hda->priv, 0); + } + + return ret; +} + +static const struct dev_pm_ops tas2781_hda_pm_ops = { + RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume) +}; + +static const struct spi_device_id tas2781_hda_spi_id[] = { + { "tas2781-hda", }, + {} +}; + +static const struct acpi_device_id tas2781_acpi_hda_match[] = { + {"TXNW2781", }, + {} +}; +MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match); + +static struct spi_driver tas2781_hda_spi_driver = { + .driver = { + .name = "tas2781-hda", + .acpi_match_table = tas2781_acpi_hda_match, + .pm = &tas2781_hda_pm_ops, + }, + .id_table = tas2781_hda_spi_id, + .probe = tas2781_hda_spi_probe, + .remove = tas2781_hda_spi_remove, +}; +module_spi_driver(tas2781_hda_spi_driver); + +MODULE_DESCRIPTION("TAS2781 HDA SPI Driver"); +MODULE_AUTHOR("Baojun, Xu, <baojun.xug@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/tas2781_spi_fwlib.c b/sound/pci/hda/tas2781_spi_fwlib.c new file mode 100644 index 000000000000..d90d022d8449 --- /dev/null +++ b/sound/pci/hda/tas2781_spi_fwlib.c @@ -0,0 +1,2006 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// TAS2781 HDA SPI driver +// +// Copyright 2024 - 2025 Texas Instruments, Inc. +// +// Author: Baojun Xu <baojun.xu@ti.com> + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/unaligned.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tas2781-dsp.h> +#include <sound/tlv.h> + +#include "tas2781-spi.h" + +#define OFFSET_ERROR_BIT BIT(31) + +#define ERROR_PRAM_CRCCHK 0x0000000 +#define ERROR_YRAM_CRCCHK 0x0000001 +#define PPC_DRIVER_CRCCHK 0x00000200 + +#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c) +#define TAS2781_YRAM_BOOK1 140 +#define TAS2781_YRAM1_PAGE 42 +#define TAS2781_YRAM1_START_REG 88 + +#define TAS2781_YRAM2_START_PAGE 43 +#define TAS2781_YRAM2_END_PAGE 49 +#define TAS2781_YRAM2_START_REG 8 +#define TAS2781_YRAM2_END_REG 127 + +#define TAS2781_YRAM3_PAGE 50 +#define TAS2781_YRAM3_START_REG 8 +#define TAS2781_YRAM3_END_REG 27 + +/* should not include B0_P53_R44-R47 */ +#define TAS2781_YRAM_BOOK2 0 +#define TAS2781_YRAM4_START_PAGE 50 +#define TAS2781_YRAM4_END_PAGE 60 + +#define TAS2781_YRAM5_PAGE 61 +#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG +#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG + +#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10 +#define MAIN_ALL_DEVICES_1X 0x01 +#define MAIN_DEVICE_A_1X 0x02 +#define MAIN_DEVICE_B_1X 0x03 +#define MAIN_DEVICE_C_1X 0x04 +#define MAIN_DEVICE_D_1X 0x05 +#define COEFF_DEVICE_A_1X 0x12 +#define COEFF_DEVICE_B_1X 0x13 +#define COEFF_DEVICE_C_1X 0x14 +#define COEFF_DEVICE_D_1X 0x15 +#define PRE_DEVICE_A_1X 0x22 +#define PRE_DEVICE_B_1X 0x23 +#define PRE_DEVICE_C_1X 0x24 +#define PRE_DEVICE_D_1X 0x25 +#define PRE_SOFTWARE_RESET_DEVICE_A 0x41 +#define PRE_SOFTWARE_RESET_DEVICE_B 0x42 +#define PRE_SOFTWARE_RESET_DEVICE_C 0x43 +#define PRE_SOFTWARE_RESET_DEVICE_D 0x44 +#define POST_SOFTWARE_RESET_DEVICE_A 0x45 +#define POST_SOFTWARE_RESET_DEVICE_B 0x46 +#define POST_SOFTWARE_RESET_DEVICE_C 0x47 +#define POST_SOFTWARE_RESET_DEVICE_D 0x48 + +struct tas_crc { + unsigned char offset; + unsigned char len; +}; + +struct blktyp_devidx_map { + unsigned char blktyp; + unsigned char dev_idx; +}; + +/* fixed m68k compiling issue: mapping table can save code field */ +static const struct blktyp_devidx_map ppc3_tas2781_mapping_table[] = { + { MAIN_ALL_DEVICES_1X, 0x80 }, + { MAIN_DEVICE_A_1X, 0x81 }, + { COEFF_DEVICE_A_1X, 0x81 }, + { PRE_DEVICE_A_1X, 0x81 }, + { PRE_SOFTWARE_RESET_DEVICE_A, 0xC1 }, + { POST_SOFTWARE_RESET_DEVICE_A, 0xC1 }, + { MAIN_DEVICE_B_1X, 0x82 }, + { COEFF_DEVICE_B_1X, 0x82 }, + { PRE_DEVICE_B_1X, 0x82 }, + { PRE_SOFTWARE_RESET_DEVICE_B, 0xC2 }, + { POST_SOFTWARE_RESET_DEVICE_B, 0xC2 }, + { MAIN_DEVICE_C_1X, 0x83 }, + { COEFF_DEVICE_C_1X, 0x83 }, + { PRE_DEVICE_C_1X, 0x83 }, + { PRE_SOFTWARE_RESET_DEVICE_C, 0xC3 }, + { POST_SOFTWARE_RESET_DEVICE_C, 0xC3 }, + { MAIN_DEVICE_D_1X, 0x84 }, + { COEFF_DEVICE_D_1X, 0x84 }, + { PRE_DEVICE_D_1X, 0x84 }, + { PRE_SOFTWARE_RESET_DEVICE_D, 0xC4 }, + { POST_SOFTWARE_RESET_DEVICE_D, 0xC4 }, +}; + +static const struct blktyp_devidx_map ppc3_mapping_table[] = { + { MAIN_ALL_DEVICES_1X, 0x80 }, + { MAIN_DEVICE_A_1X, 0x81 }, + { COEFF_DEVICE_A_1X, 0xC1 }, + { PRE_DEVICE_A_1X, 0xC1 }, + { MAIN_DEVICE_B_1X, 0x82 }, + { COEFF_DEVICE_B_1X, 0xC2 }, + { PRE_DEVICE_B_1X, 0xC2 }, + { MAIN_DEVICE_C_1X, 0x83 }, + { COEFF_DEVICE_C_1X, 0xC3 }, + { PRE_DEVICE_C_1X, 0xC3 }, + { MAIN_DEVICE_D_1X, 0x84 }, + { COEFF_DEVICE_D_1X, 0xC4 }, + { PRE_DEVICE_D_1X, 0xC4 }, +}; + +static const struct blktyp_devidx_map non_ppc3_mapping_table[] = { + { MAIN_ALL_DEVICES, 0x80 }, + { MAIN_DEVICE_A, 0x81 }, + { COEFF_DEVICE_A, 0xC1 }, + { PRE_DEVICE_A, 0xC1 }, + { MAIN_DEVICE_B, 0x82 }, + { COEFF_DEVICE_B, 0xC2 }, + { PRE_DEVICE_B, 0xC2 }, + { MAIN_DEVICE_C, 0x83 }, + { COEFF_DEVICE_C, 0xC3 }, + { PRE_DEVICE_C, 0xC3 }, + { MAIN_DEVICE_D, 0x84 }, + { COEFF_DEVICE_D, 0xC4 }, + { PRE_DEVICE_D, 0xC4 }, +}; + +/* + * Device support different configurations for different scene, + * like voice, music, calibration, was write in regbin file. + * Will be stored into tas_priv after regbin was loaded. + */ +static struct tasdevice_config_info *tasdevice_add_config( + struct tasdevice_priv *tas_priv, unsigned char *config_data, + unsigned int config_size, int *status) +{ + struct tasdevice_config_info *cfg_info; + struct tasdev_blk_data **bk_da; + unsigned int config_offset = 0; + unsigned int i; + + /* + * In most projects are many audio cases, such as music, handfree, + * receiver, games, audio-to-haptics, PMIC record, bypass mode, + * portrait, landscape, etc. Even in multiple audios, one or + * two of the chips will work for the special case, such as + * ultrasonic application. In order to support these variable-numbers + * of audio cases, flexible configs have been introduced in the + * DSP firmware. + */ + cfg_info = kzalloc(sizeof(*cfg_info), GFP_KERNEL); + if (!cfg_info) { + *status = -ENOMEM; + return NULL; + } + + if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) { + if ((config_offset + 64) > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add conf: Out of boundary\n"); + goto config_err; + } + config_offset += 64; + } + + if ((config_offset + 4) > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add config: Out of boundary\n"); + goto config_err; + } + + /* + * convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + cfg_info->nblocks = get_unaligned_be32(&config_data[config_offset]); + config_offset += 4; + + /* + * Several kinds of dsp/algorithm firmwares can run on tas2781, + * the number and size of blk are not fixed and different among + * these firmwares. + */ + bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks, + sizeof(*bk_da), GFP_KERNEL); + if (!bk_da) { + *status = -ENOMEM; + goto config_err; + } + cfg_info->real_nblocks = 0; + for (i = 0; i < cfg_info->nblocks; i++) { + if (config_offset + 12 > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d nblocks = %u!\n", + __func__, i, cfg_info->nblocks); + goto block_err; + } + bk_da[i] = kzalloc(sizeof(*bk_da[i]), GFP_KERNEL); + if (!bk_da[i]) { + *status = -ENOMEM; + goto block_err; + } + + bk_da[i]->dev_idx = config_data[config_offset]; + config_offset++; + + bk_da[i]->block_type = config_data[config_offset]; + config_offset++; + + bk_da[i]->yram_checksum = + get_unaligned_be16(&config_data[config_offset]); + config_offset += 2; + bk_da[i]->block_size = + get_unaligned_be32(&config_data[config_offset]); + config_offset += 4; + + bk_da[i]->n_subblks = + get_unaligned_be32(&config_data[config_offset]); + + config_offset += 4; + + if (config_offset + bk_da[i]->block_size > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d blks = %u!\n", + __func__, i, cfg_info->nblocks); + goto block_err; + } + /* instead of kzalloc+memcpy */ + bk_da[i]->regdata = kmemdup(&config_data[config_offset], + bk_da[i]->block_size, GFP_KERNEL); + if (!bk_da[i]->regdata) { + *status = -ENOMEM; + i++; + goto block_err; + } + + config_offset += bk_da[i]->block_size; + cfg_info->real_nblocks += 1; + } + + return cfg_info; +block_err: + for (int j = 0; j < i; j++) + kfree(bk_da[j]); + kfree(bk_da); +config_err: + kfree(cfg_info); + return NULL; +} + +/* Regbin file parser function. */ +int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_config_info **cfg_info; + struct tasdevice_rca_hdr *fw_hdr; + struct tasdevice_rca *rca; + unsigned int total_config_sz = 0; + int offset = 0, ret = 0, i; + unsigned char *buf; + + rca = &tas_priv->rcabin; + fw_hdr = &rca->fw_hdr; + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "Failed to read %s\n", + tas_priv->rca_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -EINVAL; + } + buf = (unsigned char *)fmw->data; + fw_hdr->img_sz = get_unaligned_be32(&buf[offset]); + offset += 4; + if (fw_hdr->img_sz != fmw->size) { + dev_err(tas_priv->dev, + "File size not match, %d %u", (int)fmw->size, + fw_hdr->img_sz); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -EINVAL; + } + + fw_hdr->checksum = get_unaligned_be32(&buf[offset]); + offset += 4; + fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]); + if (fw_hdr->binary_version_num < 0x103) { + dev_err(tas_priv->dev, "File version 0x%04x is too low", + fw_hdr->binary_version_num); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -EINVAL; + } + offset += 4; + fw_hdr->drv_fw_version = get_unaligned_be32(&buf[offset]); + offset += 8; + fw_hdr->plat_type = buf[offset++]; + fw_hdr->dev_family = buf[offset++]; + fw_hdr->reserve = buf[offset++]; + fw_hdr->ndev = buf[offset++]; + if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) { + dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n"); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -EINVAL; + } + + for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++) + fw_hdr->devs[i] = buf[offset]; + + fw_hdr->nconfig = get_unaligned_be32(&buf[offset]); + offset += 4; + + for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) { + fw_hdr->config_size[i] = get_unaligned_be32(&buf[offset]); + offset += 4; + total_config_sz += fw_hdr->config_size[i]; + } + + if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) { + dev_err(tas_priv->dev, "Bin file err %d - %d != %d!\n", + fw_hdr->img_sz, total_config_sz, (int)offset); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -EINVAL; + } + + cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL); + if (!cfg_info) { + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return -ENOMEM; + } + rca->cfg_info = cfg_info; + rca->ncfgs = 0; + for (i = 0; i < (int)fw_hdr->nconfig; i++) { + rca->ncfgs += 1; + cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset], + fw_hdr->config_size[i], &ret); + if (ret) { + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + return ret; + } + offset += (int)fw_hdr->config_size[i]; + } + + return ret; +} + +/* fixed m68k compiling issue: mapping table can save code field */ +static unsigned char map_dev_idx(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block) +{ + struct blktyp_devidx_map *p = + (struct blktyp_devidx_map *)non_ppc3_mapping_table; + struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &fw_hdr->fixed_hdr; + int i, n = ARRAY_SIZE(non_ppc3_mapping_table); + unsigned char dev_idx = 0; + + if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781_BASIC_MIN) { + p = (struct blktyp_devidx_map *)ppc3_tas2781_mapping_table; + n = ARRAY_SIZE(ppc3_tas2781_mapping_table); + } else if (fw_fixed_hdr->ppcver >= PPC3_VERSION_BASE) { + p = (struct blktyp_devidx_map *)ppc3_mapping_table; + n = ARRAY_SIZE(ppc3_mapping_table); + } + + for (i = 0; i < n; i++) { + if (block->type == p[i].blktyp) { + dev_idx = p[i].dev_idx; + break; + } + } + + return dev_idx; +} + +/* Block parser function. */ +static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + + if (offset + 16 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + + /* + * Convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host. + */ + block->type = get_unaligned_be32(&data[offset]); + offset += 4; + + block->is_pchksum_present = data[offset++]; + block->pchksum = data[offset++]; + block->is_ychksum_present = data[offset++]; + block->ychksum = data[offset++]; + block->blk_size = get_unaligned_be32(&data[offset]); + offset += 4; + block->nr_subblocks = get_unaligned_be32(&data[offset]); + offset += 4; + + /* + * Fixed m68k compiling issue: + * 1. mapping table can save code field. + * 2. storing the dev_idx as a member of block can reduce unnecessary + * time and system resource comsumption of dev_idx mapping every + * time the block data writing to the dsp. + */ + block->dev_idx = map_dev_idx(tas_fmw, block); + + if (offset + block->blk_size > fmw->size) { + dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__); + return -EINVAL; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL); + if (!block->data) + return -ENOMEM; + + offset += block->blk_size; + + return offset; +} + +/* Data of block parser function. */ +static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = fmw->data; + struct tasdev_blk *blk; + unsigned int i; + + if (offset + 4 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + img_data->nr_blk = get_unaligned_be32(&data[offset]); + offset += 4; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) + return -ENOMEM; + + for (i = 0; i < img_data->nr_blk; i++) { + blk = &img_data->dev_blks[i]; + offset = fw_parse_block_data_kernel( + tas_fmw, blk, fmw, offset); + if (offset < 0) { + kfree(img_data->dev_blks); + return -EINVAL; + } + } + + return offset; +} + +/* Data of DSP program parser function. */ +static int fw_parse_program_data_kernel( + struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + struct tasdevice_prog *program; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &tas_fmw->programs[i]; + if (offset + 72 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + return -EINVAL; + } + /* skip 72 unused byts */ + offset += 72; + + offset = fw_parse_data_kernel(tas_fmw, &program->dev_data, + fmw, offset); + if (offset < 0) + break; + } + + return offset; +} + +/* Data of DSP configurations parser function. */ +static int fw_parse_configuration_data_kernel(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + struct tasdevice_config *config; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &tas_fmw->configs[i]; + if (offset + 80 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + return -EINVAL; + } + memcpy(config->name, &data[offset], 64); + /* skip extra 16 bytes */ + offset += 80; + + offset = fw_parse_data_kernel(tas_fmw, &config->dev_data, + fmw, offset); + if (offset < 0) + break; + } + + return offset; +} + +/* DSP firmware file header parser function for early PPC3 firmware binary. */ +static int fw_parse_variable_header_kernel(struct tasdevice_priv *tas_priv, + const struct firmware *fmw, int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr; + struct tasdevice_config *config; + struct tasdevice_prog *program; + const unsigned char *buf = fmw->data; + unsigned short max_confs; + unsigned int i; + + if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + fw_hdr->device_family = get_unaligned_be16(&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s:not TAS device\n", __func__); + return -EINVAL; + } + offset += 2; + fw_hdr->device = get_unaligned_be16(&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + return -EINVAL; + } + offset += 2; + + tas_fmw->nr_programs = get_unaligned_be32(&buf[offset]); + offset += 4; + + if (tas_fmw->nr_programs == 0 || + tas_fmw->nr_programs > TASDEVICE_MAXPROGRAM_NUM_KERNEL) { + dev_err(tas_priv->dev, "mnPrograms is invalid\n"); + return -EINVAL; + } + + tas_fmw->programs = kcalloc(tas_fmw->nr_programs, + sizeof(*tas_fmw->programs), GFP_KERNEL); + if (!tas_fmw->programs) + return -ENOMEM; + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &tas_fmw->programs[i]; + program->prog_size = get_unaligned_be32(&buf[offset]); + offset += 4; + } + + /* Skip the unused prog_size */ + offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs); + + tas_fmw->nr_configurations = get_unaligned_be32(&buf[offset]); + offset += 4; + + /* + * The max number of config in firmware greater than 4 pieces of + * tas2781s is different from the one lower than 4 pieces of + * tas2781s. + */ + max_confs = TASDEVICE_MAXCONFIG_NUM_KERNEL; + if (tas_fmw->nr_configurations == 0 || + tas_fmw->nr_configurations > max_confs) { + dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__); + kfree(tas_fmw->programs); + return -EINVAL; + } + + if (offset + 4 * max_confs > fmw->size) { + dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__); + kfree(tas_fmw->programs); + return -EINVAL; + } + + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(*tas_fmw->configs), GFP_KERNEL); + if (!tas_fmw->configs) { + kfree(tas_fmw->programs); + return -ENOMEM; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + config = &tas_fmw->configs[i]; + config->cfg_size = get_unaligned_be32(&buf[offset]); + offset += 4; + } + + /* Skip the unused configs */ + offset += 4 * (max_confs - tas_fmw->nr_programs); + + return offset; +} + +/* + * In sub-block data, have three type sub-block: + * 1. Single byte write. + * 2. Multi-byte write. + * 3. Delay. + * 4. Bits update. + * This function perform single byte write to device. + */ +static int tasdevice_single_byte_wr(void *context, int dev_idx, + unsigned char *data, int sublocksize) +{ + struct tasdevice_priv *tas_priv = context; + unsigned short len = get_unaligned_be16(&data[2]); + int i, subblk_offset, rc; + + subblk_offset = 4; + if (subblk_offset + 4 * len > sublocksize) { + dev_err(tas_priv->dev, "process_block: Out of boundary\n"); + return 0; + } + + for (i = 0; i < len; i++) { + if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) { + rc = tasdevice_spi_dev_write(tas_priv, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + data[subblk_offset + 3]); + if (rc < 0) { + dev_err(tas_priv->dev, + "process_block: single write error\n"); + subblk_offset |= OFFSET_ERROR_BIT; + } + } + subblk_offset += 4; + } + + return subblk_offset; +} + +/* + * In sub-block data, have three type sub-block: + * 1. Single byte write. + * 2. Multi-byte write. + * 3. Delay. + * 4. Bits update. + * This function perform multi-write to device. + */ +static int tasdevice_burst_wr(void *context, int dev_idx, unsigned char *data, + int sublocksize) +{ + struct tasdevice_priv *tas_priv = context; + unsigned short len = get_unaligned_be16(&data[2]); + int subblk_offset, rc; + + subblk_offset = 4; + if (subblk_offset + 4 + len > sublocksize) { + dev_err(tas_priv->dev, "%s: BST Out of boundary\n", __func__); + subblk_offset |= OFFSET_ERROR_BIT; + } + if (len % 4) { + dev_err(tas_priv->dev, "%s:Bst-len(%u)not div by 4\n", + __func__, len); + subblk_offset |= OFFSET_ERROR_BIT; + } + + if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) { + rc = tasdevice_spi_dev_bulk_write(tas_priv, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + &data[subblk_offset + 4], len); + if (rc < 0) { + dev_err(tas_priv->dev, "%s: bulk_write error = %d\n", + __func__, rc); + subblk_offset |= OFFSET_ERROR_BIT; + } + } + subblk_offset += (len + 4); + + return subblk_offset; +} + +/* Just delay for ms.*/ +static int tasdevice_delay(void *context, int dev_idx, unsigned char *data, + int sublocksize) +{ + struct tasdevice_priv *tas_priv = context; + unsigned int sleep_time, subblk_offset = 2; + + if (subblk_offset + 2 > sublocksize) { + dev_err(tas_priv->dev, "%s: delay Out of boundary\n", + __func__); + subblk_offset |= OFFSET_ERROR_BIT; + } + if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) { + sleep_time = get_unaligned_be16(&data[2]) * 1000; + fsleep(sleep_time); + } + subblk_offset += 2; + + return subblk_offset; +} + +/* + * In sub-block data, have three type sub-block: + * 1. Single byte write. + * 2. Multi-byte write. + * 3. Delay. + * 4. Bits update. + * This function perform bits update. + */ +static int tasdevice_field_wr(void *context, int dev_idx, unsigned char *data, + int sublocksize) +{ + struct tasdevice_priv *tas_priv = context; + int rc, subblk_offset = 2; + + if (subblk_offset + 6 > sublocksize) { + dev_err(tas_priv->dev, "%s: bit write Out of boundary\n", + __func__); + subblk_offset |= OFFSET_ERROR_BIT; + } + if (dev_idx == (tas_priv->index + 1) || dev_idx == 0) { + rc = tasdevice_spi_dev_update_bits(tas_priv, + TASDEVICE_REG(data[subblk_offset + 2], + data[subblk_offset + 3], + data[subblk_offset + 4]), + data[subblk_offset + 1], + data[subblk_offset + 5]); + if (rc < 0) { + dev_err(tas_priv->dev, "%s: update_bits error = %d\n", + __func__, rc); + subblk_offset |= OFFSET_ERROR_BIT; + } + } + subblk_offset += 6; + + return subblk_offset; +} + +/* Data block process function. */ +static int tasdevice_process_block(void *context, unsigned char *data, + unsigned char dev_idx, int sublocksize) +{ + struct tasdevice_priv *tas_priv = context; + int blktyp = dev_idx & 0xC0, subblk_offset; + unsigned char subblk_typ = data[1]; + + switch (subblk_typ) { + case TASDEVICE_CMD_SING_W: + subblk_offset = tasdevice_single_byte_wr(tas_priv, + dev_idx & 0x3f, data, sublocksize); + break; + case TASDEVICE_CMD_BURST: + subblk_offset = tasdevice_burst_wr(tas_priv, + dev_idx & 0x3f, data, sublocksize); + break; + case TASDEVICE_CMD_DELAY: + subblk_offset = tasdevice_delay(tas_priv, + dev_idx & 0x3f, data, sublocksize); + break; + case TASDEVICE_CMD_FIELD_W: + subblk_offset = tasdevice_field_wr(tas_priv, + dev_idx & 0x3f, data, sublocksize); + break; + default: + subblk_offset = 2; + break; + } + if (((subblk_offset & OFFSET_ERROR_BIT) != 0) && blktyp != 0) { + if (blktyp == 0x80) { + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + } else + tas_priv->cur_conf = -1; + } + subblk_offset &= ~OFFSET_ERROR_BIT; + + return subblk_offset; +} + +/* + * Device support different configurations for different scene, + * this function was used for choose different config. + */ +void tasdevice_spi_select_cfg_blk(void *pContext, int conf_no, + unsigned char block_type) +{ + struct tasdevice_priv *tas_priv = pContext; + struct tasdevice_rca *rca = &tas_priv->rcabin; + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdev_blk_data **blk_data; + unsigned int j, k; + + if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) { + dev_err(tas_priv->dev, "conf_no should be not more than %u\n", + rca->ncfgs); + return; + } + blk_data = cfg_info[conf_no]->blk_data; + + for (j = 0; j < cfg_info[conf_no]->real_nblocks; j++) { + unsigned int length = 0, rc = 0; + + if (block_type > 5 || block_type < 2) { + dev_err(tas_priv->dev, + "block_type should be in range from 2 to 5\n"); + break; + } + if (block_type != blk_data[j]->block_type) + continue; + + for (k = 0; k < blk_data[j]->n_subblks; k++) { + tas_priv->is_loading = true; + + rc = tasdevice_process_block(tas_priv, + blk_data[j]->regdata + length, + blk_data[j]->dev_idx, + blk_data[j]->block_size - length); + length += rc; + if (blk_data[j]->block_size < length) { + dev_err(tas_priv->dev, + "%s: %u %u out of boundary\n", + __func__, length, + blk_data[j]->block_size); + break; + } + } + if (length != blk_data[j]->block_size) + dev_err(tas_priv->dev, "%s: %u %u size is not same\n", + __func__, length, blk_data[j]->block_size); + } +} + +/* Block process function. */ +static int tasdevice_load_block_kernel( + struct tasdevice_priv *tasdevice, struct tasdev_blk *block) +{ + const unsigned int blk_size = block->blk_size; + unsigned char *data = block->data; + unsigned int i, length; + + for (i = 0, length = 0; i < block->nr_subblocks; i++) { + int rc = tasdevice_process_block(tasdevice, data + length, + block->dev_idx, blk_size - length); + if (rc < 0) { + dev_err(tasdevice->dev, + "%s: %u %u sublock write error\n", + __func__, length, blk_size); + return rc; + } + length += rc; + if (blk_size < length) { + dev_err(tasdevice->dev, "%s: %u %u out of boundary\n", + __func__, length, blk_size); + rc = -ENOMEM; + return rc; + } + } + + return 0; +} + +/* DSP firmware file header parser function. */ +static int fw_parse_variable_hdr(struct tasdevice_priv *tas_priv, + struct tasdevice_dspfw_hdr *fw_hdr, + const struct firmware *fmw, int offset) +{ + const unsigned char *buf = fmw->data; + int len = strlen((char *)&buf[offset]); + + len++; + + if (offset + len + 8 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + + offset += len; + + fw_hdr->device_family = get_unaligned_be32(&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s: not TAS device\n", __func__); + return -EINVAL; + } + offset += 4; + + fw_hdr->device = get_unaligned_be32(&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + return -EINVAL; + } + offset += 4; + fw_hdr->ndev = 1; + + return offset; +} + +/* DSP firmware file header parser function for size variabled header. */ +static int fw_parse_variable_header_git(struct tasdevice_priv + *tas_priv, const struct firmware *fmw, int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr; + + offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset); + + return offset; +} + +/* DSP firmware file block parser function. */ +static int fw_parse_block_data(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + int n; + + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Type error\n", __func__); + return -EINVAL; + } + block->type = get_unaligned_be32(&data[offset]); + offset += 4; + + if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) { + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "PChkSumPresent error\n"); + return -EINVAL; + } + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + } else { + block->is_pchksum_present = 0; + block->is_ychksum_present = 0; + } + + block->nr_cmds = get_unaligned_be32(&data[offset]); + offset += 4; + + n = block->nr_cmds * 4; + if (offset + n > fmw->size) { + dev_err(tas_fmw->dev, + "%s: File Size(%lu) error offset = %d n = %d\n", + __func__, (unsigned long)fmw->size, offset, n); + return -EINVAL; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], n, GFP_KERNEL); + if (!block->data) + return -ENOMEM; + + offset += n; + + return offset; +} + +/* + * When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_data(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = (unsigned char *)fmw->data; + struct tasdev_blk *blk; + unsigned int i, n; + + if (offset + 64 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Name error\n", __func__); + return -EINVAL; + } + memcpy(img_data->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n++; + if (offset + n + 2 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Description error\n", __func__); + return -EINVAL; + } + offset += n; + img_data->nr_blk = get_unaligned_be16(&data[offset]); + offset += 2; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(*img_data->dev_blks), GFP_KERNEL); + if (!img_data->dev_blks) + return -ENOMEM; + + for (i = 0; i < img_data->nr_blk; i++) { + blk = &img_data->dev_blks[i]; + offset = fw_parse_block_data(tas_fmw, blk, fmw, offset); + if (offset < 0) + return -EINVAL; + } + + return offset; +} + +/* + * When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_program_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + unsigned char *buf = (unsigned char *)fmw->data; + struct tasdevice_prog *program; + int i; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + tas_fmw->nr_programs = get_unaligned_be16(&buf[offset]); + offset += 2; + + if (tas_fmw->nr_programs == 0) { + /* Not error in calibration Data file, return directly */ + dev_dbg(tas_priv->dev, "%s: No Programs data, maybe calbin\n", + __func__); + return offset; + } + + tas_fmw->programs = + kcalloc(tas_fmw->nr_programs, sizeof(*tas_fmw->programs), + GFP_KERNEL); + if (!tas_fmw->programs) + return -ENOMEM; + + for (i = 0; i < tas_fmw->nr_programs; i++) { + int n = 0; + + program = &tas_fmw->programs[i]; + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + return -EINVAL; + } + offset += 64; + + n = strlen((char *)&buf[offset]); + /* skip '\0' and 5 unused bytes */ + n += 6; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + return -EINVAL; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &program->dev_data, fmw, + offset); + if (offset < 0) + return offset; + } + + return offset; +} + +/* + * When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_configuration_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + struct tasdevice_config *config; + unsigned int i, n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + tas_fmw->nr_configurations = get_unaligned_be16(&data[offset]); + offset += 2; + + if (tas_fmw->nr_configurations == 0) { + dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__); + /* Not error for calibration Data file, return directly */ + return offset; + } + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(*tas_fmw->configs), GFP_KERNEL); + if (!tas_fmw->configs) + return -ENOMEM; + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &tas_fmw->configs[i]; + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "File Size err\n"); + return -EINVAL; + } + memcpy(config->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n += 15; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + return -EINVAL; + } + offset += n; + offset = fw_parse_data(tas_fmw, &config->dev_data, + fmw, offset); + if (offset < 0) + break; + } + + return offset; +} + +/* yram5 page check. */ +static bool check_inpage_yram_rg(struct tas_crc *cd, + unsigned char reg, unsigned char len) +{ + bool in = false; + + if (reg <= TAS2781_YRAM5_END_REG && + reg >= TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_END_REG) + cd->len = TAS2781_YRAM5_END_REG - reg + 1; + else + cd->len = len; + cd->offset = reg; + in = true; + } else if (reg < TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_START_REG) { + cd->offset = TAS2781_YRAM5_START_REG; + cd->len = len - TAS2781_YRAM5_START_REG + reg; + in = true; + } + } + + return in; +} + +/* DSP firmware yram block check. */ +static bool check_inpage_yram_bk1(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (page == TAS2781_YRAM1_PAGE) { + if (reg >= TAS2781_YRAM1_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg + len > TAS2781_YRAM1_START_REG) { + cd->offset = TAS2781_YRAM1_START_REG; + cd->len = len - TAS2781_YRAM1_START_REG + reg; + in = true; + } + } else if (page == TAS2781_YRAM3_PAGE) { + in = check_inpage_yram_rg(cd, reg, len); + } + + return in; +} + +/* + * Return Code: + * true -- the registers are in the inpage yram + * false -- the registers are NOT in the inpage yram + */ +static bool check_inpage_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1) + in = check_inpage_yram_bk1(cd, page, reg, len); + else if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + + return in; +} + +/* yram4 page check. */ +static bool check_inblock_yram_bk(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if ((page >= TAS2781_YRAM4_START_PAGE && + page <= TAS2781_YRAM4_END_PAGE) || + (page >= TAS2781_YRAM2_START_PAGE && + page <= TAS2781_YRAM2_END_PAGE)) { + if (reg <= TAS2781_YRAM2_END_REG && + reg >= TAS2781_YRAM2_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg < TAS2781_YRAM2_START_REG) { + if (reg + len - 1 >= TAS2781_YRAM2_START_REG) { + cd->offset = TAS2781_YRAM2_START_REG; + cd->len = reg + len - TAS2781_YRAM2_START_REG; + in = true; + } + } + } + + return in; +} + +/* + * Return Code: + * true -- the registers are in the inblock yram + * false -- the registers are NOT in the inblock yram + */ +static bool check_inblock_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2) + in = check_inblock_yram_bk(cd, page, reg, len); + + return in; +} + +/* yram page check. */ +static bool check_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in; + + in = check_inpage_yram(cd, book, page, reg, len); + if (!in) + in = check_inblock_yram(cd, book, page, reg, len); + + return in; +} + +/* Checksum for data block. */ +static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice, + unsigned char book, unsigned char page, + unsigned char reg, unsigned int len) +{ + struct tas_crc crc_data; + unsigned char crc_chksum = 0; + unsigned char nBuf1[128]; + int ret = 0, i; + bool in; + + if ((reg + len - 1) > 127) { + ret = -EINVAL; + dev_err(tasdevice->dev, "firmware error\n"); + goto end; + } + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (reg == TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (len == 4)) { + /* DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, len); + if (!in) + goto end; + + if (len == 1) { + dev_err(tasdevice->dev, "firmware error\n"); + ret = -EINVAL; + goto end; + } + + ret = tasdevice_spi_dev_bulk_read(tasdevice, + TASDEVICE_REG(book, page, crc_data.offset), + nBuf1, crc_data.len); + if (ret < 0) + goto end; + + for (i = 0; i < crc_data.len; i++) { + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) && + ((i + crc_data.offset) >= + TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) && + ((i + crc_data.offset) <= + (TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG) + 4))) + /* DSP swap command, bypass */ + continue; + else + crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i], + 1, 0); + } + + ret = crc_chksum; + +end: + return ret; +} + +/* Checksum for single register. */ +static int do_singlereg_checksum(struct tasdevice_priv *tasdevice, + unsigned char book, unsigned char page, + unsigned char reg, unsigned char val) +{ + struct tas_crc crc_data; + unsigned int nData1; + int ret = 0; + bool in; + + /* DSP swap command, pass */ + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (reg >= TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG)) && + (reg <= (TASDEVICE_REG_ID(TAS2781_SA_COEFF_SWAP_REG) + 4))) + return 0; + + in = check_yram(&crc_data, book, page, reg, 1); + if (!in) + return 0; + ret = tasdevice_spi_dev_read(tasdevice, + TASDEVICE_REG(book, page, reg), &nData1); + if (ret < 0) + return ret; + + if (nData1 != val) { + dev_err(tasdevice->dev, + "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n", + book, page, reg, val, nData1); + tasdevice->err_code |= ERROR_YRAM_CRCCHK; + return -EAGAIN; + } + + ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0); + + return ret; +} + +/* Block type check. */ +static void set_err_prg_cfg(unsigned int type, struct tasdevice_priv *p) +{ + if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A) || + (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C) || + (type == MAIN_DEVICE_D)) + p->cur_prog = -1; + else + p->cur_conf = -1; +} + +/* Checksum for data bytes. */ +static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, unsigned char book, + unsigned char page, unsigned char reg, unsigned int len, + unsigned char val, unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) + ret = tasdev_multibytes_chksum(tas_priv, book, page, reg, + len); + else + ret = do_singlereg_checksum(tas_priv, book, page, reg, val); + + if (ret > 0) { + *crc_chksum += ret; + goto end; + } + + if (ret != -EAGAIN) + goto end; + + block->nr_retry--; + if (block->nr_retry > 0) + goto end; + + set_err_prg_cfg(block->type, tas_priv); + +end: + return ret; +} + +/* Multi-data byte write. */ +static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, unsigned char book, + unsigned char page, unsigned char reg, unsigned char *data, + unsigned int len, unsigned int *nr_cmds, + unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) { + ret = tasdevice_spi_dev_bulk_write(tas_priv, + TASDEVICE_REG(book, page, reg), data + 3, len); + if (ret < 0) + return ret; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, + book, page, reg, len, 0, crc_chksum); + } else { + ret = tasdevice_spi_dev_write(tas_priv, + TASDEVICE_REG(book, page, reg), data[3]); + if (ret < 0) + return ret; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, book, + page, reg, 1, data[3], crc_chksum); + } + + if (!block->is_ychksum_present || ret >= 0) { + *nr_cmds += 1; + if (len >= 2) + *nr_cmds += ((len - 2) / 4) + 1; + } + + return ret; +} + +/* Checksum for block. */ +static int tasdev_block_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + unsigned int nr_value; + int ret; + + ret = tasdevice_spi_dev_read(tas_priv, TASDEVICE_CHECKSUM, &nr_value); + if (ret < 0) { + dev_err(tas_priv->dev, "%s: read error %d.\n", __func__, ret); + set_err_prg_cfg(block->type, tas_priv); + return ret; + } + + if ((nr_value & 0xff) != block->pchksum) { + dev_err(tas_priv->dev, "%s: PChkSum err %d ", __func__, ret); + dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n", + block->pchksum, (nr_value & 0xff)); + tas_priv->err_code |= ERROR_PRAM_CRCCHK; + ret = -EAGAIN; + block->nr_retry--; + + if (block->nr_retry <= 0) + set_err_prg_cfg(block->type, tas_priv); + } else { + tas_priv->err_code &= ~ERROR_PRAM_CRCCHK; + } + + return ret; +} + +/* Firmware block load function. */ +static int tasdev_load_blk(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + unsigned int sleep_time, len, nr_cmds; + unsigned char offset, book, page, val; + unsigned char *data = block->data; + unsigned char crc_chksum = 0; + int ret = 0; + + while (block->nr_retry > 0) { + if (block->is_pchksum_present) { + ret = tasdevice_spi_dev_write(tas_priv, + TASDEVICE_CHECKSUM, 0); + if (ret < 0) + break; + } + + if (block->is_ychksum_present) + crc_chksum = 0; + + nr_cmds = 0; + + while (nr_cmds < block->nr_cmds) { + data = block->data + nr_cmds * 4; + + book = data[0]; + page = data[1]; + offset = data[2]; + val = data[3]; + + nr_cmds++; + /* Single byte write */ + if (offset <= 0x7F) { + ret = tasdevice_spi_dev_write(tas_priv, + TASDEVICE_REG(book, page, offset), + val); + if (ret < 0) + break; + if (block->is_ychksum_present) { + ret = tasdev_bytes_chksum(tas_priv, + block, book, page, offset, + 1, val, &crc_chksum); + if (ret < 0) + break; + } + continue; + } + /* sleep command */ + if (offset == 0x81) { + /* book -- data[0] page -- data[1] */ + sleep_time = ((book << 8) + page)*1000; + fsleep(sleep_time); + continue; + } + /* Multiple bytes write */ + if (offset == 0x85) { + data += 4; + len = (book << 8) + page; + book = data[0]; + page = data[1]; + offset = data[2]; + ret = tasdev_multibytes_wr(tas_priv, + block, book, page, offset, data, + len, &nr_cmds, &crc_chksum); + if (ret < 0) + break; + } + } + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) { + /* err in current device, skip it */ + break; + } + + if (block->is_pchksum_present) { + ret = tasdev_block_chksum(tas_priv, block); + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) { + /* err in current device, skip it */ + break; + } + } + + if (block->is_ychksum_present) { + /* TBD, open it when FW ready */ + dev_err(tas_priv->dev, + "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n", + block->ychksum, crc_chksum); + + tas_priv->err_code &= + ~ERROR_YRAM_CRCCHK; + ret = 0; + } + /* skip current blk */ + break; + } + + return ret; +} + +/* Firmware block load function. */ +static int tasdevice_load_block(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + int ret = 0; + + block->nr_retry = 6; + if (tas_priv->is_loading == false) + return 0; + ret = tasdev_load_blk(tas_priv, block); + if (ret < 0) + dev_err(tas_priv->dev, "Blk (%d) load error\n", block->type); + + return ret; +} + +/* + * Select firmware binary parser & load callback functions by ppc3 version + * and firmware binary version. + */ +static int dspfw_default_callback(struct tasdevice_priv *tas_priv, + unsigned int drv_ver, unsigned int ppcver) +{ + int rc = 0; + + if (drv_ver == 0x100) { + if (ppcver >= PPC3_VERSION_BASE) { + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + } else if (ppcver == 0x00) { + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + } else { + dev_err(tas_priv->dev, + "Wrong PPCVer :0x%08x\n", ppcver); + rc = -EINVAL; + } + } else { + dev_err(tas_priv->dev, "Wrong DrvVer : 0x%02x\n", drv_ver); + rc = -EINVAL; + } + + return rc; +} + +/* DSP firmware binary file header parser function. */ +static int fw_parse_header(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &tas_fmw->fw_hdr; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &fw_hdr->fixed_hdr; + static const unsigned char magic_number[] = {0x35, 0x35, 0x35, 0x32, }; + const unsigned char *buf = (unsigned char *)fmw->data; + + if (offset + 92 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + if (memcmp(&buf[offset], magic_number, 4)) { + dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + /* + * Convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + fw_fixed_hdr->fwsize = get_unaligned_be32(&buf[offset]); + offset += 4; + if (fw_fixed_hdr->fwsize != fmw->size) { + dev_err(tas_priv->dev, "File size not match, %lu %u", + (unsigned long)fmw->size, fw_fixed_hdr->fwsize); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_fixed_hdr->ppcver = get_unaligned_be32(&buf[offset]); + offset += 8; + fw_fixed_hdr->drv_ver = get_unaligned_be32(&buf[offset]); + offset += 72; + +out: + return offset; +} + +/* DSP firmware binary file parser function. */ +static int tasdevice_dspfw_ready(const struct firmware *fmw, void *context) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr; + struct tasdevice_fw *tas_fmw; + int offset = 0, ret = 0; + + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n", + __func__, tas_priv->coef_binaryname); + return -EINVAL; + } + + tas_priv->fmw = kzalloc(sizeof(*tas_priv->fmw), GFP_KERNEL); + if (!tas_priv->fmw) + return -ENOMEM; + tas_fmw = tas_priv->fmw; + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset); + + if (offset == -EINVAL) + return -EINVAL; + + fw_fixed_hdr = &tas_fmw->fw_hdr.fixed_hdr; + /* Support different versions of firmware */ + switch (fw_fixed_hdr->drv_ver) { + case 0x301: + case 0x302: + case 0x502: + case 0x503: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + break; + case 0x202: + case 0x400: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + break; + default: + ret = dspfw_default_callback(tas_priv, + fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver); + if (ret) + return ret; + break; + } + + offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset); + if (offset < 0) + return offset; + + offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw, + offset); + if (offset < 0) + return offset; + + offset = tas_priv->fw_parse_configuration_data(tas_priv, + tas_fmw, fmw, offset); + if (offset < 0) + ret = offset; + + return ret; +} + +/* DSP firmware binary file parser function. */ +int tasdevice_spi_dsp_parser(void *context) +{ + struct tasdevice_priv *tas_priv = context; + const struct firmware *fw_entry; + int ret; + + ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, + tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: load %s error\n", __func__, + tas_priv->coef_binaryname); + return ret; + } + + ret = tasdevice_dspfw_ready(fw_entry, tas_priv); + release_firmware(fw_entry); + fw_entry = NULL; + + return ret; +} + +/* DSP firmware program block data remove function. */ +static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (!prog) + return; + + tas_dt = &prog->dev_data; + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &tas_dt->dev_blks[i]; + kfree(blk->data); + } + kfree(tas_dt->dev_blks); +} + +/* DSP firmware program block data remove function. */ +static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_prog_blk_remove(&prog[i]); + kfree(prog); +} + +/* DSP firmware config block data remove function. */ +static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (cfg) { + tas_dt = &cfg->dev_data; + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &tas_dt->dev_blks[i]; + kfree(blk->data); + } + kfree(tas_dt->dev_blks); + } +} + +/* DSP firmware config remove function. */ +static void tasdev_dsp_cfg_remove(struct tasdevice_config *config, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_cfg_blk_remove(&config[i]); + kfree(config); +} + +/* DSP firmware remove function. */ +void tasdevice_spi_dsp_remove(void *context) +{ + struct tasdevice_priv *tas_dev = context; + + if (!tas_dev->fmw) + return; + + if (tas_dev->fmw->programs) + tasdev_dsp_prog_remove(tas_dev->fmw->programs, + tas_dev->fmw->nr_programs); + if (tas_dev->fmw->configs) + tasdev_dsp_cfg_remove(tas_dev->fmw->configs, + tas_dev->fmw->nr_configurations); + kfree(tas_dev->fmw); + tas_dev->fmw = NULL; +} + +/* DSP firmware calibration data remove function. */ +static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw) +{ + struct tasdevice_calibration *calibration; + struct tasdev_blk *block; + unsigned int blks; + int i; + + if (!tas_fmw->calibrations) + goto out; + + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + calibration = &tas_fmw->calibrations[i]; + if (!calibration) + continue; + + if (!calibration->dev_data.dev_blks) + continue; + + for (blks = 0; blks < calibration->dev_data.nr_blk; blks++) { + block = &calibration->dev_data.dev_blks[blks]; + if (!block) + continue; + kfree(block->data); + } + kfree(calibration->dev_data.dev_blks); + } + kfree(tas_fmw->calibrations); +out: + kfree(tas_fmw); +} + +/* Calibration data from firmware remove function. */ +void tasdevice_spi_calbin_remove(void *context) +{ + struct tasdevice_priv *tas_priv = context; + + if (tas_priv->cali_data_fmw) { + tas2781_clear_calfirmware(tas_priv->cali_data_fmw); + tas_priv->cali_data_fmw = NULL; + } +} + +/* Configuration remove function. */ +void tasdevice_spi_config_info_remove(void *context) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_rca *rca = &tas_priv->rcabin; + struct tasdevice_config_info **ci = rca->cfg_info; + unsigned int i, j; + + if (!ci) + return; + for (i = 0; i < rca->ncfgs; i++) { + if (!ci[i]) + continue; + if (ci[i]->blk_data) { + for (j = 0; j < ci[i]->real_nblocks; j++) { + if (!ci[i]->blk_data[j]) + continue; + kfree(ci[i]->blk_data[j]->regdata); + kfree(ci[i]->blk_data[j]); + } + kfree(ci[i]->blk_data); + } + kfree(ci[i]); + } + kfree(ci); +} + +/* DSP firmware program block data load function. */ +static int tasdevice_load_data(struct tasdevice_priv *tas_priv, + struct tasdevice_data *dev_data) +{ + struct tasdev_blk *block; + unsigned int i; + int ret = 0; + + for (i = 0; i < dev_data->nr_blk; i++) { + block = &dev_data->dev_blks[i]; + ret = tas_priv->tasdevice_load_block(tas_priv, block); + if (ret < 0) + break; + } + + return ret; +} + +/* DSP firmware program load interface function. */ +int tasdevice_spi_prmg_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + struct tasdevice_config *conf; + int ret = 0; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + return -EINVAL; + } + if (prm_no >= 0 && prm_no <= tas_fmw->nr_programs) { + tas_priv->cur_conf = 0; + tas_priv->is_loading = true; + program = &tas_fmw->programs[prm_no]; + ret = tasdevice_load_data(tas_priv, &program->dev_data); + if (ret < 0) { + dev_err(tas_priv->dev, "Program failed %d.\n", ret); + return ret; + } + tas_priv->cur_prog = prm_no; + + conf = &tas_fmw->configs[tas_priv->cur_conf]; + ret = tasdevice_load_data(tas_priv, &conf->dev_data); + if (ret < 0) + dev_err(tas_priv->dev, "Config failed %d.\n", ret); + } else { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + return -EINVAL; + } + + return ret; +} + +/* RCABIN configuration switch interface function. */ +void tasdevice_spi_tuning_switch(void *context, int state) +{ + struct tasdevice_priv *tas_priv = context; + int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + return; + } + + if (state == 0) + tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_POWER_UP); + else + tasdevice_spi_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_SHUTDOWN); +} diff --git a/sound/pci/lola/lola.h b/sound/pci/lola/lola.h index 0ff772cf66e6..25f72f9e3f9b 100644 --- a/sound/pci/lola/lola.h +++ b/sound/pci/lola/lola.h @@ -499,8 +499,6 @@ int lola_init_mixer_widget(struct lola *chip, int nid); void lola_free_mixer(struct lola *chip); int lola_create_mixer(struct lola *chip); int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute); -void lola_save_mixer(struct lola *chip); -void lola_restore_mixer(struct lola *chip); int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update); /* proc */ diff --git a/sound/pci/lola/lola_clock.c b/sound/pci/lola/lola_clock.c index cafd30e30913..2e73fbf335ed 100644 --- a/sound/pci/lola/lola_clock.c +++ b/sound/pci/lola/lola_clock.c @@ -35,7 +35,7 @@ unsigned int lola_sample_rate_convert(unsigned int coded) default: return 0; /* error */ } - /* ajustement */ + /* adjustement */ switch (coded & 0x60) { case (0 << 5): break; case (1 << 5): freq = (freq * 999) / 1000; break; diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c index 6b162489cb5f..9cb26a8a4e1a 100644 --- a/sound/pci/lola/lola_mixer.c +++ b/sound/pci/lola/lola_mixer.c @@ -336,49 +336,6 @@ int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute) return lola_codec_flush(chip); } -void lola_save_mixer(struct lola *chip) -{ - /* mute analog output */ - if (chip->mixer.array_saved) { - /* store contents of mixer array */ - memcpy_fromio(chip->mixer.array_saved, chip->mixer.array, - sizeof(*chip->mixer.array)); - } - lola_setup_all_analog_gains(chip, PLAY, true); /* output mute */ -} - -void lola_restore_mixer(struct lola *chip) -{ - int i; - - /*lola_reset_setups(chip);*/ - if (chip->mixer.array_saved) { - /* restore contents of mixer array */ - memcpy_toio(chip->mixer.array, chip->mixer.array_saved, - sizeof(*chip->mixer.array)); - /* inform micro-controller about all restored values - * and ignore return values - */ - for (i = 0; i < chip->mixer.src_phys_ins; i++) - lola_codec_write(chip, chip->mixer.nid, - LOLA_VERB_SET_SOURCE_GAIN, - i, 0); - for (i = 0; i < chip->mixer.src_stream_outs; i++) - lola_codec_write(chip, chip->mixer.nid, - LOLA_VERB_SET_SOURCE_GAIN, - chip->mixer.src_stream_out_ofs + i, 0); - for (i = 0; i < chip->mixer.dest_stream_ins; i++) - lola_codec_write(chip, chip->mixer.nid, - LOLA_VERB_SET_DESTINATION_GAIN, - i, 0); - for (i = 0; i < chip->mixer.dest_phys_outs; i++) - lola_codec_write(chip, chip->mixer.nid, - LOLA_VERB_SET_DESTINATION_GAIN, - chip->mixer.dest_phys_out_ofs + i, 0); - lola_codec_flush(chip); - } -} - /* */ diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 11ba7d4eac2a..44085237fb44 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -696,7 +696,9 @@ snd_nm256_playback_copy(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - return copy_from_iter_toio(s->bufptr + pos, src, count); + if (copy_from_iter_toio(s->bufptr + pos, count, src) != count) + return -EFAULT; + return 0; } /* @@ -710,7 +712,9 @@ snd_nm256_capture_copy(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - return copy_to_iter_fromio(dst, s->bufptr + pos, count); + if (copy_to_iter_fromio(s->bufptr + pos, count, dst) != count) + return -EFAULT; + return 0; } #endif /* !__i386__ */ diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index c346f42befc2..ff7439634d76 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -854,11 +854,9 @@ static struct pci_driver oxygen_driver = { .name = KBUILD_MODNAME, .id_table = oxygen_ids, .probe = generic_oxygen_probe, -#ifdef CONFIG_PM_SLEEP .driver = { - .pm = &oxygen_pci_pm, + .pm = pm_sleep_ptr(&oxygen_pci_pm), }, -#endif }; module_pci_driver(oxygen_driver); diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index 0cae640708f3..820026daf838 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -161,9 +161,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, const struct pci_device_id *id ) ); -#ifdef CONFIG_PM_SLEEP extern const struct dev_pm_ops oxygen_pci_pm; -#endif void oxygen_pci_shutdown(struct pci_dev *pci); /* oxygen_mixer.c */ diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 92ffe9dc20c5..9340d3c9ffd6 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -713,7 +713,6 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, } EXPORT_SYMBOL(oxygen_pci_probe); -#ifdef CONFIG_PM_SLEEP static int oxygen_pci_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -789,9 +788,7 @@ static int oxygen_pci_resume(struct device *dev) return 0; } -SIMPLE_DEV_PM_OPS(oxygen_pci_pm, oxygen_pci_suspend, oxygen_pci_resume); -EXPORT_SYMBOL(oxygen_pci_pm); -#endif /* CONFIG_PM_SLEEP */ +EXPORT_SIMPLE_DEV_PM_OPS(oxygen_pci_pm, oxygen_pci_suspend, oxygen_pci_resume); void oxygen_pci_shutdown(struct pci_dev *pci) { diff --git a/sound/pci/oxygen/se6x.c b/sound/pci/oxygen/se6x.c index 17650a5b1bfa..9d009015d97e 100644 --- a/sound/pci/oxygen/se6x.c +++ b/sound/pci/oxygen/se6x.c @@ -137,11 +137,9 @@ static struct pci_driver se6x_driver = { .name = KBUILD_MODNAME, .id_table = se6x_ids, .probe = se6x_probe, -#ifdef CONFIG_PM_SLEEP .driver = { - .pm = &oxygen_pci_pm, + .pm = pm_sleep_ptr(&oxygen_pci_pm), }, -#endif .shutdown = oxygen_pci_shutdown, }; diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 2e405133371f..ded62199da7f 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -82,11 +82,9 @@ static struct pci_driver xonar_driver = { .name = KBUILD_MODNAME, .id_table = xonar_ids, .probe = xonar_probe, -#ifdef CONFIG_PM_SLEEP .driver = { - .pm = &oxygen_pci_pm, + .pm = pm_sleep_ptr(&oxygen_pci_pm), }, -#endif .shutdown = oxygen_pci_shutdown, }; diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 02144bbee6d5..a8c2ceaadef5 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -256,8 +256,10 @@ static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - return copy_from_iter_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, - src, count); + if (copy_from_iter_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, + count, src) != count) + return -EFAULT; + return 0; } /* copy callback for halfduplex mode */ @@ -267,9 +269,10 @@ static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - return copy_to_iter_fromio(dst, - rme32->iobase + RME32_IO_DATA_BUFFER + pos, - count); + if (copy_to_iter_fromio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, + count, dst) != count) + return -EFAULT; + return 0; } /* diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index d50ad25574ad..1265a7efac60 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -322,8 +322,10 @@ snd_rme96_playback_copy(struct snd_pcm_substream *substream, { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - return copy_from_iter_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, - src, count); + if (copy_from_iter_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, + count, src) != count) + return -EFAULT; + return 0; } static int @@ -333,9 +335,10 @@ snd_rme96_capture_copy(struct snd_pcm_substream *substream, { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - return copy_to_iter_fromio(dst, - rme96->iobase + RME96_IO_REC_BUFFER + pos, - count); + if (copy_to_iter_fromio(rme96->iobase + RME96_IO_REC_BUFFER + pos, + count, dst) != count) + return -EFAULT; + return 0; } /* diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 1c504a591948..fd3dfbad397a 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -3444,7 +3444,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0)); snd_iprintf(buffer, "MIDI2 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut1)); snd_iprintf(buffer, "MIDI2 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn1)); - snd_iprintf(buffer, "Use Midi Tasklet: %s\n", hdsp->use_midi_work ? "on" : "off"); + snd_iprintf(buffer, "Use Midi Tasklet: %s\n", str_on_off(hdsp->use_midi_work)); snd_iprintf(buffer, "\n"); @@ -3452,8 +3452,8 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) snd_iprintf(buffer, "Buffer Size (Latency): %d samples (2 periods of %lu bytes)\n", x, (unsigned long) hdsp->period_bytes); snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", hdsp_hw_pointer(hdsp)); - snd_iprintf(buffer, "Precise pointer: %s\n", hdsp->precise_ptr ? "on" : "off"); - snd_iprintf(buffer, "Line out: %s\n", (hdsp->control_register & HDSP_LineOut) ? "on" : "off"); + snd_iprintf(buffer, "Precise pointer: %s\n", str_on_off(hdsp->precise_ptr)); + snd_iprintf(buffer, "Line out: %s\n", str_on_off(hdsp->control_register & HDSP_LineOut)); snd_iprintf(buffer, "Firmware version: %d\n", (status2&HDSP_version0)|(status2&HDSP_version1)<<1|(status2&HDSP_version2)<<2); @@ -3750,8 +3750,8 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) snd_iprintf(buffer, "Phones Gain : %s\n", tmp); snd_iprintf(buffer, "XLR Breakout Cable : %s\n", - hdsp_toggle_setting(hdsp, HDSP_XLRBreakoutCable) ? - "yes" : "no"); + str_yes_no(hdsp_toggle_setting(hdsp, + HDSP_XLRBreakoutCable))); if (hdsp->control_register & HDSP_AnalogExtensionBoard) snd_iprintf(buffer, "AEB : on (ADAT1 internal)\n"); diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index d7290463d654..f89718b19d23 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -4927,14 +4927,14 @@ snd_hdspm_proc_read_madi(struct snd_info_entry *entry, x, (unsigned long) hdspm->period_bytes); snd_iprintf(buffer, "Line out: %s\n", - (hdspm->control_register & HDSPM_LineOut) ? "on " : "off"); + str_on_off(hdspm->control_register & HDSPM_LineOut)); snd_iprintf(buffer, "ClearTrackMarker = %s, Transmit in %s Channel Mode, " "Auto Input %s\n", - (hdspm->control_register & HDSPM_clr_tms) ? "on" : "off", + str_on_off(hdspm->control_register & HDSPM_clr_tms), (hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56", - (hdspm->control_register & HDSPM_AutoInp) ? "on" : "off"); + str_on_off(hdspm->control_register & HDSPM_AutoInp)); if (!(hdspm->control_register & HDSPM_ClockModeMaster)) @@ -5088,12 +5088,9 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, snd_iprintf(buffer, "ClearTrackMarker %s, Emphasis %s, Dolby %s\n", - (hdspm-> - control_register & HDSPM_clr_tms) ? "on" : "off", - (hdspm-> - control_register & HDSPM_Emphasis) ? "on" : "off", - (hdspm-> - control_register & HDSPM_Dolby) ? "on" : "off"); + str_on_off(hdspm->control_register & HDSPM_clr_tms), + str_on_off(hdspm->control_register & HDSPM_Emphasis), + str_on_off(hdspm->control_register & HDSPM_Dolby)); pref_syncref = hdspm_pref_sync_ref(hdspm); diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index d066c70ae160..5b8dd7b0a02c 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -1561,8 +1561,7 @@ snd_rme9652_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buff x, (unsigned long) rme9652->period_bytes); snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", rme9652_hw_pointer(rme9652)); - snd_iprintf(buffer, "Passthru: %s\n", - rme9652->passthru ? "yes" : "no"); + snd_iprintf(buffer, "Passthru: %s\n", str_yes_no(rme9652->passthru)); if ((rme9652->control_register & (RME9652_Master | RME9652_wsel)) == 0) { snd_iprintf(buffer, "Clock mode: autosync\n"); @@ -1685,7 +1684,7 @@ snd_rme9652_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buff snd_iprintf(buffer, "\n"); snd_iprintf(buffer, "Timecode signal: %s\n", - (status & RME9652_tc_valid) ? "yes" : "no"); + str_yes_no(status & RME9652_tc_valid)); /* thru modes */ diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index f91cbf6eeca0..c30eaf1038e7 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -1118,7 +1118,7 @@ static void snd_sonicvibes_proc_read(struct snd_info_entry *entry, tmp = sonic->srs_space & 0x0f; snd_iprintf(buffer, "SRS 3D : %s\n", - sonic->srs_space & 0x80 ? "off" : "on"); + str_off_on(sonic->srs_space & 0x80)); snd_iprintf(buffer, "SRS Space : %s\n", tmp == 0x00 ? "100%" : tmp == 0x01 ? "75%" : @@ -1135,9 +1135,9 @@ static void snd_sonicvibes_proc_read(struct snd_info_entry *entry, tmp == 0x00 ? "on-board ROM" : tmp == 0x01 ? "PCI bus" : "on-board ROM + PCI bus"); tmp = sonic->mpu_switch; - snd_iprintf(buffer, "Onboard synth : %s\n", tmp & 0x01 ? "on" : "off"); - snd_iprintf(buffer, "Ext. Rx to synth : %s\n", tmp & 0x02 ? "on" : "off"); - snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", tmp & 0x04 ? "on" : "off"); + snd_iprintf(buffer, "Onboard synth : %s\n", str_on_off(tmp & 0x01)); + snd_iprintf(buffer, "Ext. Rx to synth : %s\n", str_on_off(tmp & 0x02)); + snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", str_on_off(tmp & 0x04)); } static void snd_sonicvibes_proc_init(struct sonicvibes *sonic) diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index e98eea1e6d81..8039f445bee2 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3278,9 +3278,9 @@ static void snd_trident_proc_read(struct snd_info_entry *entry, snd_iprintf(buffer, "Spurious IRQs : %d\n", trident->spurious_irq_count); snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta); if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) - snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off"); + snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", str_on_off(trident->spdif_ctrl == 0x28)); if (trident->device == TRIDENT_DEVICE_ID_NX) { - snd_iprintf(buffer, "Rear Speakers : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off"); + snd_iprintf(buffer, "Rear Speakers : %s\n", str_on_off(trident->ac97_ctrl & 0x00000010)); if (trident->tlb.entries) { snd_iprintf(buffer,"\nVirtual Memory\n"); snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size); diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 468a6a20dc1e..fdb039896205 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -204,7 +204,6 @@ static int snd_vx222_probe(struct pci_dev *pci, return 0; } -#ifdef CONFIG_PM_SLEEP static int snd_vx222_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); @@ -221,18 +220,14 @@ static int snd_vx222_resume(struct device *dev) return snd_vx_resume(&vx->core); } -static SIMPLE_DEV_PM_OPS(snd_vx222_pm, snd_vx222_suspend, snd_vx222_resume); -#define SND_VX222_PM_OPS &snd_vx222_pm -#else -#define SND_VX222_PM_OPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(snd_vx222_pm, snd_vx222_suspend, snd_vx222_resume); static struct pci_driver vx222_driver = { .name = KBUILD_MODNAME, .id_table = snd_vx222_ids, .probe = snd_vx222_probe, .driver = { - .pm = SND_VX222_PM_OPS, + .pm = pm_ptr(&snd_vx222_pm), }, }; diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c index 3d3513d9def5..4ce81ac7f700 100644 --- a/sound/ppc/keywest.c +++ b/sound/ppc/keywest.c @@ -61,12 +61,6 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter) return -ENODEV; } - /* - * Let i2c-core delete that device on driver removal. - * This is safe because i2c-core holds the core_lock mutex for us. - */ - list_add_tail(&keywest_ctx->client->detected, - &to_i2c_driver(keywest_ctx->client->dev.driver)->clients); return 0; } @@ -99,6 +93,7 @@ static struct i2c_driver keywest_driver = { void snd_pmac_keywest_cleanup(struct pmac_keywest *i2c) { if (keywest_ctx && keywest_ctx == i2c) { + i2c_unregister_device(keywest_ctx->client); i2c_del_driver(&keywest_driver); keywest_ctx = NULL; } diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index 3f5422145c5e..84a4b17a0cc2 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -312,8 +312,7 @@ static int snd_sh_dac_create(struct snd_card *card, chip->card = card; - hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - chip->hrtimer.function = sh_dac_audio_timer; + hrtimer_setup(&chip->hrtimer, sh_dac_audio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); dac_audio_reset(chip); chip->rate = 8000; diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 5efba76abb31..8b7d51266f81 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -81,6 +81,13 @@ config SND_SOC_UTILS_KUNIT_TEST help If you want to perform tests on ALSA SoC utils library say Y here. +config SND_SOC_OPS_KUNIT_TEST + tristate "KUnit tests for SoC ops" + depends on KUNIT + default KUNIT_ALL_TESTS + help + If you want to perform tests on ALSA SoC ops library say Y here. + config SND_SOC_ACPI tristate diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 08baaa11d813..358e227c5ab6 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -21,6 +21,10 @@ ifneq ($(CONFIG_SND_SOC_UTILS_KUNIT_TEST),) obj-$(CONFIG_SND_SOC_UTILS_KUNIT_TEST) += soc-utils-test.o endif +ifneq ($(CONFIG_SND_SOC_OPS_KUNIT_TEST),) +obj-$(CONFIG_SND_SOC_OPS_KUNIT_TEST) += soc-ops-test.o +endif + ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-y += soc-generic-dmaengine-pcm.o endif diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index c7590d4989bb..c7daae392d74 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -105,7 +105,7 @@ config SND_SOC_AMD_ACP6x config SND_SOC_AMD_YC_MACH tristate "AMD YC support for DMIC" select SND_SOC_DMIC - depends on SND_SOC_AMD_ACP6x + depends on SND_SOC_AMD_ACP6x && ACPI help This option enables machine driver for Yellow Carp platform using dmic. ACP IP has PDM Decoder block with DMA controller. @@ -161,15 +161,15 @@ config SND_SOC_AMD_SOUNDWIRE If unsure select "N". config SND_SOC_AMD_PS - tristate "AMD Audio Coprocessor-v6.3 Pink Sardine support" + tristate "AMD Audio Coprocessor-v6.3/v7.0/v7.1 support" select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE select SND_SOC_ACPI_AMD_MATCH depends on X86 && PCI && ACPI help - This option enables Audio Coprocessor i.e ACP v6.3 support on - AMD Pink sardine platform. By enabling this flag build will be - triggered for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire - DMA driver. + This option enables Audio Coprocessor i.e ACP6.3/ACP7.0/ACP7.1 + variants support. By enabling this flag build will be triggered + for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire DMA + driver. Say m if you have such a device. If unsure select "N". diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index 03f3fcbba5af..b9432052c638 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -28,6 +28,9 @@ config SND_SOC_AMD_ACP_LEGACY_COMMON config SND_SOC_AMD_ACP_I2S tristate +config SND_SOC_AMD_ACPI_MACH + tristate + config SND_SOC_AMD_ACP_PCM tristate select SND_SOC_ACPI if ACPI @@ -37,6 +40,7 @@ config SND_SOC_AMD_ACP_PCI depends on X86 && PCI depends on ACPI select SND_SOC_AMD_ACP_LEGACY_COMMON + select SND_SOC_AMD_ACPI_MACH help This options enables generic PCI driver for ACP device. @@ -47,6 +51,7 @@ config SND_AMD_ASOC_RENOIR select SND_SOC_AMD_ACP_I2S select SND_SOC_AMD_ACP_PDM select SND_SOC_AMD_ACP_LEGACY_COMMON + select SND_SOC_AMD_ACPI_MACH depends on X86 && PCI help This option enables Renoir I2S support on AMD platform. @@ -58,6 +63,8 @@ config SND_AMD_ASOC_REMBRANDT select SND_SOC_AMD_ACP_I2S select SND_SOC_AMD_ACP_PDM select SND_SOC_AMD_ACP_LEGACY_COMMON + select SND_SOC_AMD_ACPI_MACH + depends on AMD_NODE depends on X86 && PCI help This option enables Rembrandt I2S support on AMD platform. @@ -68,10 +75,12 @@ config SND_AMD_ASOC_ACP63 tristate "AMD ACP ASOC ACP6.3 Support" depends on X86 && PCI depends on ACPI + depends on AMD_NODE select SND_SOC_AMD_ACP_PCM select SND_SOC_AMD_ACP_I2S select SND_SOC_AMD_ACP_PDM select SND_SOC_AMD_ACP_LEGACY_COMMON + select SND_SOC_AMD_ACPI_MACH help This option enables Acp6.3 I2S support on AMD platform. Say Y if you want to enable AUDIO on ACP6.3 @@ -81,10 +90,12 @@ config SND_AMD_ASOC_ACP70 tristate "AMD ACP ASOC Acp7.0 Support" depends on X86 && PCI depends on ACPI + depends on AMD_NODE select SND_SOC_AMD_ACP_PCM select SND_SOC_AMD_ACP_I2S select SND_SOC_AMD_ACP_PDM select SND_SOC_AMD_ACP_LEGACY_COMMON + select SND_SOC_AMD_ACPI_MACH help This option enables Acp7.0 PDM support on AMD platform. Say Y if you want to enable AUDIO on ACP7.0 @@ -156,6 +167,7 @@ config SND_SOC_AMD_LEGACY_SDW_MACH select SND_SOC_RT712_SDCA_SDW select SND_SOC_RT712_SDCA_DMIC_SDW select SND_SOC_RT1316_SDW + select SND_SOC_RT1320_SDW select SND_SOC_RT715_SDW select SND_SOC_RT715_SDCA_SDW select SND_SOC_RT722_SDCA_SDW diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index bb2702036338..08220b9a3802 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -11,6 +11,7 @@ snd-acp-pdm-y := acp-pdm.o snd-acp-legacy-common-y := acp-legacy-common.o snd-acp-pci-y := acp-pci.o snd-amd-sdw-acpi-y := amd-sdw-acpi.o +snd-amd-acpi-mach-y := amd-acpi-mach.o #platform specific driver snd-acp-renoir-y := acp-renoir.o @@ -22,7 +23,7 @@ snd-acp70-y := acp70.o snd-acp-mach-y := acp-mach-common.o snd-acp-legacy-mach-y := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o snd-acp-sof-mach-y := acp-sof-mach.o -snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o +snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o snd-acp-sdw-mach-y := acp-sdw-mach-common.o snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o @@ -32,6 +33,7 @@ obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o obj-$(CONFIG_SND_SOC_AMD_ACP_LEGACY_COMMON) += snd-acp-legacy-common.o obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o +obj-$(CONFIG_SND_SOC_AMD_ACPI_MACH) += snd-amd-acpi-mach.o obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 1f59ee248771..a38409dd1d34 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -31,14 +31,10 @@ #define ACP63_LRCLK_DIV_FIELD GENMASK(12, 2) #define ACP63_BCLK_DIV_FIELD GENMASK(23, 13) -static inline void acp_set_i2s_clk(struct acp_dev_data *adata, int dai_id) +static inline void acp_set_i2s_clk(struct acp_chip_info *chip, int dai_id) { u32 i2s_clk_reg, val; - struct acp_chip_info *chip; - struct device *dev; - dev = adata->dev; - chip = dev_get_platdata(dev); switch (dai_id) { case I2S_SP_INSTANCE: i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN; @@ -55,36 +51,37 @@ static inline void acp_set_i2s_clk(struct acp_dev_data *adata, int dai_id) } val = I2S_MASTER_MODE_ENABLE; - if (adata->tdm_mode) + if (chip->tdm_mode) val |= BIT(1); switch (chip->acp_rev) { case ACP63_PCI_ID: case ACP70_PCI_ID: case ACP71_PCI_ID: - val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, adata->lrclk_div); - val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, adata->bclk_div); + val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, chip->lrclk_div); + val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, chip->bclk_div); break; default: - val |= FIELD_PREP(LRCLK_DIV_FIELD, adata->lrclk_div); - val |= FIELD_PREP(BCLK_DIV_FIELD, adata->bclk_div); + val |= FIELD_PREP(LRCLK_DIV_FIELD, chip->lrclk_div); + val |= FIELD_PREP(BCLK_DIV_FIELD, chip->bclk_div); } - writel(val, adata->acp_base + i2s_clk_reg); + writel(val, chip->base + i2s_clk_reg); } static int acp_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - struct acp_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = cpu_dai->component->dev; + struct acp_chip_info *chip = dev_get_platdata(dev); int mode; mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK; switch (mode) { case SND_SOC_DAIFMT_I2S: - adata->tdm_mode = TDM_DISABLE; + chip->tdm_mode = TDM_DISABLE; break; case SND_SOC_DAIFMT_DSP_A: - adata->tdm_mode = TDM_ENABLE; + chip->tdm_mode = TDM_ENABLE; break; default: return -EINVAL; @@ -96,7 +93,6 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas int slots, int slot_width) { struct device *dev = dai->component->dev; - struct acp_dev_data *adata = snd_soc_dai_get_drvdata(dai); struct acp_chip_info *chip; struct acp_stream *stream; int slot_len, no_of_slots; @@ -157,34 +153,35 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas slots = no_of_slots; - spin_lock_irq(&adata->acp_lock); - list_for_each_entry(stream, &adata->stream_list, list) { + spin_lock_irq(&chip->acp_lock); + list_for_each_entry(stream, &chip->stream_list, list) { switch (chip->acp_rev) { case ACP_RN_PCI_ID: case ACP_RMB_PCI_ID: if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK) - adata->tdm_tx_fmt[stream->dai_id - 1] = + chip->tdm_tx_fmt[stream->dai_id - 1] = FRM_LEN | (slots << 15) | (slot_len << 18); else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE) - adata->tdm_rx_fmt[stream->dai_id - 1] = + chip->tdm_rx_fmt[stream->dai_id - 1] = FRM_LEN | (slots << 15) | (slot_len << 18); break; case ACP63_PCI_ID: case ACP70_PCI_ID: case ACP71_PCI_ID: if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK) - adata->tdm_tx_fmt[stream->dai_id - 1] = + chip->tdm_tx_fmt[stream->dai_id - 1] = FRM_LEN | (slots << 13) | (slot_len << 18); else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE) - adata->tdm_rx_fmt[stream->dai_id - 1] = + chip->tdm_rx_fmt[stream->dai_id - 1] = FRM_LEN | (slots << 13) | (slot_len << 18); break; default: dev_err(dev, "Unknown chip revision %d\n", chip->acp_rev); + spin_unlock_irq(&chip->acp_lock); return -EINVAL; } } - spin_unlock_irq(&adata->acp_lock); + spin_unlock_irq(&chip->acp_lock); return 0; } @@ -192,15 +189,15 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_ struct snd_soc_dai *dai) { struct device *dev = dai->component->dev; - struct acp_dev_data *adata; + struct acp_chip_info *chip; struct acp_resource *rsrc; u32 val; u32 xfer_resolution; u32 reg_val, fmt_reg, tdm_fmt; u32 lrclk_div_val, bclk_div_val; - adata = snd_soc_dai_get_drvdata(dai); - rsrc = adata->rsrc; + chip = dev_get_platdata(dev); + rsrc = chip->rsrc; /* These values are as per Hardware Spec */ switch (params_format(params)) { @@ -239,7 +236,7 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_ dev_err(dev, "Invalid dai id %x\n", dai->driver->id); return -EINVAL; } - adata->xfer_tx_resolution[dai->driver->id - 1] = xfer_resolution; + chip->xfer_tx_resolution[dai->driver->id - 1] = xfer_resolution; } else { switch (dai->driver->id) { case I2S_BT_INSTANCE: @@ -258,22 +255,22 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_ dev_err(dev, "Invalid dai id %x\n", dai->driver->id); return -EINVAL; } - adata->xfer_rx_resolution[dai->driver->id - 1] = xfer_resolution; + chip->xfer_rx_resolution[dai->driver->id - 1] = xfer_resolution; } - val = readl(adata->acp_base + reg_val); + val = readl(chip->base + reg_val); val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK; val = val | (xfer_resolution << 3); - writel(val, adata->acp_base + reg_val); + writel(val, chip->base + reg_val); - if (adata->tdm_mode) { - val = readl(adata->acp_base + reg_val); - writel(val | BIT(1), adata->acp_base + reg_val); + if (chip->tdm_mode) { + val = readl(chip->base + reg_val); + writel(val | BIT(1), chip->base + reg_val); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - tdm_fmt = adata->tdm_tx_fmt[dai->driver->id - 1]; + tdm_fmt = chip->tdm_tx_fmt[dai->driver->id - 1]; else - tdm_fmt = adata->tdm_rx_fmt[dai->driver->id - 1]; - writel(tdm_fmt, adata->acp_base + fmt_reg); + tdm_fmt = chip->tdm_rx_fmt[dai->driver->id - 1]; + writel(tdm_fmt, chip->base + fmt_reg); } if (rsrc->soc_mclk) { @@ -376,8 +373,8 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_ default: break; } - adata->lrclk_div = lrclk_div_val; - adata->bclk_div = bclk_div_val; + chip->lrclk_div = lrclk_div_val; + chip->bclk_div = bclk_div_val; } return 0; } @@ -386,8 +383,8 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct { struct acp_stream *stream = substream->runtime->private_data; struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); - struct acp_resource *rsrc = adata->rsrc; + struct acp_chip_info *chip = dev_get_platdata(dev); + struct acp_resource *rsrc = chip->rsrc; u32 val, period_bytes, reg_val, ier_val, water_val, buf_size, buf_reg; period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size); @@ -397,20 +394,20 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - stream->bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream); + stream->bytescount = acp_get_byte_count(chip, stream->dai_id, substream->stream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { switch (dai->driver->id) { case I2S_BT_INSTANCE: - water_val = ACP_BT_TX_INTR_WATERMARK_SIZE(adata); + water_val = ACP_BT_TX_INTR_WATERMARK_SIZE(chip); reg_val = ACP_BTTDM_ITER; ier_val = ACP_BTTDM_IER; - buf_reg = ACP_BT_TX_RINGBUFSIZE(adata); + buf_reg = ACP_BT_TX_RINGBUFSIZE(chip); break; case I2S_SP_INSTANCE: - water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE(adata); + water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE(chip); reg_val = ACP_I2STDM_ITER; ier_val = ACP_I2STDM_IER; - buf_reg = ACP_I2S_TX_RINGBUFSIZE(adata); + buf_reg = ACP_I2S_TX_RINGBUFSIZE(chip); break; case I2S_HS_INSTANCE: water_val = ACP_HS_TX_INTR_WATERMARK_SIZE; @@ -425,16 +422,16 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct } else { switch (dai->driver->id) { case I2S_BT_INSTANCE: - water_val = ACP_BT_RX_INTR_WATERMARK_SIZE(adata); + water_val = ACP_BT_RX_INTR_WATERMARK_SIZE(chip); reg_val = ACP_BTTDM_IRER; ier_val = ACP_BTTDM_IER; - buf_reg = ACP_BT_RX_RINGBUFSIZE(adata); + buf_reg = ACP_BT_RX_RINGBUFSIZE(chip); break; case I2S_SP_INSTANCE: - water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE(adata); + water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE(chip); reg_val = ACP_I2STDM_IRER; ier_val = ACP_I2STDM_IER; - buf_reg = ACP_I2S_RX_RINGBUFSIZE(adata); + buf_reg = ACP_I2S_RX_RINGBUFSIZE(chip); break; case I2S_HS_INSTANCE: water_val = ACP_HS_RX_INTR_WATERMARK_SIZE; @@ -448,14 +445,14 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct } } - writel(period_bytes, adata->acp_base + water_val); - writel(buf_size, adata->acp_base + buf_reg); + writel(period_bytes, chip->base + water_val); + writel(buf_size, chip->base + buf_reg); if (rsrc->soc_mclk) - acp_set_i2s_clk(adata, dai->driver->id); - val = readl(adata->acp_base + reg_val); + acp_set_i2s_clk(chip, dai->driver->id); + val = readl(chip->base + reg_val); val = val | BIT(0); - writel(val, adata->acp_base + reg_val); - writel(1, adata->acp_base + ier_val); + writel(val, chip->base + reg_val); + writel(1, chip->base + ier_val); return 0; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: @@ -492,19 +489,19 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct return -EINVAL; } } - val = readl(adata->acp_base + reg_val); + val = readl(chip->base + reg_val); val = val & ~BIT(0); - writel(val, adata->acp_base + reg_val); - - if (!(readl(adata->acp_base + ACP_BTTDM_ITER) & BIT(0)) && - !(readl(adata->acp_base + ACP_BTTDM_IRER) & BIT(0))) - writel(0, adata->acp_base + ACP_BTTDM_IER); - if (!(readl(adata->acp_base + ACP_I2STDM_ITER) & BIT(0)) && - !(readl(adata->acp_base + ACP_I2STDM_IRER) & BIT(0))) - writel(0, adata->acp_base + ACP_I2STDM_IER); - if (!(readl(adata->acp_base + ACP_HSTDM_ITER) & BIT(0)) && - !(readl(adata->acp_base + ACP_HSTDM_IRER) & BIT(0))) - writel(0, adata->acp_base + ACP_HSTDM_IER); + writel(val, chip->base + reg_val); + + if (!(readl(chip->base + ACP_BTTDM_ITER) & BIT(0)) && + !(readl(chip->base + ACP_BTTDM_IRER) & BIT(0))) + writel(0, chip->base + ACP_BTTDM_IER); + if (!(readl(chip->base + ACP_I2STDM_ITER) & BIT(0)) && + !(readl(chip->base + ACP_I2STDM_IRER) & BIT(0))) + writel(0, chip->base + ACP_I2STDM_IER); + if (!(readl(chip->base + ACP_HSTDM_ITER) & BIT(0)) && + !(readl(chip->base + ACP_HSTDM_IRER) & BIT(0))) + writel(0, chip->base + ACP_HSTDM_IER); return 0; default: return -EINVAL; @@ -516,9 +513,8 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); - struct acp_chip_info *chip; - struct acp_resource *rsrc = adata->rsrc; + struct acp_chip_info *chip = dev_get_platdata(dev); + struct acp_resource *rsrc = chip->rsrc; struct acp_stream *stream = substream->runtime->private_data; u32 reg_dma_size = 0, reg_fifo_size = 0, reg_fifo_addr = 0; u32 phy_addr = 0, acp_fifo_addr = 0, ext_int_ctrl; @@ -528,56 +524,56 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d switch (dai->driver->id) { case I2S_SP_INSTANCE: if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - reg_dma_size = ACP_I2S_TX_DMA_SIZE(adata); + reg_dma_size = ACP_I2S_TX_DMA_SIZE(chip); acp_fifo_addr = rsrc->sram_pte_offset + SP_PB_FIFO_ADDR_OFFSET; - reg_fifo_addr = ACP_I2S_TX_FIFOADDR(adata); - reg_fifo_size = ACP_I2S_TX_FIFOSIZE(adata); + reg_fifo_addr = ACP_I2S_TX_FIFOADDR(chip); + reg_fifo_size = ACP_I2S_TX_FIFOSIZE(chip); if (chip->acp_rev >= ACP70_PCI_ID) phy_addr = ACP7x_I2S_SP_TX_MEM_WINDOW_START; else phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR(adata)); + writel(phy_addr, chip->base + ACP_I2S_TX_RINGBUFADDR(chip)); } else { - reg_dma_size = ACP_I2S_RX_DMA_SIZE(adata); + reg_dma_size = ACP_I2S_RX_DMA_SIZE(chip); acp_fifo_addr = rsrc->sram_pte_offset + SP_CAPT_FIFO_ADDR_OFFSET; - reg_fifo_addr = ACP_I2S_RX_FIFOADDR(adata); - reg_fifo_size = ACP_I2S_RX_FIFOSIZE(adata); + reg_fifo_addr = ACP_I2S_RX_FIFOADDR(chip); + reg_fifo_size = ACP_I2S_RX_FIFOSIZE(chip); if (chip->acp_rev >= ACP70_PCI_ID) phy_addr = ACP7x_I2S_SP_RX_MEM_WINDOW_START; else phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR(adata)); + writel(phy_addr, chip->base + ACP_I2S_RX_RINGBUFADDR(chip)); } break; case I2S_BT_INSTANCE: if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - reg_dma_size = ACP_BT_TX_DMA_SIZE(adata); + reg_dma_size = ACP_BT_TX_DMA_SIZE(chip); acp_fifo_addr = rsrc->sram_pte_offset + BT_PB_FIFO_ADDR_OFFSET; - reg_fifo_addr = ACP_BT_TX_FIFOADDR(adata); - reg_fifo_size = ACP_BT_TX_FIFOSIZE(adata); + reg_fifo_addr = ACP_BT_TX_FIFOADDR(chip); + reg_fifo_size = ACP_BT_TX_FIFOSIZE(chip); if (chip->acp_rev >= ACP70_PCI_ID) phy_addr = ACP7x_I2S_BT_TX_MEM_WINDOW_START; else phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR(adata)); + writel(phy_addr, chip->base + ACP_BT_TX_RINGBUFADDR(chip)); } else { - reg_dma_size = ACP_BT_RX_DMA_SIZE(adata); + reg_dma_size = ACP_BT_RX_DMA_SIZE(chip); acp_fifo_addr = rsrc->sram_pte_offset + BT_CAPT_FIFO_ADDR_OFFSET; - reg_fifo_addr = ACP_BT_RX_FIFOADDR(adata); - reg_fifo_size = ACP_BT_RX_FIFOSIZE(adata); + reg_fifo_addr = ACP_BT_RX_FIFOADDR(chip); + reg_fifo_size = ACP_BT_RX_FIFOSIZE(chip); if (chip->acp_rev >= ACP70_PCI_ID) phy_addr = ACP7x_I2S_BT_RX_MEM_WINDOW_START; else phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR(adata)); + writel(phy_addr, chip->base + ACP_BT_RX_RINGBUFADDR(chip)); } break; case I2S_HS_INSTANCE: @@ -592,7 +588,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d phy_addr = ACP7x_I2S_HS_TX_MEM_WINDOW_START; else phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_HS_TX_RINGBUFADDR); + writel(phy_addr, chip->base + ACP_HS_TX_RINGBUFADDR); } else { reg_dma_size = ACP_HS_RX_DMA_SIZE; acp_fifo_addr = rsrc->sram_pte_offset + @@ -604,7 +600,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d phy_addr = ACP7x_I2S_HS_RX_MEM_WINDOW_START; else phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_HS_RX_RINGBUFADDR); + writel(phy_addr, chip->base + ACP_HS_RX_RINGBUFADDR); } break; default: @@ -612,11 +608,11 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d return -EINVAL; } - writel(DMA_SIZE, adata->acp_base + reg_dma_size); - writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr); - writel(FIFO_SIZE, adata->acp_base + reg_fifo_size); + writel(DMA_SIZE, chip->base + reg_dma_size); + writel(acp_fifo_addr, chip->base + reg_fifo_addr); + writel(FIFO_SIZE, chip->base + reg_fifo_size); - ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); + ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used)); ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) | BIT(BT_RX_THRESHOLD(rsrc->offset)) | BIT(I2S_TX_THRESHOLD(rsrc->offset)) | @@ -624,7 +620,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d BIT(HS_RX_THRESHOLD(rsrc->offset)) | BIT(HS_TX_THRESHOLD(rsrc->offset)); - writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); + writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used)); return 0; } @@ -633,8 +629,8 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d { struct acp_stream *stream = substream->runtime->private_data; struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); - struct acp_resource *rsrc = adata->rsrc; + struct acp_chip_info *chip = dev_get_platdata(dev); + struct acp_resource *rsrc = chip->rsrc; unsigned int dir = substream->stream; unsigned int irq_bit = 0; diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c index 7acc7ed2e8cc..b4d68484e06d 100644 --- a/sound/soc/amd/acp/acp-legacy-common.c +++ b/sound/soc/amd/acp/acp-legacy-common.c @@ -13,32 +13,132 @@ */ #include "amd.h" +#include <linux/acpi.h> #include <linux/pci.h> #include <linux/export.h> +#include "../mach-config.h" + #define ACP_RENOIR_PDM_ADDR 0x02 #define ACP_REMBRANDT_PDM_ADDR 0x03 #define ACP63_PDM_ADDR 0x02 #define ACP70_PDM_ADDR 0x02 -void acp_enable_interrupts(struct acp_dev_data *adata) +struct acp_resource rn_rsrc = { + .offset = 20, + .no_of_ctrls = 1, + .irqp_used = 0, + .irq_reg_offset = 0x1800, + .scratch_reg_offset = 0x12800, + .sram_pte_offset = 0x02052800, +}; +EXPORT_SYMBOL_NS_GPL(rn_rsrc, "SND_SOC_ACP_COMMON"); + +struct acp_resource rmb_rsrc = { + .offset = 0, + .no_of_ctrls = 2, + .irqp_used = 1, + .soc_mclk = true, + .irq_reg_offset = 0x1a00, + .scratch_reg_offset = 0x12800, + .sram_pte_offset = 0x03802800, +}; +EXPORT_SYMBOL_NS_GPL(rmb_rsrc, "SND_SOC_ACP_COMMON"); + +struct acp_resource acp63_rsrc = { + .offset = 0, + .no_of_ctrls = 2, + .irqp_used = 1, + .soc_mclk = true, + .irq_reg_offset = 0x1a00, + .scratch_reg_offset = 0x12800, + .sram_pte_offset = 0x03802800, +}; +EXPORT_SYMBOL_NS_GPL(acp63_rsrc, "SND_SOC_ACP_COMMON"); + +struct acp_resource acp70_rsrc = { + .offset = 0, + .no_of_ctrls = 2, + .irqp_used = 1, + .soc_mclk = true, + .irq_reg_offset = 0x1a00, + .scratch_reg_offset = 0x10000, + .sram_pte_offset = 0x03800000, +}; +EXPORT_SYMBOL_NS_GPL(acp70_rsrc, "SND_SOC_ACP_COMMON"); + +static const struct snd_acp_hw_ops acp_common_hw_ops = { + /* ACP hardware initilizations */ + .acp_init = acp_init, + .acp_deinit = acp_deinit, + + /* ACP Interrupts*/ + .irq = acp_irq_handler, + .en_interrupts = acp_enable_interrupts, + .dis_interrupts = acp_disable_interrupts, +}; + +irqreturn_t acp_irq_handler(int irq, void *data) { - struct acp_resource *rsrc = adata->rsrc; + struct acp_chip_info *chip = data; + struct acp_resource *rsrc = chip->rsrc; + struct acp_stream *stream; + u16 i2s_flag = 0; + u32 ext_intr_stat, ext_intr_stat1; + + if (rsrc->no_of_ctrls == 2) + ext_intr_stat1 = readl(ACP_EXTERNAL_INTR_STAT(chip, (rsrc->irqp_used - 1))); + + ext_intr_stat = readl(ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used)); + + spin_lock(&chip->acp_lock); + list_for_each_entry(stream, &chip->stream_list, list) { + if (ext_intr_stat & stream->irq_bit) { + writel(stream->irq_bit, + ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used)); + snd_pcm_period_elapsed(stream->substream); + i2s_flag = 1; + } + if (chip->rsrc->no_of_ctrls == 2) { + if (ext_intr_stat1 & stream->irq_bit) { + writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(chip, + (rsrc->irqp_used - 1))); + snd_pcm_period_elapsed(stream->substream); + i2s_flag = 1; + } + } + } + spin_unlock(&chip->acp_lock); + if (i2s_flag) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +int acp_enable_interrupts(struct acp_chip_info *chip) +{ + struct acp_resource *rsrc; u32 ext_intr_ctrl; - writel(0x01, ACP_EXTERNAL_INTR_ENB(adata)); - ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); + rsrc = chip->rsrc; + writel(0x01, ACP_EXTERNAL_INTR_ENB(chip)); + ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used)); ext_intr_ctrl |= ACP_ERROR_MASK; - writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); + writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used)); + + return 0; } EXPORT_SYMBOL_NS_GPL(acp_enable_interrupts, "SND_SOC_ACP_COMMON"); -void acp_disable_interrupts(struct acp_dev_data *adata) +int acp_disable_interrupts(struct acp_chip_info *chip) { - struct acp_resource *rsrc = adata->rsrc; + struct acp_resource *rsrc; + + rsrc = chip->rsrc; + writel(ACP_EXT_INTR_STAT_CLEAR_MASK, ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used)); + writel(0x00, ACP_EXTERNAL_INTR_ENB(chip)); - writel(ACP_EXT_INTR_STAT_CLEAR_MASK, ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); - writel(0x00, ACP_EXTERNAL_INTR_ENB(adata)); + return 0; } EXPORT_SYMBOL_NS_GPL(acp_disable_interrupts, "SND_SOC_ACP_COMMON"); @@ -48,7 +148,7 @@ static void set_acp_pdm_ring_buffer(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct acp_stream *stream = runtime->private_data; struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); u32 physical_addr, pdm_size, period_bytes; @@ -57,29 +157,29 @@ static void set_acp_pdm_ring_buffer(struct snd_pcm_substream *substream, physical_addr = stream->reg_offset + MEM_WINDOW_START; /* Init ACP PDM Ring buffer */ - writel(physical_addr, adata->acp_base + ACP_WOV_RX_RINGBUFADDR); - writel(pdm_size, adata->acp_base + ACP_WOV_RX_RINGBUFSIZE); - writel(period_bytes, adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE); - writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL); + writel(physical_addr, chip->base + ACP_WOV_RX_RINGBUFADDR); + writel(pdm_size, chip->base + ACP_WOV_RX_RINGBUFSIZE); + writel(period_bytes, chip->base + ACP_WOV_RX_INTR_WATERMARK_SIZE); + writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL); } static void set_acp_pdm_clk(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); unsigned int pdm_ctrl; /* Enable default ACP PDM clk */ - writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL); - pdm_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL); + writel(PDM_CLK_FREQ_MASK, chip->base + ACP_WOV_CLK_CTRL); + pdm_ctrl = readl(chip->base + ACP_WOV_MISC_CTRL); pdm_ctrl |= PDM_MISC_CTRL_MASK; - writel(pdm_ctrl, adata->acp_base + ACP_WOV_MISC_CTRL); + writel(pdm_ctrl, chip->base + ACP_WOV_MISC_CTRL); set_acp_pdm_ring_buffer(substream, dai); } void restore_acp_pdm_params(struct snd_pcm_substream *substream, - struct acp_dev_data *adata) + struct acp_chip_info *chip) { struct snd_soc_dai *dai; struct snd_soc_pcm_runtime *soc_runtime; @@ -87,14 +187,15 @@ void restore_acp_pdm_params(struct snd_pcm_substream *substream, soc_runtime = snd_soc_substream_to_rtd(substream); dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + /* Programming channel mask and sampling rate */ - writel(adata->ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS); - writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR); + writel(chip->ch_mask, chip->base + ACP_WOV_PDM_NO_OF_CHANNELS); + writel(PDM_DEC_64, chip->base + ACP_WOV_PDM_DECIMATION_FACTOR); /* Enabling ACP Pdm interuppts */ - ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0)); + ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0)); ext_int_ctrl |= PDM_DMA_INTR_MASK; - writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0)); + writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0)); set_acp_pdm_clk(substream, dai); } EXPORT_SYMBOL_NS_GPL(restore_acp_pdm_params, "SND_SOC_ACP_COMMON"); @@ -103,8 +204,8 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); - struct acp_resource *rsrc = adata->rsrc; + struct acp_chip_info *chip = dev_get_platdata(dev); + struct acp_resource *rsrc = chip->rsrc; struct acp_stream *stream = substream->runtime->private_data; u32 reg_dma_size, reg_fifo_size, reg_fifo_addr; u32 phy_addr, acp_fifo_addr, ext_int_ctrl; @@ -113,40 +214,40 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, switch (dai->driver->id) { case I2S_SP_INSTANCE: if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - reg_dma_size = ACP_I2S_TX_DMA_SIZE(adata); + reg_dma_size = ACP_I2S_TX_DMA_SIZE(chip); acp_fifo_addr = rsrc->sram_pte_offset + SP_PB_FIFO_ADDR_OFFSET; - reg_fifo_addr = ACP_I2S_TX_FIFOADDR(adata); - reg_fifo_size = ACP_I2S_TX_FIFOSIZE(adata); + reg_fifo_addr = ACP_I2S_TX_FIFOADDR(chip); + reg_fifo_size = ACP_I2S_TX_FIFOSIZE(chip); phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR(adata)); + writel(phy_addr, chip->base + ACP_I2S_TX_RINGBUFADDR(chip)); } else { - reg_dma_size = ACP_I2S_RX_DMA_SIZE(adata); + reg_dma_size = ACP_I2S_RX_DMA_SIZE(chip); acp_fifo_addr = rsrc->sram_pte_offset + SP_CAPT_FIFO_ADDR_OFFSET; - reg_fifo_addr = ACP_I2S_RX_FIFOADDR(adata); - reg_fifo_size = ACP_I2S_RX_FIFOSIZE(adata); + reg_fifo_addr = ACP_I2S_RX_FIFOADDR(chip); + reg_fifo_size = ACP_I2S_RX_FIFOSIZE(chip); phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR(adata)); + writel(phy_addr, chip->base + ACP_I2S_RX_RINGBUFADDR(chip)); } break; case I2S_BT_INSTANCE: if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - reg_dma_size = ACP_BT_TX_DMA_SIZE(adata); + reg_dma_size = ACP_BT_TX_DMA_SIZE(chip); acp_fifo_addr = rsrc->sram_pte_offset + BT_PB_FIFO_ADDR_OFFSET; - reg_fifo_addr = ACP_BT_TX_FIFOADDR(adata); - reg_fifo_size = ACP_BT_TX_FIFOSIZE(adata); + reg_fifo_addr = ACP_BT_TX_FIFOADDR(chip); + reg_fifo_size = ACP_BT_TX_FIFOSIZE(chip); phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR(adata)); + writel(phy_addr, chip->base + ACP_BT_TX_RINGBUFADDR(chip)); } else { - reg_dma_size = ACP_BT_RX_DMA_SIZE(adata); + reg_dma_size = ACP_BT_RX_DMA_SIZE(chip); acp_fifo_addr = rsrc->sram_pte_offset + BT_CAPT_FIFO_ADDR_OFFSET; - reg_fifo_addr = ACP_BT_RX_FIFOADDR(adata); - reg_fifo_size = ACP_BT_RX_FIFOSIZE(adata); + reg_fifo_addr = ACP_BT_RX_FIFOADDR(chip); + reg_fifo_size = ACP_BT_RX_FIFOSIZE(chip); phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR(adata)); + writel(phy_addr, chip->base + ACP_BT_RX_RINGBUFADDR(chip)); } break; case I2S_HS_INSTANCE: @@ -157,7 +258,7 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, reg_fifo_addr = ACP_HS_TX_FIFOADDR; reg_fifo_size = ACP_HS_TX_FIFOSIZE; phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_HS_TX_RINGBUFADDR); + writel(phy_addr, chip->base + ACP_HS_TX_RINGBUFADDR); } else { reg_dma_size = ACP_HS_RX_DMA_SIZE; acp_fifo_addr = rsrc->sram_pte_offset + @@ -165,7 +266,7 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, reg_fifo_addr = ACP_HS_RX_FIFOADDR; reg_fifo_size = ACP_HS_RX_FIFOSIZE; phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset; - writel(phy_addr, adata->acp_base + ACP_HS_RX_RINGBUFADDR); + writel(phy_addr, chip->base + ACP_HS_RX_RINGBUFADDR); } break; default: @@ -173,11 +274,11 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, return -EINVAL; } - writel(DMA_SIZE, adata->acp_base + reg_dma_size); - writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr); - writel(FIFO_SIZE, adata->acp_base + reg_fifo_size); + writel(DMA_SIZE, chip->base + reg_dma_size); + writel(acp_fifo_addr, chip->base + reg_fifo_addr); + writel(FIFO_SIZE, chip->base + reg_fifo_size); - ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); + ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used)); ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) | BIT(BT_RX_THRESHOLD(rsrc->offset)) | BIT(I2S_TX_THRESHOLD(rsrc->offset)) | @@ -185,12 +286,12 @@ static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream, BIT(HS_RX_THRESHOLD(rsrc->offset)) | BIT(HS_TX_THRESHOLD(rsrc->offset)); - writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); + writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used)); return 0; } int restore_acp_i2s_params(struct snd_pcm_substream *substream, - struct acp_dev_data *adata, + struct acp_chip_info *chip, struct acp_stream *stream) { struct snd_soc_dai *dai; @@ -200,7 +301,7 @@ int restore_acp_i2s_params(struct snd_pcm_substream *substream, soc_runtime = snd_soc_substream_to_rtd(substream); dai = snd_soc_rtd_to_cpu(soc_runtime, 0); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - tdm_fmt = adata->tdm_tx_fmt[stream->dai_id - 1]; + tdm_fmt = chip->tdm_tx_fmt[stream->dai_id - 1]; switch (stream->dai_id) { case I2S_BT_INSTANCE: reg_val = ACP_BTTDM_ITER; @@ -218,9 +319,9 @@ int restore_acp_i2s_params(struct snd_pcm_substream *substream, pr_err("Invalid dai id %x\n", stream->dai_id); return -EINVAL; } - val = adata->xfer_tx_resolution[stream->dai_id - 1] << 3; + val = chip->xfer_tx_resolution[stream->dai_id - 1] << 3; } else { - tdm_fmt = adata->tdm_rx_fmt[stream->dai_id - 1]; + tdm_fmt = chip->tdm_rx_fmt[stream->dai_id - 1]; switch (stream->dai_id) { case I2S_BT_INSTANCE: reg_val = ACP_BTTDM_IRER; @@ -238,13 +339,13 @@ int restore_acp_i2s_params(struct snd_pcm_substream *substream, pr_err("Invalid dai id %x\n", stream->dai_id); return -EINVAL; } - val = adata->xfer_rx_resolution[stream->dai_id - 1] << 3; + val = chip->xfer_rx_resolution[stream->dai_id - 1] << 3; } - writel(val, adata->acp_base + reg_val); - if (adata->tdm_mode == TDM_ENABLE) { - writel(tdm_fmt, adata->acp_base + fmt_reg); - val = readl(adata->acp_base + reg_val); - writel(val | 0x2, adata->acp_base + reg_val); + writel(val, chip->base + reg_val); + if (chip->tdm_mode == TDM_ENABLE) { + writel(tdm_fmt, chip->base + fmt_reg); + val = readl(chip->base + reg_val); + writel(val | 0x2, chip->base + reg_val); } return set_acp_i2s_dma_fifo(substream, dai); } @@ -344,24 +445,32 @@ int acp_deinit(struct acp_chip_info *chip) return 0; } EXPORT_SYMBOL_NS_GPL(acp_deinit, "SND_SOC_ACP_COMMON"); - -int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data) +int acp_machine_select(struct acp_chip_info *chip) { - pci_write_config_dword(dev, 0x60, smn_addr); - pci_write_config_dword(dev, 0x64, data); + struct snd_soc_acpi_mach *mach; + int size, platform; + + if (chip->flag == FLAG_AMD_LEGACY_ONLY_DMIC) { + platform = chip->acp_rev; + chip->mach_dev = platform_device_register_data(chip->dev, "acp-pdm-mach", + PLATFORM_DEVID_NONE, &platform, + sizeof(platform)); + } else { + size = sizeof(*chip->machines); + mach = snd_soc_acpi_find_machine(chip->machines); + if (!mach) { + dev_err(chip->dev, "warning: No matching ASoC machine driver found\n"); + return -EINVAL; + } + mach->mach_params.subsystem_rev = chip->acp_rev; + chip->mach_dev = platform_device_register_data(chip->dev, mach->drv_name, + PLATFORM_DEVID_NONE, mach, size); + } + if (IS_ERR(chip->mach_dev)) + dev_warn(chip->dev, "Unable to register Machine device\n"); return 0; } -EXPORT_SYMBOL_NS_GPL(smn_write, "SND_SOC_ACP_COMMON"); - -int smn_read(struct pci_dev *dev, u32 smn_addr) -{ - u32 data; - - pci_write_config_dword(dev, 0x60, smn_addr); - pci_read_config_dword(dev, 0x64, &data); - return data; -} -EXPORT_SYMBOL_NS_GPL(smn_read, "SND_SOC_ACP_COMMON"); +EXPORT_SYMBOL_NS_GPL(acp_machine_select, "SND_SOC_ACP_COMMON"); static void check_acp3x_config(struct acp_chip_info *chip) { @@ -445,7 +554,9 @@ void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip) { struct acpi_device *pdm_dev; const union acpi_object *obj; - u32 pdm_addr; + acpi_handle handle; + acpi_integer dmic_status; + u32 pdm_addr, ret; switch (chip->acp_rev) { case ACP_RN_PCI_ID: @@ -477,9 +588,58 @@ void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip) obj->integer.value == pdm_addr) chip->is_pdm_dev = true; } + + handle = ACPI_HANDLE(&pci->dev); + ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status); + if (!ACPI_FAILURE(ret)) + chip->is_pdm_dev = dmic_status; } } EXPORT_SYMBOL_NS_GPL(check_acp_config, "SND_SOC_ACP_COMMON"); +struct snd_acp_hw_ops acp31_common_hw_ops; +EXPORT_SYMBOL_NS_GPL(acp31_common_hw_ops, "SND_SOC_ACP_COMMON"); +int acp31_hw_ops_init(struct acp_chip_info *chip) +{ + memcpy(&acp31_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops)); + chip->acp_hw_ops = &acp31_common_hw_ops; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(acp31_hw_ops_init, "SND_SOC_ACP_COMMON"); + +struct snd_acp_hw_ops acp6x_common_hw_ops; +EXPORT_SYMBOL_NS_GPL(acp6x_common_hw_ops, "SND_SOC_ACP_COMMON"); +int acp6x_hw_ops_init(struct acp_chip_info *chip) +{ + memcpy(&acp6x_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops)); + chip->acp_hw_ops = &acp6x_common_hw_ops; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(acp6x_hw_ops_init, "SND_SOC_ACP_COMMON"); + +struct snd_acp_hw_ops acp63_common_hw_ops; +EXPORT_SYMBOL_NS_GPL(acp63_common_hw_ops, "SND_SOC_ACP_COMMON"); +int acp63_hw_ops_init(struct acp_chip_info *chip) +{ + memcpy(&acp63_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops)); + chip->acp_hw_ops = &acp63_common_hw_ops; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(acp63_hw_ops_init, "SND_SOC_ACP_COMMON"); + +struct snd_acp_hw_ops acp70_common_hw_ops; +EXPORT_SYMBOL_NS_GPL(acp70_common_hw_ops, "SND_SOC_ACP_COMMON"); +int acp70_hw_ops_init(struct acp_chip_info *chip) +{ + memcpy(&acp70_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops)); + chip->acp_hw_ops = &acp70_common_hw_ops; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(acp70_hw_ops_init, "SND_SOC_ACP_COMMON"); + MODULE_DESCRIPTION("AMD ACP legacy common features"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index f7602c1769bf..a0dab85088ec 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -919,7 +919,7 @@ static int acp_max98388_hw_params(struct snd_pcm_substream *substream, int ret; ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF); if (ret < 0) return ret; diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index e0fc42d939d3..0b2aa33cc426 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -26,32 +26,88 @@ #define ACP3x_REG_START 0x1240000 #define ACP3x_REG_END 0x125C000 -static struct platform_device *dmic_dev; -static struct platform_device *pdev; - -static const struct resource acp_res[] = { - { - .start = 0, - .end = ACP3x_REG_END - ACP3x_REG_START, - .name = "acp_mem", - .flags = IORESOURCE_MEM, - }, - { - .start = 0, - .end = 0, - .name = "acp_dai_irq", - .flags = IORESOURCE_IRQ, - }, -}; +static irqreturn_t irq_handler(int irq, void *data) +{ + struct acp_chip_info *chip = data; -static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) + if (chip && chip->acp_hw_ops && chip->acp_hw_ops->irq) + return chip->acp_hw_ops->irq(irq, chip); + + return IRQ_NONE; +} +static void acp_fill_platform_dev_info(struct platform_device_info *pdevinfo, + struct device *parent, + struct fwnode_handle *fw_node, + char *name, unsigned int id, + const struct resource *res, + unsigned int num_res, + const void *data, + size_t size_data) +{ + pdevinfo->name = name; + pdevinfo->id = id; + pdevinfo->parent = parent; + pdevinfo->num_res = num_res; + pdevinfo->res = res; + pdevinfo->data = data; + pdevinfo->size_data = size_data; + pdevinfo->fwnode = fw_node; +} + +static int create_acp_platform_devs(struct pci_dev *pci, struct acp_chip_info *chip, u32 addr) { struct platform_device_info pdevinfo; + struct device *parent; + int ret; + + parent = &pci->dev; + + if (chip->is_i2s_config || chip->is_pdm_dev) { + chip->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL); + if (!chip->res) { + ret = -ENOMEM; + goto err; + } + chip->res->flags = IORESOURCE_MEM; + chip->res->start = addr; + chip->res->end = addr + (ACP3x_REG_END - ACP3x_REG_START); + memset(&pdevinfo, 0, sizeof(pdevinfo)); + } + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + acp_fill_platform_dev_info(&pdevinfo, parent, NULL, chip->name, + 0, chip->res, 1, chip, sizeof(*chip)); + + chip->acp_plat_dev = platform_device_register_full(&pdevinfo); + if (IS_ERR(chip->acp_plat_dev)) { + dev_err(&pci->dev, + "cannot register %s device\n", pdevinfo.name); + ret = PTR_ERR(chip->acp_plat_dev); + goto err; + } + if (chip->is_pdm_dev && chip->is_pdm_config) { + chip->dmic_codec_dev = platform_device_register_data(&pci->dev, + "dmic-codec", + PLATFORM_DEVID_NONE, + NULL, 0); + if (IS_ERR(chip->dmic_codec_dev)) { + dev_err(&pci->dev, "failed to create DMIC device\n"); + ret = PTR_ERR(chip->dmic_codec_dev); + goto unregister_acp_plat_dev; + } + } + return 0; +unregister_acp_plat_dev: + platform_device_unregister(chip->acp_plat_dev); +err: + return ret; +} + +static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ struct device *dev = &pci->dev; - const struct resource *res_acp; struct acp_chip_info *chip; - struct resource *res; - unsigned int flag, addr, num_res, i; + unsigned int flag, addr; int ret; flag = snd_amd_acp_find_config(pci); @@ -75,24 +131,32 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id pci_set_master(pci); - res_acp = acp_res; - num_res = ARRAY_SIZE(acp_res); chip->acp_rev = pci->revision; switch (pci->revision) { case 0x01: chip->name = "acp_asoc_renoir"; + chip->rsrc = &rn_rsrc; + chip->acp_hw_ops_init = acp31_hw_ops_init; + chip->machines = &snd_soc_acpi_amd_acp_machines; break; case 0x6f: chip->name = "acp_asoc_rembrandt"; + chip->rsrc = &rmb_rsrc; + chip->acp_hw_ops_init = acp6x_hw_ops_init; + chip->machines = &snd_soc_acpi_amd_rmb_acp_machines; break; case 0x63: chip->name = "acp_asoc_acp63"; + chip->rsrc = &acp63_rsrc; + chip->acp_hw_ops_init = acp63_hw_ops_init; + chip->machines = &snd_soc_acpi_amd_acp63_acp_machines; break; case 0x70: - chip->name = "acp_asoc_acp70"; - break; case 0x71: chip->name = "acp_asoc_acp70"; + chip->rsrc = &acp70_rsrc; + chip->acp_hw_ops_init = acp70_hw_ops_init; + chip->machines = &snd_soc_acpi_amd_acp70_acp_machines; break; default: dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision); @@ -100,64 +164,46 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id goto release_regions; } chip->flag = flag; - dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0); - if (IS_ERR(dmic_dev)) { - dev_err(dev, "failed to create DMIC device\n"); - ret = PTR_ERR(dmic_dev); - goto release_regions; - } addr = pci_resource_start(pci, 0); chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0)); if (!chip->base) { ret = -ENOMEM; - goto unregister_dmic_dev; + goto release_regions; } - ret = acp_init(chip); + chip->addr = addr; + + chip->acp_hw_ops_init(chip); + ret = acp_hw_init(chip); if (ret) - goto unregister_dmic_dev; + goto release_regions; + + ret = devm_request_irq(dev, pci->irq, irq_handler, + IRQF_SHARED, "ACP_I2S_IRQ", chip); + if (ret) { + dev_err(&pci->dev, "ACP I2S IRQ request failed %d\n", ret); + goto de_init; + } check_acp_config(pci, chip); if (!chip->is_pdm_dev && !chip->is_i2s_config) goto skip_pdev_creation; - res = devm_kcalloc(&pci->dev, num_res, sizeof(struct resource), GFP_KERNEL); - if (!res) { - ret = -ENOMEM; - goto unregister_dmic_dev; + ret = create_acp_platform_devs(pci, chip, addr); + if (ret < 0) { + dev_err(&pci->dev, "ACP platform devices creation failed\n"); + goto de_init; } - for (i = 0; i < num_res; i++, res_acp++) { - res[i].name = res_acp->name; - res[i].flags = res_acp->flags; - res[i].start = addr + res_acp->start; - res[i].end = addr + res_acp->end; - if (res_acp->flags == IORESOURCE_IRQ) { - res[i].start = pci->irq; - res[i].end = res[i].start; - } - } + chip->chip_pdev = chip->acp_plat_dev; + chip->dev = &chip->acp_plat_dev->dev; - memset(&pdevinfo, 0, sizeof(pdevinfo)); - - pdevinfo.name = chip->name; - pdevinfo.id = 0; - pdevinfo.parent = &pci->dev; - pdevinfo.num_res = num_res; - pdevinfo.res = &res[0]; - pdevinfo.data = chip; - pdevinfo.size_data = sizeof(*chip); - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) { - dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name); - ret = PTR_ERR(pdev); - goto unregister_dmic_dev; - } + acp_machine_select(chip); + INIT_LIST_HEAD(&chip->stream_list); + spin_lock_init(&chip->acp_lock); skip_pdev_creation: - chip->chip_pdev = pdev; dev_set_drvdata(&pci->dev, chip); pm_runtime_set_autosuspend_delay(&pci->dev, 2000); pm_runtime_use_autosuspend(&pci->dev); @@ -165,8 +211,8 @@ skip_pdev_creation: pm_runtime_allow(&pci->dev); return ret; -unregister_dmic_dev: - platform_device_unregister(dmic_dev); +de_init: + acp_hw_deinit(chip); release_regions: pci_release_regions(pci); disable_pci: @@ -175,41 +221,38 @@ disable_pci: return ret; }; -static int __maybe_unused snd_acp_suspend(struct device *dev) +static int snd_acp_suspend(struct device *dev) { struct acp_chip_info *chip; int ret; chip = dev_get_drvdata(dev); - ret = acp_deinit(chip); + ret = acp_hw_deinit(chip); if (ret) dev_err(dev, "ACP de-init failed\n"); return ret; } -static int __maybe_unused snd_acp_resume(struct device *dev) +static int snd_acp_resume(struct device *dev) { struct acp_chip_info *chip; - struct acp_dev_data *adata; - struct device child; int ret; chip = dev_get_drvdata(dev); - ret = acp_init(chip); + ret = acp_hw_init(chip); if (ret) dev_err(dev, "ACP init failed\n"); - if (chip->chip_pdev) { - child = chip->chip_pdev->dev; - adata = dev_get_drvdata(&child); - if (adata) - acp_enable_interrupts(adata); - } + + ret = acp_hw_en_interrupts(chip); + if (ret) + dev_err(dev, "ACP en-interrupts failed\n"); + return ret; } static const struct dev_pm_ops acp_pm_ops = { - SET_RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume) + RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_resume, NULL) + SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume) }; static void acp_pci_remove(struct pci_dev *pci) @@ -220,11 +263,14 @@ static void acp_pci_remove(struct pci_dev *pci) chip = pci_get_drvdata(pci); pm_runtime_forbid(&pci->dev); pm_runtime_get_noresume(&pci->dev); - if (dmic_dev) - platform_device_unregister(dmic_dev); - if (pdev) - platform_device_unregister(pdev); - ret = acp_deinit(chip); + if (chip->dmic_codec_dev) + platform_device_unregister(chip->dmic_codec_dev); + if (chip->acp_plat_dev) + platform_device_unregister(chip->acp_plat_dev); + if (chip->mach_dev) + platform_device_unregister(chip->mach_dev); + + ret = acp_hw_deinit(chip); if (ret) dev_err(&pci->dev, "ACP de-init failed\n"); } @@ -243,7 +289,7 @@ static struct pci_driver snd_amd_acp_pci_driver = { .probe = acp_pci_probe, .remove = acp_pci_remove, .driver = { - .pm = &acp_pm_ops, + .pm = pm_ptr(&acp_pm_ops), }, }; module_pci_driver(snd_amd_acp_pci_driver); diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c index d4855da05b6a..1bfc34c2aa53 100644 --- a/sound/soc/amd/acp/acp-pdm.c +++ b/sound/soc/amd/acp/acp-pdm.c @@ -30,17 +30,16 @@ static int acp_dmic_prepare(struct snd_pcm_substream *substream, { struct acp_stream *stream = substream->runtime->private_data; struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); struct acp_chip_info *chip; u32 physical_addr, size_dmic, period_bytes; unsigned int dmic_ctrl; chip = dev_get_platdata(dev); /* Enable default DMIC clk */ - writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL); - dmic_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL); + writel(PDM_CLK_FREQ_MASK, chip->base + ACP_WOV_CLK_CTRL); + dmic_ctrl = readl(chip->base + ACP_WOV_MISC_CTRL); dmic_ctrl |= PDM_MISC_CTRL_MASK; - writel(dmic_ctrl, adata->acp_base + ACP_WOV_MISC_CTRL); + writel(dmic_ctrl, chip->base + ACP_WOV_MISC_CTRL); period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size); @@ -53,10 +52,10 @@ static int acp_dmic_prepare(struct snd_pcm_substream *substream, physical_addr = stream->reg_offset + MEM_WINDOW_START; /* Init DMIC Ring buffer */ - writel(physical_addr, adata->acp_base + ACP_WOV_RX_RINGBUFADDR); - writel(size_dmic, adata->acp_base + ACP_WOV_RX_RINGBUFSIZE); - writel(period_bytes, adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE); - writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL); + writel(physical_addr, chip->base + ACP_WOV_RX_RINGBUFADDR); + writel(size_dmic, chip->base + ACP_WOV_RX_RINGBUFSIZE); + writel(period_bytes, chip->base + ACP_WOV_RX_INTR_WATERMARK_SIZE); + writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL); return 0; } @@ -65,7 +64,7 @@ static int acp_dmic_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); unsigned int dma_enable; int ret = 0; @@ -73,27 +72,27 @@ static int acp_dmic_dai_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - dma_enable = readl(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); + dma_enable = readl(chip->base + ACP_WOV_PDM_DMA_ENABLE); if (!(dma_enable & DMA_EN_MASK)) { - writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_ENABLE); - writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); + writel(PDM_ENABLE, chip->base + ACP_WOV_PDM_ENABLE); + writel(PDM_ENABLE, chip->base + ACP_WOV_PDM_DMA_ENABLE); } - ret = readl_poll_timeout_atomic(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE, + ret = readl_poll_timeout_atomic(chip->base + ACP_WOV_PDM_DMA_ENABLE, dma_enable, (dma_enable & DMA_EN_MASK), DELAY_US, PDM_TIMEOUT); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - dma_enable = readl(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); + dma_enable = readl(chip->base + ACP_WOV_PDM_DMA_ENABLE); if ((dma_enable & DMA_EN_MASK)) { - writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_ENABLE); - writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE); + writel(PDM_DISABLE, chip->base + ACP_WOV_PDM_ENABLE); + writel(PDM_DISABLE, chip->base + ACP_WOV_PDM_DMA_ENABLE); } - ret = readl_poll_timeout_atomic(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE, + ret = readl_poll_timeout_atomic(chip->base + ACP_WOV_PDM_DMA_ENABLE, dma_enable, !(dma_enable & DMA_EN_MASK), DELAY_US, PDM_TIMEOUT); break; @@ -109,7 +108,7 @@ static int acp_dmic_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai) { struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); unsigned int channels, ch_mask; channels = params_channels(hwparams); @@ -128,14 +127,14 @@ static int acp_dmic_hwparams(struct snd_pcm_substream *substream, return -EINVAL; } - adata->ch_mask = ch_mask; + chip->ch_mask = ch_mask; if (params_format(hwparams) != SNDRV_PCM_FORMAT_S32_LE) { dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams)); return -EINVAL; } - writel(ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS); - writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR); + writel(ch_mask, chip->base + ACP_WOV_PDM_NO_OF_CHANNELS); + writel(PDM_DEC_64, chip->base + ACP_WOV_PDM_DECIMATION_FACTOR); return 0; } @@ -145,7 +144,7 @@ static int acp_dmic_dai_startup(struct snd_pcm_substream *substream, { struct acp_stream *stream = substream->runtime->private_data; struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); u32 ext_int_ctrl; stream->dai_id = DMIC_INSTANCE; @@ -154,9 +153,9 @@ static int acp_dmic_dai_startup(struct snd_pcm_substream *substream, stream->reg_offset = ACP_REGION2_OFFSET; /* Enable DMIC Interrupts */ - ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0)); + ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0)); ext_int_ctrl |= PDM_DMA_INTR_MASK; - writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0)); + writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0)); return 0; } @@ -165,13 +164,13 @@ static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct device *dev = dai->component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); u32 ext_int_ctrl; /* Disable DMIC interrupts */ - ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0)); + ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0)); ext_int_ctrl &= ~PDM_DMA_INTR_MASK; - writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0)); + writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0)); } const struct snd_soc_dai_ops acp_dmic_dai_ops = { diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index aa330aeeb301..b3eddf76aaa4 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -21,7 +21,6 @@ #include <linux/dma-mapping.h> #include "amd.h" -#include "../mach-config.h" #include "acp-mach.h" #define DRV_NAME "acp_i2s_dma" @@ -108,101 +107,37 @@ static const struct snd_pcm_hardware acp6x_pcm_hardware_capture = { .periods_max = CAPTURE_MAX_NUM_PERIODS, }; -int acp_machine_select(struct acp_dev_data *adata) +void config_pte_for_stream(struct acp_chip_info *chip, struct acp_stream *stream) { - struct snd_soc_acpi_mach *mach; - int size, platform; - - if (adata->flag == FLAG_AMD_LEGACY_ONLY_DMIC) { - platform = adata->acp_rev; - adata->mach_dev = platform_device_register_data(adata->dev, "acp-pdm-mach", - PLATFORM_DEVID_NONE, &platform, - sizeof(platform)); - } else { - size = sizeof(*adata->machines); - mach = snd_soc_acpi_find_machine(adata->machines); - if (!mach) { - dev_err(adata->dev, "warning: No matching ASoC machine driver found\n"); - return -EINVAL; - } - mach->mach_params.subsystem_rev = adata->acp_rev; - adata->mach_dev = platform_device_register_data(adata->dev, mach->drv_name, - PLATFORM_DEVID_NONE, mach, size); - } - if (IS_ERR(adata->mach_dev)) - dev_warn(adata->dev, "Unable to register Machine device\n"); - return 0; -} -EXPORT_SYMBOL_NS_GPL(acp_machine_select, "SND_SOC_ACP_COMMON"); - -static irqreturn_t i2s_irq_handler(int irq, void *data) -{ - struct acp_dev_data *adata = data; - struct acp_resource *rsrc = adata->rsrc; - struct acp_stream *stream; - u16 i2s_flag = 0; - u32 ext_intr_stat, ext_intr_stat1; - - if (adata->rsrc->no_of_ctrls == 2) - ext_intr_stat1 = readl(ACP_EXTERNAL_INTR_STAT(adata, (rsrc->irqp_used - 1))); - - ext_intr_stat = readl(ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); - - spin_lock(&adata->acp_lock); - list_for_each_entry(stream, &adata->stream_list, list) { - if (ext_intr_stat & stream->irq_bit) { - writel(stream->irq_bit, - ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); - snd_pcm_period_elapsed(stream->substream); - i2s_flag = 1; - } - if (adata->rsrc->no_of_ctrls == 2) { - if (ext_intr_stat1 & stream->irq_bit) { - writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(adata, - (rsrc->irqp_used - 1))); - snd_pcm_period_elapsed(stream->substream); - i2s_flag = 1; - } - } - } - spin_unlock(&adata->acp_lock); - if (i2s_flag) - return IRQ_HANDLED; - - return IRQ_NONE; -} - -void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream) -{ - struct acp_resource *rsrc = adata->rsrc; + struct acp_resource *rsrc = chip->rsrc; u32 reg_val; reg_val = rsrc->sram_pte_offset; stream->reg_offset = 0x02000000; - writel((reg_val + GRP1_OFFSET) | BIT(31), adata->acp_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1); - writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); + writel((reg_val + GRP1_OFFSET) | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1); + writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); - writel((reg_val + GRP2_OFFSET) | BIT(31), adata->acp_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2); - writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2); + writel((reg_val + GRP2_OFFSET) | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2); + writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2); - writel(reg_val | BIT(31), adata->acp_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_5); - writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5); + writel(reg_val | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_5); + writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5); - writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL); + writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL); } EXPORT_SYMBOL_NS_GPL(config_pte_for_stream, "SND_SOC_ACP_COMMON"); -void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size) +void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int size) { struct snd_pcm_substream *substream = stream->substream; - struct acp_resource *rsrc = adata->rsrc; + struct acp_resource *rsrc = chip->rsrc; dma_addr_t addr = substream->dma_buffer.addr; int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); u32 low, high, val; u16 page_idx; - switch (adata->acp_rev) { + switch (chip->acp_rev) { case ACP70_PCI_ID: case ACP71_PCI_ID: switch (stream->dai_id) { @@ -228,7 +163,7 @@ void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int s val = 0x6000; break; default: - dev_err(adata->dev, "Invalid dai id %x\n", stream->dai_id); + dev_err(chip->dev, "Invalid dai id %x\n", stream->dai_id); return; } break; @@ -241,9 +176,9 @@ void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int s /* Load the low address of page int ACP SRAM through SRBM */ low = lower_32_bits(addr); high = upper_32_bits(addr); - writel(low, adata->acp_base + rsrc->scratch_reg_offset + val); + writel(low, chip->base + rsrc->scratch_reg_offset + val); high |= BIT(31); - writel(high, adata->acp_base + rsrc->scratch_reg_offset + val + 4); + writel(high, chip->base + rsrc->scratch_reg_offset + val + 4); /* Move to next physically contiguous page */ val += 8; @@ -256,7 +191,6 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs { struct snd_pcm_runtime *runtime = substream->runtime; struct device *dev = component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); struct acp_chip_info *chip; struct acp_stream *stream; int ret; @@ -266,7 +200,7 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs return -ENOMEM; stream->substream = substream; - chip = dev_get_platdata(dev); + chip = dev_get_drvdata(dev->parent); switch (chip->acp_rev) { case ACP63_PCI_ID: case ACP70_PCI_ID: @@ -306,11 +240,11 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs } runtime->private_data = stream; - writel(1, ACP_EXTERNAL_INTR_ENB(adata)); + writel(1, ACP_EXTERNAL_INTR_ENB(chip)); - spin_lock_irq(&adata->acp_lock); - list_add_tail(&stream->list, &adata->stream_list); - spin_unlock_irq(&adata->acp_lock); + spin_lock_irq(&chip->acp_lock); + list_add_tail(&stream->list, &chip->stream_list); + spin_unlock_irq(&chip->acp_lock); return ret; } @@ -319,13 +253,14 @@ static int acp_dma_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct acp_dev_data *adata = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + struct acp_chip_info *chip = dev_get_drvdata(dev->parent); struct acp_stream *stream = substream->runtime->private_data; u64 size = params_buffer_bytes(params); /* Configure ACP DMA block with params */ - config_pte_for_stream(adata, stream); - config_acp_dma(adata, stream, size); + config_pte_for_stream(chip, stream); + config_acp_dma(chip, stream, size); return 0; } @@ -334,7 +269,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct device *dev = component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_drvdata(dev->parent); struct acp_stream *stream = substream->runtime->private_data; u32 pos, buffersize; u64 bytescount; @@ -342,7 +277,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, buffersize = frames_to_bytes(substream->runtime, substream->runtime->buffer_size); - bytescount = acp_get_byte_count(adata, stream->dai_id, substream->stream); + bytescount = acp_get_byte_count(chip, stream->dai_id, substream->stream); if (bytescount > stream->bytescount) bytescount -= stream->bytescount; @@ -366,13 +301,13 @@ static int acp_dma_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct device *dev = component->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_drvdata(dev->parent); struct acp_stream *stream = substream->runtime->private_data; /* Remove entry from list */ - spin_lock_irq(&adata->acp_lock); + spin_lock_irq(&chip->acp_lock); list_del(&stream->list); - spin_unlock_irq(&adata->acp_lock); + spin_unlock_irq(&chip->acp_lock); kfree(stream); return 0; @@ -390,38 +325,30 @@ static const struct snd_soc_component_driver acp_pcm_component = { int acp_platform_register(struct device *dev) { - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip; struct snd_soc_dai_driver; unsigned int status; - status = devm_request_irq(dev, adata->i2s_irq, i2s_irq_handler, - IRQF_SHARED, "ACP_I2S_IRQ", adata); - if (status) { - dev_err(dev, "ACP I2S IRQ request failed\n"); - return status; + chip = dev_get_platdata(dev); + if (!chip || !chip->base) { + dev_err(dev, "ACP chip data is NULL\n"); + return -ENODEV; } status = devm_snd_soc_register_component(dev, &acp_pcm_component, - adata->dai_driver, - adata->num_dai); + chip->dai_driver, + chip->num_dai); if (status) { dev_err(dev, "Fail to register acp i2s component\n"); return status; } - INIT_LIST_HEAD(&adata->stream_list); - spin_lock_init(&adata->acp_lock); - return 0; } EXPORT_SYMBOL_NS_GPL(acp_platform_register, "SND_SOC_ACP_COMMON"); int acp_platform_unregister(struct device *dev) { - struct acp_dev_data *adata = dev_get_drvdata(dev); - - if (adata->mach_dev) - platform_device_unregister(adata->mach_dev); return 0; } EXPORT_SYMBOL_NS_GPL(acp_platform_unregister, "SND_SOC_ACP_COMMON"); diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c index 2648256fa129..746b6ed72029 100644 --- a/sound/soc/amd/acp/acp-rembrandt.c +++ b/sound/soc/amd/acp/acp-rembrandt.c @@ -22,6 +22,8 @@ #include <linux/pci.h> #include <linux/pm_runtime.h> +#include <asm/amd_node.h> + #include "amd.h" #include "../mach-config.h" #include "acp-mach.h" @@ -31,47 +33,6 @@ #define MP1_C2PMSG_69 0x3B10A14 #define MP1_C2PMSG_85 0x3B10A54 #define MP1_C2PMSG_93 0x3B10A74 -#define HOST_BRIDGE_ID 0x14B5 - -static struct acp_resource rsrc = { - .offset = 0, - .no_of_ctrls = 2, - .irqp_used = 1, - .soc_mclk = true, - .irq_reg_offset = 0x1a00, - .scratch_reg_offset = 0x12800, - .sram_pte_offset = 0x03802800, -}; - -static struct snd_soc_acpi_codecs amp_rt1019 = { - .num_codecs = 1, - .codecs = {"10EC1019"} -}; - -static struct snd_soc_acpi_codecs amp_max = { - .num_codecs = 1, - .codecs = {"MX98360A"} -}; - -static struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[] = { - { - .id = "10508825", - .drv_name = "rmb-nau8825-max", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &_max, - }, - { - .id = "AMDI0007", - .drv_name = "rembrandt-acp", - }, - { - .id = "RTL5682", - .drv_name = "rmb-rt5682s-rt1019", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &_rt1019, - }, - {}, -}; static struct snd_soc_dai_driver acp_rmb_dai[] = { { @@ -166,29 +127,26 @@ static struct snd_soc_dai_driver acp_rmb_dai[] = { static int acp6x_master_clock_generate(struct device *dev) { - int data = 0; - struct pci_dev *smn_dev; - - smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, HOST_BRIDGE_ID, NULL); - if (!smn_dev) { - dev_err(dev, "Failed to get host bridge device\n"); - return -ENODEV; - } - - smn_write(smn_dev, MP1_C2PMSG_93, 0); - smn_write(smn_dev, MP1_C2PMSG_85, 0xC4); - smn_write(smn_dev, MP1_C2PMSG_69, 0x4); - read_poll_timeout(smn_read, data, data, DELAY_US, - ACP_TIMEOUT, false, smn_dev, MP1_C2PMSG_93); - return 0; + int data, rc; + + rc = amd_smn_write(0, MP1_C2PMSG_93, 0); + if (rc) + return rc; + rc = amd_smn_write(0, MP1_C2PMSG_85, 0xC4); + if (rc) + return rc; + rc = amd_smn_write(0, MP1_C2PMSG_69, 0x4); + if (rc) + return rc; + + return read_poll_timeout(smn_read_register, data, data > 0, DELAY_US, + ACP_TIMEOUT, false, MP1_C2PMSG_93); } static int rembrandt_audio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct acp_chip_info *chip; - struct acp_dev_data *adata; - struct resource *res; u32 ret; chip = dev_get_platdata(&pdev->dev); @@ -202,45 +160,20 @@ static int rembrandt_audio_probe(struct platform_device *pdev) return -ENODEV; } - adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); - if (!adata) - return -ENOMEM; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem"); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); - return -ENODEV; - } - - adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!adata->acp_base) - return -ENOMEM; + chip->dev = dev; + chip->dai_driver = acp_rmb_dai; + chip->num_dai = ARRAY_SIZE(acp_rmb_dai); - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq"); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); - return -ENODEV; - } - - adata->i2s_irq = res->start; - adata->dev = dev; - adata->dai_driver = acp_rmb_dai; - adata->num_dai = ARRAY_SIZE(acp_rmb_dai); - adata->rsrc = &rsrc; - adata->acp_rev = chip->acp_rev; - adata->flag = chip->flag; - adata->is_i2s_config = chip->is_i2s_config; - adata->machines = snd_soc_acpi_amd_rmb_acp_machines; - acp_machine_select(adata); - - dev_set_drvdata(dev, adata); - - if (chip->is_i2s_config && rsrc.soc_mclk) { + if (chip->is_i2s_config && chip->rsrc->soc_mclk) { ret = acp6x_master_clock_generate(dev); if (ret) return ret; } - acp_enable_interrupts(adata); + ret = acp_hw_en_interrupts(chip); + if (ret) { + dev_err(dev, "ACP en-interrupts failed\n"); + return ret; + } acp_platform_register(dev); pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(&pdev->dev); @@ -253,44 +186,48 @@ static int rembrandt_audio_probe(struct platform_device *pdev) static void rembrandt_audio_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); + int ret; + + ret = acp_hw_dis_interrupts(chip); + if (ret) + dev_err(dev, "ACP dis-interrupts failed\n"); - acp_disable_interrupts(adata); acp_platform_unregister(dev); pm_runtime_disable(&pdev->dev); } -static int __maybe_unused rmb_pcm_resume(struct device *dev) +static int rmb_pcm_resume(struct device *dev) { - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); struct acp_stream *stream; struct snd_pcm_substream *substream; snd_pcm_uframes_t buf_in_frames; u64 buf_size; - if (adata->is_i2s_config && adata->rsrc->soc_mclk) + if (chip->is_i2s_config && chip->rsrc->soc_mclk) acp6x_master_clock_generate(dev); - spin_lock(&adata->acp_lock); - list_for_each_entry(stream, &adata->stream_list, list) { + spin_lock(&chip->acp_lock); + list_for_each_entry(stream, &chip->stream_list, list) { substream = stream->substream; if (substream && substream->runtime) { buf_in_frames = (substream->runtime->buffer_size); buf_size = frames_to_bytes(substream->runtime, buf_in_frames); - config_pte_for_stream(adata, stream); - config_acp_dma(adata, stream, buf_size); + config_pte_for_stream(chip, stream); + config_acp_dma(chip, stream, buf_size); if (stream->dai_id) - restore_acp_i2s_params(substream, adata, stream); + restore_acp_i2s_params(substream, chip, stream); else - restore_acp_pdm_params(substream, adata); + restore_acp_pdm_params(substream, chip); } } - spin_unlock(&adata->acp_lock); + spin_unlock(&chip->acp_lock); return 0; } static const struct dev_pm_ops rmb_dma_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume) + SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume) }; static struct platform_driver rembrandt_driver = { @@ -298,7 +235,7 @@ static struct platform_driver rembrandt_driver = { .remove = rembrandt_audio_remove, .driver = { .name = "acp_asoc_rembrandt", - .pm = &rmb_dma_pm_ops, + .pm = pm_ptr(&rmb_dma_pm_ops), }, }; diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index ca2b74283d8f..ebf0106fc737 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -27,55 +27,6 @@ #define DRV_NAME "acp_asoc_renoir" -static struct acp_resource rsrc = { - .offset = 20, - .no_of_ctrls = 1, - .irqp_used = 0, - .irq_reg_offset = 0x1800, - .scratch_reg_offset = 0x12800, - .sram_pte_offset = 0x02052800, -}; - -static struct snd_soc_acpi_codecs amp_rt1019 = { - .num_codecs = 1, - .codecs = {"10EC1019"} -}; - -static struct snd_soc_acpi_codecs amp_max = { - .num_codecs = 1, - .codecs = {"MX98360A"} -}; - -static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { - { - .id = "10EC5682", - .drv_name = "acp3xalc56821019", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &_rt1019, - }, - { - .id = "RTL5682", - .drv_name = "acp3xalc5682sm98360", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &_max, - }, - { - .id = "RTL5682", - .drv_name = "acp3xalc5682s1019", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &_rt1019, - }, - { - .id = "AMDI1019", - .drv_name = "renoir-acp", - }, - { - .id = "ESSX8336", - .drv_name = "acp3x-es83xx", - }, - {}, -}; - static struct snd_soc_dai_driver acp_renoir_dai[] = { { .name = "acp-i2s-sp", @@ -147,8 +98,6 @@ static int renoir_audio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct acp_chip_info *chip; - struct acp_dev_data *adata; - struct resource *res; int ret; chip = dev_get_platdata(&pdev->dev); @@ -162,37 +111,16 @@ static int renoir_audio_probe(struct platform_device *pdev) return -ENODEV; } - adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); - if (!adata) - return -ENOMEM; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem"); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); - return -ENODEV; - } - - adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!adata->acp_base) - return -ENOMEM; + chip->dev = dev; + chip->dai_driver = acp_renoir_dai; + chip->num_dai = ARRAY_SIZE(acp_renoir_dai); - ret = platform_get_irq_byname(pdev, "acp_dai_irq"); - if (ret < 0) + ret = acp_hw_en_interrupts(chip); + if (ret) { + dev_err(dev, "ACP en-interrupts failed\n"); return ret; - adata->i2s_irq = ret; - - adata->dev = dev; - adata->dai_driver = acp_renoir_dai; - adata->num_dai = ARRAY_SIZE(acp_renoir_dai); - adata->rsrc = &rsrc; - adata->acp_rev = chip->acp_rev; - adata->flag = chip->flag; - - adata->machines = snd_soc_acpi_amd_acp_machines; - acp_machine_select(adata); + } - dev_set_drvdata(dev, adata); - acp_enable_interrupts(adata); acp_platform_register(dev); pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); @@ -206,40 +134,44 @@ static int renoir_audio_probe(struct platform_device *pdev) static void renoir_audio_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); + int ret; + + ret = acp_hw_dis_interrupts(chip); + if (ret) + dev_err(dev, "ACP dis-interrupts failed\n"); - acp_disable_interrupts(adata); acp_platform_unregister(dev); } -static int __maybe_unused rn_pcm_resume(struct device *dev) +static int rn_pcm_resume(struct device *dev) { - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); struct acp_stream *stream; struct snd_pcm_substream *substream; snd_pcm_uframes_t buf_in_frames; u64 buf_size; - spin_lock(&adata->acp_lock); - list_for_each_entry(stream, &adata->stream_list, list) { + spin_lock(&chip->acp_lock); + list_for_each_entry(stream, &chip->stream_list, list) { substream = stream->substream; if (substream && substream->runtime) { buf_in_frames = (substream->runtime->buffer_size); buf_size = frames_to_bytes(substream->runtime, buf_in_frames); - config_pte_for_stream(adata, stream); - config_acp_dma(adata, stream, buf_size); + config_pte_for_stream(chip, stream); + config_acp_dma(chip, stream, buf_size); if (stream->dai_id) - restore_acp_i2s_params(substream, adata, stream); + restore_acp_i2s_params(substream, chip, stream); else - restore_acp_pdm_params(substream, adata); + restore_acp_pdm_params(substream, chip); } } - spin_unlock(&adata->acp_lock); + spin_unlock(&chip->acp_lock); return 0; } static const struct dev_pm_ops rn_dma_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(NULL, rn_pcm_resume) + SYSTEM_SLEEP_PM_OPS(NULL, rn_pcm_resume) }; static struct platform_driver renoir_driver = { @@ -247,7 +179,7 @@ static struct platform_driver renoir_driver = { .remove = renoir_audio_remove, .driver = { .name = "acp_asoc_renoir", - .pm = &rn_dma_pm_ops, + .pm = pm_ptr(&rn_dma_pm_ops), }, }; diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 9280cd30d19c..2020c5cfb3d5 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -28,6 +28,8 @@ static void log_quirks(struct device *dev) SOC_JACK_JDSRC(soc_sdw_quirk)); if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n"); + if (soc_sdw_quirk & ASOC_SDW_CODEC_SPKR) + dev_dbg(dev, "quirk ASOC_SDW_CODEC_SPKR enabled\n"); } static int soc_sdw_quirk_cb(const struct dmi_system_id *id) @@ -45,6 +47,38 @@ static const struct dmi_system_id soc_sdw_quirk_table[] = { }, .driver_data = (void *)RT711_JD2, }, + { + .callback = soc_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D80"), + }, + .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), + }, + { + .callback = soc_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D81"), + }, + .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), + }, + { + .callback = soc_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D82"), + }, + .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), + }, + { + .callback = soc_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D83"), + }, + .driver_data = (void *)(ASOC_SDW_CODEC_SPKR), + }, {} }; @@ -122,6 +156,13 @@ static int create_sdw_dailink(struct snd_soc_card *card, if (ret) return ret; break; + case ACP70_PCI_REV: + case ACP71_PCI_REV: + ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1), + *be_id, &cpu_pin_id, dev); + if (ret) + return ret; + break; default: return -EINVAL; } @@ -221,6 +262,8 @@ static int create_sdw_dailinks(struct snd_soc_card *card, switch (amd_ctx->acp_rev) { case ACP63_PCI_REV: + case ACP70_PCI_REV: + case ACP71_PCI_REV: sdw_platform_component->name = "amd_ps_sdw_dma.0"; break; default: @@ -266,6 +309,8 @@ static int create_dmic_dailinks(struct snd_soc_card *card, switch (amd_ctx->acp_rev) { case ACP63_PCI_REV: + case ACP70_PCI_REV: + case ACP71_PCI_REV: pdm_cpu->name = "acp_ps_pdm_dma.0"; pdm_platform->name = "acp_ps_pdm_dma.0"; break; diff --git a/sound/soc/amd/acp/acp-sdw-mach-common.c b/sound/soc/amd/acp/acp-sdw-mach-common.c index 6f5c39ed1a18..e5f394dc2f4c 100644 --- a/sound/soc/amd/acp/acp-sdw-mach-common.c +++ b/sound/soc/amd/acp/acp-sdw-mach-common.c @@ -59,6 +59,40 @@ int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct dev } EXPORT_SYMBOL_NS_GPL(get_acp63_cpu_pin_id, "SND_SOC_AMD_SDW_MACH"); +int get_acp70_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev) +{ + switch (sdw_link_id) { + case AMD_SDW0: + case AMD_SDW1: + switch (be_id) { + case SOC_SDW_JACK_OUT_DAI_ID: + *cpu_pin_id = ACP70_SW_AUDIO0_TX; + break; + case SOC_SDW_JACK_IN_DAI_ID: + *cpu_pin_id = ACP70_SW_AUDIO0_RX; + break; + case SOC_SDW_AMP_OUT_DAI_ID: + *cpu_pin_id = ACP70_SW_AUDIO1_TX; + break; + case SOC_SDW_AMP_IN_DAI_ID: + *cpu_pin_id = ACP70_SW_AUDIO1_RX; + break; + case SOC_SDW_DMIC_DAI_ID: + *cpu_pin_id = ACP70_SW_AUDIO2_RX; + break; + default: + dev_err(dev, "Invalid be id:%d\n", be_id); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + dev_dbg(dev, "sdw_link_id:%d, be_id:%d, cpu_pin_id:%d\n", sdw_link_id, be_id, *cpu_pin_id); + return 0; +} +EXPORT_SYMBOL_NS_GPL(get_acp70_cpu_pin_id, "SND_SOC_AMD_SDW_MACH"); + MODULE_DESCRIPTION("AMD SoundWire Common Machine driver"); MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c index 2b0aa270a3e9..eb5d4a5baef2 100644 --- a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c +++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c @@ -19,6 +19,7 @@ #include <linux/io.h> #include <linux/acpi.h> #include <linux/dmi.h> +#include <linux/string_choices.h> #include "../acp-mach.h" #include "acp3x-es83xx.h" @@ -241,9 +242,9 @@ static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv) dev_info(priv->codec_dev, "speaker gpio %d active %s, headphone gpio %d active %s\n", priv->enable_spk_gpio.crs_entry_index, - priv->enable_spk_gpio.active_low ? "low" : "high", + str_low_high(priv->enable_spk_gpio.active_low), priv->enable_hp_gpio.crs_entry_index, - priv->enable_hp_gpio.active_low ? "low" : "high"); + str_low_high(priv->enable_hp_gpio.active_low)); return 0; } diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c index 81496e713440..52d895e624c7 100644 --- a/sound/soc/amd/acp/acp63.c +++ b/sound/soc/amd/acp/acp63.c @@ -20,6 +20,9 @@ #include <linux/dma-mapping.h> #include <linux/pm_runtime.h> #include <linux/pci.h> + +#include <asm/amd_node.h> + #include "amd.h" #include "acp-mach.h" #include "../mach-config.h" @@ -49,24 +52,6 @@ union clk_pll_req_no { u32 clk_pll_req_no_reg; }; -static struct acp_resource rsrc = { - .offset = 0, - .no_of_ctrls = 2, - .irqp_used = 1, - .soc_mclk = true, - .irq_reg_offset = 0x1a00, - .scratch_reg_offset = 0x12800, - .sram_pte_offset = 0x03802800, -}; - -static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[] = { - { - .id = "AMDI0052", - .drv_name = "acp63-acp", - }, - {}, -}; - static struct snd_soc_dai_driver acp63_dai[] = { { .name = "acp-i2s-sp", @@ -158,47 +143,61 @@ static struct snd_soc_dai_driver acp63_dai[] = { }, }; -static int acp63_i2s_master_clock_generate(struct acp_dev_data *adata) +static int acp63_i2s_master_clock_generate(struct acp_chip_info *chip) { + int rc; u32 data; union clk_pll_req_no clk_pll; - struct pci_dev *smn_dev; - - smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x14E8, NULL); - if (!smn_dev) - return -ENODEV; /* Clk5 pll register values to get mclk as 196.6MHz*/ clk_pll.bits.fb_mult_int = 0x31; clk_pll.bits.pll_spine_div = 0; clk_pll.bits.gb_mult_frac = 0x26E9; - data = smn_read(smn_dev, CLK_PLL_PWR_REQ_N0); - smn_write(smn_dev, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_STOP_REQ); - - data = smn_read(smn_dev, CLK_SPLL_FIELD_2_N0); - if (data & PLL_FRANCE_EN) - smn_write(smn_dev, CLK_SPLL_FIELD_2_N0, data | PLL_FRANCE_EN); - - smn_write(smn_dev, CLK_PLL_REQ_N0, clk_pll.clk_pll_req_no_reg); - - data = smn_read(smn_dev, CLK_PLL_PWR_REQ_N0); - smn_write(smn_dev, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_START_REQ); - - data = smn_read(smn_dev, CLK_DFSBYPASS_CONTR); - smn_write(smn_dev, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_0); - smn_write(smn_dev, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_1); + rc = amd_smn_read(0, CLK_PLL_PWR_REQ_N0, &data); + if (rc) + return rc; + rc = amd_smn_write(0, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_STOP_REQ); + if (rc) + return rc; + + rc = amd_smn_read(0, CLK_SPLL_FIELD_2_N0, &data); + if (rc) + return rc; + if (data & PLL_FRANCE_EN) { + rc = amd_smn_write(0, CLK_SPLL_FIELD_2_N0, data | PLL_FRANCE_EN); + if (rc) + return rc; + } - smn_write(smn_dev, CLK_DFS_CNTL_N0, CLK0_DIVIDER); - return 0; + rc = amd_smn_write(0, CLK_PLL_REQ_N0, clk_pll.clk_pll_req_no_reg); + if (rc) + return rc; + + rc = amd_smn_read(0, CLK_PLL_PWR_REQ_N0, &data); + if (rc) + return rc; + rc = amd_smn_write(0, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_START_REQ); + if (rc) + return rc; + + rc = amd_smn_read(0, CLK_DFSBYPASS_CONTR, &data); + if (rc) + return rc; + rc = amd_smn_write(0, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_0); + if (rc) + return rc; + rc = amd_smn_write(0, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_1); + if (rc) + return rc; + + return amd_smn_write(0, CLK_DFS_CNTL_N0, CLK0_DIVIDER); } static int acp63_audio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct acp_chip_info *chip; - struct acp_dev_data *adata; - struct resource *res; int ret; chip = dev_get_platdata(&pdev->dev); @@ -212,44 +211,20 @@ static int acp63_audio_probe(struct platform_device *pdev) return -ENODEV; } - adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); - if (!adata) - return -ENOMEM; + chip->dev = dev; + chip->dai_driver = acp63_dai; + chip->num_dai = ARRAY_SIZE(acp63_dai); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem"); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); - return -ENODEV; - } - - adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!adata->acp_base) - return -ENOMEM; - - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq"); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); - return -ENODEV; - } - - adata->i2s_irq = res->start; - adata->dev = dev; - adata->dai_driver = acp63_dai; - adata->num_dai = ARRAY_SIZE(acp63_dai); - adata->rsrc = &rsrc; - adata->acp_rev = chip->acp_rev; - adata->flag = chip->flag; - adata->is_i2s_config = chip->is_i2s_config; - adata->machines = snd_soc_acpi_amd_acp63_acp_machines; - acp_machine_select(adata); - dev_set_drvdata(dev, adata); - - if (chip->is_i2s_config && rsrc.soc_mclk) { - ret = acp63_i2s_master_clock_generate(adata); + if (chip->is_i2s_config && chip->rsrc->soc_mclk) { + ret = acp63_i2s_master_clock_generate(chip); if (ret) return ret; } - acp_enable_interrupts(adata); + ret = acp_hw_en_interrupts(chip); + if (ret) { + dev_err(dev, "ACP en-interrupts failed\n"); + return ret; + } acp_platform_register(dev); pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(&pdev->dev); @@ -262,44 +237,48 @@ static int acp63_audio_probe(struct platform_device *pdev) static void acp63_audio_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); + int ret; + + ret = acp_hw_dis_interrupts(chip); + if (ret) + dev_err(dev, "ACP dis-interrupts failed\n"); - acp_disable_interrupts(adata); acp_platform_unregister(dev); pm_runtime_disable(&pdev->dev); } -static int __maybe_unused acp63_pcm_resume(struct device *dev) +static int acp63_pcm_resume(struct device *dev) { - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); struct acp_stream *stream; struct snd_pcm_substream *substream; snd_pcm_uframes_t buf_in_frames; u64 buf_size; - if (adata->is_i2s_config && adata->rsrc->soc_mclk) - acp63_i2s_master_clock_generate(adata); + if (chip->is_i2s_config && chip->rsrc->soc_mclk) + acp63_i2s_master_clock_generate(chip); - spin_lock(&adata->acp_lock); - list_for_each_entry(stream, &adata->stream_list, list) { + spin_lock(&chip->acp_lock); + list_for_each_entry(stream, &chip->stream_list, list) { substream = stream->substream; if (substream && substream->runtime) { buf_in_frames = (substream->runtime->buffer_size); buf_size = frames_to_bytes(substream->runtime, buf_in_frames); - config_pte_for_stream(adata, stream); - config_acp_dma(adata, stream, buf_size); + config_pte_for_stream(chip, stream); + config_acp_dma(chip, stream, buf_size); if (stream->dai_id) - restore_acp_i2s_params(substream, adata, stream); + restore_acp_i2s_params(substream, chip, stream); else - restore_acp_pdm_params(substream, adata); + restore_acp_pdm_params(substream, chip); } } - spin_unlock(&adata->acp_lock); + spin_unlock(&chip->acp_lock); return 0; } static const struct dev_pm_ops acp63_dma_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(NULL, acp63_pcm_resume) + SYSTEM_SLEEP_PM_OPS(NULL, acp63_pcm_resume) }; static struct platform_driver acp63_driver = { @@ -307,7 +286,7 @@ static struct platform_driver acp63_driver = { .remove = acp63_audio_remove, .driver = { .name = "acp_asoc_acp63", - .pm = &acp63_dma_pm_ops, + .pm = pm_ptr(&acp63_dma_pm_ops), }, }; diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c index 9e23729fd1a7..6d5f5ade075c 100644 --- a/sound/soc/amd/acp/acp70.c +++ b/sound/soc/amd/acp/acp70.c @@ -23,29 +23,13 @@ #include "amd.h" #include "acp-mach.h" +#include <asm/amd_node.h> + #define DRV_NAME "acp_asoc_acp70" #define CLK7_CLK0_DFS_CNTL_N1 0X0006C1A4 #define CLK0_DIVIDER 0X19 -static struct acp_resource rsrc = { - .offset = 0, - .no_of_ctrls = 2, - .irqp_used = 1, - .soc_mclk = true, - .irq_reg_offset = 0x1a00, - .scratch_reg_offset = 0x10000, - .sram_pte_offset = 0x03800000, -}; - -static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[] = { - { - .id = "AMDI0029", - .drv_name = "acp70-acp", - }, - {}, -}; - static struct snd_soc_dai_driver acp70_dai[] = { { .name = "acp-i2s-sp", @@ -137,35 +121,10 @@ static struct snd_soc_dai_driver acp70_dai[] = { }, }; -static int acp70_i2s_master_clock_generate(struct acp_dev_data *adata) -{ - struct pci_dev *smn_dev; - u32 device_id; - - if (adata->acp_rev == ACP70_PCI_ID) - device_id = 0x1507; - else if (adata->acp_rev == ACP71_PCI_ID) - device_id = 0x1122; - else - return -ENODEV; - - smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, device_id, NULL); - - if (!smn_dev) - return -ENODEV; - - /* Set clk7 DFS clock divider register value to get mclk as 196.608MHz*/ - smn_write(smn_dev, CLK7_CLK0_DFS_CNTL_N1, CLK0_DIVIDER); - - return 0; -} - static int acp_acp70_audio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct acp_chip_info *chip; - struct acp_dev_data *adata; - struct resource *res; int ret; chip = dev_get_platdata(&pdev->dev); @@ -183,44 +142,21 @@ static int acp_acp70_audio_probe(struct platform_device *pdev) return -ENODEV; } - adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); - if (!adata) - return -ENOMEM; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem"); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); - return -ENODEV; - } - - adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!adata->acp_base) - return -ENOMEM; - - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq"); - if (!res) { - dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); - return -ENODEV; - } - - adata->i2s_irq = res->start; - adata->dev = dev; - adata->dai_driver = acp70_dai; - adata->num_dai = ARRAY_SIZE(acp70_dai); - adata->rsrc = &rsrc; - adata->machines = snd_soc_acpi_amd_acp70_acp_machines; - adata->acp_rev = chip->acp_rev; - adata->flag = chip->flag; - acp_machine_select(adata); - - dev_set_drvdata(dev, adata); + chip->dev = dev; + chip->dai_driver = acp70_dai; + chip->num_dai = ARRAY_SIZE(acp70_dai); - ret = acp70_i2s_master_clock_generate(adata); + /* Set clk7 DFS clock divider register value to get mclk as 196.608MHz*/ + ret = amd_smn_write(0, CLK7_CLK0_DFS_CNTL_N1, CLK0_DIVIDER); if (ret) { dev_err(&pdev->dev, "Failed to set I2S master clock as 196.608MHz\n"); return ret; } - acp_enable_interrupts(adata); + ret = acp_hw_en_interrupts(chip); + if (ret) { + dev_err(dev, "ACP en-interrupts failed\n"); + return ret; + } acp_platform_register(dev); pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(&pdev->dev); @@ -233,43 +169,45 @@ static int acp_acp70_audio_probe(struct platform_device *pdev) static void acp_acp70_audio_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); + int ret; + + ret = acp_hw_dis_interrupts(chip); + if (ret) + dev_err(dev, "ACP dis-interrupts failed\n"); - acp_disable_interrupts(adata); acp_platform_unregister(dev); pm_runtime_disable(&pdev->dev); } -static int __maybe_unused acp70_pcm_resume(struct device *dev) +static int acp70_pcm_resume(struct device *dev) { - struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_chip_info *chip = dev_get_platdata(dev); struct acp_stream *stream; struct snd_pcm_substream *substream; snd_pcm_uframes_t buf_in_frames; u64 buf_size; - spin_lock(&adata->acp_lock); - list_for_each_entry(stream, &adata->stream_list, list) { - if (stream) { - substream = stream->substream; - if (substream && substream->runtime) { - buf_in_frames = (substream->runtime->buffer_size); - buf_size = frames_to_bytes(substream->runtime, buf_in_frames); - config_pte_for_stream(adata, stream); - config_acp_dma(adata, stream, buf_size); - if (stream->dai_id) - restore_acp_i2s_params(substream, adata, stream); - else - restore_acp_pdm_params(substream, adata); - } + spin_lock(&chip->acp_lock); + list_for_each_entry(stream, &chip->stream_list, list) { + substream = stream->substream; + if (substream && substream->runtime) { + buf_in_frames = (substream->runtime->buffer_size); + buf_size = frames_to_bytes(substream->runtime, buf_in_frames); + config_pte_for_stream(chip, stream); + config_acp_dma(chip, stream, buf_size); + if (stream->dai_id) + restore_acp_i2s_params(substream, chip, stream); + else + restore_acp_pdm_params(substream, chip); } } - spin_unlock(&adata->acp_lock); + spin_unlock(&chip->acp_lock); return 0; } static const struct dev_pm_ops acp70_dma_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(NULL, acp70_pcm_resume) + SYSTEM_SLEEP_PM_OPS(NULL, acp70_pcm_resume) }; static struct platform_driver acp70_driver = { @@ -277,7 +215,7 @@ static struct platform_driver acp70_driver = { .remove = acp_acp70_audio_remove, .driver = { .name = "acp_asoc_acp70", - .pm = &acp70_dma_pm_ops, + .pm = pm_ptr(&acp70_dma_pm_ops), }, }; diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c new file mode 100644 index 000000000000..e87ccfeee5bd --- /dev/null +++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * amd-acp70-acpi-match.c - tables and support for ACP 7.0 & ACP7.1 + * ACPI enumeration. + * + * Copyright 2025 Advanced Micro Devices, Inc. + */ + +#include <sound/soc-acpi.h> +#include "../mach-config.h" + +static const struct snd_soc_acpi_endpoint single_endpoint = { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0 +}; + +static const struct snd_soc_acpi_endpoint spk_l_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 0, + .group_id = 1 +}; + +static const struct snd_soc_acpi_endpoint spk_r_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1 +}; + +static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = { + { + .adr = 0x000030025D071101ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt711" + }, + { + .adr = 0x000030025D131601ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "rt1316-1" + }, + { + .adr = 0x000032025D131601ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1316-2" + }, +}; + +static const struct snd_soc_acpi_adr_device rt714_adr[] = { + { + .adr = 0x130025d071401ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt714" + } +}; + +static const struct snd_soc_acpi_link_adr acp70_4_in_1_sdca[] = { + { .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_rt1316_group_adr), + .adr_d = rt711_rt1316_group_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt714_adr), + .adr_d = rt714_adr, + }, + {} +}; + +static const struct snd_soc_acpi_endpoint rt722_endpoints[] = { + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = { + { + .adr = 0x000030025d072201ull, + .num_endpoints = ARRAY_SIZE(rt722_endpoints), + .endpoints = rt722_endpoints, + .name_prefix = "rt722" + } +}; + +static const struct snd_soc_acpi_adr_device rt1320_1_single_adr[] = { + { + .adr = 0x000130025D132001ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt1320-1" + } +}; + +static const struct snd_soc_acpi_link_adr acp70_rt722_only[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt722_0_single_adr), + .adr_d = rt722_0_single_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr acp70_rt722_l0_rt1320_l1[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt722_0_single_adr), + .adr_d = rt722_0_single_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1320_1_single_adr), + .adr_d = rt1320_1_single_adr, + }, + {} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = { + { + .link_mask = BIT(0) | BIT(1), + .links = acp70_rt722_l0_rt1320_l1, + .drv_name = "amd_sdw", + }, + { + .link_mask = BIT(0), + .links = acp70_rt722_only, + .drv_name = "amd_sdw", + }, + { + .link_mask = BIT(0) | BIT(1), + .links = acp70_4_in_1_sdca, + .drv_name = "amd_sdw", + }, + {}, +}; +EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines); + +MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); diff --git a/sound/soc/amd/acp/amd-acpi-mach.c b/sound/soc/amd/acp/amd-acpi-mach.c new file mode 100644 index 000000000000..d95047d2ee94 --- /dev/null +++ b/sound/soc/amd/acp/amd-acpi-mach.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * amd-acpi-match.c - tables and support for ACP platforms + * ACPI enumeration. + * + * Copyright 2025 Advanced Micro Devices, Inc. + */ + +#include <sound/soc-acpi.h> + +struct snd_soc_acpi_codecs amp_rt1019 = { + .num_codecs = 1, + .codecs = {"10EC1019"} +}; + +struct snd_soc_acpi_codecs amp_max = { + .num_codecs = 1, + .codecs = {"MX98360A"} +}; + +struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { + { + .id = "10EC5682", + .drv_name = "acp3xalc56821019", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_rt1019, + }, + { + .id = "RTL5682", + .drv_name = "acp3xalc5682sm98360", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_max, + }, + { + .id = "RTL5682", + .drv_name = "acp3xalc5682s1019", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_rt1019, + }, + { + .id = "AMDI1019", + .drv_name = "renoir-acp", + }, + { + .id = "ESSX8336", + .drv_name = "acp3x-es83xx", + }, + {}, +}; +EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp_machines, "SND_SOC_ACP_COMMON"); + +struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[] = { + { + .id = "10508825", + .drv_name = "rmb-nau8825-max", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_max, + }, + { + .id = "AMDI0007", + .drv_name = "rembrandt-acp", + }, + { + .id = "RTL5682", + .drv_name = "rmb-rt5682s-rt1019", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &_rt1019, + }, + {}, +}; +EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_rmb_acp_machines, "SND_SOC_ACP_COMMON"); + +struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[] = { + { + .id = "AMDI0052", + .drv_name = "acp63-acp", + }, + {}, +}; +EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp63_acp_machines, "SND_SOC_ACP_COMMON"); + +struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[] = { + { + .id = "AMDI0029", + .drv_name = "acp70-acp", + }, + {}, +}; +EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp70_acp_machines, "SND_SOC_ACP_COMMON"); + +MODULE_DESCRIPTION("AMD ACP tables and support for ACPI enumeration"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Venkataprasad.potturu@amd.com"); diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index ee69dfb10cb8..863e74fcee43 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -140,13 +140,36 @@ struct acp_chip_info { char *name; /* Platform name */ + struct resource *res; + struct device *dev; + struct snd_soc_dai_driver *dai_driver; + unsigned int acp_rev; /* ACP Revision id */ void __iomem *base; /* ACP memory PCI base */ + struct snd_acp_hw_ops *acp_hw_ops; + int (*acp_hw_ops_init)(struct acp_chip_info *chip); struct platform_device *chip_pdev; + struct acp_resource *rsrc; /* Platform specific resources*/ + struct list_head stream_list; + spinlock_t acp_lock; /* Used to protect stream_list */ + struct platform_device *dmic_codec_dev; + struct platform_device *acp_plat_dev; + struct platform_device *mach_dev; + struct snd_soc_acpi_mach *machines; + int num_dai; + u32 addr; + u32 bclk_div; + u32 lrclk_div; + u32 ch_mask; + u32 tdm_tx_fmt[3]; + u32 tdm_rx_fmt[3]; + u32 xfer_tx_resolution[3]; + u32 xfer_rx_resolution[3]; unsigned int flag; /* Distinguish b/w Legacy or Only PDM */ bool is_pdm_dev; /* flag set to true when ACP PDM controller exists */ bool is_pdm_config; /* flag set to true when PDM configuration is selected from BIOS */ bool is_i2s_config; /* flag set to true when I2S configuration is selected from BIOS */ + bool tdm_mode; }; struct acp_stream { @@ -172,35 +195,23 @@ struct acp_resource { u64 sram_pte_offset; }; -struct acp_dev_data { - char *name; - struct device *dev; - void __iomem *acp_base; - unsigned int i2s_irq; - unsigned int acp_rev; /* ACP Revision id */ - - bool tdm_mode; - bool is_i2s_config; - /* SOC specific dais */ - struct snd_soc_dai_driver *dai_driver; - int num_dai; - - struct list_head stream_list; - spinlock_t acp_lock; - - struct snd_soc_acpi_mach *machines; - struct platform_device *mach_dev; - - u32 bclk_div; - u32 lrclk_div; - - struct acp_resource *rsrc; - u32 ch_mask; - u32 tdm_tx_fmt[3]; - u32 tdm_rx_fmt[3]; - u32 xfer_tx_resolution[3]; - u32 xfer_rx_resolution[3]; - unsigned int flag; +/** + * struct snd_acp_hw_ops - ACP PCI driver platform specific ops + * @acp_init: ACP initialization + * @acp_deinit: ACP de-initialization + * @irq: ACP irq handler + * @en_interrupts: ACP enable interrupts + * @dis_interrupts: ACP disable interrupts + */ +struct snd_acp_hw_ops { + /* ACP hardware initilizations */ + int (*acp_init)(struct acp_chip_info *chip); + int (*acp_deinit)(struct acp_chip_info *chip); + + /* ACP Interrupts*/ + irqreturn_t (*irq)(int irq, void *data); + int (*en_interrupts)(struct acp_chip_info *chip); + int (*dis_interrupts)(struct acp_chip_info *chip); }; enum acp_config { @@ -227,76 +238,121 @@ enum acp_config { ACP_CONFIG_20, }; +extern struct acp_resource rn_rsrc; +extern struct acp_resource rmb_rsrc; +extern struct acp_resource acp63_rsrc; +extern struct acp_resource acp70_rsrc; + +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines; + extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops; extern const struct snd_soc_dai_ops acp_dmic_dai_ops; int acp_platform_register(struct device *dev); int acp_platform_unregister(struct device *dev); -int acp_machine_select(struct acp_dev_data *adata); - -int smn_read(struct pci_dev *dev, u32 smn_addr); -int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data); +int acp_machine_select(struct acp_chip_info *chip); int acp_init(struct acp_chip_info *chip); int acp_deinit(struct acp_chip_info *chip); -void acp_enable_interrupts(struct acp_dev_data *adata); -void acp_disable_interrupts(struct acp_dev_data *adata); +int acp_enable_interrupts(struct acp_chip_info *chip); +int acp_disable_interrupts(struct acp_chip_info *chip); +irqreturn_t acp_irq_handler(int irq, void *data); + +extern struct snd_acp_hw_ops acp31_common_hw_ops; +extern struct snd_acp_hw_ops acp6x_common_hw_ops; +extern struct snd_acp_hw_ops acp63_common_hw_ops; +extern struct snd_acp_hw_ops acp70_common_hw_ops; +extern int acp31_hw_ops_init(struct acp_chip_info *chip); +extern int acp6x_hw_ops_init(struct acp_chip_info *chip); +extern int acp63_hw_ops_init(struct acp_chip_info *chip); +extern int acp70_hw_ops_init(struct acp_chip_info *chip); /* Machine configuration */ int snd_amd_acp_find_config(struct pci_dev *pci); -void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream); -void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size); +void config_pte_for_stream(struct acp_chip_info *chip, struct acp_stream *stream); +void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int size); void restore_acp_pdm_params(struct snd_pcm_substream *substream, - struct acp_dev_data *adata); + struct acp_chip_info *chip); int restore_acp_i2s_params(struct snd_pcm_substream *substream, - struct acp_dev_data *adata, struct acp_stream *stream); + struct acp_chip_info *chip, struct acp_stream *stream); void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip); -static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction) +static inline int acp_hw_init(struct acp_chip_info *chip) +{ + if (chip && chip->acp_hw_ops && chip->acp_hw_ops->acp_init) + return chip->acp_hw_ops->acp_init(chip); + return -EOPNOTSUPP; +} + +static inline int acp_hw_deinit(struct acp_chip_info *chip) +{ + if (chip && chip->acp_hw_ops && chip->acp_hw_ops->acp_deinit) + return chip->acp_hw_ops->acp_deinit(chip); + return -EOPNOTSUPP; +} + +static inline int acp_hw_en_interrupts(struct acp_chip_info *chip) +{ + if (chip && chip->acp_hw_ops && chip->acp_hw_ops->en_interrupts) + return chip->acp_hw_ops->en_interrupts(chip); + return -EOPNOTSUPP; +} + +static inline int acp_hw_dis_interrupts(struct acp_chip_info *chip) +{ + if (chip && chip->acp_hw_ops && chip->acp_hw_ops->dis_interrupts) + chip->acp_hw_ops->dis_interrupts(chip); + return -EOPNOTSUPP; +} + +static inline u64 acp_get_byte_count(struct acp_chip_info *chip, int dai_id, int direction) { u64 byte_count = 0, low = 0, high = 0; if (direction == SNDRV_PCM_STREAM_PLAYBACK) { switch (dai_id) { case I2S_BT_INSTANCE: - high = readl(adata->acp_base + ACP_BT_TX_LINEARPOSITIONCNTR_HIGH(adata)); - low = readl(adata->acp_base + ACP_BT_TX_LINEARPOSITIONCNTR_LOW(adata)); + high = readl(chip->base + ACP_BT_TX_LINEARPOSITIONCNTR_HIGH(chip)); + low = readl(chip->base + ACP_BT_TX_LINEARPOSITIONCNTR_LOW(chip)); break; case I2S_SP_INSTANCE: - high = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH(adata)); - low = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW(adata)); + high = readl(chip->base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH(chip)); + low = readl(chip->base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW(chip)); break; case I2S_HS_INSTANCE: - high = readl(adata->acp_base + ACP_HS_TX_LINEARPOSITIONCNTR_HIGH); - low = readl(adata->acp_base + ACP_HS_TX_LINEARPOSITIONCNTR_LOW); + high = readl(chip->base + ACP_HS_TX_LINEARPOSITIONCNTR_HIGH); + low = readl(chip->base + ACP_HS_TX_LINEARPOSITIONCNTR_LOW); break; default: - dev_err(adata->dev, "Invalid dai id %x\n", dai_id); + dev_err(chip->dev, "Invalid dai id %x\n", dai_id); goto POINTER_RETURN_BYTES; } } else { switch (dai_id) { case I2S_BT_INSTANCE: - high = readl(adata->acp_base + ACP_BT_RX_LINEARPOSITIONCNTR_HIGH(adata)); - low = readl(adata->acp_base + ACP_BT_RX_LINEARPOSITIONCNTR_LOW(adata)); + high = readl(chip->base + ACP_BT_RX_LINEARPOSITIONCNTR_HIGH(chip)); + low = readl(chip->base + ACP_BT_RX_LINEARPOSITIONCNTR_LOW(chip)); break; case I2S_SP_INSTANCE: - high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH(adata)); - low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW(adata)); + high = readl(chip->base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH(chip)); + low = readl(chip->base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW(chip)); break; case I2S_HS_INSTANCE: - high = readl(adata->acp_base + ACP_HS_RX_LINEARPOSITIONCNTR_HIGH); - low = readl(adata->acp_base + ACP_HS_RX_LINEARPOSITIONCNTR_LOW); + high = readl(chip->base + ACP_HS_RX_LINEARPOSITIONCNTR_HIGH); + low = readl(chip->base + ACP_HS_RX_LINEARPOSITIONCNTR_LOW); break; case DMIC_INSTANCE: - high = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH); - low = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW); + high = readl(chip->base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH); + low = readl(chip->base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW); break; default: - dev_err(adata->dev, "Invalid dai id %x\n", dai_id); + dev_err(chip->dev, "Invalid dai id %x\n", dai_id); goto POINTER_RETURN_BYTES; } } diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h index 117ea63e85c6..82275c9de53a 100644 --- a/sound/soc/amd/acp/chip_offset_byte.h +++ b/sound/soc/amd/acp/chip_offset_byte.h @@ -29,13 +29,13 @@ #define ACP_PIN_CONFIG 0x1440 #define ACP3X_PIN_CONFIG 0x1400 -#define ACP_EXTERNAL_INTR_REG_ADDR(adata, offset, ctrl) \ - (adata->acp_base + adata->rsrc->irq_reg_offset + offset + (ctrl * 0x04)) +#define ACP_EXTERNAL_INTR_REG_ADDR(chip, offset, ctrl) \ + (chip->base + chip->rsrc->irq_reg_offset + offset + (ctrl * 0x04)) -#define ACP_EXTERNAL_INTR_ENB(adata) ACP_EXTERNAL_INTR_REG_ADDR(adata, 0x0, 0x0) -#define ACP_EXTERNAL_INTR_CNTL(adata, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(adata, 0x4, ctrl) -#define ACP_EXTERNAL_INTR_STAT(adata, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(adata, \ - (0x4 + (adata->rsrc->no_of_ctrls * 0x04)), ctrl) +#define ACP_EXTERNAL_INTR_ENB(chip) ACP_EXTERNAL_INTR_REG_ADDR(chip, 0x0, 0x0) +#define ACP_EXTERNAL_INTR_CNTL(chip, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(chip, 0x4, ctrl) +#define ACP_EXTERNAL_INTR_STAT(chip, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(chip, \ + (0x4 + (chip->rsrc->no_of_ctrls * 0x04)), ctrl) /* Registers from ACP_AUDIO_BUFFERS block */ diff --git a/sound/soc/amd/acp/soc_amd_sdw_common.h b/sound/soc/amd/acp/soc_amd_sdw_common.h index b7bae107c13e..1f24e0e06487 100644 --- a/sound/soc/amd/acp/soc_amd_sdw_common.h +++ b/sound/soc/amd/acp/soc_amd_sdw_common.h @@ -19,9 +19,12 @@ #define AMD_SDW_MAX_GROUPS 9 #define ACP63_PCI_REV 0x63 +#define ACP70_PCI_REV 0x70 +#define ACP71_PCI_REV 0x71 #define SOC_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0)) #define ASOC_SDW_FOUR_SPK BIT(4) #define ASOC_SDW_ACP_DMIC BIT(5) +#define ASOC_SDW_CODEC_SPKR BIT(15) #define AMD_SDW0 0 #define AMD_SDW1 1 @@ -38,11 +41,20 @@ #define ACP_DMIC_BE_ID 4 +#define ACP70_SW_AUDIO0_TX 0 +#define ACP70_SW_AUDIO1_TX 1 +#define ACP70_SW_AUDIO2_TX 2 + +#define ACP70_SW_AUDIO0_RX 3 +#define ACP70_SW_AUDIO1_RX 4 +#define ACP70_SW_AUDIO2_RX 5 + struct amd_mc_ctx { unsigned int acp_rev; unsigned int max_sdw_links; }; int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev); +int get_acp70_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev); #endif diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h index a86c76f781f9..fdf016a64bbf 100644 --- a/sound/soc/amd/mach-config.h +++ b/sound/soc/amd/mach-config.h @@ -26,6 +26,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[]; struct config_entry { u32 flags; diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile index b5efb1c5382c..778ee4726389 100644 --- a/sound/soc/amd/ps/Makefile +++ b/sound/soc/amd/ps/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only # Pink Sardine platform Support -snd-pci-ps-y := pci-ps.o +snd-pci-ps-y := pci-ps.o ps-common.o snd-ps-pdm-dma-y := ps-pdm-dma.o snd-soc-ps-mach-y := ps-mach.o snd-ps-sdw-dma-y := ps-sdw-dma.o diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index e54eabaa4d3e..85feae45c44c 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * AMD ALSA SoC PDM Driver + * AMD Common ACP header file for ACP6.3, ACP7.0 & ACP7.1 platforms * - * Copyright (C) 2022, 2023 Advanced Micro Devices, Inc. All rights reserved. + * Copyright (C) 2022, 2023, 2025 Advanced Micro Devices, Inc. All rights reserved. */ #include <linux/soundwire/sdw_amd.h> @@ -11,15 +11,18 @@ #define ACP_DEVICE_ID 0x15E2 #define ACP63_REG_START 0x1240000 #define ACP63_REG_END 0x125C000 +#define ACP63_PCI_REV 0x63 +#define ACP70_PCI_REV 0x70 +#define ACP71_PCI_REV 0x71 #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 -#define ACP_PGFSM_CNTL_POWER_ON_MASK 1 -#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0 -#define ACP_PGFSM_STATUS_MASK 3 -#define ACP_POWERED_ON 0 -#define ACP_POWER_ON_IN_PROGRESS 1 -#define ACP_POWERED_OFF 2 -#define ACP_POWER_OFF_IN_PROGRESS 3 +#define ACP63_PGFSM_CNTL_POWER_ON_MASK 1 +#define ACP63_PGFSM_CNTL_POWER_OFF_MASK 0 +#define ACP63_PGFSM_STATUS_MASK 3 +#define ACP63_POWERED_ON 0 +#define ACP63_POWER_ON_IN_PROGRESS 1 +#define ACP63_POWERED_OFF 2 +#define ACP63_POWER_OFF_IN_PROGRESS 3 #define ACP_ERROR_MASK 0x20000000 #define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF @@ -60,7 +63,7 @@ #define AMD_SDW_MAX_MANAGERS 2 /* time in ms for acp timeout */ -#define ACP_TIMEOUT 500 +#define ACP63_TIMEOUT 500 #define ACP_SDW0_STAT BIT(21) #define ACP_SDW1_STAT BIT(2) @@ -72,13 +75,13 @@ #define ACP_AUDIO0_RX_THRESHOLD 0x1b #define ACP_AUDIO1_RX_THRESHOLD 0x19 #define ACP_AUDIO2_RX_THRESHOLD 0x17 -#define ACP_P1_AUDIO1_TX_THRESHOLD BIT(6) -#define ACP_P1_AUDIO1_RX_THRESHOLD BIT(5) -#define ACP_SDW_DMA_IRQ_MASK 0x1F800000 -#define ACP_P1_SDW_DMA_IRQ_MASK 0x60 +#define ACP63_P1_AUDIO1_TX_THRESHOLD BIT(6) +#define ACP63_P1_AUDIO1_RX_THRESHOLD BIT(5) +#define ACP63_SDW_DMA_IRQ_MASK 0x1F800000 +#define ACP63_P1_SDW_DMA_IRQ_MASK 0x60 #define ACP63_SDW0_DMA_MAX_STREAMS 6 #define ACP63_SDW1_DMA_MAX_STREAMS 2 -#define ACP_P1_AUDIO_TX_THRESHOLD 6 +#define ACP63_P1_AUDIO_TX_THRESHOLD 6 /* * Below entries describes SDW0 instance DMA stream id and DMA irq bit mapping @@ -91,8 +94,8 @@ * 4 (SDW0_AUDIO1_RX) 25 * 5 (SDW0_AUDIO2_RX) 23 */ -#define SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i))) -#define SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3))) +#define ACP63_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i))) +#define ACP63_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3))) /* * Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping @@ -101,7 +104,7 @@ * 0 (SDW1_AUDIO1_TX) 6 * 1 (SDW1_AUDIO1_RX) 5 */ -#define SDW1_DMA_IRQ_MASK(i) (ACP_P1_AUDIO_TX_THRESHOLD - (i)) +#define ACP63_SDW1_DMA_IRQ_MASK(i) (ACP63_P1_AUDIO_TX_THRESHOLD - (i)) #define ACP_DELAY_US 5 #define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024) @@ -129,6 +132,61 @@ #define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS) #define SDW_MIN_BUFFER SDW_MAX_BUFFER +#define ACP_HW_OPS(acp_data, cb) ((acp_data)->hw_ops->cb) + +#define ACP70_PGFSM_CNTL_POWER_ON_MASK 0x1F +#define ACP70_PGFSM_CNTL_POWER_OFF_MASK 0 +#define ACP70_PGFSM_STATUS_MASK 0xFF +#define ACP70_TIMEOUT 2000 +#define ACP70_SDW_HOST_WAKE_MASK 0x0C00000 +#define ACP70_SDW0_HOST_WAKE_STAT BIT(24) +#define ACP70_SDW1_HOST_WAKE_STAT BIT(25) +#define ACP70_SDW0_PME_STAT BIT(26) +#define ACP70_SDW1_PME_STAT BIT(27) + +#define ACP70_SDW0_DMA_MAX_STREAMS 6 +#define ACP70_SDW1_DMA_MAX_STREAMS ACP70_SDW0_DMA_MAX_STREAMS +#define ACP70_SDW_DMA_IRQ_MASK 0x1F800000 +#define ACP70_P1_SDW_DMA_IRQ_MASK 0x1F8 + +#define ACP70_P1_AUDIO0_TX_THRESHOLD 0x8 +#define ACP70_P1_AUDIO1_TX_THRESHOLD 0x6 +#define ACP70_P1_AUDIO2_TX_THRESHOLD 0x4 +#define ACP70_P1_AUDIO0_RX_THRESHOLD 0x7 +#define ACP70_P1_AUDIO1_RX_THRESHOLD 0x5 +#define ACP70_P1_AUDIO2_RX_THRESHOLD 0x3 + +#define ACP70_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i))) +#define ACP70_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3))) + +/* + * Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping + * in ACP_EXTENAL_INTR_CNTL1 register for ACP70/ACP71 platforms + * Stream id IRQ Bit + * 0 (SDW1_AUDIO0_TX) 8 + * 1 (SDW1_AUDIO1_TX) 6 + * 2 (SDW1_AUDIO2_TX) 4 + * 3 (SDW1_AUDIO0_RX) 7 + * 4 (SDW1_AUDIO1_RX) 5 + * 5 (SDW1_AUDIO2_RX) 3 + */ +#define ACP70_SDW1_DMA_TX_IRQ_MASK(i) (ACP70_P1_AUDIO0_TX_THRESHOLD - (2 * (i))) +#define ACP70_SDW1_DMA_RX_IRQ_MASK(i) (ACP70_P1_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3))) + +#define ACP70_SW0_AUDIO0_TX_EN ACP_SW0_AUDIO0_TX_EN +#define ACP70_SW0_AUDIO1_TX_EN ACP_SW0_AUDIO1_TX_EN +#define ACP70_SW0_AUDIO2_TX_EN ACP_SW0_AUDIO2_TX_EN +#define ACP70_SW0_AUDIO0_RX_EN ACP_SW0_AUDIO0_RX_EN +#define ACP70_SW0_AUDIO1_RX_EN ACP_SW0_AUDIO1_RX_EN +#define ACP70_SW0_AUDIO2_RX_EN ACP_SW0_AUDIO2_RX_EN + +#define ACP70_SW1_AUDIO0_TX_EN 0x0003C10 +#define ACP70_SW1_AUDIO1_TX_EN 0x0003C50 +#define ACP70_SW1_AUDIO2_TX_EN 0x0003C6C +#define ACP70_SW1_AUDIO0_RX_EN 0x0003C88 +#define ACP70_SW1_AUDIO1_RX_EN 0x0003D28 +#define ACP70_SW1_AUDIO2_RX_EN 0x0003D44 + enum acp_config { ACP_CONFIG_0 = 0, ACP_CONFIG_1, @@ -146,20 +204,34 @@ enum acp_config { ACP_CONFIG_13, ACP_CONFIG_14, ACP_CONFIG_15, + ACP_CONFIG_16, + ACP_CONFIG_17, + ACP_CONFIG_18, + ACP_CONFIG_19, + ACP_CONFIG_20, +}; + +enum amd_acp63_sdw0_channel { + ACP63_SDW0_AUDIO0_TX = 0, + ACP63_SDW0_AUDIO1_TX, + ACP63_SDW0_AUDIO2_TX, + ACP63_SDW0_AUDIO0_RX, + ACP63_SDW0_AUDIO1_RX, + ACP63_SDW0_AUDIO2_RX, }; -enum amd_sdw0_channel { - ACP_SDW0_AUDIO0_TX = 0, - ACP_SDW0_AUDIO1_TX, - ACP_SDW0_AUDIO2_TX, - ACP_SDW0_AUDIO0_RX, - ACP_SDW0_AUDIO1_RX, - ACP_SDW0_AUDIO2_RX, +enum amd_acp63_sdw1_channel { + ACP63_SDW1_AUDIO1_TX, + ACP63_SDW1_AUDIO1_RX, }; -enum amd_sdw1_channel { - ACP_SDW1_AUDIO1_TX, - ACP_SDW1_AUDIO1_RX, +enum amd_acp70_sdw_channel { + ACP70_SDW_AUDIO0_TX = 0, + ACP70_SDW_AUDIO1_TX, + ACP70_SDW_AUDIO2_TX, + ACP70_SDW_AUDIO0_RX, + ACP70_SDW_AUDIO1_RX, + ACP70_SDW_AUDIO2_RX, }; struct pdm_stream_instance { @@ -180,8 +252,11 @@ struct pdm_dev_data { struct sdw_dma_dev_data { void __iomem *acp_base; struct mutex *acp_lock; /* used to protect acp common register access */ - struct snd_pcm_substream *sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS]; - struct snd_pcm_substream *sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS]; + u32 acp_rev; + struct snd_pcm_substream *acp63_sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS]; + struct snd_pcm_substream *acp63_sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS]; + struct snd_pcm_substream *acp70_sdw0_dma_stream[ACP70_SDW0_DMA_MAX_STREAMS]; + struct snd_pcm_substream *acp70_sdw1_dma_stream[ACP70_SDW1_DMA_MAX_STREAMS]; }; struct acp_sdw_dma_stream { @@ -212,10 +287,35 @@ struct sdw_dma_ring_buf_reg { u32 pos_high_reg; }; +struct acp63_dev_data; + +/** + * struct acp_hw_ops - ACP PCI driver platform specific ops + * @acp_init: ACP initialization + * @acp_deinit: ACP de-initialization + * @acp_get_config: function to read the acp pin configuration + * @acp_sdw_dma_irq_thread: ACP SoundWire DMA interrupt thread + * acp_suspend: ACP system level suspend callback + * acp_resume: ACP system level resume callback + * acp_suspend_runtime: ACP runtime suspend callback + * acp_resume_runtime: ACP runtime resume callback + */ +struct acp_hw_ops { + int (*acp_init)(void __iomem *acp_base, struct device *dev); + int (*acp_deinit)(void __iomem *acp_base, struct device *dev); + void (*acp_get_config)(struct pci_dev *pci, struct acp63_dev_data *acp_data); + void (*acp_sdw_dma_irq_thread)(struct acp63_dev_data *acp_data); + int (*acp_suspend)(struct device *dev); + int (*acp_resume)(struct device *dev); + int (*acp_suspend_runtime)(struct device *dev); + int (*acp_resume_runtime)(struct device *dev); +}; + /** * struct acp63_dev_data - acp pci driver context * @acp63_base: acp mmio base * @res: resource + * @hw_ops: ACP pci driver platform-specific ops * @pdm_dev: ACP PDM controller platform device * @dmic_codec: platform device for DMIC Codec * sdw_dma_dev: platform device for SoundWire DMA controller @@ -229,16 +329,25 @@ struct sdw_dma_ring_buf_reg { * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS * @is_sdw_config: flag set to true when SDW configuration is selected from BIOS * @sdw_en_stat: flag set to true when any one of the SoundWire manager instance is enabled + * @acp70_sdw0_wake_event: flag set to true when wake irq asserted for SW0 instance + * @acp70_sdw1_wake_event: flag set to true when wake irq asserted for SW1 instance * @addr: pci ioremap address * @reg_range: ACP reigister range * @acp_rev: ACP PCI revision id - * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance - * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance + * @acp63_sdw0-dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire + * manager-SW0 instance + * @acp63_sdw_dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire + * manager-SW1 instance + * @acp70_sdw0-dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire + * manager-SW0 instance + * @acp70_sdw_dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire + * manager-SW1 instance */ struct acp63_dev_data { void __iomem *acp63_base; struct resource *res; + struct acp_hw_ops *hw_ops; struct platform_device *pdm_dev; struct platform_device *dmic_codec_dev; struct platform_device *sdw_dma_dev; @@ -253,11 +362,80 @@ struct acp63_dev_data { bool is_pdm_config; bool is_sdw_config; bool sdw_en_stat; + bool acp70_sdw0_wake_event; + bool acp70_sdw1_wake_event; u32 addr; u32 reg_range; u32 acp_rev; - u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS]; - u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS]; + u16 acp63_sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS]; + u16 acp63_sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS]; + u16 acp70_sdw0_dma_intr_stat[ACP70_SDW0_DMA_MAX_STREAMS]; + u16 acp70_sdw1_dma_intr_stat[ACP70_SDW1_DMA_MAX_STREAMS]; }; +void acp63_hw_init_ops(struct acp_hw_ops *hw_ops); +void acp70_hw_init_ops(struct acp_hw_ops *hw_ops); + +static inline int acp_hw_init(struct acp63_dev_data *adata, struct device *dev) +{ + if (adata && adata->hw_ops && adata->hw_ops->acp_init) + return ACP_HW_OPS(adata, acp_init)(adata->acp63_base, dev); + return -EOPNOTSUPP; +} + +static inline int acp_hw_deinit(struct acp63_dev_data *adata, struct device *dev) +{ + if (adata && adata->hw_ops && adata->hw_ops->acp_deinit) + return ACP_HW_OPS(adata, acp_deinit)(adata->acp63_base, dev); + return -EOPNOTSUPP; +} + +static inline void acp_hw_get_config(struct pci_dev *pci, struct acp63_dev_data *adata) +{ + if (adata && adata->hw_ops && adata->hw_ops->acp_get_config) + ACP_HW_OPS(adata, acp_get_config)(pci, adata); +} + +static inline void acp_hw_sdw_dma_irq_thread(struct acp63_dev_data *adata) +{ + if (adata && adata->hw_ops && adata->hw_ops->acp_sdw_dma_irq_thread) + ACP_HW_OPS(adata, acp_sdw_dma_irq_thread)(adata); +} + +static inline int acp_hw_suspend(struct device *dev) +{ + struct acp63_dev_data *adata = dev_get_drvdata(dev); + + if (adata && adata->hw_ops && adata->hw_ops->acp_suspend) + return ACP_HW_OPS(adata, acp_suspend)(dev); + return -EOPNOTSUPP; +} + +static inline int acp_hw_resume(struct device *dev) +{ + struct acp63_dev_data *adata = dev_get_drvdata(dev); + + if (adata && adata->hw_ops && adata->hw_ops->acp_resume) + return ACP_HW_OPS(adata, acp_resume)(dev); + return -EOPNOTSUPP; +} + +static inline int acp_hw_suspend_runtime(struct device *dev) +{ + struct acp63_dev_data *adata = dev_get_drvdata(dev); + + if (adata && adata->hw_ops && adata->hw_ops->acp_suspend_runtime) + return ACP_HW_OPS(adata, acp_suspend_runtime)(dev); + return -EOPNOTSUPP; +} + +static inline int acp_hw_runtime_resume(struct device *dev) +{ + struct acp63_dev_data *adata = dev_get_drvdata(dev); + + if (adata && adata->hw_ops && adata->hw_ops->acp_resume_runtime) + return ACP_HW_OPS(adata, acp_resume_runtime)(dev); + return -EOPNOTSUPP; +} + int snd_amd_acp_find_config(struct pci_dev *pci); diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index 4575326d0635..8e57f31ef7f7 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * AMD Pink Sardine ACP PCI Driver + * AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms. * - * Copyright 2022 Advanced Micro Devices, Inc. + * Copyright 2022, 2025 Advanced Micro Devices, Inc. */ #include <linux/pci.h> @@ -21,107 +21,168 @@ #include "acp63.h" -static int acp63_power_on(void __iomem *acp_base) +static void handle_acp70_sdw_wake_event(struct acp63_dev_data *adata) { - u32 val; - - val = readl(acp_base + ACP_PGFSM_STATUS); - - if (!val) - return val; + struct amd_sdw_manager *amd_manager; - if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS) - writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL); + if (adata->acp70_sdw0_wake_event) { + amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev); + if (amd_manager) + pm_request_resume(amd_manager->dev); + adata->acp70_sdw0_wake_event = 0; + } - return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT); + if (adata->acp70_sdw1_wake_event) { + amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev); + if (amd_manager) + pm_request_resume(amd_manager->dev); + adata->acp70_sdw1_wake_event = 0; + } } -static int acp63_reset(void __iomem *acp_base) +static short int check_and_handle_acp70_sdw_wake_irq(struct acp63_dev_data *adata) { - u32 val; - int ret; + u32 ext_intr_stat1; + int irq_flag = 0; + bool sdw_wake_irq = false; - writel(1, acp_base + ACP_SOFT_RESET); - - ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, - val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK, - DELAY_US, ACP_TIMEOUT); - if (ret) - return ret; - - writel(0, acp_base + ACP_SOFT_RESET); - - return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT); -} - -static void acp63_enable_interrupts(void __iomem *acp_base) -{ - writel(1, acp_base + ACP_EXTERNAL_INTR_ENB); - writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL); -} + ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + if (ext_intr_stat1 & ACP70_SDW0_HOST_WAKE_STAT) { + writel(ACP70_SDW0_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + adata->acp70_sdw0_wake_event = true; + sdw_wake_irq = true; + } -static void acp63_disable_interrupts(void __iomem *acp_base) -{ - writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT); - writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL); - writel(0, acp_base + ACP_EXTERNAL_INTR_ENB); -} + if (ext_intr_stat1 & ACP70_SDW1_HOST_WAKE_STAT) { + writel(ACP70_SDW1_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + adata->acp70_sdw1_wake_event = true; + sdw_wake_irq = true; + } -static int acp63_init(void __iomem *acp_base, struct device *dev) -{ - int ret; + if (ext_intr_stat1 & ACP70_SDW0_PME_STAT) { + writel(0, adata->acp63_base + ACP_SW0_WAKE_EN); + writel(ACP70_SDW0_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + adata->acp70_sdw0_wake_event = true; + sdw_wake_irq = true; + } - ret = acp63_power_on(acp_base); - if (ret) { - dev_err(dev, "ACP power on failed\n"); - return ret; + if (ext_intr_stat1 & ACP70_SDW1_PME_STAT) { + writel(0, adata->acp63_base + ACP_SW1_WAKE_EN); + writel(ACP70_SDW1_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + adata->acp70_sdw1_wake_event = true; + sdw_wake_irq = true; } - writel(0x01, acp_base + ACP_CONTROL); - ret = acp63_reset(acp_base); - if (ret) { - dev_err(dev, "ACP reset failed\n"); - return ret; + + if (sdw_wake_irq) { + handle_acp70_sdw_wake_event(adata); + irq_flag = 1; } - acp63_enable_interrupts(acp_base); - return 0; + return irq_flag; } -static int acp63_deinit(void __iomem *acp_base, struct device *dev) +static short int check_and_handle_sdw_dma_irq(struct acp63_dev_data *adata, u32 ext_intr_stat, + u32 ext_intr_stat1) { - int ret; + u32 stream_id = 0; + u16 sdw_dma_irq_flag = 0; + u16 index; - acp63_disable_interrupts(acp_base); - ret = acp63_reset(acp_base); - if (ret) { - dev_err(dev, "ACP reset failed\n"); - return ret; + if (ext_intr_stat & ACP63_SDW_DMA_IRQ_MASK) { + for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) { + if (ext_intr_stat & BIT(index)) { + writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); + switch (index) { + case ACP_AUDIO0_TX_THRESHOLD: + stream_id = ACP63_SDW0_AUDIO0_TX; + break; + case ACP_AUDIO1_TX_THRESHOLD: + stream_id = ACP63_SDW0_AUDIO1_TX; + break; + case ACP_AUDIO2_TX_THRESHOLD: + stream_id = ACP63_SDW0_AUDIO2_TX; + break; + case ACP_AUDIO0_RX_THRESHOLD: + stream_id = ACP63_SDW0_AUDIO0_RX; + break; + case ACP_AUDIO1_RX_THRESHOLD: + stream_id = ACP63_SDW0_AUDIO1_RX; + break; + case ACP_AUDIO2_RX_THRESHOLD: + stream_id = ACP63_SDW0_AUDIO2_RX; + break; + } + switch (adata->acp_rev) { + case ACP63_PCI_REV: + adata->acp63_sdw0_dma_intr_stat[stream_id] = 1; + break; + case ACP70_PCI_REV: + case ACP71_PCI_REV: + adata->acp70_sdw0_dma_intr_stat[stream_id] = 1; + break; + } + sdw_dma_irq_flag = 1; + } + } } - writel(0, acp_base + ACP_CONTROL); - return 0; + switch (adata->acp_rev) { + case ACP63_PCI_REV: + if (ext_intr_stat1 & ACP63_P1_AUDIO1_RX_THRESHOLD) { + writel(ACP63_P1_AUDIO1_RX_THRESHOLD, + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_RX] = 1; + sdw_dma_irq_flag = 1; + } + if (ext_intr_stat1 & ACP63_P1_AUDIO1_TX_THRESHOLD) { + writel(ACP63_P1_AUDIO1_TX_THRESHOLD, + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_TX] = 1; + sdw_dma_irq_flag = 1; + } + break; + case ACP70_PCI_REV: + case ACP71_PCI_REV: + if (ext_intr_stat1 & ACP70_P1_SDW_DMA_IRQ_MASK) { + for (index = ACP70_P1_AUDIO2_RX_THRESHOLD; + index <= ACP70_P1_AUDIO0_TX_THRESHOLD; index++) { + if (ext_intr_stat1 & BIT(index)) { + writel(BIT(index), + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + switch (index) { + case ACP70_P1_AUDIO0_TX_THRESHOLD: + stream_id = ACP70_SDW_AUDIO0_TX; + break; + case ACP70_P1_AUDIO1_TX_THRESHOLD: + stream_id = ACP70_SDW_AUDIO1_TX; + break; + case ACP70_P1_AUDIO2_TX_THRESHOLD: + stream_id = ACP70_SDW_AUDIO2_TX; + break; + case ACP70_P1_AUDIO0_RX_THRESHOLD: + stream_id = ACP70_SDW_AUDIO0_RX; + break; + case ACP70_P1_AUDIO1_RX_THRESHOLD: + stream_id = ACP70_SDW_AUDIO1_RX; + break; + case ACP70_P1_AUDIO2_RX_THRESHOLD: + stream_id = ACP70_SDW_AUDIO2_RX; + break; + } + + adata->acp70_sdw1_dma_intr_stat[stream_id] = 1; + sdw_dma_irq_flag = 1; + } + } + } + break; + } + return sdw_dma_irq_flag; } static irqreturn_t acp63_irq_thread(int irq, void *context) { - struct sdw_dma_dev_data *sdw_dma_data; struct acp63_dev_data *adata = context; - u32 stream_index; - sdw_dma_data = dev_get_drvdata(&adata->sdw_dma_dev->dev); - - for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) { - if (adata->sdw0_dma_intr_stat[stream_index]) { - if (sdw_dma_data->sdw0_dma_stream[stream_index]) - snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]); - adata->sdw0_dma_intr_stat[stream_index] = 0; - } - } - for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) { - if (adata->sdw1_dma_intr_stat[stream_index]) { - if (sdw_dma_data->sdw1_dma_stream[stream_index]) - snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]); - adata->sdw1_dma_intr_stat[stream_index] = 0; - } - } + acp_hw_sdw_dma_irq_thread(adata); return IRQ_HANDLED; } @@ -131,10 +192,8 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) struct pdm_dev_data *ps_pdm_data; struct amd_sdw_manager *amd_manager; u32 ext_intr_stat, ext_intr_stat1; - u32 stream_id = 0; u16 irq_flag = 0; u16 sdw_dma_irq_flag = 0; - u16 index; adata = dev_id; if (!adata) @@ -171,6 +230,9 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) irq_flag = 1; } + if (adata->acp_rev >= ACP70_PCI_REV) + irq_flag = check_and_handle_acp70_sdw_wake_irq(adata); + if (ext_intr_stat & BIT(PDM_DMA_STAT)) { ps_pdm_data = dev_get_drvdata(&adata->pdm_dev->dev); writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); @@ -178,51 +240,8 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id) snd_pcm_period_elapsed(ps_pdm_data->capture_stream); irq_flag = 1; } - if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) { - for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) { - if (ext_intr_stat & BIT(index)) { - writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); - switch (index) { - case ACP_AUDIO0_TX_THRESHOLD: - stream_id = ACP_SDW0_AUDIO0_TX; - break; - case ACP_AUDIO1_TX_THRESHOLD: - stream_id = ACP_SDW0_AUDIO1_TX; - break; - case ACP_AUDIO2_TX_THRESHOLD: - stream_id = ACP_SDW0_AUDIO2_TX; - break; - case ACP_AUDIO0_RX_THRESHOLD: - stream_id = ACP_SDW0_AUDIO0_RX; - break; - case ACP_AUDIO1_RX_THRESHOLD: - stream_id = ACP_SDW0_AUDIO1_RX; - break; - case ACP_AUDIO2_RX_THRESHOLD: - stream_id = ACP_SDW0_AUDIO2_RX; - break; - } - - adata->sdw0_dma_intr_stat[stream_id] = 1; - sdw_dma_irq_flag = 1; - } - } - } - - if (ext_intr_stat1 & ACP_P1_AUDIO1_RX_THRESHOLD) { - writel(ACP_P1_AUDIO1_RX_THRESHOLD, - adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); - adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1; - sdw_dma_irq_flag = 1; - } - - if (ext_intr_stat1 & ACP_P1_AUDIO1_TX_THRESHOLD) { - writel(ACP_P1_AUDIO1_TX_THRESHOLD, - adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); - adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_TX] = 1; - sdw_dma_irq_flag = 1; - } + sdw_dma_irq_flag = check_and_handle_sdw_dma_irq(adata, ext_intr_stat, ext_intr_stat1); if (sdw_dma_irq_flag) return IRQ_WAKE_THREAD; @@ -312,6 +331,7 @@ static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev) if (mach && mach->link_mask) { mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; + mach->mach_params.subsystem_rev = acp_data->acp_rev; return mach; } } @@ -377,7 +397,6 @@ static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *a const union acpi_object *obj; acpi_handle handle; acpi_integer dmic_status; - u32 config; bool is_dmic_dev = false; bool is_sdw_dev = false; bool wov_en, dmic_en; @@ -387,30 +406,7 @@ static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *a wov_en = true; dmic_en = false; - config = readl(acp_data->acp63_base + ACP_PIN_CONFIG); - switch (config) { - case ACP_CONFIG_4: - case ACP_CONFIG_5: - case ACP_CONFIG_10: - case ACP_CONFIG_11: - acp_data->is_pdm_config = true; - break; - case ACP_CONFIG_2: - case ACP_CONFIG_3: - acp_data->is_sdw_config = true; - break; - case ACP_CONFIG_6: - case ACP_CONFIG_7: - case ACP_CONFIG_12: - case ACP_CONFIG_8: - case ACP_CONFIG_13: - case ACP_CONFIG_14: - acp_data->is_pdm_config = true; - acp_data->is_sdw_config = true; - break; - default: - break; - } + acp_hw_get_config(pci, acp_data); if (acp_data->is_pdm_config) { pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); @@ -537,11 +533,33 @@ unregister_dmic_codec_dev: unregister_pdm_dev: platform_device_unregister(adata->pdm_dev); de_init: - if (acp63_deinit(adata->acp63_base, &pci->dev)) + if (acp_hw_deinit(adata, &pci->dev)) dev_err(&pci->dev, "ACP de-init failed\n"); return ret; } +static int acp_hw_init_ops(struct acp63_dev_data *adata, struct pci_dev *pci) +{ + adata->hw_ops = devm_kzalloc(&pci->dev, sizeof(struct acp_hw_ops), + GFP_KERNEL); + if (!adata->hw_ops) + return -ENOMEM; + + switch (adata->acp_rev) { + case ACP63_PCI_REV: + acp63_hw_init_ops(adata->hw_ops); + break; + case ACP70_PCI_REV: + case ACP71_PCI_REV: + acp70_hw_init_ops(adata->hw_ops); + break; + default: + dev_err(&pci->dev, "ACP device not found\n"); + return -ENODEV; + } + return 0; +} + static int snd_acp63_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { @@ -557,12 +575,14 @@ static int snd_acp63_probe(struct pci_dev *pci, if (flag) return -ENODEV; - /* Pink Sardine device check */ + /* ACP PCI revision id check for ACP6.3, ACP7.0 & ACP7.1 platforms */ switch (pci->revision) { - case 0x63: + case ACP63_PCI_REV: + case ACP70_PCI_REV: + case ACP71_PCI_REV: break; default: - dev_dbg(&pci->dev, "acp63 pci device not found\n"); + dev_dbg(&pci->dev, "acp63/acp70/acp71 pci device not found\n"); return -ENODEV; } if (pci_enable_device(pci)) { @@ -595,7 +615,12 @@ static int snd_acp63_probe(struct pci_dev *pci, pci_set_master(pci); pci_set_drvdata(pci, adata); mutex_init(&adata->acp_lock); - ret = acp63_init(adata->acp63_base, &pci->dev); + ret = acp_hw_init_ops(adata, pci); + if (ret) { + dev_err(&pci->dev, "ACP hw ops init failed\n"); + goto release_regions; + } + ret = acp_hw_init(adata, &pci->dev); if (ret) goto release_regions; ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler, @@ -615,7 +640,11 @@ static int snd_acp63_probe(struct pci_dev *pci, dev_err(&pci->dev, "ACP platform devices creation failed\n"); goto de_init; } - adata->machines = snd_soc_acpi_amd_acp63_sdw_machines; + if (adata->acp_rev >= ACP70_PCI_REV) + adata->machines = snd_soc_acpi_amd_acp70_sdw_machines; + else + adata->machines = snd_soc_acpi_amd_acp63_sdw_machines; + ret = acp63_machine_register(&pci->dev); if (ret) { dev_err(&pci->dev, "ACP machine register failed\n"); @@ -629,7 +658,7 @@ skip_pdev_creation: pm_runtime_allow(&pci->dev); return 0; de_init: - if (acp63_deinit(adata->acp63_base, &pci->dev)) + if (acp_hw_deinit(adata, &pci->dev)) dev_err(&pci->dev, "ACP de-init failed\n"); release_regions: pci_release_regions(pci); @@ -639,85 +668,24 @@ disable_pci: return ret; } -static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata) +static int snd_acp_suspend(struct device *dev) { - u32 sdw0_en, sdw1_en; - - sdw0_en = readl(adata->acp63_base + ACP_SW0_EN); - sdw1_en = readl(adata->acp63_base + ACP_SW1_EN); - return (sdw0_en || sdw1_en); -} - -static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata) -{ - u32 val; - - val = readl(adata->acp63_base + ACP_SW0_WAKE_EN); - if (val && adata->sdw->pdev[0]) - pm_request_resume(&adata->sdw->pdev[0]->dev); - - val = readl(adata->acp63_base + ACP_SW1_WAKE_EN); - if (val && adata->sdw->pdev[1]) - pm_request_resume(&adata->sdw->pdev[1]->dev); -} - -static int __maybe_unused snd_acp63_suspend(struct device *dev) -{ - struct acp63_dev_data *adata; - int ret; - - adata = dev_get_drvdata(dev); - if (adata->is_sdw_dev) { - adata->sdw_en_stat = check_acp_sdw_enable_status(adata); - if (adata->sdw_en_stat) - return 0; - } - ret = acp63_deinit(adata->acp63_base, dev); - if (ret) - dev_err(dev, "ACP de-init failed\n"); - - return ret; + return acp_hw_suspend(dev); } -static int __maybe_unused snd_acp63_runtime_resume(struct device *dev) +static int snd_acp_runtime_resume(struct device *dev) { - struct acp63_dev_data *adata; - int ret; - - adata = dev_get_drvdata(dev); - if (adata->sdw_en_stat) - return 0; - - ret = acp63_init(adata->acp63_base, dev); - if (ret) { - dev_err(dev, "ACP init failed\n"); - return ret; - } - - if (!adata->sdw_en_stat) - handle_acp63_sdw_pme_event(adata); - return 0; + return acp_hw_runtime_resume(dev); } -static int __maybe_unused snd_acp63_resume(struct device *dev) +static int snd_acp_resume(struct device *dev) { - struct acp63_dev_data *adata; - int ret; - - adata = dev_get_drvdata(dev); - if (adata->sdw_en_stat) - return 0; - - ret = acp63_init(adata->acp63_base, dev); - if (ret) - dev_err(dev, "ACP init failed\n"); - - return ret; + return acp_hw_resume(dev); } static const struct dev_pm_ops acp63_pm_ops = { - SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(snd_acp63_suspend, snd_acp63_resume) + RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume) }; static void snd_acp63_remove(struct pci_dev *pci) @@ -736,7 +704,7 @@ static void snd_acp63_remove(struct pci_dev *pci) } if (adata->mach_dev) platform_device_unregister(adata->mach_dev); - ret = acp63_deinit(adata->acp63_base, &pci->dev); + ret = acp_hw_deinit(adata, &pci->dev); if (ret) dev_err(&pci->dev, "ACP de-init failed\n"); pm_runtime_forbid(&pci->dev); @@ -759,7 +727,7 @@ static struct pci_driver ps_acp63_driver = { .probe = snd_acp63_probe, .remove = snd_acp63_remove, .driver = { - .pm = &acp63_pm_ops, + .pm = pm_ptr(&acp63_pm_ops), } }; @@ -767,7 +735,7 @@ module_pci_driver(ps_acp63_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); MODULE_AUTHOR("Syed.SabaKareem@amd.com"); -MODULE_DESCRIPTION("AMD ACP Pink Sardine PCI driver"); +MODULE_DESCRIPTION("AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms"); MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT"); MODULE_IMPORT_NS("SND_AMD_SOUNDWIRE_ACPI"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/ps/ps-common.c b/sound/soc/amd/ps/ps-common.c new file mode 100644 index 000000000000..1c89fb5fe1da --- /dev/null +++ b/sound/soc/amd/ps/ps-common.c @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AMD ACP PCI driver callback routines for ACP6.3, ACP7.0 & ACP7.1 + * platforms. + * + * Copyright 2025 Advanced Micro Devices, Inc. + * Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com> + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <sound/pcm_params.h> + +#include "acp63.h" + +static int acp63_power_on(void __iomem *acp_base) +{ + u32 val; + + val = readl(acp_base + ACP_PGFSM_STATUS); + + if (!val) + return val; + + if ((val & ACP63_PGFSM_STATUS_MASK) != ACP63_POWER_ON_IN_PROGRESS) + writel(ACP63_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL); + + return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP63_TIMEOUT); +} + +static int acp63_reset(void __iomem *acp_base) +{ + u32 val; + int ret; + + writel(1, acp_base + ACP_SOFT_RESET); + + ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, + val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK, + DELAY_US, ACP63_TIMEOUT); + if (ret) + return ret; + + writel(0, acp_base + ACP_SOFT_RESET); + + return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP63_TIMEOUT); +} + +static void acp63_enable_interrupts(void __iomem *acp_base) +{ + writel(1, acp_base + ACP_EXTERNAL_INTR_ENB); + writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL); +} + +static void acp63_disable_interrupts(void __iomem *acp_base) +{ + writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT); + writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL); + writel(0, acp_base + ACP_EXTERNAL_INTR_ENB); +} + +static int acp63_init(void __iomem *acp_base, struct device *dev) +{ + int ret; + + ret = acp63_power_on(acp_base); + if (ret) { + dev_err(dev, "ACP power on failed\n"); + return ret; + } + writel(0x01, acp_base + ACP_CONTROL); + ret = acp63_reset(acp_base); + if (ret) { + dev_err(dev, "ACP reset failed\n"); + return ret; + } + acp63_enable_interrupts(acp_base); + writel(0, acp_base + ACP_ZSC_DSP_CTRL); + return 0; +} + +static int acp63_deinit(void __iomem *acp_base, struct device *dev) +{ + int ret; + + acp63_disable_interrupts(acp_base); + ret = acp63_reset(acp_base); + if (ret) { + dev_err(dev, "ACP reset failed\n"); + return ret; + } + writel(0, acp_base + ACP_CONTROL); + writel(1, acp_base + ACP_ZSC_DSP_CTRL); + return 0; +} + +static void acp63_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data) +{ + u32 config; + + config = readl(acp_data->acp63_base + ACP_PIN_CONFIG); + dev_dbg(&pci->dev, "ACP config value: %d\n", config); + switch (config) { + case ACP_CONFIG_4: + case ACP_CONFIG_5: + case ACP_CONFIG_10: + case ACP_CONFIG_11: + acp_data->is_pdm_config = true; + break; + case ACP_CONFIG_2: + case ACP_CONFIG_3: + acp_data->is_sdw_config = true; + break; + case ACP_CONFIG_6: + case ACP_CONFIG_7: + case ACP_CONFIG_12: + case ACP_CONFIG_8: + case ACP_CONFIG_13: + case ACP_CONFIG_14: + acp_data->is_pdm_config = true; + acp_data->is_sdw_config = true; + break; + default: + break; + } +} + +static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata) +{ + u32 sdw0_en, sdw1_en; + + sdw0_en = readl(adata->acp63_base + ACP_SW0_EN); + sdw1_en = readl(adata->acp63_base + ACP_SW1_EN); + return (sdw0_en || sdw1_en); +} + +static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata) +{ + u32 val; + + val = readl(adata->acp63_base + ACP_SW0_WAKE_EN); + if (val && adata->sdw->pdev[0]) + pm_request_resume(&adata->sdw->pdev[0]->dev); + + val = readl(adata->acp63_base + ACP_SW1_WAKE_EN); + if (val && adata->sdw->pdev[1]) + pm_request_resume(&adata->sdw->pdev[1]->dev); +} + +static int __maybe_unused snd_acp63_suspend(struct device *dev) +{ + struct acp63_dev_data *adata; + int ret; + + adata = dev_get_drvdata(dev); + if (adata->is_sdw_dev) { + adata->sdw_en_stat = check_acp_sdw_enable_status(adata); + if (adata->sdw_en_stat) { + writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL); + return 0; + } + } + ret = acp_hw_deinit(adata, dev); + if (ret) + dev_err(dev, "ACP de-init failed\n"); + + return ret; +} + +static int __maybe_unused snd_acp63_runtime_resume(struct device *dev) +{ + struct acp63_dev_data *adata; + int ret; + + adata = dev_get_drvdata(dev); + if (adata->sdw_en_stat) { + writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL); + return 0; + } + ret = acp_hw_init(adata, dev); + if (ret) { + dev_err(dev, "ACP init failed\n"); + return ret; + } + + if (!adata->sdw_en_stat) + handle_acp63_sdw_pme_event(adata); + return 0; +} + +static int __maybe_unused snd_acp63_resume(struct device *dev) +{ + struct acp63_dev_data *adata; + int ret; + + adata = dev_get_drvdata(dev); + if (adata->sdw_en_stat) { + writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL); + return 0; + } + + ret = acp_hw_init(adata, dev); + if (ret) + dev_err(dev, "ACP init failed\n"); + + return ret; +} + +static void acp63_sdw_dma_irq_thread(struct acp63_dev_data *adata) +{ + struct sdw_dma_dev_data *sdw_data; + u32 stream_id; + + sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev); + + for (stream_id = 0; stream_id < ACP63_SDW0_DMA_MAX_STREAMS; stream_id++) { + if (adata->acp63_sdw0_dma_intr_stat[stream_id]) { + if (sdw_data->acp63_sdw0_dma_stream[stream_id]) + snd_pcm_period_elapsed(sdw_data->acp63_sdw0_dma_stream[stream_id]); + adata->acp63_sdw0_dma_intr_stat[stream_id] = 0; + } + } + for (stream_id = 0; stream_id < ACP63_SDW1_DMA_MAX_STREAMS; stream_id++) { + if (adata->acp63_sdw1_dma_intr_stat[stream_id]) { + if (sdw_data->acp63_sdw1_dma_stream[stream_id]) + snd_pcm_period_elapsed(sdw_data->acp63_sdw1_dma_stream[stream_id]); + adata->acp63_sdw1_dma_intr_stat[stream_id] = 0; + } + } +} + +void acp63_hw_init_ops(struct acp_hw_ops *hw_ops) +{ + hw_ops->acp_init = acp63_init; + hw_ops->acp_deinit = acp63_deinit; + hw_ops->acp_get_config = acp63_get_config; + hw_ops->acp_sdw_dma_irq_thread = acp63_sdw_dma_irq_thread; + hw_ops->acp_suspend = snd_acp63_suspend; + hw_ops->acp_resume = snd_acp63_resume; + hw_ops->acp_suspend_runtime = snd_acp63_suspend; + hw_ops->acp_resume_runtime = snd_acp63_runtime_resume; +} + +static int acp70_power_on(void __iomem *acp_base) +{ + u32 val = 0; + + val = readl(acp_base + ACP_PGFSM_STATUS); + + if (!val) + return 0; + if (val & ACP70_PGFSM_STATUS_MASK) + writel(ACP70_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL); + + return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP70_TIMEOUT); +} + +static int acp70_reset(void __iomem *acp_base) +{ + u32 val; + int ret; + + writel(1, acp_base + ACP_SOFT_RESET); + + ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, + val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK, + DELAY_US, ACP70_TIMEOUT); + if (ret) + return ret; + + writel(0, acp_base + ACP_SOFT_RESET); + + return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP70_TIMEOUT); +} + +static void acp70_enable_sdw_host_wake_interrupts(void __iomem *acp_base) +{ + u32 ext_intr_cntl1; + + ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1); + ext_intr_cntl1 |= ACP70_SDW_HOST_WAKE_MASK; + writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1); +} + +static void acp70_enable_interrupts(void __iomem *acp_base) +{ + u32 sdw0_wake_en, sdw1_wake_en; + + writel(1, acp_base + ACP_EXTERNAL_INTR_ENB); + writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL); + sdw0_wake_en = readl(acp_base + ACP_SW0_WAKE_EN); + sdw1_wake_en = readl(acp_base + ACP_SW1_WAKE_EN); + if (sdw0_wake_en || sdw1_wake_en) + acp70_enable_sdw_host_wake_interrupts(acp_base); +} + +static void acp70_disable_interrupts(void __iomem *acp_base) +{ + writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT); + writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL); + writel(0, acp_base + ACP_EXTERNAL_INTR_ENB); +} + +static int acp70_init(void __iomem *acp_base, struct device *dev) +{ + int ret; + + ret = acp70_power_on(acp_base); + if (ret) { + dev_err(dev, "ACP power on failed\n"); + return ret; + } + writel(0x01, acp_base + ACP_CONTROL); + ret = acp70_reset(acp_base); + if (ret) { + dev_err(dev, "ACP reset failed\n"); + return ret; + } + writel(0, acp_base + ACP_ZSC_DSP_CTRL); + acp70_enable_interrupts(acp_base); + writel(0x1, acp_base + ACP_PME_EN); + return 0; +} + +static int acp70_deinit(void __iomem *acp_base, struct device *dev) +{ + int ret; + + acp70_disable_interrupts(acp_base); + ret = acp70_reset(acp_base); + if (ret) { + dev_err(dev, "ACP reset failed\n"); + return ret; + } + writel(0x01, acp_base + ACP_ZSC_DSP_CTRL); + return 0; +} + +static void acp70_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data) +{ + u32 config; + + config = readl(acp_data->acp63_base + ACP_PIN_CONFIG); + dev_dbg(&pci->dev, "ACP config value: %d\n", config); + switch (config) { + case ACP_CONFIG_4: + case ACP_CONFIG_5: + case ACP_CONFIG_10: + case ACP_CONFIG_11: + case ACP_CONFIG_20: + acp_data->is_pdm_config = true; + break; + case ACP_CONFIG_2: + case ACP_CONFIG_3: + case ACP_CONFIG_16: + acp_data->is_sdw_config = true; + break; + case ACP_CONFIG_6: + case ACP_CONFIG_7: + case ACP_CONFIG_12: + case ACP_CONFIG_8: + case ACP_CONFIG_13: + case ACP_CONFIG_14: + case ACP_CONFIG_17: + case ACP_CONFIG_18: + case ACP_CONFIG_19: + acp_data->is_pdm_config = true; + acp_data->is_sdw_config = true; + break; + default: + break; + } +} + +static void acp70_sdw_dma_irq_thread(struct acp63_dev_data *adata) +{ + struct sdw_dma_dev_data *sdw_data; + u32 stream_id; + + sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev); + + for (stream_id = 0; stream_id < ACP70_SDW0_DMA_MAX_STREAMS; stream_id++) { + if (adata->acp70_sdw0_dma_intr_stat[stream_id]) { + if (sdw_data->acp70_sdw0_dma_stream[stream_id]) + snd_pcm_period_elapsed(sdw_data->acp70_sdw0_dma_stream[stream_id]); + adata->acp70_sdw0_dma_intr_stat[stream_id] = 0; + } + } + for (stream_id = 0; stream_id < ACP70_SDW1_DMA_MAX_STREAMS; stream_id++) { + if (adata->acp70_sdw1_dma_intr_stat[stream_id]) { + if (sdw_data->acp70_sdw1_dma_stream[stream_id]) + snd_pcm_period_elapsed(sdw_data->acp70_sdw1_dma_stream[stream_id]); + adata->acp70_sdw1_dma_intr_stat[stream_id] = 0; + } + } +} + +static int __maybe_unused snd_acp70_suspend(struct device *dev) +{ + struct acp63_dev_data *adata; + int ret; + + adata = dev_get_drvdata(dev); + if (adata->is_sdw_dev) { + adata->sdw_en_stat = check_acp_sdw_enable_status(adata); + if (adata->sdw_en_stat) { + writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL); + return 0; + } + } + ret = acp_hw_deinit(adata, dev); + if (ret) + dev_err(dev, "ACP de-init failed\n"); + + return ret; +} + +static int __maybe_unused snd_acp70_runtime_resume(struct device *dev) +{ + struct acp63_dev_data *adata; + int ret; + + adata = dev_get_drvdata(dev); + + if (adata->sdw_en_stat) { + writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL); + writel(1, adata->acp63_base + ACP_PME_EN); + return 0; + } + + ret = acp_hw_init(adata, dev); + if (ret) { + dev_err(dev, "ACP init failed\n"); + return ret; + } + return 0; +} + +static int __maybe_unused snd_acp70_resume(struct device *dev) +{ + struct acp63_dev_data *adata; + int ret; + + adata = dev_get_drvdata(dev); + + if (adata->sdw_en_stat) { + writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL); + writel(1, adata->acp63_base + ACP_PME_EN); + return 0; + } + + ret = acp_hw_init(adata, dev); + if (ret) + dev_err(dev, "ACP init failed\n"); + + return ret; +} + +void acp70_hw_init_ops(struct acp_hw_ops *hw_ops) +{ + hw_ops->acp_init = acp70_init; + hw_ops->acp_deinit = acp70_deinit; + hw_ops->acp_get_config = acp70_get_config; + hw_ops->acp_sdw_dma_irq_thread = acp70_sdw_dma_irq_thread; + hw_ops->acp_suspend = snd_acp70_suspend; + hw_ops->acp_resume = snd_acp70_resume; + hw_ops->acp_suspend_runtime = snd_acp70_suspend; + hw_ops->acp_resume_runtime = snd_acp70_runtime_resume; +} diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c index 318fc260f293..9cfbe05ad996 100644 --- a/sound/soc/amd/ps/ps-pdm-dma.c +++ b/sound/soc/amd/ps/ps-pdm-dma.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * AMD ALSA SoC Pink Sardine PDM Driver + * AMD ALSA SoC common PDM Driver for ACP6.3, ACP7.0 & ACP7.1 platforms. * - * Copyright 2022 Advanced Micro Devices, Inc. + * Copyright 2022, 2025 Advanced Micro Devices, Inc. */ #include <linux/platform_device.h> @@ -402,7 +402,7 @@ static void acp63_pdm_audio_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused acp63_pdm_resume(struct device *dev) +static int acp63_pdm_resume(struct device *dev) { struct pdm_dev_data *adata; struct snd_pcm_runtime *runtime; @@ -423,7 +423,7 @@ static int __maybe_unused acp63_pdm_resume(struct device *dev) return 0; } -static int __maybe_unused acp63_pdm_suspend(struct device *dev) +static int acp63_pdm_suspend(struct device *dev) { struct pdm_dev_data *adata; @@ -432,7 +432,7 @@ static int __maybe_unused acp63_pdm_suspend(struct device *dev) return 0; } -static int __maybe_unused acp63_pdm_runtime_resume(struct device *dev) +static int acp63_pdm_runtime_resume(struct device *dev) { struct pdm_dev_data *adata; @@ -442,8 +442,8 @@ static int __maybe_unused acp63_pdm_runtime_resume(struct device *dev) } static const struct dev_pm_ops acp63_pdm_pm_ops = { - SET_RUNTIME_PM_OPS(acp63_pdm_suspend, acp63_pdm_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(acp63_pdm_suspend, acp63_pdm_resume) + RUNTIME_PM_OPS(acp63_pdm_suspend, acp63_pdm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(acp63_pdm_suspend, acp63_pdm_resume) }; static struct platform_driver acp63_pdm_dma_driver = { @@ -451,13 +451,13 @@ static struct platform_driver acp63_pdm_dma_driver = { .remove = acp63_pdm_audio_remove, .driver = { .name = "acp_ps_pdm_dma", - .pm = &acp63_pdm_pm_ops, + .pm = pm_ptr(&acp63_pdm_pm_ops), }, }; module_platform_driver(acp63_pdm_dma_driver); MODULE_AUTHOR("Syed.SabaKareem@amd.com"); -MODULE_DESCRIPTION("AMD PINK SARDINE PDM Driver"); +MODULE_DESCRIPTION("AMD common PDM Driver for ACP6.3, ACP7,0 & ACP7.1 platforms"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c index b602cca92b8b..1b933a017c06 100644 --- a/sound/soc/amd/ps/ps-sdw-dma.c +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * AMD ALSA SoC Pink Sardine SoundWire DMA Driver + * AMD ALSA SoC common SoundWire DMA Driver for ACP6.3, ACP7.0 and ACP7.1 + * platforms. * - * Copyright 2023 Advanced Micro Devices, Inc. + * Copyright 2023, 2025 Advanced Micro Devices, Inc. */ #include <linux/err.h> @@ -18,7 +19,7 @@ #define DRV_NAME "amd_ps_sdw_dma" -static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { +static struct sdw_dma_ring_buf_reg acp63_sdw0_dma_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE, ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH}, @@ -44,7 +45,7 @@ static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STRE * For TX/RX streams DMA registers programming for SDW1 instance, it uses ACP_P1_AUDIO1 register * set as per hardware register documentation */ -static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { +static struct sdw_dma_ring_buf_reg acp63_sdw1_dma_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE, ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR, ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE, @@ -55,7 +56,7 @@ static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STRE ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, }; -static u32 sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { +static u32 acp63_sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { ACP_SW0_AUDIO0_TX_EN, ACP_SW0_AUDIO1_TX_EN, ACP_SW0_AUDIO2_TX_EN, @@ -70,11 +71,77 @@ static u32 sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { * it uses ACP_SW1_AUDIO1_TX_EN and ACP_SW1_AUDIO1_RX_EN registers * as per hardware register documentation. */ -static u32 sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { +static u32 acp63_sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { ACP_SW1_AUDIO1_TX_EN, ACP_SW1_AUDIO1_RX_EN, }; +static struct sdw_dma_ring_buf_reg acp70_sdw0_dma_reg[ACP70_SDW0_DMA_MAX_STREAMS] = { + {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE, + ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE, + ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE, + ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE, + ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE, + ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE, + ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE, + ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE, + ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE, + ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE, + ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE, + ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE, + ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH} +}; + +static struct sdw_dma_ring_buf_reg acp70_sdw1_dma_reg[ACP70_SDW1_DMA_MAX_STREAMS] = { + {ACP_P1_AUDIO0_TX_DMA_SIZE, ACP_P1_AUDIO0_TX_FIFOADDR, ACP_P1_AUDIO0_TX_FIFOSIZE, + ACP_P1_AUDIO0_TX_RINGBUFSIZE, ACP_P1_AUDIO0_TX_RINGBUFADDR, + ACP_P1_AUDIO0_TX_INTR_WATERMARK_SIZE, + ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE, + ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR, + ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE, + ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_P1_AUDIO2_TX_DMA_SIZE, ACP_P1_AUDIO2_TX_FIFOADDR, ACP_P1_AUDIO2_TX_FIFOSIZE, + ACP_P1_AUDIO2_TX_RINGBUFSIZE, ACP_P1_AUDIO2_TX_RINGBUFADDR, + ACP_P1_AUDIO2_TX_INTR_WATERMARK_SIZE, + ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_P1_AUDIO0_RX_DMA_SIZE, ACP_P1_AUDIO0_RX_FIFOADDR, ACP_P1_AUDIO0_RX_FIFOSIZE, + ACP_P1_AUDIO0_RX_RINGBUFSIZE, ACP_P1_AUDIO0_RX_RINGBUFADDR, + ACP_P1_AUDIO0_RX_INTR_WATERMARK_SIZE, + ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH}, + {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE, + ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR, + ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE, + ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, + {ACP_P1_AUDIO2_RX_DMA_SIZE, ACP_P1_AUDIO2_RX_FIFOADDR, ACP_P1_AUDIO2_RX_FIFOSIZE, + ACP_P1_AUDIO2_RX_RINGBUFSIZE, ACP_P1_AUDIO2_RX_RINGBUFADDR, + ACP_P1_AUDIO2_RX_INTR_WATERMARK_SIZE, + ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH} +}; + +static u32 acp70_sdw0_dma_enable_reg[ACP70_SDW0_DMA_MAX_STREAMS] = { + ACP70_SW0_AUDIO0_TX_EN, + ACP70_SW0_AUDIO1_TX_EN, + ACP70_SW0_AUDIO2_TX_EN, + ACP70_SW0_AUDIO0_RX_EN, + ACP70_SW0_AUDIO1_RX_EN, + ACP70_SW0_AUDIO2_RX_EN, +}; + +static u32 acp70_sdw1_dma_enable_reg[ACP70_SDW1_DMA_MAX_STREAMS] = { + ACP70_SW1_AUDIO0_TX_EN, + ACP70_SW1_AUDIO1_TX_EN, + ACP70_SW1_AUDIO2_TX_EN, + ACP70_SW1_AUDIO0_RX_EN, + ACP70_SW1_AUDIO1_RX_EN, + ACP70_SW1_AUDIO2_RX_EN, +}; + static const struct snd_pcm_hardware acp63_sdw_hardware_playback = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -114,11 +181,10 @@ static const struct snd_pcm_hardware acp63_sdw_hardware_capture = { .periods_max = SDW_CAPTURE_MAX_NUM_PERIODS, }; -static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, bool enable) +static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, u32 irq_mask, + u32 irq_mask1, bool enable) { u32 ext_intr_cntl, ext_intr_cntl1; - u32 irq_mask = ACP_SDW_DMA_IRQ_MASK; - u32 irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK; if (enable) { ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL); @@ -167,7 +233,7 @@ static void acp63_config_dma(struct acp_sdw_dma_stream *stream, void __iomem *ac } static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size, - u32 manager_instance) + u32 manager_instance, u32 acp_rev) { u32 reg_dma_size; u32 reg_fifo_addr; @@ -180,20 +246,47 @@ static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 sdw_ring_buf_size; u32 sdw_mem_window_offset; - switch (manager_instance) { - case ACP_SDW0: - reg_dma_size = sdw0_dma_ring_buf_reg[stream_id].reg_dma_size; - reg_fifo_addr = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_addr; - reg_fifo_size = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_size; - reg_ring_buf_size = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_size; - reg_ring_buf_addr = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_addr; + switch (acp_rev) { + case ACP63_PCI_REV: + switch (manager_instance) { + case ACP_SDW0: + reg_dma_size = acp63_sdw0_dma_reg[stream_id].reg_dma_size; + reg_fifo_addr = acp63_sdw0_dma_reg[stream_id].reg_fifo_addr; + reg_fifo_size = acp63_sdw0_dma_reg[stream_id].reg_fifo_size; + reg_ring_buf_size = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_size; + reg_ring_buf_addr = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_addr; + break; + case ACP_SDW1: + reg_dma_size = acp63_sdw1_dma_reg[stream_id].reg_dma_size; + reg_fifo_addr = acp63_sdw1_dma_reg[stream_id].reg_fifo_addr; + reg_fifo_size = acp63_sdw1_dma_reg[stream_id].reg_fifo_size; + reg_ring_buf_size = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_size; + reg_ring_buf_addr = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_addr; + break; + default: + return -EINVAL; + } break; - case ACP_SDW1: - reg_dma_size = sdw1_dma_ring_buf_reg[stream_id].reg_dma_size; - reg_fifo_addr = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_addr; - reg_fifo_size = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_size; - reg_ring_buf_size = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_size; - reg_ring_buf_addr = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_addr; + case ACP70_PCI_REV: + case ACP71_PCI_REV: + switch (manager_instance) { + case ACP_SDW0: + reg_dma_size = acp70_sdw0_dma_reg[stream_id].reg_dma_size; + reg_fifo_addr = acp70_sdw0_dma_reg[stream_id].reg_fifo_addr; + reg_fifo_size = acp70_sdw0_dma_reg[stream_id].reg_fifo_size; + reg_ring_buf_size = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_size; + reg_ring_buf_addr = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_addr; + break; + case ACP_SDW1: + reg_dma_size = acp70_sdw1_dma_reg[stream_id].reg_dma_size; + reg_fifo_addr = acp70_sdw1_dma_reg[stream_id].reg_fifo_addr; + reg_fifo_size = acp70_sdw1_dma_reg[stream_id].reg_fifo_size; + reg_ring_buf_size = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_size; + reg_ring_buf_addr = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_addr; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; @@ -265,21 +358,53 @@ static int acp63_sdw_dma_hw_params(struct snd_soc_component *component, if (!stream) return -EINVAL; stream_id = stream->stream_id; - switch (stream->instance) { - case ACP_SDW0: - sdw_data->sdw0_dma_stream[stream_id] = substream; - water_mark_size_reg = sdw0_dma_ring_buf_reg[stream_id].water_mark_size_reg; - acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - irq_mask = BIT(SDW0_DMA_TX_IRQ_MASK(stream_id)); - else - irq_mask = BIT(SDW0_DMA_RX_IRQ_MASK(stream_id)); + switch (sdw_data->acp_rev) { + case ACP63_PCI_REV: + switch (stream->instance) { + case ACP_SDW0: + sdw_data->acp63_sdw0_dma_stream[stream_id] = substream; + water_mark_size_reg = acp63_sdw0_dma_reg[stream_id].water_mark_size_reg; + acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + irq_mask = BIT(ACP63_SDW0_DMA_TX_IRQ_MASK(stream_id)); + else + irq_mask = BIT(ACP63_SDW0_DMA_RX_IRQ_MASK(stream_id)); + break; + case ACP_SDW1: + sdw_data->acp63_sdw1_dma_stream[stream_id] = substream; + acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1; + water_mark_size_reg = acp63_sdw1_dma_reg[stream_id].water_mark_size_reg; + irq_mask = BIT(ACP63_SDW1_DMA_IRQ_MASK(stream_id)); + break; + default: + return -EINVAL; + } break; - case ACP_SDW1: - sdw_data->sdw1_dma_stream[stream_id] = substream; - acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1; - water_mark_size_reg = sdw1_dma_ring_buf_reg[stream_id].water_mark_size_reg; - irq_mask = BIT(SDW1_DMA_IRQ_MASK(stream_id)); + case ACP70_PCI_REV: + case ACP71_PCI_REV: + switch (stream->instance) { + case ACP_SDW0: + sdw_data->acp70_sdw0_dma_stream[stream_id] = substream; + water_mark_size_reg = acp70_sdw0_dma_reg[stream_id].water_mark_size_reg; + acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + irq_mask = BIT(ACP70_SDW0_DMA_TX_IRQ_MASK(stream_id)); + else + irq_mask = BIT(ACP70_SDW0_DMA_RX_IRQ_MASK(stream_id)); + break; + case ACP_SDW1: + sdw_data->acp70_sdw1_dma_stream[stream_id] = substream; + acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1; + water_mark_size_reg = acp70_sdw1_dma_reg[stream_id].water_mark_size_reg; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + irq_mask = BIT(ACP70_SDW1_DMA_TX_IRQ_MASK(stream_id)); + else + irq_mask = BIT(ACP70_SDW1_DMA_RX_IRQ_MASK(stream_id)); + + break; + default: + return -EINVAL; + } break; default: return -EINVAL; @@ -290,7 +415,7 @@ static int acp63_sdw_dma_hw_params(struct snd_soc_component *component, stream->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); acp63_config_dma(stream, sdw_data->acp_base, stream_id); ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, stream_id, size, - stream->instance); + stream->instance, sdw_data->acp_rev); if (ret) { dev_err(component->dev, "Invalid DMA channel\n"); return -EINVAL; @@ -302,20 +427,42 @@ static int acp63_sdw_dma_hw_params(struct snd_soc_component *component, return 0; } -static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base) +static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base, + u32 acp_rev) { union acp_sdw_dma_count byte_count; u32 pos_low_reg, pos_high_reg; byte_count.bytescount = 0; - switch (stream->instance) { - case ACP_SDW0: - pos_low_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_low_reg; - pos_high_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_high_reg; + switch (acp_rev) { + case ACP63_PCI_REV: + switch (stream->instance) { + case ACP_SDW0: + pos_low_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_low_reg; + pos_high_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_high_reg; + break; + case ACP_SDW1: + pos_low_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_low_reg; + pos_high_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_high_reg; + break; + default: + goto POINTER_RETURN_BYTES; + } break; - case ACP_SDW1: - pos_low_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_low_reg; - pos_high_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_high_reg; + case ACP70_PCI_REV: + case ACP71_PCI_REV: + switch (stream->instance) { + case ACP_SDW0: + pos_low_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_low_reg; + pos_high_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_high_reg; + break; + case ACP_SDW1: + pos_low_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_low_reg; + pos_high_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_high_reg; + break; + default: + goto POINTER_RETURN_BYTES; + } break; default: goto POINTER_RETURN_BYTES; @@ -340,7 +487,7 @@ static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp, stream = substream->runtime->private_data; buffersize = frames_to_bytes(substream->runtime, substream->runtime->buffer_size); - bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base); + bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base, sdw_data->acp_rev); if (bytescount > stream->bytescount) bytescount -= stream->bytescount; pos = do_div(bytescount, buffersize); @@ -367,12 +514,31 @@ static int acp63_sdw_dma_close(struct snd_soc_component *component, stream = substream->runtime->private_data; if (!stream) return -EINVAL; - switch (stream->instance) { - case ACP_SDW0: - sdw_data->sdw0_dma_stream[stream->stream_id] = NULL; + switch (sdw_data->acp_rev) { + case ACP63_PCI_REV: + switch (stream->instance) { + case ACP_SDW0: + sdw_data->acp63_sdw0_dma_stream[stream->stream_id] = NULL; + break; + case ACP_SDW1: + sdw_data->acp63_sdw1_dma_stream[stream->stream_id] = NULL; + break; + default: + return -EINVAL; + } break; - case ACP_SDW1: - sdw_data->sdw1_dma_stream[stream->stream_id] = NULL; + case ACP70_PCI_REV: + case ACP71_PCI_REV: + switch (stream->instance) { + case ACP_SDW0: + sdw_data->acp70_sdw0_dma_stream[stream->stream_id] = NULL; + break; + case ACP_SDW1: + sdw_data->acp70_sdw1_dma_stream[stream->stream_id] = NULL; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; @@ -382,7 +548,7 @@ static int acp63_sdw_dma_close(struct snd_soc_component *component, } static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream, - void __iomem *acp_base, bool sdw_dma_enable) + void __iomem *acp_base, u32 acp_rev, bool sdw_dma_enable) { struct acp_sdw_dma_stream *stream; u32 stream_id; @@ -393,12 +559,31 @@ static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream, stream = substream->runtime->private_data; stream_id = stream->stream_id; - switch (stream->instance) { - case ACP_SDW0: - sdw_dma_en_reg = sdw0_dma_enable_reg[stream_id]; + switch (acp_rev) { + case ACP63_PCI_REV: + switch (stream->instance) { + case ACP_SDW0: + sdw_dma_en_reg = acp63_sdw0_dma_enable_reg[stream_id]; + break; + case ACP_SDW1: + sdw_dma_en_reg = acp63_sdw1_dma_enable_reg[stream_id]; + break; + default: + return -EINVAL; + } break; - case ACP_SDW1: - sdw_dma_en_reg = sdw1_dma_enable_reg[stream_id]; + case ACP70_PCI_REV: + case ACP71_PCI_REV: + switch (stream->instance) { + case ACP_SDW0: + sdw_dma_en_reg = acp70_sdw0_dma_enable_reg[stream_id]; + break; + case ACP_SDW1: + sdw_dma_en_reg = acp70_sdw1_dma_enable_reg[stream_id]; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; @@ -422,12 +607,12 @@ static int acp63_sdw_dma_trigger(struct snd_soc_component *comp, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: - ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, true); + ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, true); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, false); + ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, false); break; default: ret = -EINVAL; @@ -474,6 +659,7 @@ static int acp63_sdw_platform_probe(struct platform_device *pdev) return -ENOMEM; sdw_data->acp_lock = &acp_data->acp_lock; + sdw_data->acp_rev = acp_data->acp_rev; dev_set_drvdata(&pdev->dev, sdw_data); status = devm_snd_soc_register_component(&pdev->dev, &acp63_sdw_component, @@ -495,15 +681,17 @@ static void acp63_sdw_platform_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) +static int acp63_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) { struct acp_sdw_dma_stream *stream; struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; u32 period_bytes, buf_size, water_mark_size_reg; - u32 stream_count; + u32 stream_count, irq_mask, irq_mask1; int index, instance, ret; + irq_mask = ACP63_SDW_DMA_IRQ_MASK; + irq_mask1 = ACP63_P1_SDW_DMA_IRQ_MASK; for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) { if (instance == ACP_SDW0) stream_count = ACP63_SDW0_DMA_MAX_STREAMS; @@ -512,13 +700,11 @@ static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) for (index = 0; index < stream_count; index++) { if (instance == ACP_SDW0) { - substream = sdw_data->sdw0_dma_stream[index]; - water_mark_size_reg = - sdw0_dma_ring_buf_reg[index].water_mark_size_reg; + substream = sdw_data->acp63_sdw0_dma_stream[index]; + water_mark_size_reg = acp63_sdw0_dma_reg[index].water_mark_size_reg; } else { - substream = sdw_data->sdw1_dma_stream[index]; - water_mark_size_reg = - sdw1_dma_ring_buf_reg[index].water_mark_size_reg; + substream = sdw_data->acp63_sdw1_dma_stream[index]; + water_mark_size_reg = acp63_sdw1_dma_reg[index].water_mark_size_reg; } if (substream && substream->runtime) { @@ -528,27 +714,72 @@ static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) buf_size = frames_to_bytes(runtime, runtime->buffer_size); acp63_config_dma(stream, sdw_data->acp_base, index); ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index, - buf_size, instance); + buf_size, instance, + ACP63_PCI_REV); if (ret) return ret; writel(period_bytes, sdw_data->acp_base + water_mark_size_reg); } } } - acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, true); + acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true); return 0; } -static int __maybe_unused acp63_sdw_pcm_resume(struct device *dev) +static int acp70_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) +{ + struct acp_sdw_dma_stream *stream; + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + u32 period_bytes, buf_size, water_mark_size_reg; + u32 stream_count, irq_mask, irq_mask1; + int index, instance, ret; + + irq_mask = ACP70_SDW_DMA_IRQ_MASK; + irq_mask1 = ACP70_P1_SDW_DMA_IRQ_MASK; + stream_count = ACP70_SDW0_DMA_MAX_STREAMS; + for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) { + for (index = 0; index < stream_count; index++) { + if (instance == ACP_SDW0) { + substream = sdw_data->acp70_sdw0_dma_stream[index]; + water_mark_size_reg = acp70_sdw0_dma_reg[index].water_mark_size_reg; + } else { + substream = sdw_data->acp70_sdw1_dma_stream[index]; + water_mark_size_reg = acp70_sdw1_dma_reg[index].water_mark_size_reg; + } + + if (substream && substream->runtime) { + runtime = substream->runtime; + stream = runtime->private_data; + period_bytes = frames_to_bytes(runtime, runtime->period_size); + buf_size = frames_to_bytes(runtime, runtime->buffer_size); + acp63_config_dma(stream, sdw_data->acp_base, index); + ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index, + buf_size, instance, + sdw_data->acp_rev); + if (ret) + return ret; + writel(period_bytes, sdw_data->acp_base + water_mark_size_reg); + } + } + } + acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true); + return 0; +} + +static int acp63_sdw_pcm_resume(struct device *dev) { struct sdw_dma_dev_data *sdw_data; sdw_data = dev_get_drvdata(dev); - return acp_restore_sdw_dma_config(sdw_data); + if (sdw_data->acp_rev == ACP63_PCI_REV) + return acp63_restore_sdw_dma_config(sdw_data); + else + return acp70_restore_sdw_dma_config(sdw_data); } static const struct dev_pm_ops acp63_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(NULL, acp63_sdw_pcm_resume) + SYSTEM_SLEEP_PM_OPS(NULL, acp63_sdw_pcm_resume) }; static struct platform_driver acp63_sdw_dma_driver = { @@ -556,13 +787,13 @@ static struct platform_driver acp63_sdw_dma_driver = { .remove = acp63_sdw_platform_remove, .driver = { .name = "amd_ps_sdw_dma", - .pm = &acp63_pm_ops, + .pm = pm_ptr(&acp63_pm_ops), }, }; module_platform_driver(acp63_sdw_dma_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); -MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver"); +MODULE_DESCRIPTION("AMD common SDW DMA Driver for ACP6.3, ACP7.0 & ACP7.1 platforms"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/rpl/rpl-pci-acp6x.c b/sound/soc/amd/rpl/rpl-pci-acp6x.c index a8e548ed991b..e3afe9172bdf 100644 --- a/sound/soc/amd/rpl/rpl-pci-acp6x.c +++ b/sound/soc/amd/rpl/rpl-pci-acp6x.c @@ -159,7 +159,7 @@ disable_pci: return ret; } -static int __maybe_unused snd_rpl_suspend(struct device *dev) +static int snd_rpl_suspend(struct device *dev) { struct rpl_dev_data *adata; int ret; @@ -171,7 +171,7 @@ static int __maybe_unused snd_rpl_suspend(struct device *dev) return ret; } -static int __maybe_unused snd_rpl_resume(struct device *dev) +static int snd_rpl_resume(struct device *dev) { struct rpl_dev_data *adata; int ret; @@ -184,8 +184,8 @@ static int __maybe_unused snd_rpl_resume(struct device *dev) } static const struct dev_pm_ops rpl_pm = { - SET_RUNTIME_PM_OPS(snd_rpl_suspend, snd_rpl_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(snd_rpl_suspend, snd_rpl_resume) + RUNTIME_PM_OPS(snd_rpl_suspend, snd_rpl_resume, NULL) + SYSTEM_SLEEP_PM_OPS(snd_rpl_suspend, snd_rpl_resume) }; static void snd_rpl_remove(struct pci_dev *pci) @@ -217,7 +217,7 @@ static struct pci_driver rpl_acp6x_driver = { .probe = snd_rpl_probe, .remove = snd_rpl_remove, .driver = { - .pm = &rpl_pm, + .pm = pm_ptr(&rpl_pm), } }; diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c index d5965f2b09bc..aa4726899434 100644 --- a/sound/soc/amd/vangogh/acp5x-pcm-dma.c +++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c @@ -420,7 +420,7 @@ static void acp5x_audio_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused acp5x_pcm_resume(struct device *dev) +static int acp5x_pcm_resume(struct device *dev) { struct i2s_dev_data *adata; struct i2s_stream_instance *rtd; @@ -473,7 +473,7 @@ static int __maybe_unused acp5x_pcm_resume(struct device *dev) return 0; } -static int __maybe_unused acp5x_pcm_suspend(struct device *dev) +static int acp5x_pcm_suspend(struct device *dev) { struct i2s_dev_data *adata; @@ -482,7 +482,7 @@ static int __maybe_unused acp5x_pcm_suspend(struct device *dev) return 0; } -static int __maybe_unused acp5x_pcm_runtime_resume(struct device *dev) +static int acp5x_pcm_runtime_resume(struct device *dev) { struct i2s_dev_data *adata; @@ -492,9 +492,8 @@ static int __maybe_unused acp5x_pcm_runtime_resume(struct device *dev) } static const struct dev_pm_ops acp5x_pm_ops = { - SET_RUNTIME_PM_OPS(acp5x_pcm_suspend, - acp5x_pcm_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume) + RUNTIME_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume) }; static struct platform_driver acp5x_dma_driver = { diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index ecf57a6cb7c3..e632f16c9102 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -252,6 +252,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { .driver_data = &acp6x_card, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21M6"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_NAME, "21ME"), } }, @@ -307,6 +314,41 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { { .driver_data = &acp6x_card, .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83L3"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83N6"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83Q2"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83Q3"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83J2"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "UM5302TA"), } @@ -549,6 +591,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "pang13"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7UCX"), + } + }, {} }; diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c index 3eb3e82efb10..ac758b90f441 100644 --- a/sound/soc/amd/yc/acp6x-pdm-dma.c +++ b/sound/soc/amd/yc/acp6x-pdm-dma.c @@ -394,7 +394,7 @@ static void acp6x_pdm_audio_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused acp6x_pdm_resume(struct device *dev) +static int acp6x_pdm_resume(struct device *dev) { struct pdm_dev_data *adata; struct snd_pcm_runtime *runtime; @@ -415,7 +415,7 @@ static int __maybe_unused acp6x_pdm_resume(struct device *dev) return 0; } -static int __maybe_unused acp6x_pdm_suspend(struct device *dev) +static int acp6x_pdm_suspend(struct device *dev) { struct pdm_dev_data *adata; @@ -424,7 +424,7 @@ static int __maybe_unused acp6x_pdm_suspend(struct device *dev) return 0; } -static int __maybe_unused acp6x_pdm_runtime_resume(struct device *dev) +static int acp6x_pdm_runtime_resume(struct device *dev) { struct pdm_dev_data *adata; @@ -434,8 +434,8 @@ static int __maybe_unused acp6x_pdm_runtime_resume(struct device *dev) } static const struct dev_pm_ops acp6x_pdm_pm_ops = { - SET_RUNTIME_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_resume) + RUNTIME_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_resume) }; static struct platform_driver acp6x_pdm_dma_driver = { @@ -443,7 +443,7 @@ static struct platform_driver acp6x_pdm_dma_driver = { .remove = acp6x_pdm_audio_remove, .driver = { .name = "acp_yc_pdm_dma", - .pm = &acp6x_pdm_pm_ops, + .pm = pm_ptr(&acp6x_pdm_pm_ops), }, }; diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c index 7af6a349b1d4..1140ed1cbb3d 100644 --- a/sound/soc/amd/yc/pci-acp6x.c +++ b/sound/soc/amd/yc/pci-acp6x.c @@ -277,7 +277,7 @@ disable_pci: return ret; } -static int __maybe_unused snd_acp6x_suspend(struct device *dev) +static int snd_acp6x_suspend(struct device *dev) { struct acp6x_dev_data *adata; int ret; @@ -289,7 +289,7 @@ static int __maybe_unused snd_acp6x_suspend(struct device *dev) return ret; } -static int __maybe_unused snd_acp6x_resume(struct device *dev) +static int snd_acp6x_resume(struct device *dev) { struct acp6x_dev_data *adata; int ret; @@ -302,8 +302,8 @@ static int __maybe_unused snd_acp6x_resume(struct device *dev) } static const struct dev_pm_ops acp6x_pm = { - SET_RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume) + RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL) + SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume) }; static void snd_acp6x_remove(struct pci_dev *pci) @@ -339,7 +339,7 @@ static struct pci_driver yc_acp6x_driver = { .probe = snd_acp6x_probe, .remove = snd_acp6x_remove, .driver = { - .pm = &acp6x_pm, + .pm = pm_ptr(&acp6x_pm), } }; diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index ba314b279919..1f8c60d2de82 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <linux/string_choices.h> #include <sound/core.h> #include <sound/dmaengine_pcm.h> #include <sound/pcm_params.h> @@ -275,7 +276,7 @@ static int atmel_classd_component_probe(struct snd_soc_component *component) dev_info(component->dev, "PWM modulation type is %s, non-overlapping is %s\n", pwm_type[pdata->pwm_type], - pdata->non_overlap_enable?"enabled":"disabled"); + str_enabled_disabled(pdata->non_overlap_enable)); return 0; } diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c index 0a9efd5f2861..2a1d0408a4cf 100644 --- a/sound/soc/atmel/tse850-pcm5142.c +++ b/sound/soc/atmel/tse850-pcm5142.c @@ -227,16 +227,9 @@ static const struct snd_kcontrol_new mux1 = static const struct snd_kcontrol_new mux2 = SOC_DAPM_ENUM_EXT("MUX2", mux_enum, tse850_get_mux2, tse850_put_mux2); -#define TSE850_DAPM_SINGLE_EXT(xname, reg, shift, max, invert, xget, xput) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_volsw, \ - .get = xget, \ - .put = xput, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } - static const struct snd_kcontrol_new mix[] = { - TSE850_DAPM_SINGLE_EXT("IN Switch", SND_SOC_NOPM, 0, 1, 0, - tse850_get_mix, tse850_put_mix), + SOC_SINGLE_EXT("IN Switch", SND_SOC_NOPM, 0, 1, 0, + tse850_get_mix, tse850_put_mix), }; static const char * const ana_text[] = { diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c index 7d296f29dade..57735004f416 100644 --- a/sound/soc/au1x/i2sc.c +++ b/sound/soc/au1x/i2sc.c @@ -279,7 +279,6 @@ static void au1xi2s_drvremove(struct platform_device *pdev) WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */ } -#ifdef CONFIG_PM static int au1xi2s_drvsuspend(struct device *dev) { struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev); @@ -294,23 +293,13 @@ static int au1xi2s_drvresume(struct device *dev) return 0; } -static const struct dev_pm_ops au1xi2sc_pmops = { - .suspend = au1xi2s_drvsuspend, - .resume = au1xi2s_drvresume, -}; - -#define AU1XI2SC_PMOPS (&au1xi2sc_pmops) - -#else - -#define AU1XI2SC_PMOPS NULL - -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(au1xi2sc_pmops, au1xi2s_drvsuspend, + au1xi2s_drvresume); static struct platform_driver au1xi2s_driver = { .driver = { .name = "alchemy-i2sc", - .pm = AU1XI2SC_PMOPS, + .pm = pm_ptr(&au1xi2sc_pmops), }, .probe = au1xi2s_drvprobe, .remove = au1xi2s_drvremove, diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 8a59a50978b9..94698e08a513 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -436,7 +436,6 @@ static void au1xpsc_ac97_drvremove(struct platform_device *pdev) au1xpsc_ac97_workdata = NULL; /* MDEV */ } -#ifdef CONFIG_PM static int au1xpsc_ac97_drvsuspend(struct device *dev) { struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); @@ -467,23 +466,13 @@ static int au1xpsc_ac97_drvresume(struct device *dev) return 0; } -static const struct dev_pm_ops au1xpscac97_pmops = { - .suspend = au1xpsc_ac97_drvsuspend, - .resume = au1xpsc_ac97_drvresume, -}; - -#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops - -#else - -#define AU1XPSCAC97_PMOPS NULL - -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(au1xpscac97_pmops, au1xpsc_ac97_drvsuspend, + au1xpsc_ac97_drvresume); static struct platform_driver au1xpsc_ac97_driver = { .driver = { .name = "au1xpsc_ac97", - .pm = AU1XPSCAC97_PMOPS, + .pm = pm_ptr(&au1xpscac97_pmops), }, .probe = au1xpsc_ac97_drvprobe, .remove = au1xpsc_ac97_drvremove, diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index bee013555e7a..bf59105fcb7a 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c @@ -354,7 +354,6 @@ static void au1xpsc_i2s_drvremove(struct platform_device *pdev) wmb(); /* drain writebuffer */ } -#ifdef CONFIG_PM static int au1xpsc_i2s_drvsuspend(struct device *dev) { struct au1xpsc_audio_data *wd = dev_get_drvdata(dev); @@ -385,23 +384,13 @@ static int au1xpsc_i2s_drvresume(struct device *dev) return 0; } -static const struct dev_pm_ops au1xpsci2s_pmops = { - .suspend = au1xpsc_i2s_drvsuspend, - .resume = au1xpsc_i2s_drvresume, -}; - -#define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops - -#else - -#define AU1XPSCI2S_PMOPS NULL - -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(au1xpsci2s_pmops, au1xpsc_i2s_drvsuspend, + au1xpsc_i2s_drvresume); static struct platform_driver au1xpsc_i2s_driver = { .driver = { .name = "au1xpsc_i2s", - .pm = AU1XPSCI2S_PMOPS, + .pm = pm_ptr(&au1xpsci2s_pmops), }, .probe = au1xpsc_i2s_drvprobe, .remove = au1xpsc_i2s_drvremove, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0b9e87dc2b6c..40bb7a1d44bc 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -58,6 +58,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AW87390 imply SND_SOC_AW88395 imply SND_SOC_AW88081 + imply SND_SOC_AW88166 imply SND_SOC_AW88261 imply SND_SOC_AW88399 imply SND_SOC_BT_SCO @@ -678,6 +679,18 @@ config SND_SOC_AW88395 digital Smart K audio amplifier with an integrated 10V smart boost convert. +config SND_SOC_AW88166 + tristate "Soc Audio for awinic aw88166" + depends on I2C + select REGMAP_I2C + select GPIOLIB + select SND_SOC_AW88395_LIB + help + This option enables support for aw88166 Smart PA. + The awinic AW88166 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier with sound quality + enhancement algorithms and speaker protection. + config SND_SOC_AW88261 tristate "Soc Audio for awinic aw88261" depends on I2C @@ -692,7 +705,7 @@ config SND_SOC_AW88261 the input amplitude. config SND_SOC_AW88081 - tristate "Soc Audio for awinic aw88081" + tristate "Soc Audio for awinic aw88081/aw88083" depends on I2C select REGMAP_I2C select SND_SOC_AW88395_LIB diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f37e82ddb7a1..10f726066b6c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -53,6 +53,7 @@ snd-soc-aw88081-y := aw88081.o snd-soc-aw88395-lib-y := aw88395/aw88395_lib.o snd-soc-aw88395-y := aw88395/aw88395.o \ aw88395/aw88395_device.o +snd-soc-aw88166-y := aw88166.o snd-soc-aw88261-y := aw88261.o snd-soc-aw88399-y := aw88399.o snd-soc-bd28623-y := bd28623.o @@ -80,7 +81,7 @@ snd-soc-cs35l56-shared-y := cs35l56-shared.o snd-soc-cs35l56-i2c-y := cs35l56-i2c.o snd-soc-cs35l56-spi-y := cs35l56-spi.o snd-soc-cs35l56-sdw-y := cs35l56-sdw.o -snd-soc-cs40l50-objs := cs40l50-codec.o +snd-soc-cs40l50-y := cs40l50-codec.o snd-soc-cs42l42-y := cs42l42.o snd-soc-cs42l42-i2c-y := cs42l42-i2c.o snd-soc-cs42l42-sdw-y := cs42l42-sdw.o @@ -92,7 +93,7 @@ snd-soc-cs42l52-y := cs42l52.o snd-soc-cs42l56-y := cs42l56.o snd-soc-cs42l73-y := cs42l73.o snd-soc-cs42l83-i2c-y := cs42l83-i2c.o -snd-soc-cs42l84-objs := cs42l84.o +snd-soc-cs42l84-y := cs42l84.o snd-soc-cs4234-y := cs4234.o snd-soc-cs4265-y := cs4265.o snd-soc-cs4270-y := cs4270.o @@ -334,8 +335,8 @@ snd-soc-wcd-classh-y := wcd-clsh-v2.o snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o snd-soc-wcd9335-y := wcd9335.o snd-soc-wcd934x-y := wcd934x.o -snd-soc-wcd937x-objs := wcd937x.o -snd-soc-wcd937x-sdw-objs := wcd937x-sdw.o +snd-soc-wcd937x-y := wcd937x.o +snd-soc-wcd937x-sdw-y := wcd937x-sdw.o snd-soc-wcd938x-y := wcd938x.o snd-soc-wcd938x-sdw-y := wcd938x-sdw.o snd-soc-wcd939x-y := wcd939x.o @@ -470,6 +471,7 @@ obj-$(CONFIG_SND_SOC_AW87390) += snd-soc-aw87390.o obj-$(CONFIG_SND_SOC_AW88081) += snd-soc-aw88081.o obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o +obj-$(CONFIG_SND_SOC_AW88166) +=snd-soc-aw88166.o obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o obj-$(CONFIG_SND_SOC_AW88399) += snd-soc-aw88399.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c index 15d74bb31c4c..6aa168e01fbb 100644 --- a/sound/soc/codecs/ad193x-i2c.c +++ b/sound/soc/codecs/ad193x-i2c.c @@ -23,7 +23,6 @@ MODULE_DEVICE_TABLE(i2c, ad193x_id); static int ad193x_i2c_probe(struct i2c_client *client) { struct regmap_config config; - const struct i2c_device_id *id = i2c_match_id(ad193x_id, client); config = ad193x_regmap_config; config.val_bits = 8; @@ -31,7 +30,7 @@ static int ad193x_i2c_probe(struct i2c_client *client) return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config), - (enum ad193x_type)id->driver_data); + (uintptr_t)i2c_get_match_data(client)); } static struct i2c_driver ad193x_i2c_driver = { diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 291249e0a2a3..6876462d8bdb 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -325,9 +325,7 @@ static int adau1701_reset(struct snd_soc_component *component, unsigned int clkd __assign_bit(1, values, 1); break; } - gpiod_set_array_value_cansleep(adau1701->gpio_pll_mode->ndescs, - adau1701->gpio_pll_mode->desc, adau1701->gpio_pll_mode->info, - values); + gpiod_multi_set_value_cansleep(adau1701->gpio_pll_mode, values); } adau1701->pll_clkdiv = clkdiv; diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index a554255186ae..eba7e4f42c78 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -14,12 +14,9 @@ #include "adau1761.h" -static const struct i2c_device_id adau1761_i2c_ids[]; - static int adau1761_i2c_probe(struct i2c_client *client) { struct regmap_config config; - const struct i2c_device_id *id = i2c_match_id(adau1761_i2c_ids, client); config = adau1761_regmap_config; config.val_bits = 8; @@ -27,7 +24,7 @@ static int adau1761_i2c_probe(struct i2c_client *client) return adau1761_probe(&client->dev, devm_regmap_init_i2c(client, &config), - id->driver_data, NULL); + (uintptr_t)i2c_get_match_data(client), NULL); } static void adau1761_i2c_remove(struct i2c_client *client) diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c index 3a170fd78ff3..cb67fde8d9a8 100644 --- a/sound/soc/codecs/adau1781-i2c.c +++ b/sound/soc/codecs/adau1781-i2c.c @@ -14,12 +14,9 @@ #include "adau1781.h" -static const struct i2c_device_id adau1781_i2c_ids[]; - static int adau1781_i2c_probe(struct i2c_client *client) { struct regmap_config config; - const struct i2c_device_id *id = i2c_match_id(adau1781_i2c_ids, client); config = adau1781_regmap_config; config.val_bits = 8; @@ -27,7 +24,7 @@ static int adau1781_i2c_probe(struct i2c_client *client) return adau1781_probe(&client->dev, devm_regmap_init_i2c(client, &config), - id->driver_data, NULL); + (uintptr_t)i2c_get_match_data(client), NULL); } static void adau1781_i2c_remove(struct i2c_client *client) diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 4dcc984761e0..0b6b0d2115eb 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -255,7 +255,7 @@ static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol, #define DECLARE_ADAU17X1_DSP_MUX_CTRL(_name, _label, _stream, _text) \ const struct snd_kcontrol_new _name = \ - SOC_DAPM_ENUM_EXT(_label, (const struct soc_enum)\ + SOC_ENUM_EXT(_label, (const struct soc_enum)\ SOC_ENUM_SINGLE(SND_SOC_NOPM, _stream, \ ARRAY_SIZE(_text), _text), \ adau17x1_dsp_mux_enum_get, adau17x1_dsp_mux_enum_put) diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index 24c7b9c84c19..441c8079246a 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -14,12 +14,9 @@ #include "adau1977.h" -static const struct i2c_device_id adau1977_i2c_ids[]; - static int adau1977_i2c_probe(struct i2c_client *client) { struct regmap_config config; - const struct i2c_device_id *id = i2c_match_id(adau1977_i2c_ids, client); config = adau1977_regmap_config; config.val_bits = 8; @@ -27,7 +24,7 @@ static int adau1977_i2c_probe(struct i2c_client *client) return adau1977_probe(&client->dev, devm_regmap_init_i2c(client, &config), - id->driver_data, NULL); + (uintptr_t)i2c_get_match_data(client), NULL); } static const struct i2c_device_id adau1977_i2c_ids[] = { diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c index 3ee5a5c3c5fe..452559d8c97b 100644 --- a/sound/soc/codecs/ak4375.c +++ b/sound/soc/codecs/ak4375.c @@ -438,7 +438,7 @@ static int ak4375_power_on(struct ak4375_priv *ak4375) return 0; } -static int __maybe_unused ak4375_runtime_suspend(struct device *dev) +static int ak4375_runtime_suspend(struct device *dev) { struct ak4375_priv *ak4375 = dev_get_drvdata(dev); @@ -448,7 +448,7 @@ static int __maybe_unused ak4375_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused ak4375_runtime_resume(struct device *dev) +static int ak4375_runtime_resume(struct device *dev) { struct ak4375_priv *ak4375 = dev_get_drvdata(dev); int ret; @@ -490,9 +490,8 @@ static const struct ak4375_drvdata ak4375_drvdata = { }; static const struct dev_pm_ops ak4375_pm = { - SET_RUNTIME_PM_OPS(ak4375_runtime_suspend, ak4375_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(ak4375_runtime_suspend, ak4375_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static int ak4375_i2c_probe(struct i2c_client *i2c) @@ -594,7 +593,7 @@ MODULE_DEVICE_TABLE(of, ak4375_of_match); static struct i2c_driver ak4375_i2c_driver = { .driver = { .name = "ak4375", - .pm = &ak4375_pm, + .pm = pm_ptr(&ak4375_pm), .of_match_table = ak4375_of_match, }, .probe = ak4375_i2c_probe, diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index d472d9952628..5f3a68dfe7bd 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -639,8 +639,7 @@ static void ak4458_reset(struct ak4458_priv *ak4458, bool active) } } -#ifdef CONFIG_PM -static int __maybe_unused ak4458_runtime_suspend(struct device *dev) +static int ak4458_runtime_suspend(struct device *dev) { struct ak4458_priv *ak4458 = dev_get_drvdata(dev); @@ -656,7 +655,7 @@ static int __maybe_unused ak4458_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused ak4458_runtime_resume(struct device *dev) +static int ak4458_runtime_resume(struct device *dev) { struct ak4458_priv *ak4458 = dev_get_drvdata(dev); int ret; @@ -678,7 +677,6 @@ static int __maybe_unused ak4458_runtime_resume(struct device *dev) return regcache_sync(ak4458->regmap); } -#endif /* CONFIG_PM */ static const struct snd_soc_component_driver soc_codec_dev_ak4458 = { .controls = ak4458_snd_controls, @@ -727,9 +725,8 @@ static const struct ak4458_drvdata ak4497_drvdata = { }; static const struct dev_pm_ops ak4458_pm = { - SET_RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static int ak4458_i2c_probe(struct i2c_client *i2c) @@ -805,7 +802,7 @@ MODULE_DEVICE_TABLE(of, ak4458_of_match); static struct i2c_driver ak4458_i2c_driver = { .driver = { .name = "ak4458", - .pm = &ak4458_pm, + .pm = pm_ptr(&ak4458_pm), .of_match_table = ak4458_of_match, }, .probe = ak4458_i2c_probe, diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 6c767609f95d..683f3e472f50 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -342,7 +342,7 @@ static void ak5558_remove(struct snd_soc_component *component) ak5558_reset(ak5558, true); } -static int __maybe_unused ak5558_runtime_suspend(struct device *dev) +static int ak5558_runtime_suspend(struct device *dev) { struct ak5558_priv *ak5558 = dev_get_drvdata(dev); @@ -354,7 +354,7 @@ static int __maybe_unused ak5558_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused ak5558_runtime_resume(struct device *dev) +static int ak5558_runtime_resume(struct device *dev) { struct ak5558_priv *ak5558 = dev_get_drvdata(dev); int ret; @@ -376,9 +376,8 @@ static int __maybe_unused ak5558_runtime_resume(struct device *dev) } static const struct dev_pm_ops ak5558_pm = { - SET_RUNTIME_PM_OPS(ak5558_runtime_suspend, ak5558_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(ak5558_runtime_suspend, ak5558_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static const struct snd_soc_component_driver soc_codec_dev_ak5558 = { @@ -495,7 +494,7 @@ static struct i2c_driver ak5558_i2c_driver = { .driver = { .name = "ak5558", .of_match_table = of_match_ptr(ak5558_i2c_dt_ids), - .pm = &ak5558_pm, + .pm = pm_ptr(&ak5558_pm), }, .probe = ak5558_i2c_probe, .remove = ak5558_i2c_remove, diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index b24c32206884..fbf723758079 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -987,9 +987,9 @@ static int alc5623_i2c_probe(struct i2c_client *client) struct alc5623_priv *alc5623; struct device_node *np; unsigned int vid1, vid2; + unsigned int matched_id; int ret; u32 val32; - const struct i2c_device_id *id; alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv), GFP_KERNEL); @@ -1016,12 +1016,12 @@ static int alc5623_i2c_probe(struct i2c_client *client) } vid2 >>= 8; - id = i2c_match_id(alc5623_i2c_table, client); + matched_id = (uintptr_t)i2c_get_match_data(client); - if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) { + if ((vid1 != 0x10ec) || (vid2 != matched_id)) { dev_err(&client->dev, "unknown or wrong codec\n"); - dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n", - 0x10ec, id->driver_data, + dev_err(&client->dev, "Expected %x:%x, got %x:%x\n", + 0x10ec, matched_id, vid1, vid2); return -ENODEV; } diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index d5021f266930..72f4622204ff 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1108,7 +1108,7 @@ static int alc5632_i2c_probe(struct i2c_client *client) struct alc5632_priv *alc5632; int ret, ret1, ret2; unsigned int vid1, vid2; - const struct i2c_device_id *id; + unsigned int matched_id; alc5632 = devm_kzalloc(&client->dev, sizeof(struct alc5632_priv), GFP_KERNEL); @@ -1134,9 +1134,9 @@ static int alc5632_i2c_probe(struct i2c_client *client) vid2 >>= 8; - id = i2c_match_id(alc5632_i2c_table, client); + matched_id = (uintptr_t)i2c_get_match_data(client); - if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) { + if ((vid1 != 0x10EC) || (vid2 != matched_id)) { dev_err(&client->dev, "Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2); return -EINVAL; diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 402b9a2ff024..f2f47f1c1ac8 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -967,7 +967,7 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, case ARIZONA_OUT3L_ENA_SHIFT: case ARIZONA_OUT3R_ENA_SHIFT: priv->out_up_pending++; - priv->out_up_delay += 17; + priv->out_up_delay += 17000; break; case ARIZONA_OUT4L_ENA_SHIFT: case ARIZONA_OUT4R_ENA_SHIFT: @@ -977,7 +977,7 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, case WM8997: break; default: - priv->out_up_delay += 10; + priv->out_up_delay += 10000; break; } break; @@ -999,7 +999,7 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, if (!priv->out_up_pending && priv->out_up_delay) { dev_dbg(component->dev, "Power up delay: %d\n", priv->out_up_delay); - msleep(priv->out_up_delay); + fsleep(priv->out_up_delay); priv->out_up_delay = 0; } break; @@ -1017,7 +1017,7 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, case ARIZONA_OUT3L_ENA_SHIFT: case ARIZONA_OUT3R_ENA_SHIFT: priv->out_down_pending++; - priv->out_down_delay++; + priv->out_down_delay += 1000; break; case ARIZONA_OUT4L_ENA_SHIFT: case ARIZONA_OUT4R_ENA_SHIFT: @@ -1028,10 +1028,10 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, break; case WM8998: case WM1814: - priv->out_down_delay += 5; + priv->out_down_delay += 5000; break; default: - priv->out_down_delay++; + priv->out_down_delay += 1000; break; } break; @@ -1053,7 +1053,7 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w, if (!priv->out_down_pending && priv->out_down_delay) { dev_dbg(component->dev, "Power down delay: %d\n", priv->out_down_delay); - msleep(priv->out_down_delay); + fsleep(priv->out_down_delay); priv->out_down_delay = 0; } break; @@ -1457,7 +1457,7 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) break; case SND_SOC_DAIFMT_DSP_B: if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) - != SND_SOC_DAIFMT_CBM_CFM) { + != SND_SOC_DAIFMT_CBP_CFP) { arizona_aif_err(dai, "DSP_B not valid in slave mode\n"); return -EINVAL; } @@ -1468,7 +1468,7 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) break; case SND_SOC_DAIFMT_LEFT_J: if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) - != SND_SOC_DAIFMT_CBM_CFM) { + != SND_SOC_DAIFMT_CBP_CFP) { arizona_aif_err(dai, "LEFT_J not valid in slave mode\n"); return -EINVAL; } @@ -1481,15 +1481,15 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: bclk |= ARIZONA_AIF1_BCLK_MSTR; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: bclk |= ARIZONA_AIF1_BCLK_MSTR; lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR; break; diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c index 58b8e002d76f..ad16ab6812cd 100644 --- a/sound/soc/codecs/aw88081.c +++ b/sound/soc/codecs/aw88081.c @@ -14,13 +14,18 @@ #include "aw88081.h" #include "aw88395/aw88395_device.h" +enum aw8808x_type { + AW88081, + AW88083, +}; + struct aw88081 { struct aw_device *aw_pa; struct mutex lock; struct delayed_work start_work; struct regmap *regmap; struct aw_container *aw_cfg; - + enum aw8808x_type devtype; bool phase_sync; }; @@ -32,6 +37,14 @@ static const struct regmap_config aw88081_regmap_config = { .val_format_endian = REGMAP_ENDIAN_BIG, }; +static const struct regmap_config aw88083_regmap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88083_REG_MAX, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + static int aw88081_dev_get_iis_status(struct aw_device *aw_dev) { unsigned int reg_val; @@ -196,6 +209,41 @@ static void aw88081_dev_amppd(struct aw_device *aw_dev, bool amppd) ~AW88081_EN_PA_MASK, AW88081_EN_PA_WORKING_VALUE); } +static void aw88083_i2c_wen(struct aw88081 *aw88081, bool flag) +{ + struct aw_device *aw_dev = aw88081->aw_pa; + + if (aw88081->devtype != AW88083) + return; + + if (flag) + regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG, + ~AW88083_I2C_WEN_MASK, AW88083_I2C_WEN_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG, + ~AW88083_I2C_WEN_MASK, AW88083_I2C_WEN_DISABLE_VALUE); +} + +static void aw88083_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + if (amppd) + regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG, + ~AW88083_AMPPD_MASK, AW88083_AMPPD_POWER_DOWN_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG, + ~AW88083_AMPPD_MASK, AW88083_AMPPD_WORKING_VALUE); +} + +static void aw88083_dev_pllpd(struct aw_device *aw_dev, bool pllpd) +{ + if (pllpd) + regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG, + ~AW88083_PLL_PD_MASK, AW88083_PLL_PD_WORKING_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG, + ~AW88083_PLL_PD_MASK, AW88083_PLL_PD_POWER_DOWN_VALUE); +} + static void aw88081_dev_clear_int_status(struct aw_device *aw_dev) { unsigned int int_status; @@ -284,12 +332,90 @@ static void aw88081_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute) AW88081_ULS_HMUTE_DISABLE_VALUE); } +static int aw88081_dev_reg_value_check(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned short *reg_val) +{ + unsigned int read_vol; + + if (reg_addr == AW88081_SYSCTRL_REG) { + *reg_val &= ~(~AW88081_EN_PA_MASK | + ~AW88081_PWDN_MASK | + ~AW88081_HMUTE_MASK | + ~AW88081_ULS_HMUTE_MASK); + + *reg_val |= AW88081_EN_PA_POWER_DOWN_VALUE | + AW88081_PWDN_POWER_DOWN_VALUE | + AW88081_HMUTE_ENABLE_VALUE | + AW88081_ULS_HMUTE_ENABLE_VALUE; + } + + if (reg_addr == AW88081_SYSCTRL2_REG) { + read_vol = (*reg_val & (~AW88081_VOL_MASK)) >> AW88081_VOL_START_BIT; + aw_dev->volume_desc.init_volume = read_vol; + } + + /* i2stxen */ + if (reg_addr == AW88081_I2SCTRL3_REG) { + /* close tx */ + *reg_val &= AW88081_I2STXEN_MASK; + *reg_val |= AW88081_I2STXEN_DISABLE_VALUE; + } + + return 0; +} + +static int aw88083_dev_reg_value_check(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned short *reg_val) +{ + unsigned int read_vol; + + if (reg_addr == AW88081_SYSCTRL_REG) { + *reg_val &= ~(~AW88083_AMPPD_MASK | + ~AW88081_PWDN_MASK | + ~AW88081_HMUTE_MASK | + ~AW88083_I2C_WEN_MASK); + + *reg_val |= AW88083_AMPPD_POWER_DOWN_VALUE | + AW88081_PWDN_POWER_DOWN_VALUE | + AW88081_HMUTE_ENABLE_VALUE | + AW88083_I2C_WEN_ENABLE_VALUE; + } + + if (reg_addr == AW88081_SYSCTRL2_REG) { + read_vol = (*reg_val & (~AW88081_VOL_MASK)) >> AW88081_VOL_START_BIT; + aw_dev->volume_desc.init_volume = read_vol; + } + + return 0; +} + +static int aw88081_reg_value_check(struct aw88081 *aw88081, + unsigned char reg_addr, unsigned short *reg_val) +{ + struct aw_device *aw_dev = aw88081->aw_pa; + int ret; + + switch (aw88081->devtype) { + case AW88081: + ret = aw88081_dev_reg_value_check(aw_dev, reg_addr, reg_val); + break; + case AW88083: + ret = aw88083_dev_reg_value_check(aw_dev, reg_addr, reg_val); + break; + default: + dev_err(aw_dev->dev, "unsupported device\n"); + ret = -EINVAL; + break; + } + + return ret; +} + static int aw88081_dev_reg_update(struct aw88081 *aw88081, unsigned char *data, unsigned int len) { struct aw_device *aw_dev = aw88081->aw_pa; struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; - unsigned int read_vol; int data_len, i, ret; int16_t *reg_data; u16 reg_val; @@ -312,30 +438,9 @@ static int aw88081_dev_reg_update(struct aw88081 *aw88081, reg_addr = reg_data[i]; reg_val = reg_data[i + 1]; - if (reg_addr == AW88081_SYSCTRL_REG) { - reg_val &= ~(~AW88081_EN_PA_MASK | - ~AW88081_PWDN_MASK | - ~AW88081_HMUTE_MASK | - ~AW88081_ULS_HMUTE_MASK); - - reg_val |= AW88081_EN_PA_POWER_DOWN_VALUE | - AW88081_PWDN_POWER_DOWN_VALUE | - AW88081_HMUTE_ENABLE_VALUE | - AW88081_ULS_HMUTE_ENABLE_VALUE; - } - - if (reg_addr == AW88081_SYSCTRL2_REG) { - read_vol = (reg_val & (~AW88081_VOL_MASK)) >> - AW88081_VOL_START_BIT; - aw_dev->volume_desc.init_volume = read_vol; - } - - /* i2stxen */ - if (reg_addr == AW88081_I2SCTRL3_REG) { - /* close tx */ - reg_val &= AW88081_I2STXEN_MASK; - reg_val |= AW88081_I2STXEN_DISABLE_VALUE; - } + ret = aw88081_reg_value_check(aw88081, reg_addr, ®_val); + if (ret) + return ret; ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); if (ret) @@ -474,8 +579,60 @@ pll_check_fail: return ret; } -static int aw88081_dev_stop(struct aw_device *aw_dev) +static int aw88083_dev_start(struct aw88081 *aw88081) +{ + struct aw_device *aw_dev = aw88081->aw_pa; + + if (aw_dev->status == AW88081_DEV_PW_ON) { + dev_dbg(aw_dev->dev, "already power on"); + return 0; + } + + aw88083_i2c_wen(aw88081, true); + + /* power on */ + aw88081_dev_pwd(aw_dev, false); + usleep_range(AW88081_2000_US, AW88081_2000_US + 10); + + aw88083_dev_pllpd(aw_dev, true); + /* amppd on */ + aw88083_dev_amppd(aw_dev, false); + usleep_range(AW88081_2000_US, AW88081_2000_US + 50); + + /* close mute */ + aw88081_dev_mute(aw_dev, false); + + aw88083_i2c_wen(aw88081, false); + + aw_dev->status = AW88081_DEV_PW_ON; + + return 0; +} + +static int aw88081_device_start(struct aw88081 *aw88081) +{ + int ret; + + switch (aw88081->devtype) { + case AW88081: + ret = aw88081_dev_start(aw88081); + break; + case AW88083: + ret = aw88083_dev_start(aw88081); + break; + default: + ret = -EINVAL; + dev_err(aw88081->aw_pa->dev, "unsupported device\n"); + break; + } + + return ret; +} + +static int aw88081_dev_stop(struct aw88081 *aw88081) { + struct aw_device *aw_dev = aw88081->aw_pa; + if (aw_dev->status == AW88081_DEV_PW_OFF) { dev_dbg(aw_dev->dev, "already power off"); return 0; @@ -503,6 +660,56 @@ static int aw88081_dev_stop(struct aw_device *aw_dev) return 0; } +static int aw88083_dev_stop(struct aw88081 *aw88081) +{ + struct aw_device *aw_dev = aw88081->aw_pa; + + if (aw_dev->status == AW88081_DEV_PW_OFF) { + dev_dbg(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88081_DEV_PW_OFF; + + aw88083_i2c_wen(aw88081, true); + /* set mute */ + aw88081_dev_mute(aw_dev, true); + + usleep_range(AW88081_2000_US, AW88081_2000_US + 100); + + /* enable amppd */ + aw88083_dev_amppd(aw_dev, true); + + aw88083_dev_pllpd(aw_dev, false); + + /* set power down */ + aw88081_dev_pwd(aw_dev, true); + + aw88083_i2c_wen(aw88081, false); + + return 0; +} + +static int aw88081_stop(struct aw88081 *aw88081) +{ + int ret; + + switch (aw88081->devtype) { + case AW88081: + ret = aw88081_dev_stop(aw88081); + break; + case AW88083: + ret = aw88083_dev_stop(aw88081); + break; + default: + dev_err(aw88081->aw_pa->dev, "unsupported device\n"); + ret = -EINVAL; + break; + } + + return ret; +} + static int aw88081_reg_update(struct aw88081 *aw88081, bool force) { struct aw_device *aw_dev = aw88081->aw_pa; @@ -540,7 +747,7 @@ static void aw88081_start_pa(struct aw88081 *aw88081) dev_err(aw88081->aw_pa->dev, "fw update failed, cnt:%d\n", i); continue; } - ret = aw88081_dev_start(aw88081); + ret = aw88081_device_start(aw88081); if (ret) { dev_err(aw88081->aw_pa->dev, "aw88081 device start failed. retry = %d", i); continue; @@ -745,7 +952,7 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol, } if (aw88081->aw_pa->status) { - aw88081_dev_stop(aw88081->aw_pa); + aw88081_stop(aw88081); aw88081_start(aw88081, AW88081_SYNC_START); } @@ -781,12 +988,16 @@ static int aw88081_volume_set(struct snd_kcontrol *kcontrol, if (value < mc->min || value > mc->max) return -EINVAL; + aw88083_i2c_wen(aw88081, true); + if (vol_desc->ctl_volume != value) { vol_desc->ctl_volume = value; aw88081_dev_set_volume(aw88081->aw_pa, vol_desc->ctl_volume); return 1; } + aw88083_i2c_wen(aw88081, false); + return 0; } @@ -860,13 +1071,19 @@ static int aw88081_init(struct aw88081 *aw88081, struct i2c_client *i2c, struct dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret); return ret; } - if (chip_id != AW88081_CHIP_ID) { + + switch (chip_id) { + case AW88081_CHIP_ID: + dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id); + break; + case AW88083_CHIP_ID: + dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id); + break; + default: dev_err(&i2c->dev, "unsupported device"); return -ENXIO; } - dev_dbg(&i2c->dev, "chip id = %x\n", chip_id); - aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); if (!aw_dev) return -ENOMEM; @@ -875,7 +1092,7 @@ static int aw88081_init(struct aw88081 *aw88081, struct i2c_client *i2c, struct aw_dev->i2c = i2c; aw_dev->regmap = regmap; aw_dev->dev = &i2c->dev; - aw_dev->chip_id = AW88081_CHIP_ID; + aw_dev->chip_id = chip_id; aw_dev->acf = NULL; aw_dev->prof_info.prof_desc = NULL; aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; @@ -912,21 +1129,8 @@ static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg return ret; } - aw88081_dev_clear_int_status(aw_dev); - - aw88081_dev_uls_hmute(aw_dev, true); - - aw88081_dev_mute(aw_dev, true); - - usleep_range(AW88081_5000_US, AW88081_5000_US + 10); - - aw88081_dev_i2s_tx_enable(aw_dev, false); - - usleep_range(AW88081_1000_US, AW88081_1000_US + 100); - - aw88081_dev_amppd(aw_dev, true); - - aw88081_dev_pwd(aw_dev, true); + aw_dev->status = AW88081_DEV_PW_ON; + aw88081_stop(aw88081); return 0; } @@ -977,7 +1181,7 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w, aw88081_start(aw88081, AW88081_ASYNC_START); break; case SND_SOC_DAPM_POST_PMD: - aw88081_dev_stop(aw88081->aw_pa); + aw88081_stop(aw88081); break; default: break; @@ -1036,8 +1240,17 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88081 = { .num_controls = ARRAY_SIZE(aw88081_controls), }; +static const struct i2c_device_id aw88081_i2c_id[] = { + { AW88081_I2C_NAME, AW88081}, + { AW88083_I2C_NAME, AW88083}, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw88081_i2c_id); + static int aw88081_i2c_probe(struct i2c_client *i2c) { + const struct regmap_config *regmap_config; + const struct i2c_device_id *id; struct aw88081 *aw88081; int ret; @@ -1049,11 +1262,25 @@ static int aw88081_i2c_probe(struct i2c_client *i2c) if (!aw88081) return -ENOMEM; + id = i2c_match_id(aw88081_i2c_id, i2c); + aw88081->devtype = id->driver_data; + mutex_init(&aw88081->lock); i2c_set_clientdata(i2c, aw88081); - aw88081->regmap = devm_regmap_init_i2c(i2c, &aw88081_regmap_config); + switch (aw88081->devtype) { + case AW88081: + regmap_config = &aw88081_regmap_config; + break; + case AW88083: + regmap_config = &aw88083_regmap_config; + break; + default: + return -EINVAL; + } + + aw88081->regmap = devm_regmap_init_i2c(i2c, regmap_config); if (IS_ERR(aw88081->regmap)) return dev_err_probe(&i2c->dev, PTR_ERR(aw88081->regmap), "failed to init regmap\n"); @@ -1068,12 +1295,6 @@ static int aw88081_i2c_probe(struct i2c_client *i2c) aw88081_dai, ARRAY_SIZE(aw88081_dai)); } -static const struct i2c_device_id aw88081_i2c_id[] = { - { AW88081_I2C_NAME }, - { } -}; -MODULE_DEVICE_TABLE(i2c, aw88081_i2c_id); - static struct i2c_driver aw88081_i2c_driver = { .driver = { .name = AW88081_I2C_NAME, diff --git a/sound/soc/codecs/aw88081.h b/sound/soc/codecs/aw88081.h index b4bf7288021a..7a4564270ab3 100644 --- a/sound/soc/codecs/aw88081.h +++ b/sound/soc/codecs/aw88081.h @@ -231,6 +231,49 @@ #define AW88081_CCO_MUX_BYPASS_VALUE \ (AW88081_CCO_MUX_BYPASS << AW88081_CCO_MUX_START_BIT) +#define AW88083_I2C_WEN_START_BIT (14) +#define AW88083_I2C_WEN_BITS_LEN (2) +#define AW88083_I2C_WEN_MASK \ + (~(((1<<AW88083_I2C_WEN_BITS_LEN)-1) << AW88083_I2C_WEN_START_BIT)) + +#define AW88083_I2C_WEN_DISABLE (0) +#define AW88083_I2C_WEN_DISABLE_VALUE \ + (AW88083_I2C_WEN_DISABLE << AW88083_I2C_WEN_START_BIT) + +#define AW88083_I2C_WEN_ENABLE (2) +#define AW88083_I2C_WEN_ENABLE_VALUE \ + (AW88083_I2C_WEN_ENABLE << AW88083_I2C_WEN_START_BIT) + +#define AW88083_PLL_PD_START_BIT (2) +#define AW88083_PLL_PD_BITS_LEN (1) +#define AW88083_PLL_PD_MASK \ + (~(((1<<AW88083_PLL_PD_BITS_LEN)-1) << AW88083_PLL_PD_START_BIT)) + +#define AW88083_PLL_PD_POWER_DOWN (1) +#define AW88083_PLL_PD_POWER_DOWN_VALUE \ + (AW88083_PLL_PD_POWER_DOWN << AW88083_PLL_PD_START_BIT) + +#define AW88083_PLL_PD_WORKING (0) +#define AW88083_PLL_PD_WORKING_VALUE \ + (AW88083_PLL_PD_WORKING << AW88083_PLL_PD_START_BIT) + +#define AW88083_AMPPD_START_BIT (1) +#define AW88083_AMPPD_BITS_LEN (1) +#define AW88083_AMPPD_MASK \ + (~(((1<<AW88083_AMPPD_BITS_LEN)-1) << AW88083_AMPPD_START_BIT)) + +#define AW88083_AMPPD_WORKING (0) +#define AW88083_AMPPD_WORKING_VALUE \ + (AW88083_AMPPD_WORKING << AW88083_AMPPD_START_BIT) + +#define AW88083_AMPPD_POWER_DOWN (1) +#define AW88083_AMPPD_POWER_DOWN_VALUE \ + (AW88083_AMPPD_POWER_DOWN << AW88083_AMPPD_START_BIT) + +#define AW88083_REG_MAX (0x7D) +#define AW88083_I2C_NAME "aw88083" +#define AW88083_CHIP_ID 0x2407 + #define AW88081_START_RETRIES (5) #define AW88081_START_WORK_DELAY_MS (0) diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c new file mode 100644 index 000000000000..6c50c4a18b6a --- /dev/null +++ b/sound/soc/codecs/aw88166.c @@ -0,0 +1,1933 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88166.c -- ALSA SoC AW88166 codec support +// +// Copyright (c) 2025 AWINIC Technology CO., LTD +// +// Author: Weidong Wang <wangweidong.a@awinic.com> +// + +#include <linux/crc32.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "aw88166.h" +#include "aw88395/aw88395_device.h" + +struct aw88166 { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; + + unsigned int check_val; + unsigned int crc_init_val; + unsigned int vcalb_init_val; + unsigned int re_init_val; + unsigned int dither_st; + bool phase_sync; +}; + +static const struct regmap_config aw88166_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88166_REG_MAX, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + int ret; + + ret = regmap_write(aw_dev->regmap, AW88166_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_write(aw_dev->regmap, AW88166_DSPMDAT_REG, (u16)dsp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write data error, ret=%d", __func__, ret); + return ret; + } + + return 0; +} + +static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + unsigned int temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88166_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_read(aw_dev->regmap, AW88166_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data = temp_data; + + return 0; +} + +static int aw_dev_dsp_read_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + unsigned int temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88166_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_read(aw_dev->regmap, AW88166_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data = temp_data; + + ret = regmap_read(aw_dev->regmap, AW88166_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data |= (temp_data << 16); + + return 0; +} + +static int aw_dev_dsp_read(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type) +{ + u32 reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW88166_DSP_16_DATA: + ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "read dsp_addr[0x%x] 16-bit failed", (u32)dsp_addr); + break; + case AW88166_DSP_32_DATA: + ret = aw_dev_dsp_read_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "read dsp_addr[0x%x] 32-bit failed", (u32)dsp_addr); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + /* clear dsp chip select state */ + if (regmap_read(aw_dev->regmap, AW88166_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. ret=%d\n", __func__, ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + int ret; + + if (pwd) + ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG, + ~AW88166_PWDN_MASK, AW88166_PWDN_POWER_DOWN_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG, + ~AW88166_PWDN_MASK, AW88166_PWDN_WORKING_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88166_SYSINT_REG, ®_val); + if (ret) + dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + dev_dbg(aw_dev->dev, "read interrupt reg=0x%04x", *int_status); +} + +static void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + u16 int_status; + + /* read int status and clear */ + aw_dev_get_int_status(aw_dev, &int_status); + /* make sure int status is clear */ + aw_dev_get_int_status(aw_dev, &int_status); + if (int_status) + dev_dbg(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); +} + +static int aw_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88166_SYSST_REG, ®_val); + if (ret) + return ret; + if ((reg_val & AW88166_BIT_PLL_CHECK) != AW88166_BIT_PLL_CHECK) { + dev_err(aw_dev->dev, "check pll lock fail, reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW88166_2000_US, AW88166_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88166_PLLCTRL2_REG, ®_val); + if (ret) + return ret; + + reg_val &= (~AW88166_CCO_MUX_MASK); + if (reg_val == AW88166_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret = regmap_update_bits(aw_dev->regmap, AW88166_PLLCTRL2_REG, + ~AW88166_CCO_MUX_MASK, AW88166_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW88166_2000_US, AW88166_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + regmap_update_bits(aw_dev->regmap, AW88166_PLLCTRL2_REG, + ~AW88166_CCO_MUX_MASK, AW88166_CCO_MUX_BYPASS_VALUE); + if (ret == 0) { + usleep_range(AW88166_2000_US, AW88166_2000_US + 10); + for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW88166_2000_US, AW88166_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +static int aw_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret = aw_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return 0; +} + +static int aw_dev_check_sysst(struct aw_device *aw_dev) +{ + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88166_PWMCTRL3_REG, ®_val); + if (ret) + return ret; + + if (reg_val & (~AW88166_NOISE_GATE_EN_MASK)) + check_val = AW88166_BIT_SYSST_NOSWS_CHECK; + else + check_val = AW88166_BIT_SYSST_SWS_CHECK; + + for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88166_SYSST_REG, ®_val); + if (ret) + return ret; + + if ((reg_val & (~AW88166_BIT_SYSST_CHECK_MASK) & check_val) != check_val) { + dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x", + i, reg_val, AW88166_BIT_SYSST_NOSWS_CHECK); + usleep_range(AW88166_2000_US, AW88166_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + int ret; + + if (amppd) + ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG, + ~AW88166_AMPPD_MASK, AW88166_AMPPD_POWER_DOWN_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG, + ~AW88166_AMPPD_MASK, AW88166_AMPPD_WORKING_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + + if (is_enable) + ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG, + ~AW88166_DSPBY_MASK, AW88166_DSPBY_WORKING_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG, + ~AW88166_DSPBY_MASK, AW88166_DSPBY_BYPASS_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed\n", __func__); +} + +static int aw88166_dev_get_icalk(struct aw88166 *aw88166, int16_t *icalk) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + unsigned int efrm_reg_val, efrl_reg_val; + uint16_t ef_isn_geslp, ef_isn_h5bits; + uint16_t icalk_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88166_EFRM2_REG, &efrm_reg_val); + if (ret) + return ret; + + ef_isn_geslp = (efrm_reg_val & (~AW88166_EF_ISN_GESLP_MASK)) >> + AW88166_EF_ISN_GESLP_SHIFT; + + ret = regmap_read(aw_dev->regmap, AW88166_EFRL_REG, &efrl_reg_val); + if (ret) + return ret; + + ef_isn_h5bits = (efrl_reg_val & (~AW88166_EF_ISN_H5BITS_MASK)) >> + AW88166_EF_ISN_H5BITS_SHIFT; + + if (aw88166->check_val == AW_EF_AND_CHECK) + icalk_val = ef_isn_geslp & (ef_isn_h5bits | AW88166_EF_ISN_H5BITS_SIGN_MASK); + else + icalk_val = ef_isn_geslp | (ef_isn_h5bits & (~AW88166_EF_ISN_H5BITS_SIGN_MASK)); + + if (icalk_val & (~AW88166_ICALK_SIGN_MASK)) + icalk_val = icalk_val | AW88166_ICALK_NEG_MASK; + *icalk = (int16_t)icalk_val; + + return 0; +} + +static int aw88166_dev_get_vcalk(struct aw88166 *aw88166, int16_t *vcalk) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + unsigned int efrm_reg_val, efrl_reg_val; + uint16_t ef_vsn_geslp, ef_vsn_h3bits; + uint16_t vcalk_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88166_EFRM2_REG, &efrm_reg_val); + if (ret) + return ret; + + ef_vsn_geslp = (efrm_reg_val & (~AW88166_EF_VSN_GESLP_MASK)) >> + AW88166_EF_VSN_GESLP_SHIFT; + + ret = regmap_read(aw_dev->regmap, AW88166_EFRL_REG, &efrl_reg_val); + if (ret) + return ret; + + ef_vsn_h3bits = (efrl_reg_val & (~AW88166_EF_VSN_H3BITS_MASK)) >> + AW88166_EF_VSN_H3BITS_SHIFT; + + if (aw88166->check_val == AW_EF_AND_CHECK) + vcalk_val = ef_vsn_geslp & (ef_vsn_h3bits | AW88166_EF_VSN_H3BITS_SIGN_MASK); + else + vcalk_val = ef_vsn_geslp | (ef_vsn_h3bits & (~AW88166_EF_VSN_H3BITS_SIGN_MASK)); + + if (vcalk_val & (~AW88166_VCALK_SIGN_MASK)) + vcalk_val = vcalk_val | AW88166_VCALK_NEG_MASK; + *vcalk = (int16_t)vcalk_val; + + return 0; +} + +static int aw88166_dev_set_vcalb(struct aw88166 *aw88166) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + int32_t ical_k, vcal_k, vcalb; + int16_t icalk, vcalk; + unsigned int reg_val; + int ret; + + ret = aw88166_dev_get_icalk(aw88166, &icalk); + if (ret) { + dev_err(aw_dev->dev, "get icalk failed\n"); + return ret; + } + ical_k = icalk * AW88166_ICABLK_FACTOR + AW88166_CABL_BASE_VALUE; + + ret = aw88166_dev_get_vcalk(aw88166, &vcalk); + if (ret) { + dev_err(aw_dev->dev, "get vbcalk failed\n"); + return ret; + } + vcal_k = vcalk * AW88166_VCABLK_FACTOR + AW88166_CABL_BASE_VALUE; + + vcalb = AW88166_VCALB_ACCURACY * AW88166_VSCAL_FACTOR / + AW88166_ISCAL_FACTOR * ical_k / vcal_k * aw88166->vcalb_init_val; + + vcalb = vcalb >> AW88166_VCALB_ADJ_FACTOR; + reg_val = (uint32_t)vcalb; + + regmap_write(aw_dev->regmap, AW88166_DSPVCALB_REG, reg_val); + + return 0; +} + +static int aw_dev_init_vcalb_update(struct aw88166 *aw88166, int flag) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + int ret; + + switch (flag) { + case AW88166_RECOVERY_SEC_DATA: + ret = regmap_write(aw_dev->regmap, AW88166_DSPVCALB_REG, aw88166->vcalb_init_val); + break; + case AW88166_RECORD_SEC_DATA: + ret = regmap_read(aw_dev->regmap, AW88166_DSPVCALB_REG, &aw88166->vcalb_init_val); + break; + default: + dev_err(aw_dev->dev, "unsupported type:%d\n", flag); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_init_re_update(struct aw88166 *aw88166, int flag) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + unsigned int re_temp_h, re_temp_l; + int ret; + + switch (flag) { + case AW88166_RECOVERY_SEC_DATA: + ret = regmap_write(aw_dev->regmap, AW88166_ACR1_REG, aw88166->re_init_val >> 16); + if (ret) + return ret; + ret = regmap_write(aw_dev->regmap, AW88166_ACR2_REG, + (uint16_t)aw88166->re_init_val); + if (ret) + return ret; + break; + case AW88166_RECORD_SEC_DATA: + ret = regmap_read(aw_dev->regmap, AW88166_ACR1_REG, &re_temp_h); + if (ret) + return ret; + ret = regmap_read(aw_dev->regmap, AW88166_ACR2_REG, &re_temp_l); + if (ret) + return ret; + aw88166->re_init_val = (re_temp_h << 16) + re_temp_l; + break; + default: + dev_err(aw_dev->dev, "unsupported type:%d\n", flag); + ret = -EINVAL; + break; + } + + return ret; +} + +static void aw_dev_backup_sec_record(struct aw88166 *aw88166) +{ + aw_dev_init_vcalb_update(aw88166, AW88166_RECORD_SEC_DATA); + aw_dev_init_re_update(aw88166, AW88166_RECOVERY_SEC_DATA); +} + +static void aw_dev_backup_sec_recovery(struct aw88166 *aw88166) +{ + aw_dev_init_vcalb_update(aw88166, AW88166_RECOVERY_SEC_DATA); + aw_dev_init_re_update(aw88166, AW88166_RECOVERY_SEC_DATA); +} + +static int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + uint16_t re_lbits, re_hbits; + u32 cali_re; + int ret; + + if ((aw_dev->cali_desc.cali_re >= AW88166_CALI_RE_MAX) || + (aw_dev->cali_desc.cali_re <= AW88166_CALI_RE_MIN)) + return -EINVAL; + + cali_re = AW88166_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + + aw_dev->cali_desc.ra), AW88166_DSP_RE_SHIFT); + + re_hbits = (cali_re & (~AW88166_CALI_RE_HBITS_MASK)) >> AW88166_CALI_RE_HBITS_SHIFT; + re_lbits = (cali_re & (~AW88166_CALI_RE_LBITS_MASK)) >> AW88166_CALI_RE_LBITS_SHIFT; + + ret = regmap_write(aw_dev->regmap, AW88166_ACR1_REG, re_hbits); + if (ret) { + dev_err(aw_dev->dev, "set cali re error"); + return ret; + } + + ret = regmap_write(aw_dev->regmap, AW88166_ACR2_REG, re_lbits); + if (ret) + dev_err(aw_dev->dev, "set cali re error"); + + return ret; +} + +static int aw_dev_fw_crc_check(struct aw_device *aw_dev) +{ + uint16_t check_val, fw_len_val; + unsigned int reg_val; + int ret; + + /* calculate fw_end_addr */ + fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88166_CRC_FW_BASE_ADDR; + + /* write fw_end_addr to crc_end_addr */ + ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG, + ~AW88166_CRC_END_ADDR_MASK, fw_len_val); + if (ret) + return ret; + /* enable fw crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG, + ~AW88166_CRC_CODE_EN_MASK, AW88166_CRC_CODE_EN_ENABLE_VALUE); + + usleep_range(AW88166_2000_US, AW88166_2000_US + 10); + + /* read crc check result */ + regmap_read(aw_dev->regmap, AW88166_HAGCST_REG, ®_val); + if (ret) + return ret; + + check_val = (reg_val & (~AW88166_CRC_CHECK_BITS_MASK)) >> AW88166_CRC_CHECK_START_BIT; + + /* disable fw crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG, + ~AW88166_CRC_CODE_EN_MASK, AW88166_CRC_CODE_EN_DISABLE_VALUE); + if (ret) + return ret; + + if (check_val != AW88166_CRC_CHECK_PASS_VAL) { + dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x\n", + __func__, check_val, AW88166_CRC_CHECK_PASS_VAL); + ret = -EINVAL; + } + + return ret; +} + +static int aw_dev_cfg_crc_check(struct aw_device *aw_dev) +{ + uint16_t check_val, cfg_len_val; + unsigned int reg_val; + int ret; + + /* calculate cfg end addr */ + cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88166_CRC_CFG_BASE_ADDR; + + /* write cfg_end_addr to crc_end_addr */ + ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG, + ~AW88166_CRC_END_ADDR_MASK, cfg_len_val); + if (ret) + return ret; + + /* enable cfg crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG, + ~AW88166_CRC_CFG_EN_MASK, AW88166_CRC_CFG_EN_ENABLE_VALUE); + if (ret) + return ret; + + usleep_range(AW88166_1000_US, AW88166_1000_US + 10); + + /* read crc check result */ + ret = regmap_read(aw_dev->regmap, AW88166_HAGCST_REG, ®_val); + if (ret) + return ret; + + check_val = (reg_val & (~AW88166_CRC_CHECK_BITS_MASK)) >> AW88166_CRC_CHECK_START_BIT; + + /* disable cfg crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG, + ~AW88166_CRC_CFG_EN_MASK, AW88166_CRC_CFG_EN_DISABLE_VALUE); + if (ret) + return ret; + + if (check_val != AW88166_CRC_CHECK_PASS_VAL) { + dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x\n", + check_val, AW88166_CRC_CHECK_PASS_VAL); + ret = -EINVAL; + } + + return ret; +} + +static int aw_dev_hw_crc_check(struct aw88166 *aw88166) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + int ret; + + ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG, + ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_BYPASS_VALUE); + if (ret) + return ret; + + ret = aw_dev_fw_crc_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "fw_crc_check failed\n"); + goto crc_check_failed; + } + + ret = aw_dev_cfg_crc_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "cfg_crc_check failed\n"); + goto crc_check_failed; + } + + ret = regmap_write(aw_dev->regmap, AW88166_CRCCTRL_REG, aw88166->crc_init_val); + if (ret) + return ret; + + ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG, + ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_WORK_VALUE); + + return ret; + +crc_check_failed: + regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG, + ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_WORK_VALUE); + return ret; +} + +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) + ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCTRL3_REG, + ~AW88166_I2STXEN_MASK, AW88166_I2STXEN_ENABLE_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCTRL3_REG, + ~AW88166_I2STXEN_MASK, AW88166_I2STXEN_DISABLE_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static int aw_dev_get_dsp_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88166_WDT_REG, ®_val); + if (ret) + return ret; + if (!(reg_val & (~AW88166_WDT_CNT_MASK))) + return -EPERM; + + return 0; +} + +static int aw_dev_dsp_check(struct aw_device *aw_dev) +{ + int ret, i; + + switch (aw_dev->dsp_cfg) { + case AW88166_DEV_DSP_BYPASS: + dev_dbg(aw_dev->dev, "dsp bypass"); + ret = 0; + break; + case AW88166_DEV_DSP_WORK: + aw_dev_dsp_enable(aw_dev, false); + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW88166_1000_US, AW88166_1000_US + 10); + for (i = 0; i < AW88166_DEV_DSP_CHECK_MAX; i++) { + ret = aw_dev_get_dsp_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); + usleep_range(AW88166_2000_US, AW88166_2000_US + 10); + } + } + break; + default: + dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int reg_value; + u16 real_value; + int ret; + + real_value = min((value + vol_desc->init_volume), (unsigned int)AW88166_MUTE_VOL); + + ret = regmap_read(aw_dev->regmap, AW88166_SYSCTRL2_REG, ®_value); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); + + real_value = (real_value << AW88166_VOL_START_BIT) | (reg_value & AW88166_VOL_MASK); + + ret = regmap_write(aw_dev->regmap, AW88166_SYSCTRL2_REG, real_value); + + return ret; +} + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + u16 fade_in_vol = desc->ctl_volume; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88166_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw_dev_set_volume(aw_dev, AW88166_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88166_MUTE_VOL; i += fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i != AW88166_MUTE_VOL) { + aw_dev_set_volume(aw_dev, AW88166_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +static void aw88166_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + if (is_mute) { + aw_dev_fade_out(aw_dev); + regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG, + ~AW88166_HMUTE_MASK, AW88166_HMUTE_ENABLE_VALUE); + } else { + regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG, + ~AW88166_HMUTE_MASK, AW88166_HMUTE_DISABLE_VALUE); + aw_dev_fade_in(aw_dev); + } +} + +static void aw88166_dev_set_dither(struct aw88166 *aw88166, bool dither) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + + if (dither) + regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG, + ~AW88166_DITHER_EN_MASK, AW88166_DITHER_EN_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG, + ~AW88166_DITHER_EN_MASK, AW88166_DITHER_EN_DISABLE_VALUE); +} + +static int aw88166_dev_start(struct aw88166 *aw88166) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + int ret; + + if (aw_dev->status == AW88166_DEV_PW_ON) { + dev_dbg(aw_dev->dev, "already power on"); + return 0; + } + + aw88166_dev_set_dither(aw88166, false); + + /* power on */ + aw_dev_pwd(aw_dev, false); + usleep_range(AW88166_2000_US, AW88166_2000_US + 10); + + ret = aw_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start\n"); + goto pll_check_fail; + } + + /* amppd on */ + aw_dev_amppd(aw_dev, false); + usleep_range(AW88166_1000_US, AW88166_1000_US + 50); + + /* check i2s status */ + ret = aw_dev_check_sysst(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "sysst check failed\n"); + goto sysst_check_fail; + } + + if (aw_dev->dsp_cfg == AW88166_DEV_DSP_WORK) { + aw_dev_backup_sec_recovery(aw88166); + ret = aw_dev_hw_crc_check(aw88166); + if (ret) { + dev_err(aw_dev->dev, "dsp crc check failed\n"); + goto crc_check_fail; + } + aw_dev_dsp_enable(aw_dev, false); + aw88166_dev_set_vcalb(aw88166); + aw_dev_update_cali_re(&aw_dev->cali_desc); + ret = aw_dev_dsp_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp status check failed\n"); + goto dsp_check_fail; + } + } else { + dev_dbg(aw_dev->dev, "start pa with dsp bypass"); + } + + /* enable tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, true); + + if (aw88166->dither_st == AW88166_DITHER_EN_ENABLE_VALUE) + aw88166_dev_set_dither(aw88166, true); + + /* close mute */ + aw88166_dev_mute(aw_dev, false); + /* clear inturrupt */ + aw_dev_clear_int_status(aw_dev); + aw_dev->status = AW88166_DEV_PW_ON; + + return 0; + +dsp_check_fail: +crc_check_fail: + aw_dev_dsp_enable(aw_dev, false); +sysst_check_fail: + aw_dev_clear_int_status(aw_dev); + aw_dev_amppd(aw_dev, true); +pll_check_fail: + aw_dev_pwd(aw_dev, true); + aw_dev->status = AW88166_DEV_PW_OFF; + + return ret; +} + +static int aw_dev_dsp_update_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len, unsigned short base) +{ + u32 tmp_len; + int i, ret; + + mutex_lock(&aw_dev->dsp_lock); + ret = regmap_write(aw_dev->regmap, AW88166_DSPMADD_REG, base); + if (ret) + goto error_operation; + + for (i = 0; i < len; i += AW88166_MAX_RAM_WRITE_BYTE_SIZE) { + if ((len - i) < AW88166_MAX_RAM_WRITE_BYTE_SIZE) + tmp_len = len - i; + else + tmp_len = AW88166_MAX_RAM_WRITE_BYTE_SIZE; + + ret = regmap_raw_write(aw_dev->regmap, AW88166_DSPMDAT_REG, + &data[i], tmp_len); + if (ret) + goto error_operation; + } + mutex_unlock(&aw_dev->dsp_lock); + + return 0; + +error_operation: + mutex_unlock(&aw_dev->dsp_lock); + return ret; +} + +static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + u32 dsp_ra; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88166_DSP_REG_CFG_ADPZ_RA, + &dsp_ra, AW88166_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "read ra error\n"); + return ret; + } + + cali_desc->ra = AW88166_DSP_RE_TO_SHOW_RE(dsp_ra, + AW88166_DSP_RE_SHIFT); + + return 0; +} + +static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp config len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp config data is null or len is 0\n"); + return -EINVAL; + } + + ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88166_DSP_CFG_ADDR); + if (ret) + return ret; + + aw_dev->dsp_cfg_len = len; + + ret = aw_dev_get_ra(&aw_dev->cali_desc); + + return ret; +} + +static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp firmware data is null or len is 0\n"); + return -EINVAL; + } + + aw_dev->dsp_fw_len = len; + ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88166_DSP_FW_ADDR); + + return ret; +} + +static int aw_dev_check_sram(struct aw_device *aw_dev) +{ + unsigned int reg_val; + + mutex_lock(&aw_dev->dsp_lock); + /* read dsp_rom_check_reg */ + aw_dev_dsp_read_16bit(aw_dev, AW88166_DSP_ROM_CHECK_ADDR, ®_val); + if (reg_val != AW88166_DSP_ROM_CHECK_DATA) { + dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]\n", + reg_val, AW88166_DSP_ROM_CHECK_DATA); + goto error; + } + + /* check dsp_cfg_base_addr */ + aw_dev_dsp_write_16bit(aw_dev, AW88166_DSP_CFG_ADDR, AW88166_DSP_ODD_NUM_BIT_TEST); + aw_dev_dsp_read_16bit(aw_dev, AW88166_DSP_CFG_ADDR, ®_val); + if (reg_val != AW88166_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]\n", + reg_val, AW88166_DSP_ODD_NUM_BIT_TEST); + goto error; + } + mutex_unlock(&aw_dev->dsp_lock); + + return 0; +error: + mutex_unlock(&aw_dev->dsp_lock); + return -EPERM; +} + +static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag) +{ + int ret; + + switch (flag) { + case AW88166_DEV_MEMCLK_PLL: + ret = regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG, + ~AW88166_MEM_CLKSEL_MASK, + AW88166_MEM_CLKSEL_DAPHCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select pll failed\n"); + break; + case AW88166_DEV_MEMCLK_OSC: + ret = regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG, + ~AW88166_MEM_CLKSEL_MASK, + AW88166_MEM_CLKSEL_OSCCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select OSC failed\n"); + break; + default: + dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x\n", flag); + break; + } +} + +static int aw_dev_update_reg_container(struct aw88166 *aw88166, + unsigned char *data, unsigned int len) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + u16 read_vol, reg_val; + int data_len, i, ret; + int16_t *reg_data; + u8 reg_addr; + + reg_data = (int16_t *)data; + data_len = len >> 1; + + if (data_len & 0x1) { + dev_err(aw_dev->dev, "data len:%d unsupported\n", data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + reg_addr = reg_data[i]; + reg_val = reg_data[i + 1]; + + if (reg_addr == AW88166_DSPVCALB_REG) { + aw88166->vcalb_init_val = reg_val; + continue; + } + + if (reg_addr == AW88166_SYSCTRL_REG) { + if (reg_val & (~AW88166_DSPBY_MASK)) + aw_dev->dsp_cfg = AW88166_DEV_DSP_BYPASS; + else + aw_dev->dsp_cfg = AW88166_DEV_DSP_WORK; + + reg_val &= (AW88166_HMUTE_MASK | AW88166_PWDN_MASK | + AW88166_DSPBY_MASK); + reg_val |= (AW88166_HMUTE_ENABLE_VALUE | AW88166_PWDN_POWER_DOWN_VALUE | + AW88166_DSPBY_BYPASS_VALUE); + } + + if (reg_addr == AW88166_I2SCTRL3_REG) { + reg_val &= AW88166_I2STXEN_MASK; + reg_val |= AW88166_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr == AW88166_SYSCTRL2_REG) { + read_vol = (reg_val & (~AW88166_VOL_MASK)) >> + AW88166_VOL_START_BIT; + aw_dev->volume_desc.init_volume = read_vol; + } + + if (reg_addr == AW88166_DBGCTRL_REG) { + if ((reg_val & (~AW88166_EF_DBMD_MASK)) == AW88166_EF_DBMD_OR_VALUE) + aw88166->check_val = AW_EF_OR_CHECK; + else + aw88166->check_val = AW_EF_AND_CHECK; + + aw88166->dither_st = reg_val & (~AW88166_DITHER_EN_MASK); + } + + if (reg_addr == AW88166_ACR1_REG) { + aw88166->re_init_val |= (uint32_t)reg_val << 16; + continue; + } + + if (reg_addr == AW88166_ACR2_REG) { + aw88166->re_init_val |= (uint32_t)reg_val; + continue; + } + + if (reg_addr == AW88166_CRCCTRL_REG) + aw88166->crc_init_val = reg_val; + + ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + return ret; + } + + aw_dev_pwd(aw_dev, false); + usleep_range(AW88166_1000_US, AW88166_1000_US + 10); + + if (aw_dev->prof_cur != aw_dev->prof_index) + vol_desc->ctl_volume = 0; + else + aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); + + return 0; +} + +static int aw_dev_reg_update(struct aw88166 *aw88166, + unsigned char *data, unsigned int len) +{ + int ret; + + if (!len || !data) { + dev_err(aw88166->aw_pa->dev, "reg data is null or len is 0\n"); + return -EINVAL; + } + + ret = aw_dev_update_reg_container(aw88166, data, len); + if (ret) + dev_err(aw88166->aw_pa->dev, "reg update failed\n"); + + return ret; +} + +static int aw88166_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]\n", + index, aw_dev->prof_info.count); + return -EINVAL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + *prof_name = prof_info->prof_name_list[prof_desc->id]; + + return 0; +} + +static int aw88166_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} + +static int aw88166_dev_fw_update(struct aw88166 *aw88166, bool up_dsp_fw_en, bool force_up_en) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + if ((aw_dev->prof_cur == aw_dev->prof_index) && + (force_up_en == AW88166_FORCE_UPDATE_OFF)) { + dev_dbg(aw_dev->dev, "scene no change, not update"); + return 0; + } + + if (aw_dev->fw_status == AW88166_DEV_FW_FAILED) { + dev_err(aw_dev->dev, "fw status[%d] error\n", aw_dev->fw_status); + return -EPERM; + } + + ret = aw88166_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw88166_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) + return ret; + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw_dev_reg_update(aw88166, sec_desc[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed\n"); + return ret; + } + + aw88166_dev_mute(aw_dev, true); + + if (aw_dev->dsp_cfg == AW88166_DEV_DSP_WORK) + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_OSC); + + ret = aw_dev_check_sram(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check sram failed\n"); + goto error; + } + + aw_dev_backup_sec_recovery(aw88166); + + if (up_dsp_fw_en) { + dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); + ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data, + sec_desc[AW88395_DATA_TYPE_DSP_FW].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp fw failed\n"); + goto error; + } + } + + /* update dsp config */ + ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data, + sec_desc[AW88395_DATA_TYPE_DSP_CFG].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp cfg failed\n"); + goto error; + } + + aw_dev_backup_sec_record(aw88166); + + aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL); + + aw_dev->prof_cur = aw_dev->prof_index; + + return 0; + +error: + aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL); + return ret; +} + +static void aw88166_start_pa(struct aw88166 *aw88166) +{ + int ret, i; + + for (i = 0; i < AW88166_START_RETRIES; i++) { + ret = aw88166_dev_start(aw88166); + if (ret) { + dev_err(aw88166->aw_pa->dev, "aw88166 device start failed. retry = %d", i); + ret = aw88166_dev_fw_update(aw88166, AW88166_DSP_FW_UPDATE_ON, true); + if (ret) { + dev_err(aw88166->aw_pa->dev, "fw update failed"); + continue; + } + } else { + dev_dbg(aw88166->aw_pa->dev, "start success\n"); + break; + } + } +} + +static void aw88166_startup_work(struct work_struct *work) +{ + struct aw88166 *aw88166 = + container_of(work, struct aw88166, start_work.work); + + mutex_lock(&aw88166->lock); + aw88166_start_pa(aw88166); + mutex_unlock(&aw88166->lock); +} + +static void aw88166_start(struct aw88166 *aw88166, bool sync_start) +{ + int ret; + + if (aw88166->aw_pa->fw_status != AW88166_DEV_FW_OK) + return; + + if (aw88166->aw_pa->status == AW88166_DEV_PW_ON) + return; + + ret = aw88166_dev_fw_update(aw88166, AW88166_DSP_FW_UPDATE_OFF, aw88166->phase_sync); + if (ret) { + dev_err(aw88166->aw_pa->dev, "fw update failed\n"); + return; + } + + if (sync_start == AW88166_SYNC_START) + aw88166_start_pa(aw88166); + else + queue_delayed_work(system_wq, + &aw88166->start_work, + AW88166_START_WORK_DELAY_MS); +} + +static int aw_dev_check_sysint(struct aw_device *aw_dev) +{ + u16 reg_val; + + aw_dev_get_int_status(aw_dev, ®_val); + if (reg_val & AW88166_BIT_SYSINT_CHECK) { + dev_err(aw_dev->dev, "pa stop check fail:0x%04x\n", reg_val); + return -EINVAL; + } + + return 0; +} + +static int aw88166_stop(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_cfg = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG]; + struct aw_sec_data_desc *dsp_fw = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW]; + int int_st; + + if (aw_dev->status == AW88166_DEV_PW_OFF) { + dev_dbg(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88166_DEV_PW_OFF; + + aw88166_dev_mute(aw_dev, true); + usleep_range(AW88166_4000_US, AW88166_4000_US + 100); + + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88166_1000_US, AW88166_1000_US + 100); + + int_st = aw_dev_check_sysint(aw_dev); + + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_amppd(aw_dev, true); + + if (int_st) { + aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_OSC); + aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); + aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); + aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL); + } + + aw_dev_pwd(aw_dev, true); + + return 0; +} + +static struct snd_soc_dai_driver aw88166_dai[] = { + { + .name = "aw88166-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW88166_RATES, + .formats = AW88166_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW88166_RATES, + .formats = AW88166_FORMATS, + }, + }, +}; + +static int aw88166_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88166->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_in_time; + + return 0; +} + +static int aw88166_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88166->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_in_time) { + aw_dev->fade_in_time = time; + return 1; + } + + return 0; +} + +static int aw88166_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88166->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_out_time; + + return 0; +} + +static int aw88166_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88166->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_out_time) { + aw_dev->fade_out_time = time; + return 1; + } + + return 0; +} + +static int aw88166_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + /* check the index whether is valid */ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + /* check the index whether change */ + if (aw_dev->prof_index == index) + return -EINVAL; + + aw_dev->prof_index = index; + dev_dbg(aw_dev->dev, "set prof[%s]", + aw_dev->prof_info.prof_name_list[aw_dev->prof_info.prof_desc[index].id]); + + return 0; +} + +static int aw88166_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + char *prof_name, *name; + int count, ret; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw88166->aw_pa->prof_info.count; + if (count <= 0) { + uinfo->value.enumerated.items = 0; + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + + ret = aw88166_dev_get_prof_name(aw88166->aw_pa, count, &prof_name); + if (ret) { + strscpy(uinfo->value.enumerated.name, "null", + strlen("null") + 1); + return 0; + } + + strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw88166_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88166->aw_pa->prof_index; + + return 0; +} + +static int aw88166_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + int ret; + + mutex_lock(&aw88166->lock); + ret = aw88166_dev_set_profile_index(aw88166->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); + mutex_unlock(&aw88166->lock); + return 0; + } + + if (aw88166->aw_pa->status) { + aw88166_stop(aw88166->aw_pa); + aw88166_start(aw88166, AW88166_SYNC_START); + } + + mutex_unlock(&aw88166->lock); + + return 1; +} + +static int aw88166_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88166->aw_pa->volume_desc; + + ucontrol->value.integer.value[0] = vol_desc->ctl_volume; + + return 0; +} + +static int aw88166_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88166->aw_pa->volume_desc; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (vol_desc->ctl_volume != value) { + vol_desc->ctl_volume = value; + aw_dev_set_volume(aw88166->aw_pa, vol_desc->ctl_volume); + + return 1; + } + + return 0; +} + +static int aw88166_get_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88166->aw_pa->fade_step; + + return 0; +} + +static int aw88166_set_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw88166->aw_pa->fade_step != value) { + aw88166->aw_pa->fade_step = value; + return 1; + } + + return 0; +} + +static int aw88166_re_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw88166->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re; + + return 0; +} + +static int aw88166_re_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88166->aw_pa; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw_dev->cali_desc.cali_re != value) { + aw_dev->cali_desc.cali_re = value; + return 1; + } + + return 0; +} + +static int aw88166_dev_init(struct aw88166 *aw88166, struct aw_container *aw_cfg) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + int ret; + + ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed\n"); + return -EINVAL; + } + aw_dev->fade_in_time = AW88166_1000_US / 10; + aw_dev->fade_out_time = AW88166_1000_US >> 1; + aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id; + aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id; + + ret = aw88166_dev_fw_update(aw88166, AW88166_FORCE_UPDATE_ON, AW88166_DSP_FW_UPDATE_ON); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); + return ret; + } + + aw88166_dev_mute(aw_dev, true); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88166_1000_US, AW88166_1000_US + 100); + + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} + +static int aw88166_request_firmware_file(struct aw88166 *aw88166) +{ + const struct firmware *cont = NULL; + int ret; + + aw88166->aw_pa->fw_status = AW88166_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88166_ACF_FILE, aw88166->aw_pa->dev); + if (ret) { + dev_err(aw88166->aw_pa->dev, "request [%s] failed!\n", AW88166_ACF_FILE); + return ret; + } + + dev_dbg(aw88166->aw_pa->dev, "loaded %s - size: %zu\n", + AW88166_ACF_FILE, cont ? cont->size : 0); + + aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev, + struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL); + if (!aw88166->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw88166->aw_cfg->len = (int)cont->size; + memcpy(aw88166->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg); + if (ret) { + dev_err(aw88166->aw_pa->dev, "load [%s] failed!\n", AW88166_ACF_FILE); + return ret; + } + + mutex_lock(&aw88166->lock); + /* aw device init */ + ret = aw88166_dev_init(aw88166, aw88166->aw_cfg); + if (ret) + dev_err(aw88166->aw_pa->dev, "dev init failed\n"); + mutex_unlock(&aw88166->lock); + + return ret; +} + +static const struct snd_kcontrol_new aw88166_controls[] = { + SOC_SINGLE_EXT("PCM Playback Volume", AW88166_SYSCTRL2_REG, + 6, AW88166_MUTE_VOL, 0, aw88166_volume_get, + aw88166_volume_set), + SOC_SINGLE_EXT("Fade Step", 0, 0, AW88166_MUTE_VOL, 0, + aw88166_get_fade_step, aw88166_set_fade_step), + SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88166_get_fade_in_time, aw88166_set_fade_in_time), + SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88166_get_fade_out_time, aw88166_set_fade_out_time), + SOC_SINGLE_EXT("Calib", 0, 0, AW88166_CALI_RE_MAX, 0, + aw88166_re_get, aw88166_re_set), + AW88166_PROFILE_EXT("AW88166 Profile Set", aw88166_profile_info, + aw88166_profile_get, aw88166_profile_set), +}; + +static int aw88166_playback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); + + mutex_lock(&aw88166->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88166_start(aw88166, AW88166_ASYNC_START); + break; + case SND_SOC_DAPM_POST_PMD: + aw88166_stop(aw88166->aw_pa); + break; + default: + break; + } + mutex_unlock(&aw88166->lock); + + return 0; +} + +static const struct snd_soc_dapm_widget aw88166_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0, + aw88166_playback_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("DAC Output"), + + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("ADC Input"), +}; + +static const struct snd_soc_dapm_route aw88166_audio_map[] = { + {"DAC Output", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "ADC Input"}, +}; + +static int aw88166_codec_probe(struct snd_soc_component *component) +{ + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component); + int ret; + + INIT_DELAYED_WORK(&aw88166->start_work, aw88166_startup_work); + + ret = aw88166_request_firmware_file(aw88166); + if (ret) + dev_err(aw88166->aw_pa->dev, "%s failed\n", __func__); + + return ret; +} + +static void aw88166_codec_remove(struct snd_soc_component *aw_codec) +{ + struct aw88166 *aw88166 = snd_soc_component_get_drvdata(aw_codec); + + cancel_delayed_work_sync(&aw88166->start_work); +} + +static const struct snd_soc_component_driver soc_codec_dev_aw88166 = { + .probe = aw88166_codec_probe, + .remove = aw88166_codec_remove, + .dapm_widgets = aw88166_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aw88166_dapm_widgets), + .dapm_routes = aw88166_audio_map, + .num_dapm_routes = ARRAY_SIZE(aw88166_audio_map), + .controls = aw88166_controls, + .num_controls = ARRAY_SIZE(aw88166_controls), +}; + +static void aw88166_hw_reset(struct aw88166 *aw88166) +{ + if (aw88166->reset_gpio) { + gpiod_set_value_cansleep(aw88166->reset_gpio, 1); + usleep_range(AW88166_1000_US, AW88166_1000_US + 10); + gpiod_set_value_cansleep(aw88166->reset_gpio, 0); + usleep_range(AW88166_1000_US, AW88166_1000_US + 10); + } +} + +static void aw88166_parse_channel_dt(struct aw88166 *aw88166) +{ + struct aw_device *aw_dev = aw88166->aw_pa; + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value; + + of_property_read_u32(np, "awinic,audio-channel", &channel_value); + aw_dev->channel = channel_value; + aw88166->phase_sync = of_property_read_bool(np, "awinic,sync-flag"); +} + +static int aw88166_init(struct aw88166 *aw88166, struct i2c_client *i2c, struct regmap *regmap) +{ + struct aw_device *aw_dev; + unsigned int chip_id; + int ret; + + ret = regmap_read(regmap, AW88166_ID_REG, &chip_id); + if (ret) { + dev_err(&i2c->dev, "%s read chipid error. ret = %d\n", __func__, ret); + return ret; + } + + aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); + if (!aw_dev) + return -ENOMEM; + aw88166->aw_pa = aw_dev; + + aw_dev->i2c = i2c; + aw_dev->dev = &i2c->dev; + aw_dev->regmap = regmap; + mutex_init(&aw_dev->dsp_lock); + + aw_dev->chip_id = chip_id; + aw_dev->acf = NULL; + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = AW88166_DEV_DEFAULT_CH; + aw_dev->fw_status = AW88166_DEV_FW_FAILED; + + aw_dev->fade_step = AW88166_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume = AW88166_VOL_DEFAULT_VALUE; + + aw88166_parse_channel_dt(aw88166); + + return 0; +} + +static int aw88166_i2c_probe(struct i2c_client *i2c) +{ + struct aw88166 *aw88166; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) + return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed\n"); + + aw88166 = devm_kzalloc(&i2c->dev, sizeof(*aw88166), GFP_KERNEL); + if (!aw88166) + return -ENOMEM; + + mutex_init(&aw88166->lock); + + i2c_set_clientdata(i2c, aw88166); + + aw88166->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(aw88166->reset_gpio)) + return dev_err_probe(&i2c->dev, PTR_ERR(aw88166->reset_gpio), + "reset gpio not defined\n"); + aw88166_hw_reset(aw88166); + + aw88166->regmap = devm_regmap_init_i2c(i2c, &aw88166_remap_config); + if (IS_ERR(aw88166->regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(aw88166->regmap), + "failed to init regmap\n"); + + /* aw pa init */ + ret = aw88166_init(aw88166, i2c, aw88166->regmap); + if (ret) + return ret; + + return devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw88166, + aw88166_dai, ARRAY_SIZE(aw88166_dai)); +} + +static const struct i2c_device_id aw88166_i2c_id[] = { + { AW88166_I2C_NAME }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw88166_i2c_id); + +static struct i2c_driver aw88166_i2c_driver = { + .driver = { + .name = AW88166_I2C_NAME, + }, + .probe = aw88166_i2c_probe, + .id_table = aw88166_i2c_id, +}; +module_i2c_driver(aw88166_i2c_driver); + +MODULE_DESCRIPTION("ASoC AW88166 Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88166.h b/sound/soc/codecs/aw88166.h new file mode 100644 index 000000000000..3a53ba0ac625 --- /dev/null +++ b/sound/soc/codecs/aw88166.h @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88166.h -- ALSA SoC AW88166 codec support +// +// Copyright (c) 2025 AWINIC Technology CO., LTD +// +// Author: Weidong Wang <wangweidong.a@awinic.com> +// + +#ifndef __AW88166_H__ +#define __AW88166_H__ + +/* registers list */ +#define AW88166_ID_REG (0x00) +#define AW88166_SYSST_REG (0x01) +#define AW88166_SYSINT_REG (0x02) +#define AW88166_SYSINTM_REG (0x03) +#define AW88166_SYSCTRL_REG (0x04) +#define AW88166_SYSCTRL2_REG (0x05) +#define AW88166_I2SCTRL1_REG (0x06) +#define AW88166_I2SCTRL2_REG (0x07) +#define AW88166_I2SCTRL3_REG (0x08) +#define AW88166_DACCFG1_REG (0x09) +#define AW88166_DACCFG2_REG (0x0A) +#define AW88166_DACCFG3_REG (0x0B) +#define AW88166_DACCFG4_REG (0x0C) +#define AW88166_DACCFG5_REG (0x0D) +#define AW88166_DACCFG6_REG (0x0E) +#define AW88166_DACCFG7_REG (0x0F) +#define AW88166_MPDCFG1_REG (0x10) +#define AW88166_MPDCFG2_REG (0x11) +#define AW88166_MPDCFG3_REG (0x12) +#define AW88166_MPDCFG4_REG (0x13) +#define AW88166_PWMCTRL1_REG (0x14) +#define AW88166_PWMCTRL2_REG (0x15) +#define AW88166_PWMCTRL3_REG (0x16) +#define AW88166_I2SCFG1_REG (0x17) +#define AW88166_DBGCTRL_REG (0x18) +#define AW88166_HAGCST_REG (0x20) +#define AW88166_VBAT_REG (0x21) +#define AW88166_TEMP_REG (0x22) +#define AW88166_PVDD_REG (0x23) +#define AW88166_ISNDAT_REG (0x24) +#define AW88166_I2SINT_REG (0x25) +#define AW88166_I2SCAPCNT_REG (0x26) +#define AW88166_ANASTA1_REG (0x27) +#define AW88166_ANASTA2_REG (0x28) +#define AW88166_ANASTA3_REG (0x29) +#define AW88166_TESTDET_REG (0x2A) +#define AW88166_TESTIN_REG (0x38) +#define AW88166_TESTOUT_REG (0x39) +#define AW88166_MEMTEST_REG (0x3A) +#define AW88166_DSPMADD_REG (0x40) +#define AW88166_DSPMDAT_REG (0x41) +#define AW88166_WDT_REG (0x42) +#define AW88166_ACR1_REG (0x43) +#define AW88166_ACR2_REG (0x44) +#define AW88166_ASR1_REG (0x45) +#define AW88166_ASR2_REG (0x46) +#define AW88166_DSPCFG_REG (0x47) +#define AW88166_ASR3_REG (0x48) +#define AW88166_ASR4_REG (0x49) +#define AW88166_DSPVCALB_REG (0x4A) +#define AW88166_CRCCTRL_REG (0x4B) +#define AW88166_DSPDBG1_REG (0x4C) +#define AW88166_DSPDBG2_REG (0x4D) +#define AW88166_DSPDBG3_REG (0x4E) +#define AW88166_ISNCTRL1_REG (0x50) +#define AW88166_PLLCTRL1_REG (0x51) +#define AW88166_PLLCTRL2_REG (0x52) +#define AW88166_PLLCTRL3_REG (0x53) +#define AW88166_CDACTRL1_REG (0x54) +#define AW88166_CDACTRL2_REG (0x55) +#define AW88166_CDACTRL3_REG (0x56) +#define AW88166_SADCCTRL1_REG (0x57) +#define AW88166_SADCCTRL2_REG (0x58) +#define AW88166_BOPCTRL1_REG (0x59) +#define AW88166_BOPCTRL2_REG (0x5A) +#define AW88166_BOPCTRL3_REG (0x5B) +#define AW88166_BOPCTRL4_REG (0x5C) +#define AW88166_BOPCTRL5_REG (0x5D) +#define AW88166_BOPCTRL6_REG (0x5E) +#define AW88166_BOPCTRL7_REG (0x5F) +#define AW88166_BSTCTRL1_REG (0x60) +#define AW88166_BSTCTRL2_REG (0x61) +#define AW88166_BSTCTRL3_REG (0x62) +#define AW88166_BSTCTRL4_REG (0x63) +#define AW88166_BSTCTRL5_REG (0x64) +#define AW88166_BSTCTRL6_REG (0x65) +#define AW88166_DSMCFG1_REG (0x66) +#define AW88166_DSMCFG2_REG (0x67) +#define AW88166_DSMCFG3_REG (0x68) +#define AW88166_DSMCFG4_REG (0x69) +#define AW88166_DSMCFG5_REG (0x6A) +#define AW88166_DSMCFG6_REG (0x6B) +#define AW88166_DSMCFG7_REG (0x6C) +#define AW88166_DSMCFG8_REG (0x6D) +#define AW88166_TESTCTRL1_REG (0x70) +#define AW88166_TESTCTRL2_REG (0x71) +#define AW88166_EFCTRL1_REG (0x72) +#define AW88166_EFCTRL2_REG (0x73) +#define AW88166_EFWH_REG (0x74) +#define AW88166_EFWM2_REG (0x75) +#define AW88166_EFWM1_REG (0x76) +#define AW88166_EFRH_REG (0x77) +#define AW88166_EFRM2_REG (0x78) +#define AW88166_EFRM1_REG (0x79) +#define AW88166_EFRL_REG (0x7A) +#define AW88166_TM_REG (0x7C) +#define AW88166_TM2_REG (0x7D) + +#define AW88166_REG_MAX (0x7E) +#define AW88166_MUTE_VOL (1023) + +#define AW88166_DSP_CFG_ADDR (0x9B00) +#define AW88166_DSP_REG_CFG_ADPZ_RA (0x9B68) +#define AW88166_DSP_FW_ADDR (0x8980) +#define AW88166_DSP_ROM_CHECK_ADDR (0x1F40) + +#define AW88166_CALI_RE_HBITS_MASK (~(0xFFFF0000)) +#define AW88166_CALI_RE_HBITS_SHIFT (16) + +#define AW88166_CALI_RE_LBITS_MASK (~(0xFFFF)) +#define AW88166_CALI_RE_LBITS_SHIFT (0) + +#define AW88166_I2STXEN_START_BIT (9) +#define AW88166_I2STXEN_BITS_LEN (1) +#define AW88166_I2STXEN_MASK \ + (~(((1<<AW88166_I2STXEN_BITS_LEN)-1) << AW88166_I2STXEN_START_BIT)) + +#define AW88166_I2STXEN_DISABLE (0) +#define AW88166_I2STXEN_DISABLE_VALUE \ + (AW88166_I2STXEN_DISABLE << AW88166_I2STXEN_START_BIT) + +#define AW88166_I2STXEN_ENABLE (1) +#define AW88166_I2STXEN_ENABLE_VALUE \ + (AW88166_I2STXEN_ENABLE << AW88166_I2STXEN_START_BIT) + +#define AW88166_VOL_START_BIT (0) +#define AW88166_VOL_BITS_LEN (10) +#define AW88166_VOL_MASK \ + (~(((1<<AW88166_VOL_BITS_LEN)-1) << AW88166_VOL_START_BIT)) + +#define AW88166_PWDN_START_BIT (0) +#define AW88166_PWDN_BITS_LEN (1) +#define AW88166_PWDN_MASK \ + (~(((1<<AW88166_PWDN_BITS_LEN)-1) << AW88166_PWDN_START_BIT)) + +#define AW88166_PWDN_POWER_DOWN (1) +#define AW88166_PWDN_POWER_DOWN_VALUE \ + (AW88166_PWDN_POWER_DOWN << AW88166_PWDN_START_BIT) + +#define AW88166_PWDN_WORKING (0) +#define AW88166_PWDN_WORKING_VALUE \ + (AW88166_PWDN_WORKING << AW88166_PWDN_START_BIT) + +#define AW88166_DSPBY_START_BIT (2) +#define AW88166_DSPBY_BITS_LEN (1) +#define AW88166_DSPBY_MASK \ + (~(((1<<AW88166_DSPBY_BITS_LEN)-1) << AW88166_DSPBY_START_BIT)) + +#define AW88166_DSPBY_WORKING (0) +#define AW88166_DSPBY_WORKING_VALUE \ + (AW88166_DSPBY_WORKING << AW88166_DSPBY_START_BIT) + +#define AW88166_DSPBY_BYPASS (1) +#define AW88166_DSPBY_BYPASS_VALUE \ + (AW88166_DSPBY_BYPASS << AW88166_DSPBY_START_BIT) + +#define AW88166_MEM_CLKSEL_START_BIT (3) +#define AW88166_MEM_CLKSEL_BITS_LEN (1) +#define AW88166_MEM_CLKSEL_MASK \ + (~(((1<<AW88166_MEM_CLKSEL_BITS_LEN)-1) << AW88166_MEM_CLKSEL_START_BIT)) + +#define AW88166_MEM_CLKSEL_OSCCLK (0) +#define AW88166_MEM_CLKSEL_OSCCLK_VALUE \ + (AW88166_MEM_CLKSEL_OSCCLK << AW88166_MEM_CLKSEL_START_BIT) + +#define AW88166_MEM_CLKSEL_DAPHCLK (1) +#define AW88166_MEM_CLKSEL_DAPHCLK_VALUE \ + (AW88166_MEM_CLKSEL_DAPHCLK << AW88166_MEM_CLKSEL_START_BIT) + +#define AW88166_DITHER_EN_START_BIT (15) +#define AW88166_DITHER_EN_BITS_LEN (1) +#define AW88166_DITHER_EN_MASK \ + (~(((1<<AW88166_DITHER_EN_BITS_LEN)-1) << AW88166_DITHER_EN_START_BIT)) + +#define AW88166_DITHER_EN_DISABLE (0) +#define AW88166_DITHER_EN_DISABLE_VALUE \ + (AW88166_DITHER_EN_DISABLE << AW88166_DITHER_EN_START_BIT) + +#define AW88166_DITHER_EN_ENABLE (1) +#define AW88166_DITHER_EN_ENABLE_VALUE \ + (AW88166_DITHER_EN_ENABLE << AW88166_DITHER_EN_START_BIT) + +#define AW88166_HMUTE_START_BIT (8) +#define AW88166_HMUTE_BITS_LEN (1) +#define AW88166_HMUTE_MASK \ + (~(((1<<AW88166_HMUTE_BITS_LEN)-1) << AW88166_HMUTE_START_BIT)) + +#define AW88166_HMUTE_DISABLE (0) +#define AW88166_HMUTE_DISABLE_VALUE \ + (AW88166_HMUTE_DISABLE << AW88166_HMUTE_START_BIT) + +#define AW88166_HMUTE_ENABLE (1) +#define AW88166_HMUTE_ENABLE_VALUE \ + (AW88166_HMUTE_ENABLE << AW88166_HMUTE_START_BIT) + +#define AW88166_EF_DBMD_START_BIT (2) +#define AW88166_EF_DBMD_BITS_LEN (1) +#define AW88166_EF_DBMD_MASK \ + (~(((1<<AW88166_EF_DBMD_BITS_LEN)-1) << AW88166_EF_DBMD_START_BIT)) + +#define AW88166_EF_DBMD_OR (1) +#define AW88166_EF_DBMD_OR_VALUE \ + (AW88166_EF_DBMD_OR << AW88166_EF_DBMD_START_BIT) + +#define AW88166_CLKI_START_BIT (4) +#define AW88166_NOCLKI_START_BIT (5) +#define AW88166_PLLI_START_BIT (0) +#define AW88166_PLLI_INT_VALUE (1) +#define AW88166_PLLI_INT_INTERRUPT \ + (AW88166_PLLI_INT_VALUE << AW88166_PLLI_START_BIT) + +#define AW88166_CLKI_INT_VALUE (1) +#define AW88166_CLKI_INT_INTERRUPT \ + (AW88166_CLKI_INT_VALUE << AW88166_CLKI_START_BIT) + +#define AW88166_NOCLKI_INT_VALUE (1) +#define AW88166_NOCLKI_INT_INTERRUPT \ + (AW88166_NOCLKI_INT_VALUE << AW88166_NOCLKI_START_BIT) + +#define AW88166_BIT_SYSINT_CHECK \ + (AW88166_PLLI_INT_INTERRUPT | \ + AW88166_CLKI_INT_INTERRUPT | \ + AW88166_NOCLKI_INT_INTERRUPT) + +#define AW88166_CRC_CHECK_START_BIT (12) +#define AW88166_CRC_CHECK_BITS_LEN (3) +#define AW88166_CRC_CHECK_BITS_MASK \ + (~(((1<<AW88166_CRC_CHECK_BITS_LEN)-1) << AW88166_CRC_CHECK_START_BIT)) + +#define AW88166_RCV_MODE_RECEIVER (1) +#define AW88166_RCV_MODE_RECEIVER_VALUE \ + (AW88166_RCV_MODE_RECEIVER << AW88166_RCV_MODE_START_BIT) + +#define AW88166_AMPPD_START_BIT (1) +#define AW88166_AMPPD_BITS_LEN (1) +#define AW88166_AMPPD_MASK \ + (~(((1<<AW88166_AMPPD_BITS_LEN)-1) << AW88166_AMPPD_START_BIT)) + +#define AW88166_AMPPD_WORKING (0) +#define AW88166_AMPPD_WORKING_VALUE \ + (AW88166_AMPPD_WORKING << AW88166_AMPPD_START_BIT) + +#define AW88166_AMPPD_POWER_DOWN (1) +#define AW88166_AMPPD_POWER_DOWN_VALUE \ + (AW88166_AMPPD_POWER_DOWN << AW88166_AMPPD_START_BIT) + +#define AW88166_RAM_CG_BYP_START_BIT (0) +#define AW88166_RAM_CG_BYP_BITS_LEN (1) +#define AW88166_RAM_CG_BYP_MASK \ + (~(((1<<AW88166_RAM_CG_BYP_BITS_LEN)-1) << AW88166_RAM_CG_BYP_START_BIT)) + +#define AW88166_RAM_CG_BYP_WORK (0) +#define AW88166_RAM_CG_BYP_WORK_VALUE \ + (AW88166_RAM_CG_BYP_WORK << AW88166_RAM_CG_BYP_START_BIT) + +#define AW88166_RAM_CG_BYP_BYPASS (1) +#define AW88166_RAM_CG_BYP_BYPASS_VALUE \ + (AW88166_RAM_CG_BYP_BYPASS << AW88166_RAM_CG_BYP_START_BIT) + +#define AW88166_CRC_END_ADDR_START_BIT (0) +#define AW88166_CRC_END_ADDR_BITS_LEN (12) +#define AW88166_CRC_END_ADDR_MASK \ + (~(((1<<AW88166_CRC_END_ADDR_BITS_LEN)-1) << AW88166_CRC_END_ADDR_START_BIT)) + +#define AW88166_CRC_CODE_EN_START_BIT (13) +#define AW88166_CRC_CODE_EN_BITS_LEN (1) +#define AW88166_CRC_CODE_EN_MASK \ + (~(((1<<AW88166_CRC_CODE_EN_BITS_LEN)-1) << AW88166_CRC_CODE_EN_START_BIT)) + +#define AW88166_CRC_CODE_EN_DISABLE (0) +#define AW88166_CRC_CODE_EN_DISABLE_VALUE \ + (AW88166_CRC_CODE_EN_DISABLE << AW88166_CRC_CODE_EN_START_BIT) + +#define AW88166_CRC_CODE_EN_ENABLE (1) +#define AW88166_CRC_CODE_EN_ENABLE_VALUE \ + (AW88166_CRC_CODE_EN_ENABLE << AW88166_CRC_CODE_EN_START_BIT) + +#define AW88166_CRC_CFG_EN_START_BIT (12) +#define AW88166_CRC_CFG_EN_BITS_LEN (1) +#define AW88166_CRC_CFG_EN_MASK \ + (~(((1<<AW88166_CRC_CFG_EN_BITS_LEN)-1) << AW88166_CRC_CFG_EN_START_BIT)) + +#define AW88166_CRC_CFG_EN_DISABLE (0) +#define AW88166_CRC_CFG_EN_DISABLE_VALUE \ + (AW88166_CRC_CFG_EN_DISABLE << AW88166_CRC_CFG_EN_START_BIT) + +#define AW88166_CRC_CFG_EN_ENABLE (1) +#define AW88166_CRC_CFG_EN_ENABLE_VALUE \ + (AW88166_CRC_CFG_EN_ENABLE << AW88166_CRC_CFG_EN_START_BIT) + +#define AW88166_OCDS_START_BIT (3) +#define AW88166_OCDS_OC (1) +#define AW88166_OCDS_OC_VALUE \ + (AW88166_OCDS_OC << AW88166_OCDS_START_BIT) + +#define AW88166_NOCLKS_START_BIT (5) +#define AW88166_NOCLKS_NO_CLOCK (1) +#define AW88166_NOCLKS_NO_CLOCK_VALUE \ + (AW88166_NOCLKS_NO_CLOCK << AW88166_NOCLKS_START_BIT) + +#define AW88166_SWS_START_BIT (8) +#define AW88166_SWS_SWITCHING (1) +#define AW88166_SWS_SWITCHING_VALUE \ + (AW88166_SWS_SWITCHING << AW88166_SWS_START_BIT) + +#define AW88166_BSTS_START_BIT (9) +#define AW88166_BSTS_FINISHED (1) +#define AW88166_BSTS_FINISHED_VALUE \ + (AW88166_BSTS_FINISHED << AW88166_BSTS_START_BIT) + +#define AW88166_UVLS_START_BIT (14) +#define AW88166_UVLS_NORMAL (0) +#define AW88166_UVLS_NORMAL_VALUE \ + (AW88166_UVLS_NORMAL << AW88166_UVLS_START_BIT) + +#define AW88166_BSTOCS_START_BIT (11) +#define AW88166_BSTOCS_OVER_CURRENT (1) +#define AW88166_BSTOCS_OVER_CURRENT_VALUE \ + (AW88166_BSTOCS_OVER_CURRENT << AW88166_BSTOCS_START_BIT) + +#define AW88166_OTHS_START_BIT (1) +#define AW88166_OTHS_OT (1) +#define AW88166_OTHS_OT_VALUE \ + (AW88166_OTHS_OT << AW88166_OTHS_START_BIT) + +#define AW88166_PLLS_START_BIT (0) +#define AW88166_PLLS_LOCKED (1) +#define AW88166_PLLS_LOCKED_VALUE \ + (AW88166_PLLS_LOCKED << AW88166_PLLS_START_BIT) + +#define AW88166_CLKS_START_BIT (4) +#define AW88166_CLKS_STABLE (1) +#define AW88166_CLKS_STABLE_VALUE \ + (AW88166_CLKS_STABLE << AW88166_CLKS_START_BIT) + +#define AW88166_BIT_PLL_CHECK \ + (AW88166_CLKS_STABLE_VALUE | \ + AW88166_PLLS_LOCKED_VALUE) + +#define AW88166_BIT_SYSST_CHECK_MASK \ + (~(AW88166_UVLS_NORMAL_VALUE | \ + AW88166_BSTOCS_OVER_CURRENT_VALUE | \ + AW88166_BSTS_FINISHED_VALUE | \ + AW88166_SWS_SWITCHING_VALUE | \ + AW88166_NOCLKS_NO_CLOCK_VALUE | \ + AW88166_CLKS_STABLE_VALUE | \ + AW88166_OCDS_OC_VALUE | \ + AW88166_OTHS_OT_VALUE | \ + AW88166_PLLS_LOCKED_VALUE)) + +#define AW88166_BIT_SYSST_NOSWS_CHECK \ + (AW88166_BSTS_FINISHED_VALUE | \ + AW88166_CLKS_STABLE_VALUE | \ + AW88166_PLLS_LOCKED_VALUE) + +#define AW88166_BIT_SYSST_SWS_CHECK \ + (AW88166_BSTS_FINISHED_VALUE | \ + AW88166_CLKS_STABLE_VALUE | \ + AW88166_PLLS_LOCKED_VALUE | \ + AW88166_SWS_SWITCHING_VALUE) + +#define AW88166_CCO_MUX_START_BIT (14) +#define AW88166_CCO_MUX_BITS_LEN (1) +#define AW88166_CCO_MUX_MASK \ + (~(((1<<AW88166_CCO_MUX_BITS_LEN)-1) << AW88166_CCO_MUX_START_BIT)) + +#define AW88166_CCO_MUX_DIVIDED (0) +#define AW88166_CCO_MUX_DIVIDED_VALUE \ + (AW88166_CCO_MUX_DIVIDED << AW88166_CCO_MUX_START_BIT) + +#define AW88166_CCO_MUX_BYPASS (1) +#define AW88166_CCO_MUX_BYPASS_VALUE \ + (AW88166_CCO_MUX_BYPASS << AW88166_CCO_MUX_START_BIT) + +#define AW88166_NOISE_GATE_EN_START_BIT (13) +#define AW88166_NOISE_GATE_EN_BITS_LEN (1) +#define AW88166_NOISE_GATE_EN_MASK \ + (~(((1<<AW88166_NOISE_GATE_EN_BITS_LEN)-1) << AW88166_NOISE_GATE_EN_START_BIT)) + +#define AW88166_WDT_CNT_START_BIT (0) +#define AW88166_WDT_CNT_BITS_LEN (8) +#define AW88166_WDT_CNT_MASK \ + (~(((1<<AW88166_WDT_CNT_BITS_LEN)-1) << AW88166_WDT_CNT_START_BIT)) + +#define AW88166_EF_ISN_GESLP_START_BIT (0) +#define AW88166_EF_ISN_GESLP_BITS_LEN (10) +#define AW88166_EF_ISN_GESLP_MASK \ + (~(((1<<AW88166_EF_ISN_GESLP_BITS_LEN)-1) << AW88166_EF_ISN_GESLP_START_BIT)) +#define AW88166_EF_ISN_GESLP_SHIFT (0) + +#define AW88166_EF_VSN_GESLP_START_BIT (10) +#define AW88166_EF_VSN_GESLP_BITS_LEN (6) +#define AW88166_EF_VSN_GESLP_MASK \ + (~(((1<<AW88166_EF_VSN_GESLP_BITS_LEN)-1) << AW88166_EF_VSN_GESLP_START_BIT)) +#define AW88166_EF_VSN_GESLP_SHIFT (10) + +#define AW88166_EF_VSN_H3BITS_START_BIT (13) +#define AW88166_EF_VSN_H3BITS_BITS_LEN (3) +#define AW88166_EF_VSN_H3BITS_MASK \ + (~(((1<<AW88166_EF_VSN_H3BITS_BITS_LEN)-1) << AW88166_EF_VSN_H3BITS_START_BIT)) +#define AW88166_EF_VSN_H3BITS_SHIFT (10) +#define AW88166_EF_VSN_H3BITS_SIGN_MASK (0x7) + +#define AW88166_EF_ISN_H5BITS_START_BIT (8) +#define AW88166_EF_ISN_H5BITS_BITS_LEN (5) +#define AW88166_EF_ISN_H5BITS_MASK \ + (~(((1<<AW88166_EF_ISN_H5BITS_BITS_LEN)-1) << AW88166_EF_ISN_H5BITS_START_BIT)) +#define AW88166_EF_ISN_H5BITS_SIGN_MASK (0x1F) +#define AW88166_EF_ISN_H5BITS_SHIFT (3) + +#define AW88166_VSCAL_FACTOR (65300) +#define AW88166_ISCAL_FACTOR (34667) +#define AW88166_CABL_BASE_VALUE (1000) +#define AW88166_VCALK_SIGN_MASK (~(1 << 5)) +#define AW88166_VCALK_NEG_MASK (0xFFE0) +#define AW88166_ICALK_SIGN_MASK (~(1 << 9)) +#define AW88166_ICALK_NEG_MASK (0xFE00) +#define AW88166_ICABLK_FACTOR (1) +#define AW88166_VCABLK_FACTOR (2) +#define AW88166_VCALB_ADJ_FACTOR (12) +#define AW88166_VCALB_ACCURACY (1 << 12) +#define AW88166_DSP_RE_SHIFT (12) +#define AW88166_CALI_RE_MAX (15000) +#define AW88166_CALI_RE_MIN (4000) +#define AW88166_VOLUME_STEP_DB (64) +#define AW88166_VOL_DEFAULT_VALUE (0) +#define AW88166_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift)) +#define AW88166_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) + +#define AW88166_DSP_ODD_NUM_BIT_TEST (0x5555) +#define AW88166_DSP_ROM_CHECK_DATA (0xFF99) + +#define AW88166_DEV_DEFAULT_CH (0) +#define AW88166_DEV_DSP_CHECK_MAX (5) +#define AW88166_MAX_RAM_WRITE_BYTE_SIZE (128) +#define AW_FW_ADDR_LEN (4) +#define AW88166_CRC_CHECK_PASS_VAL (0x4) +#define AW88166_CRC_CFG_BASE_ADDR (0xD80) +#define AW88166_CRC_FW_BASE_ADDR (0x4C0) +#define AW88166_DEV_SYSST_CHECK_MAX (10) +#define AW88166_START_RETRIES (5) +#define AW88166_START_WORK_DELAY_MS (0) +#define FADE_TIME_MAX 100000 +#define FADE_TIME_MIN 0 +#define AW88166_CHIP_ID (0x2066) +#define AW88166_I2C_NAME "aw88166" +#define AW88166_ACF_FILE "aw88166_acf.bin" + +#define AW88166_RATES (SNDRV_PCM_RATE_8000_48000 | \ + SNDRV_PCM_RATE_96000) +#define AW88166_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AW88166_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = profile_info, \ + .get = profile_get, \ + .put = profile_set, \ +} + +enum { + AW_EF_AND_CHECK = 0, + AW_EF_OR_CHECK, +}; + +enum { + AW88166_DSP_FW_UPDATE_OFF = 0, + AW88166_DSP_FW_UPDATE_ON = 1, +}; + +enum { + AW88166_FORCE_UPDATE_OFF = 0, + AW88166_FORCE_UPDATE_ON = 1, +}; + +enum { + AW88166_1000_US = 1000, + AW88166_2000_US = 2000, + AW88166_3000_US = 3000, + AW88166_4000_US = 4000, +}; + +enum AW88166_DEV_STATUS { + AW88166_DEV_PW_OFF = 0, + AW88166_DEV_PW_ON, +}; + +enum AW88166_DEV_FW_STATUS { + AW88166_DEV_FW_FAILED = 0, + AW88166_DEV_FW_OK, +}; + +enum AW88166_DEV_MEMCLK { + AW88166_DEV_MEMCLK_OSC = 0, + AW88166_DEV_MEMCLK_PLL = 1, +}; + +enum AW88166_DEV_DSP_CFG { + AW88166_DEV_DSP_WORK = 0, + AW88166_DEV_DSP_BYPASS = 1, +}; + +enum { + AW88166_DSP_16_DATA = 0, + AW88166_DSP_32_DATA = 1, +}; + +enum { + AW88166_SYNC_START = 0, + AW88166_ASYNC_START, +}; + +enum { + AW88166_RECORD_SEC_DATA = 0, + AW88166_RECOVERY_SEC_DATA = 1, +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c index 6b333d1c6e94..b7ea8be0d0cb 100644 --- a/sound/soc/codecs/aw88395/aw88395_device.c +++ b/sound/soc/codecs/aw88395/aw88395_device.c @@ -424,7 +424,7 @@ static int aw_dev_dsp_set_crc32(struct aw_device *aw_dev) return -EINVAL; } - crc_value = __crc32c_le(0xFFFFFFFF, crc_dsp_cfg->data, crc_data_len) ^ 0xFFFFFFFF; + crc_value = crc32c(0xFFFFFFFF, crc_dsp_cfg->data, crc_data_len) ^ 0xFFFFFFFF; return aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_CRC_ADDR, crc_value, AW88395_DSP_32_DATA); diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c index 04304a7ad915..3eb862643b53 100644 --- a/sound/soc/codecs/cpcap.c +++ b/sound/soc/codecs/cpcap.c @@ -11,11 +11,21 @@ #include <linux/module.h> #include <linux/regmap.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include <linux/mfd/motorola-cpcap.h> #include <sound/core.h> +#include <linux/input.h> +#include <sound/jack.h> #include <sound/soc.h> #include <sound/tlv.h> +/* Register 8 - CPCAP_REG_INTS1 --- Interrupt Sense 1 */ +#define CPCAP_BIT_HS_S 9 /* Headset */ +#define CPCAP_BIT_MB2_S 10 /* Mic Bias2 */ + +/* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */ +#define CPCAP_BIT_PTT_S 11 /* Push To Talk */ + /* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */ #define CPCAP_BIT_AUDIO_LOW_PWR 6 #define CPCAP_BIT_AUD_LOWPWR_SPEED 5 @@ -260,6 +270,10 @@ struct cpcap_audio { int codec_clk_id; int codec_freq; int codec_format; + struct regulator *vaudio; + int hsirq; + int mb2irq; + struct snd_soc_jack jack; }; static int cpcap_st_workaround(struct snd_soc_dapm_widget *w, @@ -1626,17 +1640,123 @@ static int cpcap_audio_reset(struct snd_soc_component *component, return 0; } +static irqreturn_t cpcap_hs_irq_thread(int irq, void *data) +{ + struct snd_soc_component *component = data; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cpcap->regmap; + int status = 0; + int mask = SND_JACK_HEADSET; + int val; + + if (!regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_HS_S))) { + val = BIT(CPCAP_BIT_MB_ON2) | BIT(CPCAP_BIT_PTT_CMP_EN); + regmap_update_bits(regmap, CPCAP_REG_TXI, val, val); + + val = BIT(CPCAP_BIT_ST_HS_CP_EN); + regmap_update_bits(regmap, CPCAP_REG_RXOA, val, val); + + regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_NORMAL); + + /* Give PTTS time to settle */ + msleep(20); + + if (!regmap_test_bits(regmap, CPCAP_REG_INTS2, + BIT(CPCAP_BIT_PTT_S))) { + /* Headphones detected. (May also be a headset with the + * MFB pressed.) + */ + status = SND_JACK_HEADPHONE; + dev_info(component->dev, "HP plugged in\n"); + } else if (regmap_test_bits(regmap, CPCAP_REG_INTS1, + BIT(CPCAP_BIT_MB2_S)) == 1) { + status = SND_JACK_HEADSET; + dev_info(component->dev, "HS plugged in\n"); + } else + dev_info(component->dev, "Unsupported HS plugged in\n"); + } else { + bool mic = cpcap->jack.status & SND_JACK_MICROPHONE; + + dev_info(component->dev, "H%s disconnect\n", mic ? "S" : "P"); + val = BIT(CPCAP_BIT_MB_ON2) | BIT(CPCAP_BIT_PTT_CMP_EN); + regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, val, 0); + + val = BIT(CPCAP_BIT_ST_HS_CP_EN); + regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, val, 0); + + regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_STANDBY); + + mask |= SND_JACK_BTN_0; + } + + snd_soc_jack_report(&cpcap->jack, status, mask); + + return IRQ_HANDLED; +} + +static irqreturn_t cpcap_mb2_irq_thread(int irq, void *data) +{ + struct snd_soc_component *component = data; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + struct regmap *regmap = cpcap->regmap; + int status = 0; + int mb2; + int ptt; + + if (regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_HS_S)) == 1) + return IRQ_HANDLED; + + mb2 = regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_MB2_S)); + ptt = regmap_test_bits(regmap, CPCAP_REG_INTS2, BIT(CPCAP_BIT_PTT_S)); + + /* Initial detection might have been with MFB pressed */ + if (!(cpcap->jack.status & SND_JACK_MICROPHONE)) { + if (ptt == 1 && mb2 == 1) { + dev_info(component->dev, "MIC plugged in\n"); + snd_soc_jack_report(&cpcap->jack, SND_JACK_MICROPHONE, + SND_JACK_MICROPHONE); + } + + return IRQ_HANDLED; + } + + if (!mb2 || !ptt) + status = SND_JACK_BTN_0; + + snd_soc_jack_report(&cpcap->jack, status, SND_JACK_BTN_0); + + return IRQ_HANDLED; +} + static int cpcap_soc_probe(struct snd_soc_component *component) { + struct platform_device *pdev = to_platform_device(component->dev); + struct snd_soc_card *card = component->card; struct cpcap_audio *cpcap; int err; cpcap = devm_kzalloc(component->dev, sizeof(*cpcap), GFP_KERNEL); if (!cpcap) return -ENOMEM; + snd_soc_component_set_drvdata(component, cpcap); cpcap->component = component; + cpcap->vaudio = devm_regulator_get(component->dev, "VAUDIO"); + if (IS_ERR(cpcap->vaudio)) + return dev_err_probe(component->dev, PTR_ERR(cpcap->vaudio), + "Cannot get VAUDIO regulator\n"); + + err = snd_soc_card_jack_new(card, "Headphones", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &cpcap->jack); + if (err < 0) { + dev_err(component->dev, "Cannot create HS jack: %i\n", err); + return err; + } + + snd_jack_set_key(cpcap->jack.jack, SND_JACK_BTN_0, KEY_MEDIA); + cpcap->regmap = dev_get_regmap(component->dev->parent, NULL); if (!cpcap->regmap) return -ENODEV; @@ -1646,17 +1766,95 @@ static int cpcap_soc_probe(struct snd_soc_component *component) if (err) return err; - return cpcap_audio_reset(component, false); + cpcap->hsirq = platform_get_irq_byname(pdev, "hs"); + if (cpcap->hsirq < 0) + return cpcap->hsirq; + + err = devm_request_threaded_irq(component->dev, cpcap->hsirq, NULL, + cpcap_hs_irq_thread, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "cpcap-codec-hs", + component); + if (err) { + dev_warn(component->dev, "no HS irq%i: %i\n", + cpcap->hsirq, err); + return err; + } + + cpcap->mb2irq = platform_get_irq_byname(pdev, "mb2"); + if (cpcap->mb2irq < 0) + return cpcap->mb2irq; + + err = devm_request_threaded_irq(component->dev, cpcap->mb2irq, NULL, + cpcap_mb2_irq_thread, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "cpcap-codec-mb2", + component); + if (err) { + dev_warn(component->dev, "no MB2 irq%i: %i\n", + cpcap->mb2irq, err); + return err; + } + + err = cpcap_audio_reset(component, false); + if (err) + return err; + + cpcap_hs_irq_thread(cpcap->hsirq, component); + + enable_irq_wake(cpcap->hsirq); + enable_irq_wake(cpcap->mb2irq); + + return 0; +} + +static void cpcap_soc_remove(struct snd_soc_component *component) +{ + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + + disable_irq_wake(cpcap->hsirq); + disable_irq_wake(cpcap->mb2irq); +} + +static int cpcap_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + + /* VAIDIO should be kept in normal mode in order MIC/PTT to work */ + if (cpcap->jack.status & SND_JACK_MICROPHONE) + return 0; + + switch (level) { + case SND_SOC_BIAS_OFF: + break; + case SND_SOC_BIAS_PREPARE: + regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_NORMAL); + break; + case SND_SOC_BIAS_STANDBY: + regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_STANDBY); + break; + case SND_SOC_BIAS_ON: + break; + } + + return 0; } static const struct snd_soc_component_driver soc_codec_dev_cpcap = { .probe = cpcap_soc_probe, + .remove = cpcap_soc_remove, .controls = cpcap_snd_controls, .num_controls = ARRAY_SIZE(cpcap_snd_controls), .dapm_widgets = cpcap_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cpcap_dapm_widgets), .dapm_routes = intercon, .num_dapm_routes = ARRAY_SIZE(intercon), + .set_bias_level = cpcap_set_bias_level, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 11e7b3f6d410..571222ec520c 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -21,6 +21,7 @@ #include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> +#include <linux/string_choices.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -657,7 +658,7 @@ static int wov_enable_put(struct snd_kcontrol *kcontrol, (uint8_t *)&p, sizeof(p), NULL, 0); if (ret) { dev_err(priv->dev, "failed to %s wov\n", - enabled ? "enable" : "disable"); + str_enable_disable(enabled)); return ret; } diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 96555263e10b..0bb4bdb3deec 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -153,12 +153,12 @@ static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct snd_soc_component *component = codec_dai->component; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: snd_soc_component_update_bits(component, CS35L32_ADSP_CTL, CS35L32_ADSP_MASTER_MASK, CS35L32_ADSP_MASTER_MASK); break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: snd_soc_component_update_bits(component, CS35L32_ADSP_CTL, CS35L32_ADSP_MASTER_MASK, 0); break; @@ -504,7 +504,6 @@ static void cs35l32_i2c_remove(struct i2c_client *i2c_client) gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); } -#ifdef CONFIG_PM static int cs35l32_runtime_suspend(struct device *dev) { struct cs35l32_private *cs35l32 = dev_get_drvdata(dev); @@ -543,11 +542,9 @@ static int cs35l32_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops cs35l32_runtime_pm = { - SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume, - NULL) + RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume, NULL) }; static const struct of_device_id cs35l32_of_match[] = { @@ -567,7 +564,7 @@ MODULE_DEVICE_TABLE(i2c, cs35l32_id); static struct i2c_driver cs35l32_i2c_driver = { .driver = { .name = "cs35l32", - .pm = &cs35l32_runtime_pm, + .pm = pm_ptr(&cs35l32_runtime_pm), .of_match_table = cs35l32_of_match, }, .id_table = cs35l32_id, diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c index b03aab147530..c927592f90c9 100644 --- a/sound/soc/codecs/cs35l33.c +++ b/sound/soc/codecs/cs35l33.c @@ -438,12 +438,12 @@ static int cs35l33_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct cs35l33_private *priv = snd_soc_component_get_drvdata(component); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL, CS35L33_MS_MASK, CS35L33_MS_MASK); dev_dbg(component->dev, "Audio port in master mode\n"); break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL, CS35L33_MS_MASK, 0); dev_dbg(component->dev, "Audio port in slave mode\n"); @@ -853,7 +853,7 @@ static const struct regmap_config cs35l33_regmap = { .use_single_write = true, }; -static int __maybe_unused cs35l33_runtime_resume(struct device *dev) +static int cs35l33_runtime_resume(struct device *dev) { struct cs35l33_private *cs35l33 = dev_get_drvdata(dev); int ret; @@ -891,7 +891,7 @@ err: return ret; } -static int __maybe_unused cs35l33_runtime_suspend(struct device *dev) +static int cs35l33_runtime_suspend(struct device *dev) { struct cs35l33_private *cs35l33 = dev_get_drvdata(dev); @@ -909,9 +909,7 @@ static int __maybe_unused cs35l33_runtime_suspend(struct device *dev) } static const struct dev_pm_ops cs35l33_pm_ops = { - SET_RUNTIME_PM_OPS(cs35l33_runtime_suspend, - cs35l33_runtime_resume, - NULL) + RUNTIME_PM_OPS(cs35l33_runtime_suspend, cs35l33_runtime_resume, NULL) }; static int cs35l33_get_hg_data(const struct device_node *np, @@ -1273,7 +1271,7 @@ MODULE_DEVICE_TABLE(i2c, cs35l33_id); static struct i2c_driver cs35l33_i2c_driver = { .driver = { .name = "cs35l33", - .pm = &cs35l33_pm_ops, + .pm = pm_ptr(&cs35l33_pm_ops), .of_match_table = cs35l33_of_match, }, diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index 287b27476a10..a5a8075598ff 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -523,11 +523,11 @@ static int cs35l34_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct cs35l34_private *priv = snd_soc_component_get_drvdata(component); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL, 0x80, 0x80); break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL, 0x80, 0x00); break; @@ -1116,7 +1116,7 @@ static void cs35l34_i2c_remove(struct i2c_client *client) cs35l34->core_supplies); } -static int __maybe_unused cs35l34_runtime_resume(struct device *dev) +static int cs35l34_runtime_resume(struct device *dev) { struct cs35l34_private *cs35l34 = dev_get_drvdata(dev); int ret; @@ -1149,7 +1149,7 @@ err: return ret; } -static int __maybe_unused cs35l34_runtime_suspend(struct device *dev) +static int cs35l34_runtime_suspend(struct device *dev) { struct cs35l34_private *cs35l34 = dev_get_drvdata(dev); @@ -1165,9 +1165,7 @@ static int __maybe_unused cs35l34_runtime_suspend(struct device *dev) } static const struct dev_pm_ops cs35l34_pm_ops = { - SET_RUNTIME_PM_OPS(cs35l34_runtime_suspend, - cs35l34_runtime_resume, - NULL) + RUNTIME_PM_OPS(cs35l34_runtime_suspend, cs35l34_runtime_resume, NULL) }; static const struct of_device_id cs35l34_of_match[] = { @@ -1185,7 +1183,7 @@ MODULE_DEVICE_TABLE(i2c, cs35l34_id); static struct i2c_driver cs35l34_i2c_driver = { .driver = { .name = "cs35l34", - .pm = &cs35l34_pm_ops, + .pm = pm_ptr(&cs35l34_pm_ops), .of_match_table = cs35l34_of_match, }, diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c index a6db44520c06..f9b6bf7bea9c 100644 --- a/sound/soc/codecs/cs35l41-spi.c +++ b/sound/soc/codecs/cs35l41-spi.c @@ -32,13 +32,16 @@ static int cs35l41_spi_probe(struct spi_device *spi) const struct regmap_config *regmap_config = &cs35l41_regmap_spi; struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(&spi->dev); struct cs35l41_private *cs35l41; + int ret; cs35l41 = devm_kzalloc(&spi->dev, sizeof(struct cs35l41_private), GFP_KERNEL); if (!cs35l41) return -ENOMEM; spi->max_speed_hz = CS35L41_SPI_MAX_FREQ; - spi_setup(spi); + ret = spi_setup(spi); + if (ret < 0) + return ret; spi_set_drvdata(spi, cs35l41); cs35l41->regmap = devm_regmap_init_spi(spi, regmap_config); diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index 07a5cab35fe1..ff4134bee858 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1148,21 +1148,31 @@ err_dsp: return ret; } +#ifdef CONFIG_ACPI static int cs35l41_acpi_get_name(struct cs35l41_private *cs35l41) { - acpi_handle handle = ACPI_HANDLE(cs35l41->dev); + struct acpi_device *adev = ACPI_COMPANION(cs35l41->dev); + acpi_handle handle = acpi_device_handle(adev); + const char *hid; const char *sub; - /* If there is no ACPI_HANDLE, there is no ACPI for this system, return 0 */ - if (!handle) + /* If there is no acpi_device, there is no ACPI for this system, return 0 */ + if (!adev) return 0; sub = acpi_get_subsystem_id(handle); if (IS_ERR(sub)) { - /* If bad ACPI, return 0 and fallback to legacy firmware path, otherwise fail */ - if (PTR_ERR(sub) == -ENODATA) - return 0; - else + /* If no _SUB, fallback to _HID, otherwise fail */ + if (PTR_ERR(sub) == -ENODATA) { + hid = acpi_device_hid(adev); + /* If dummy hid, return 0 and fallback to legacy firmware path */ + if (!strcmp(hid, "device")) + return 0; + sub = kstrdup(hid, GFP_KERNEL); + if (!sub) + sub = ERR_PTR(-ENOMEM); + + } else return PTR_ERR(sub); } @@ -1171,6 +1181,12 @@ static int cs35l41_acpi_get_name(struct cs35l41_private *cs35l41) return 0; } +#else +static int cs35l41_acpi_get_name(struct cs35l41_private *cs35l41) +{ + return 0; +} +#endif /* CONFIG_ACPI */ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg) { diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index e0ed4fc11155..e28bfefa72f3 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -10,6 +10,7 @@ #include <linux/gpio/consumer.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> #include <linux/types.h> #include <sound/cs-amp-lib.h> @@ -303,6 +304,79 @@ void cs35l56_wait_min_reset_pulse(void) } EXPORT_SYMBOL_NS_GPL(cs35l56_wait_min_reset_pulse, "SND_SOC_CS35L56_SHARED"); +static const struct { + u32 addr; + u32 value; +} cs35l56_spi_system_reset_stages[] = { + { .addr = CS35L56_DSP_VIRTUAL1_MBOX_1, .value = CS35L56_MBOX_CMD_SYSTEM_RESET }, + /* The next write is necessary to delimit the soft reset */ + { .addr = CS35L56_DSP_MBOX_1_RAW, .value = CS35L56_MBOX_CMD_PING }, +}; + +static void cs35l56_spi_issue_bus_locked_reset(struct cs35l56_base *cs35l56_base, + struct spi_device *spi) +{ + struct cs35l56_spi_payload *buf = cs35l56_base->spi_payload_buf; + struct spi_transfer t = { + .tx_buf = buf, + .len = sizeof(*buf), + }; + struct spi_message m; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(cs35l56_spi_system_reset_stages); i++) { + buf->addr = cpu_to_be32(cs35l56_spi_system_reset_stages[i].addr); + buf->value = cpu_to_be32(cs35l56_spi_system_reset_stages[i].value); + spi_message_init_with_transfers(&m, &t, 1); + ret = spi_sync_locked(spi, &m); + if (ret) + dev_warn(cs35l56_base->dev, "spi_sync failed: %d\n", ret); + + usleep_range(CS35L56_SPI_RESET_TO_PORT_READY_US, + 2 * CS35L56_SPI_RESET_TO_PORT_READY_US); + } +} + +static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base) +{ + struct spi_device *spi = to_spi_device(cs35l56_base->dev); + unsigned int val; + int read_ret, ret; + + /* + * There must not be any other SPI bus activity while the amp is + * soft-resetting. + */ + ret = spi_bus_lock(spi->controller); + if (ret) { + dev_warn(cs35l56_base->dev, "spi_bus_lock failed: %d\n", ret); + return; + } + + cs35l56_spi_issue_bus_locked_reset(cs35l56_base, spi); + spi_bus_unlock(spi->controller); + + /* + * Check firmware boot by testing for a response in MBOX_2. + * HALO_STATE cannot be trusted yet because the reset sequence + * can leave it with stale state. But MBOX is reset. + * The regmap must remain in cache-only until the chip has + * booted, so use a bypassed read. + */ + ret = read_poll_timeout(regmap_read_bypassed, read_ret, + (val > 0) && (val < 0xffffffff), + CS35L56_HALO_STATE_POLL_US, + CS35L56_HALO_STATE_TIMEOUT_US, + false, + cs35l56_base->regmap, + CS35L56_DSP_VIRTUAL1_MBOX_2, + &val); + if (ret) { + dev_err(cs35l56_base->dev, "SPI reboot timed out(%d): MBOX2=%#x\n", + read_ret, val); + } +} + static const struct reg_sequence cs35l56_system_reset_seq[] = { REG_SEQ0(CS35L56_DSP1_HALO_STATE, 0), REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET), @@ -315,6 +389,12 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) * accesses other than the controlled system reset sequence below. */ regcache_cache_only(cs35l56_base->regmap, true); + + if (cs35l56_is_spi(cs35l56_base)) { + cs35l56_spi_system_reset(cs35l56_base); + return; + } + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, cs35l56_system_reset_seq, ARRAY_SIZE(cs35l56_system_reset_seq)); diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c index c101134e8532..ca6c03a8766d 100644 --- a/sound/soc/codecs/cs35l56-spi.c +++ b/sound/soc/codecs/cs35l56-spi.c @@ -33,6 +33,9 @@ static int cs35l56_spi_probe(struct spi_device *spi) cs35l56->base.dev = &spi->dev; cs35l56->base.can_hibernate = true; + ret = cs35l56_init_config_for_spi(&cs35l56->base, spi); + if (ret) + return ret; ret = cs35l56_common_probe(cs35l56); if (ret != 0) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index ae045c88c48d..b3158a84b87a 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -646,6 +646,12 @@ static struct snd_soc_dai_driver cs35l56_dai[] = { .rates = CS35L56_RATES, .formats = CS35L56_RX_FORMATS, }, + .symmetric_rate = 1, + .ops = &cs35l56_sdw_dai_ops, + }, + { + .name = "cs35l56-sdw1c", + .id = 2, .capture = { .stream_name = "SDW1 Capture", .channels_min = 1, @@ -655,7 +661,7 @@ static struct snd_soc_dai_driver cs35l56_dai[] = { }, .symmetric_rate = 1, .ops = &cs35l56_sdw_dai_ops, - } + }, }; static int cs35l56_write_cal(struct cs35l56_private *cs35l56) @@ -1435,7 +1441,6 @@ void cs35l56_remove(struct cs35l56_private *cs35l56) if (cs35l56->base.irq) devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base); - flush_workqueue(cs35l56->dsp_wq); destroy_workqueue(cs35l56->dsp_wq); pm_runtime_dont_use_autosuspend(cs35l56->base.dev); diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c index 69287ba7e955..dda7f5b4f2fb 100644 --- a/sound/soc/codecs/cs4234.c +++ b/sound/soc/codecs/cs4234.c @@ -307,9 +307,9 @@ static int cs4234_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int format } switch (format & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: if (cs4234->format == SND_SOC_DAIFMT_DSP_A) { dev_err(component->dev, "Unsupported DSP A format in master mode\n"); return -EINVAL; @@ -860,7 +860,7 @@ static void cs4234_i2c_remove(struct i2c_client *i2c_client) cs4234_shutdown(cs4234); } -static int __maybe_unused cs4234_runtime_resume(struct device *dev) +static int cs4234_runtime_resume(struct device *dev) { struct cs4234 *cs4234 = dev_get_drvdata(dev); int ret; @@ -881,7 +881,7 @@ static int __maybe_unused cs4234_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused cs4234_runtime_suspend(struct device *dev) +static int cs4234_runtime_suspend(struct device *dev) { struct cs4234 *cs4234 = dev_get_drvdata(dev); @@ -891,7 +891,7 @@ static int __maybe_unused cs4234_runtime_suspend(struct device *dev) } static const struct dev_pm_ops cs4234_pm = { - SET_RUNTIME_PM_OPS(cs4234_runtime_suspend, cs4234_runtime_resume, NULL) + RUNTIME_PM_OPS(cs4234_runtime_suspend, cs4234_runtime_resume, NULL) }; static const struct of_device_id cs4234_of_match[] = { @@ -903,7 +903,7 @@ MODULE_DEVICE_TABLE(of, cs4234_of_match); static struct i2c_driver cs4234_i2c_driver = { .driver = { .name = "cs4234", - .pm = &cs4234_pm, + .pm = pm_ptr(&cs4234_pm), .of_match_table = cs4234_of_match, }, .probe = cs4234_i2c_probe, diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 78ffb7fa7fc5..3f759c13d6d1 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -344,12 +344,12 @@ static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) u8 iface = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: snd_soc_component_update_bits(component, CS4265_ADC_CTL, CS4265_ADC_MASTER, CS4265_ADC_MASTER); break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: snd_soc_component_update_bits(component, CS4265_ADC_CTL, CS4265_ADC_MASTER, 0); diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 67e92bfecb56..9f9dc8b017a3 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -287,10 +287,10 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (format & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: cs4270->slave_mode = 1; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: cs4270->slave_mode = 0; break; default: diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index e864188ae5eb..6a3cca3d26c7 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -209,10 +209,10 @@ static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai, int ret; switch (format & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: cs4271->master = false; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: cs4271->master = true; val |= CS4271_MODE1_MASTER; break; diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c index 8a1d5c7a61d7..98b6718ccabf 100644 --- a/sound/soc/codecs/cs42l42-i2c.c +++ b/sound/soc/codecs/cs42l42-i2c.c @@ -48,7 +48,7 @@ static void cs42l42_i2c_remove(struct i2c_client *i2c_client) cs42l42_common_remove(cs42l42); } -static int __maybe_unused cs42l42_i2c_resume(struct device *dev) +static int cs42l42_i2c_resume(struct device *dev) { int ret; @@ -62,7 +62,7 @@ static int __maybe_unused cs42l42_i2c_resume(struct device *dev) } static const struct dev_pm_ops cs42l42_i2c_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_i2c_resume) + SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_i2c_resume) }; static const struct of_device_id __maybe_unused cs42l42_of_match[] = { @@ -87,7 +87,7 @@ MODULE_DEVICE_TABLE(i2c, cs42l42_id); static struct i2c_driver cs42l42_i2c_driver = { .driver = { .name = "cs42l42", - .pm = &cs42l42_i2c_pm_ops, + .pm = pm_ptr(&cs42l42_i2c_pm_ops), .of_match_table = of_match_ptr(cs42l42_of_match), .acpi_match_table = ACPI_PTR(cs42l42_acpi_match), }, diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c index ae1401b250a3..f837c7eff10b 100644 --- a/sound/soc/codecs/cs42l42-sdw.c +++ b/sound/soc/codecs/cs42l42-sdw.c @@ -411,7 +411,7 @@ static const struct sdw_slave_ops cs42l42_sdw_ops = { .port_prep = cs42l42_sdw_port_prep, }; -static int __maybe_unused cs42l42_sdw_runtime_suspend(struct device *dev) +static int cs42l42_sdw_runtime_suspend(struct device *dev) { struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); @@ -426,11 +426,11 @@ static int __maybe_unused cs42l42_sdw_runtime_suspend(struct device *dev) return 0; } -static const struct reg_sequence __maybe_unused cs42l42_soft_reboot_seq[] = { +static const struct reg_sequence cs42l42_soft_reboot_seq[] = { REG_SEQ0(CS42L42_SOFT_RESET_REBOOT, 0x1e), }; -static int __maybe_unused cs42l42_sdw_handle_unattach(struct cs42l42_private *cs42l42) +static int cs42l42_sdw_handle_unattach(struct cs42l42_private *cs42l42) { struct sdw_slave *peripheral = cs42l42->sdw_peripheral; @@ -460,7 +460,7 @@ static int __maybe_unused cs42l42_sdw_handle_unattach(struct cs42l42_private *cs return 0; } -static int __maybe_unused cs42l42_sdw_runtime_resume(struct device *dev) +static int cs42l42_sdw_runtime_resume(struct device *dev) { static const unsigned int ts_dbnce_ms[] = { 0, 125, 250, 500, 750, 1000, 1250, 1500}; struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); @@ -491,7 +491,7 @@ static int __maybe_unused cs42l42_sdw_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused cs42l42_sdw_resume(struct device *dev) +static int cs42l42_sdw_resume(struct device *dev) { struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); int ret; @@ -596,8 +596,8 @@ static int cs42l42_sdw_remove(struct sdw_slave *peripheral) } static const struct dev_pm_ops cs42l42_sdw_pm = { - SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_sdw_resume) - SET_RUNTIME_PM_OPS(cs42l42_sdw_runtime_suspend, cs42l42_sdw_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_sdw_resume) + RUNTIME_PM_OPS(cs42l42_sdw_runtime_suspend, cs42l42_sdw_runtime_resume, NULL) }; static const struct sdw_device_id cs42l42_sdw_id[] = { @@ -609,7 +609,7 @@ MODULE_DEVICE_TABLE(sdw, cs42l42_sdw_id); static struct sdw_driver cs42l42_sdw_driver = { .driver = { .name = "cs42l42-sdw", - .pm = &cs42l42_sdw_pm, + .pm = pm_ptr(&cs42l42_sdw_pm), }, .probe = cs42l42_sdw_probe, .remove = cs42l42_sdw_remove, diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 501c951cc327..56668c392063 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -830,11 +830,11 @@ static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) u32 asp_cfg_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: asp_cfg_val |= CS42L42_ASP_MASTER_MODE << CS42L42_ASP_MODE_SHIFT; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: asp_cfg_val |= CS42L42_ASP_SLAVE_MODE << CS42L42_ASP_MODE_SHIFT; break; diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index d9ab003e166b..ac19a572fe70 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -167,7 +167,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, autocontrol |= 0x3 << CS42L43_JACKDET_MODE_SHIFT; ret = cs42l43_find_index(priv, "cirrus,tip-fall-db-ms", 500, - NULL, cs42l43_accdet_db_ms, + &priv->tip_fall_db_ms, cs42l43_accdet_db_ms, ARRAY_SIZE(cs42l43_accdet_db_ms)); if (ret < 0) goto error; @@ -175,7 +175,7 @@ int cs42l43_set_jack(struct snd_soc_component *component, tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; ret = cs42l43_find_index(priv, "cirrus,tip-rise-db-ms", 500, - NULL, cs42l43_accdet_db_ms, + &priv->tip_rise_db_ms, cs42l43_accdet_db_ms, ARRAY_SIZE(cs42l43_accdet_db_ms)); if (ret < 0) goto error; @@ -764,6 +764,8 @@ void cs42l43_tip_sense_work(struct work_struct *work) error: mutex_unlock(&priv->jack_lock); + priv->suspend_jack_debounce = false; + pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); } @@ -771,14 +773,19 @@ error: irqreturn_t cs42l43_tip_sense(int irq, void *data) { struct cs42l43_codec *priv = data; + unsigned int db_delay = priv->tip_debounce_ms; cancel_delayed_work(&priv->bias_sense_timeout); cancel_delayed_work(&priv->tip_sense_work); cancel_delayed_work(&priv->button_press_work); cancel_work(&priv->button_release_work); + // Ensure delay after suspend is long enough to avoid false detection + if (priv->suspend_jack_debounce) + db_delay += priv->tip_fall_db_ms + priv->tip_rise_db_ms; + queue_delayed_work(system_long_wq, &priv->tip_sense_work, - msecs_to_jiffies(priv->tip_debounce_ms)); + msecs_to_jiffies(db_delay)); return IRQ_HANDLED; } diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 83c21c17fb80..ea84ac64c775 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -12,7 +12,7 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/errno.h> -#include <linux/find.h> +#include <linux/bitmap.h> #include <linux/gcd.h> #include <linux/irq.h> #include <linux/irqdomain.h> @@ -1146,7 +1146,7 @@ static const struct snd_kcontrol_new cs42l43_controls[] = { SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L43_ADC_B_CTRL1, CS42L43_ADC_B_CTRL2, CS42L43_ADC_PGA_GAIN_SHIFT, - 0xF, 5, cs42l43_adc_tlv), + 0xF, 4, cs42l43_adc_tlv), SOC_DOUBLE("PDM1 Invert Switch", CS42L43_DMIC_PDM_CTRL, CS42L43_PDM1L_INV_SHIFT, CS42L43_PDM1R_INV_SHIFT, 1, 0), @@ -2402,9 +2402,22 @@ static int cs42l43_codec_runtime_resume(struct device *dev) return 0; } +static int cs42l43_codec_runtime_force_suspend(struct device *dev) +{ + struct cs42l43_codec *priv = dev_get_drvdata(dev); + + dev_dbg(priv->dev, "Runtime suspend\n"); + + priv->suspend_jack_debounce = true; + + pm_runtime_force_suspend(dev); + + return 0; +} + static const struct dev_pm_ops cs42l43_codec_pm_ops = { RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SYSTEM_SLEEP_PM_OPS(cs42l43_codec_runtime_force_suspend, pm_runtime_force_resume) }; static const struct platform_device_id cs42l43_codec_id_table[] = { diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h index 9c144e129535..1cd9d8a71c43 100644 --- a/sound/soc/codecs/cs42l43.h +++ b/sound/soc/codecs/cs42l43.h @@ -78,6 +78,8 @@ struct cs42l43_codec { bool use_ring_sense; unsigned int tip_debounce_ms; + unsigned int tip_fall_db_ms; + unsigned int tip_rise_db_ms; unsigned int bias_low; unsigned int bias_sense_ua; unsigned int bias_ramp_ms; @@ -95,6 +97,7 @@ struct cs42l43_codec { bool button_detect_running; bool jack_present; int jack_override; + bool suspend_jack_debounce; struct work_struct hp_ilimit_work; struct delayed_work hp_ilimit_clear_work; diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index e7cc50096297..ba7e237619f2 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -13,9 +13,9 @@ #include "cs42l51.h" -static struct i2c_device_id cs42l51_i2c_id[] = { - {"cs42l51"}, - {} +static const struct i2c_device_id cs42l51_i2c_id[] = { + { "cs42l51" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id); @@ -40,7 +40,7 @@ static void cs42l51_i2c_remove(struct i2c_client *i2c) } static const struct dev_pm_ops cs42l51_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume) + SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume) }; static struct i2c_driver cs42l51_i2c_driver = { diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 6e51954bdb1e..8083a339dc7b 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -322,10 +322,10 @@ static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai, } switch (format & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: cs42l51->func = MODE_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: cs42l51->func = MODE_SLAVE_AUTO; break; default: @@ -805,7 +805,7 @@ void cs42l51_remove(struct device *dev) } EXPORT_SYMBOL_GPL(cs42l51_remove); -int __maybe_unused cs42l51_suspend(struct device *dev) +int cs42l51_suspend(struct device *dev) { struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); @@ -816,7 +816,7 @@ int __maybe_unused cs42l51_suspend(struct device *dev) } EXPORT_SYMBOL_GPL(cs42l51_suspend); -int __maybe_unused cs42l51_resume(struct device *dev) +int cs42l51_resume(struct device *dev) { struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 7128d4c62f50..cd3f92c19045 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -733,10 +733,10 @@ static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) u8 iface = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: iface = CS42L52_IFACE_CTL1_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: iface = CS42L52_IFACE_CTL1_SLAVE; break; default: diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index aaa10c459b52..aaf90c8b7339 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -757,10 +757,10 @@ static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: cs42l56->iface = CS42L56_MASTER_MODE; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: cs42l56->iface = CS42L56_SLAVE_MODE; break; default: diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 21ba796a5cd9..ddf36001100e 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -943,11 +943,11 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) mmcc = snd_soc_component_read(component, CS42L73_MMCC(id)); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: mmcc |= CS42L73_MS_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: mmcc &= ~CS42L73_MS_MASTER; break; diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c index 42c3e1efdc08..53a7fe1ab3dd 100644 --- a/sound/soc/codecs/cs42l83-i2c.c +++ b/sound/soc/codecs/cs42l83-i2c.c @@ -199,7 +199,7 @@ static void cs42l83_i2c_remove(struct i2c_client *i2c_client) cs42l42_common_remove(cs42l83); } -static int __maybe_unused cs42l83_i2c_resume(struct device *dev) +static int cs42l83_i2c_resume(struct device *dev) { int ret; @@ -213,7 +213,7 @@ static int __maybe_unused cs42l83_i2c_resume(struct device *dev) } static const struct dev_pm_ops cs42l83_i2c_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l83_i2c_resume) + SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l83_i2c_resume) }; static const struct of_device_id __maybe_unused cs42l83_of_match[] = { @@ -225,7 +225,7 @@ MODULE_DEVICE_TABLE(of, cs42l83_of_match); static struct i2c_driver cs42l83_i2c_driver = { .driver = { .name = "cs42l83", - .pm = &cs42l83_i2c_pm_ops, + .pm = pm_ptr(&cs42l83_i2c_pm_ops), .of_match_table = of_match_ptr(cs42l83_of_match), }, .probe = cs42l83_i2c_probe, diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index 17d5c96e334d..88cf3c03986e 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -1087,7 +1087,7 @@ static const struct of_device_id cs42l84_of_match[] = { MODULE_DEVICE_TABLE(of, cs42l84_of_match); static const struct i2c_device_id cs42l84_id[] = { - {"cs42l84", 0}, + { "cs42l84" }, {} }; MODULE_DEVICE_TABLE(i2c, cs42l84_id); diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c index ecaebf8e1c8f..039b3ecb3b9b 100644 --- a/sound/soc/codecs/cs42xx8-i2c.c +++ b/sound/soc/codecs/cs42xx8-i2c.c @@ -61,7 +61,7 @@ MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id); static struct i2c_driver cs42xx8_i2c_driver = { .driver = { .name = "cs42xx8", - .pm = &cs42xx8_pm, + .pm = pm_ptr(&cs42xx8_pm), .of_match_table = cs42xx8_of_match, }, .probe = cs42xx8_i2c_probe, diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index 9c44b6283b8f..6a925f3f7137 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -242,10 +242,10 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, /* Set master/slave audio interface */ switch (format & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: cs42xx8->slave_mode = true; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: cs42xx8->slave_mode = false; break; default: @@ -606,7 +606,6 @@ err_enable: } EXPORT_SYMBOL_GPL(cs42xx8_probe); -#ifdef CONFIG_PM static int cs42xx8_runtime_resume(struct device *dev) { struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev); @@ -665,14 +664,11 @@ static int cs42xx8_runtime_suspend(struct device *dev) return 0; } -#endif -const struct dev_pm_ops cs42xx8_pm = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL) +EXPORT_GPL_DEV_PM_OPS(cs42xx8_pm) = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL) }; -EXPORT_SYMBOL_GPL(cs42xx8_pm); MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver"); MODULE_AUTHOR("Freescale Semiconductor, Inc."); diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index f8e2fb69ada2..d9b3d73c8388 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -640,10 +640,10 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk, } switch (cs43130->dais[dai_id].dai_mode) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: dai_mode_val = 0; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: dai_mode_val = 1; break; default: @@ -851,7 +851,7 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM) + if (cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP) regmap_update_bits(cs43130->regmap, CS43130_DSD_INT_CFG, CS43130_DSD_MASTER, CS43130_DSD_MASTER); else @@ -951,7 +951,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, break; } - if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM) + if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP) /* Calculate SCLK in master mode if unassigned */ sclk = params_rate(params) * bitwidth_dai * params_channels(params); @@ -1516,11 +1516,11 @@ static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBS_CFS; + case SND_SOC_DAIFMT_CBC_CFC: + cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBC_CFC; break; - case SND_SOC_DAIFMT_CBM_CFM: - cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM; + case SND_SOC_DAIFMT_CBP_CFP: + cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBP_CFP; break; default: dev_err(cs43130->dev, "unsupported mode\n"); @@ -1579,11 +1579,11 @@ static int cs43130_dsd_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBS_CFS; + case SND_SOC_DAIFMT_CBC_CFC: + cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBC_CFC; break; - case SND_SOC_DAIFMT_CBM_CFM: - cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM; + case SND_SOC_DAIFMT_CBP_CFP: + cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBP_CFP; break; default: dev_err(cs43130->dev, "Unsupported DAI format.\n"); @@ -2672,7 +2672,7 @@ static void cs43130_i2c_remove(struct i2c_client *client) regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies); } -static int __maybe_unused cs43130_runtime_suspend(struct device *dev) +static int cs43130_runtime_suspend(struct device *dev) { struct cs43130_private *cs43130 = dev_get_drvdata(dev); @@ -2691,7 +2691,7 @@ static int __maybe_unused cs43130_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused cs43130_runtime_resume(struct device *dev) +static int cs43130_runtime_resume(struct device *dev) { struct cs43130_private *cs43130 = dev_get_drvdata(dev); int ret; @@ -2727,8 +2727,7 @@ err: } static const struct dev_pm_ops cs43130_runtime_pm = { - SET_RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume, - NULL) + RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume, NULL) }; #if IS_ENABLED(CONFIG_OF) @@ -2768,7 +2767,7 @@ static struct i2c_driver cs43130_i2c_driver = { .name = "cs43130", .of_match_table = of_match_ptr(cs43130_of_match), .acpi_match_table = ACPI_PTR(cs43130_acpi_match), - .pm = &cs43130_runtime_pm, + .pm = pm_ptr(&cs43130_runtime_pm), }, .id_table = cs43130_i2c_id, .probe = cs43130_i2c_probe, diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c index d87aae31c516..b726e22ef57d 100644 --- a/sound/soc/codecs/cs4341.c +++ b/sound/soc/codecs/cs4341.c @@ -49,7 +49,7 @@ static int cs4341_set_fmt(struct snd_soc_dai *dai, unsigned int format) struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component); switch (format & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index a134ca722892..d9a9c34fffe3 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -312,7 +312,6 @@ static void cs4349_i2c_remove(struct i2c_client *client) gpiod_set_value_cansleep(cs4349->reset_gpio, 0); } -#ifdef CONFIG_PM static int cs4349_runtime_suspend(struct device *dev) { struct cs4349_private *cs4349 = dev_get_drvdata(dev); @@ -346,11 +345,9 @@ static int cs4349_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops cs4349_runtime_pm = { - SET_RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume, - NULL) + RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume, NULL) }; static const struct of_device_id cs4349_of_match[] = { @@ -371,7 +368,7 @@ static struct i2c_driver cs4349_i2c_driver = { .driver = { .name = "cs4349", .of_match_table = cs4349_of_match, - .pm = &cs4349_runtime_pm, + .pm = pm_ptr(&cs4349_runtime_pm), }, .id_table = cs4349_i2c_id, .probe = cs4349_i2c_probe, diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c index 252e66c8449e..b9eff240b929 100644 --- a/sound/soc/codecs/cs530x.c +++ b/sound/soc/codecs/cs530x.c @@ -557,9 +557,9 @@ static int cs530x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int asp_fmt, asp_cfg = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: asp_cfg = CS530X_ASP_PRIMARY; break; default: diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index 28f4be37dec1..61bf72681674 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -572,10 +572,10 @@ static int cs53l30_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) u8 aspcfg = 0, aspctl1 = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aspcfg |= CS53L30_ASP_MS; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; @@ -1031,7 +1031,6 @@ static void cs53l30_i2c_remove(struct i2c_client *client) cs53l30->supplies); } -#ifdef CONFIG_PM static int cs53l30_runtime_suspend(struct device *dev) { struct cs53l30_private *cs53l30 = dev_get_drvdata(dev); @@ -1070,11 +1069,9 @@ static int cs53l30_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops cs53l30_runtime_pm = { - SET_RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume, - NULL) + RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume, NULL) }; static const struct of_device_id cs53l30_of_match[] = { @@ -1095,7 +1092,7 @@ static struct i2c_driver cs53l30_i2c_driver = { .driver = { .name = "cs53l30", .of_match_table = cs53l30_of_match, - .pm = &cs53l30_runtime_pm, + .pm = pm_ptr(&cs53l30_runtime_pm), }, .id_table = cs53l30_id, .probe = cs53l30_i2c_probe, diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 8cfec8dcf839..934526f8f292 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -1611,7 +1611,7 @@ static const struct regmap_config cx2072x_regmap = { .reg_write = cx2072x_reg_write, }; -static int __maybe_unused cx2072x_runtime_suspend(struct device *dev) +static int cx2072x_runtime_suspend(struct device *dev) { struct cx2072x_priv *cx2072x = dev_get_drvdata(dev); @@ -1619,7 +1619,7 @@ static int __maybe_unused cx2072x_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused cx2072x_runtime_resume(struct device *dev) +static int cx2072x_runtime_resume(struct device *dev) { struct cx2072x_priv *cx2072x = dev_get_drvdata(dev); @@ -1696,17 +1696,15 @@ MODULE_DEVICE_TABLE(acpi, cx2072x_acpi_match); #endif static const struct dev_pm_ops cx2072x_runtime_pm = { - SET_RUNTIME_PM_OPS(cx2072x_runtime_suspend, cx2072x_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(cx2072x_runtime_suspend, cx2072x_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct i2c_driver cx2072x_i2c_driver = { .driver = { .name = "cx2072x", .acpi_match_table = ACPI_PTR(cx2072x_acpi_match), - .pm = &cx2072x_runtime_pm, + .pm = pm_ptr(&cx2072x_runtime_pm), }, .probe = cx2072x_i2c_probe, .remove = cx2072x_i2c_remove, diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index da2d0242019e..a889f05119f8 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -882,11 +882,11 @@ static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt) return -EINVAL; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: da7210->master = 1; dai_cfg1 |= DA7210_DAI_MODE_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: da7210->master = 0; dai_cfg1 |= DA7210_DAI_MODE_SLAVE; break; diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index ca4cc954efa8..a4496cc26902 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -2203,6 +2203,8 @@ static int da7213_i2c_probe(struct i2c_client *i2c) return ret; } + mutex_init(&da7213->ctrl_lock); + pm_runtime_set_autosuspend_delay(&i2c->dev, 100); pm_runtime_use_autosuspend(&i2c->dev); pm_runtime_set_active(&i2c->dev); @@ -2222,7 +2224,7 @@ static void da7213_i2c_remove(struct i2c_client *i2c) pm_runtime_disable(&i2c->dev); } -static int __maybe_unused da7213_runtime_suspend(struct device *dev) +static int da7213_runtime_suspend(struct device *dev) { struct da7213_priv *da7213 = dev_get_drvdata(dev); @@ -2233,7 +2235,7 @@ static int __maybe_unused da7213_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused da7213_runtime_resume(struct device *dev) +static int da7213_runtime_resume(struct device *dev) { struct da7213_priv *da7213 = dev_get_drvdata(dev); int ret; @@ -2246,8 +2248,8 @@ static int __maybe_unused da7213_runtime_resume(struct device *dev) } static const struct dev_pm_ops da7213_pm = { - SET_RUNTIME_PM_OPS(da7213_runtime_suspend, da7213_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(da7213_runtime_suspend, da7213_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static const struct i2c_device_id da7213_i2c_id[] = { @@ -2262,7 +2264,7 @@ static struct i2c_driver da7213_i2c_driver = { .name = "da7213", .of_match_table = of_match_ptr(da7213_of_match), .acpi_match_table = ACPI_PTR(da7213_acpi_match), - .pm = &da7213_pm, + .pm = pm_ptr(&da7213_pm), }, .probe = da7213_i2c_probe, .remove = da7213_i2c_remove, diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index 8aacd7350798..5f2f67e3bd29 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -1935,10 +1935,10 @@ static int da7218_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) u8 dai_clk_mode = 0, dai_ctrl = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: da7218->master = true; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: da7218->master = false; break; default: diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index e2da3e317b5a..3958e88a2445 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1312,10 +1312,10 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) u8 dai_clk_mode = 0, dai_ctrl = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: da7219->master = true; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: da7219->master = false; break; default: diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index b747f6fa12e4..016c9be3ebda 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -1034,11 +1034,11 @@ static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: aif1 = DA732X_AIF_SLAVE; aif_mclk = DA732X_AIFM_FRAME_64 | DA732X_AIFM_SRC_SEL_AIFA; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aif1 = DA732X_AIF_CLK_FROM_SRC; aif_mclk = DA732X_CLK_GENERATION_AIF_A; break; diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index 8bb8fef2a1d1..eb795abe9acd 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1160,12 +1160,12 @@ static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) u8 aif_clk_mode, aif_ctrl, mode; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: /* DA9055 in I2S Master Mode */ mode = 1; aif_clk_mode = DA9055_AIF_CLK_EN_MASTER_MODE; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: /* DA9055 in I2S Slave Mode */ mode = 0; aif_clk_mode = DA9055_AIF_CLK_EN_SLAVE_MODE; diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index 4fd6f97e5a49..61e1bf1b3c9e 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -9,6 +9,7 @@ #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/module.h> #include <sound/core.h> @@ -25,6 +26,7 @@ module_param(wakeup_delay, uint, 0644); struct dmic { struct gpio_desc *gpio_en; + struct regulator *vref; int wakeup_delay; /* Delay after DMIC mode switch */ int modeswitch_delay; @@ -55,22 +57,33 @@ static int dmic_aif_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct dmic *dmic = snd_soc_component_get_drvdata(component); + int ret = 0; switch (event) { case SND_SOC_DAPM_POST_PMU: if (dmic->gpio_en) gpiod_set_value_cansleep(dmic->gpio_en, 1); + if (dmic->vref) { + ret = regulator_enable(dmic->vref); + if (ret) + return ret; + } + if (dmic->wakeup_delay) msleep(dmic->wakeup_delay); break; case SND_SOC_DAPM_POST_PMD: if (dmic->gpio_en) gpiod_set_value_cansleep(dmic->gpio_en, 0); + + if (dmic->vref) + ret = regulator_disable(dmic->vref); + break; } - return 0; + return ret; } static struct snd_soc_dai_driver dmic_dai = { @@ -85,7 +98,9 @@ static struct snd_soc_dai_driver dmic_dai = { | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_DSD_U8 | SNDRV_PCM_FMTBIT_DSD_U16_LE - | SNDRV_PCM_FMTBIT_DSD_U32_LE, + | SNDRV_PCM_FMTBIT_DSD_U32_LE + | SNDRV_PCM_FMTBIT_DSD_U16_BE + | SNDRV_PCM_FMTBIT_DSD_U32_BE, }, .ops = &dmic_dai_ops, }; @@ -98,6 +113,14 @@ static int dmic_component_probe(struct snd_soc_component *component) if (!dmic) return -ENOMEM; + dmic->vref = devm_regulator_get_optional(component->dev, "vref"); + if (IS_ERR(dmic->vref)) { + if (PTR_ERR(dmic->vref) != -ENODEV) + return dev_err_probe(component->dev, PTR_ERR(dmic->vref), + "Failed to get vref\n"); + dmic->vref = NULL; + } + dmic->gpio_en = devm_gpiod_get_optional(component->dev, "dmicen", GPIOD_OUT_LOW); if (IS_ERR(dmic->gpio_en)) diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index f508df01145b..e7bd561a8f40 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -101,7 +101,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = { SOC_DOUBLE_R_TLV("DAC Playback Volume", ES8316_DAC_VOLL, ES8316_DAC_VOLR, 0, 0xc0, 1, dac_vol_tlv), SOC_SINGLE("DAC Soft Ramp Switch", ES8316_DAC_SET1, 4, 1, 1), - SOC_SINGLE("DAC Soft Ramp Rate", ES8316_DAC_SET1, 2, 4, 0), + SOC_SINGLE("DAC Soft Ramp Rate", ES8316_DAC_SET1, 2, 3, 0), SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0), SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0), SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0), diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c index 6f4fa36ea34d..a9822998199f 100644 --- a/sound/soc/codecs/es8323.c +++ b/sound/soc/codecs/es8323.c @@ -758,7 +758,7 @@ static int es8323_i2c_probe(struct i2c_client *i2c_client) } static const struct i2c_device_id es8323_i2c_id[] = { - { "es8323", 0 }, + { "es8323" }, { } }; MODULE_DEVICE_TABLE(i2c, es8323_i2c_id); diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index b06eead7e0f6..066d92b54312 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -911,7 +911,7 @@ static void es8326_jack_detect_handler(struct work_struct *work) regmap_write(es8326->regmap, ES8326_INT_SOURCE, (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x1f); - regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x08); + regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x0d); queue_delayed_work(system_wq, &es8326->jack_detect_work, msecs_to_jiffies(400)); es8326->hp = 1; @@ -1023,7 +1023,7 @@ static void es8326_init(struct snd_soc_component *component) struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); regmap_write(es8326->regmap, ES8326_RESET, 0x1f); - regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); + regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x3E); regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0); usleep_range(10000, 15000); regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0xd9); diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index f3c97da798dc..76159c45e6b5 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -233,7 +233,6 @@ static const struct snd_kcontrol_new es8328_right_line_controls = /* Left Mixer */ static const struct snd_kcontrol_new es8328_left_mixer_controls[] = { - SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 7, 1, 0), SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 6, 1, 0), SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 7, 1, 0), SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 6, 1, 0), @@ -243,7 +242,6 @@ static const struct snd_kcontrol_new es8328_left_mixer_controls[] = { static const struct snd_kcontrol_new es8328_right_mixer_controls[] = { SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 7, 1, 0), SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 6, 1, 0), - SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 7, 1, 0), SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0), }; @@ -336,10 +334,10 @@ static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = { SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER, ES8328_DACPOWER_LDAC_OFF, 1), - SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_MIXER("Left Mixer", ES8328_DACCONTROL17, 7, 0, &es8328_left_mixer_controls[0], ARRAY_SIZE(es8328_left_mixer_controls)), - SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + SND_SOC_DAPM_MIXER("Right Mixer", ES8328_DACCONTROL20, 7, 0, &es8328_right_mixer_controls[0], ARRAY_SIZE(es8328_right_mixer_controls)), @@ -418,19 +416,14 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = { { "Right Line Mux", "PGA", "Right PGA Mux" }, { "Right Line Mux", "Differential", "Differential Mux" }, - { "Left Out 1", NULL, "Left DAC" }, - { "Right Out 1", NULL, "Right DAC" }, - { "Left Out 2", NULL, "Left DAC" }, - { "Right Out 2", NULL, "Right DAC" }, - - { "Left Mixer", "Playback Switch", "Left DAC" }, + { "Left Mixer", NULL, "Left DAC" }, { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, { "Left Mixer", "Right Playback Switch", "Right DAC" }, { "Left Mixer", "Right Bypass Switch", "Right Line Mux" }, { "Right Mixer", "Left Playback Switch", "Left DAC" }, { "Right Mixer", "Left Bypass Switch", "Left Line Mux" }, - { "Right Mixer", "Playback Switch", "Right DAC" }, + { "Right Mixer", NULL, "Right DAC" }, { "Right Mixer", "Right Bypass Switch", "Right Line Mux" }, { "DAC DIG", NULL, "DAC STM" }, diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index e1a7f0b0c0f3..1139a2754ca3 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1017,8 +1017,7 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_device *hdev, return -ENOMEM; } - se->texts = devm_kmemdup(&hdev->dev, items, - (num_items * sizeof(char *)), GFP_KERNEL); + se->texts = devm_kmemdup_array(&hdev->dev, items, num_items, sizeof(items[0]), GFP_KERNEL); if (!se->texts) return -ENOMEM; @@ -2033,7 +2032,6 @@ static void hdmi_codec_remove(struct snd_soc_component *component) pm_runtime_disable(&hdev->dev); } -#ifdef CONFIG_PM_SLEEP static int hdmi_codec_resume(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); @@ -2056,9 +2054,6 @@ static int hdmi_codec_resume(struct device *dev) hdac_hdmi_present_sense_all_pins(hdev, hdmi, false); return 0; } -#else -#define hdmi_codec_resume NULL -#endif static const struct snd_soc_component_driver hdmi_hda_codec = { .probe = hdmi_codec_probe, @@ -2228,7 +2223,6 @@ static int hdac_hdmi_dev_remove(struct hdac_device *hdev) return 0; } -#ifdef CONFIG_PM static int hdac_hdmi_runtime_suspend(struct device *dev) { struct hdac_device *hdev = dev_to_hdac_dev(dev); @@ -2297,14 +2291,10 @@ static int hdac_hdmi_runtime_resume(struct device *dev) return 0; } -#else -#define hdac_hdmi_runtime_suspend NULL -#define hdac_hdmi_runtime_resume NULL -#endif static const struct dev_pm_ops hdac_hdmi_pm = { - SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, hdmi_codec_resume) + RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, hdmi_codec_resume) }; static const struct hda_device_id hdmi_list[] = { @@ -2323,7 +2313,7 @@ MODULE_DEVICE_TABLE(hdaudio, hdmi_list); static struct hdac_driver hdmi_driver = { .driver = { .name = "HDMI HDA Codec", - .pm = &hdac_hdmi_pm, + .pm = pm_ptr(&hdac_hdmi_pm), }, .id_table = hdmi_list, .probe = hdac_hdmi_dev_probe, diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index d9df29a26f4f..497376bcd3ff 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -281,6 +281,7 @@ static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = { struct hdmi_codec_priv { struct hdmi_codec_pdata hcd; uint8_t eld[MAX_ELD_BYTES]; + struct snd_parsed_hdmi_eld eld_parsed; struct snd_pcm_chmap *chmap_info; unsigned int chmap_idx; struct mutex lock; @@ -288,6 +289,7 @@ struct hdmi_codec_priv { struct snd_soc_jack *jack; unsigned int jack_status; u8 iec_status[AES_IEC958_STATUS_SIZE]; + struct snd_info_entry *proc_entry; }; static const struct snd_soc_dapm_widget hdmi_widgets[] = { @@ -469,6 +471,9 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream, if (ret) goto err; + snd_parse_eld(dai->dev, &hcp->eld_parsed, + hcp->eld, sizeof(hcp->eld)); + ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); if (ret) goto err; @@ -714,7 +719,7 @@ static int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction) */ if (hcp->hcd.ops->mute_stream && (direction == SNDRV_PCM_STREAM_PLAYBACK || - !hcp->hcd.ops->no_capture_mute)) + !hcp->hcd.no_capture_mute)) return hcp->hcd.ops->mute_stream(dai->dev->parent, hcp->hcd.data, mute, direction); @@ -825,8 +830,54 @@ static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, return 0; } +#ifdef CONFIG_SND_PROC_FS +static void print_eld_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdmi_codec_priv *hcp = entry->private_data; + + snd_print_eld_info(&hcp->eld_parsed, buffer); +} + +static int hdmi_dai_proc_new(struct hdmi_codec_priv *hcp, + struct snd_soc_dai *dai) +{ + struct snd_info_entry *entry; + char name[32]; + int err; + + snprintf(name, sizeof(name), "eld#%d", dai->id); + err = snd_card_proc_new(dai->component->card->snd_card, name, &entry); + if (err < 0) + return err; + + snd_info_set_text_ops(entry, hcp, print_eld_info); + hcp->proc_entry = entry; + + return 0; +} + +static void hdmi_dai_proc_free(struct hdmi_codec_priv *hcp) +{ + snd_info_free_entry(hcp->proc_entry); + hcp->proc_entry = NULL; +} +#else +static int hdmi_dai_proc_new(struct hdmi_codec_priv *hcp, + struct snd_soc_dai *dai) +{ + return 0; +} + +static void hdmi_dai_proc_free(struct hdmi_codec_priv *hcp) +{ +} +#endif + static int hdmi_dai_probe(struct snd_soc_dai *dai) { + struct hdmi_codec_priv *hcp = + snd_soc_component_get_drvdata(dai->component); struct snd_soc_dapm_context *dapm; struct hdmi_codec_daifmt *daifmt; struct snd_soc_dapm_route route[] = { @@ -859,6 +910,15 @@ static int hdmi_dai_probe(struct snd_soc_dai *dai) snd_soc_dai_dma_data_set_playback(dai, daifmt); + return hdmi_dai_proc_new(hcp, dai); +} + +static int hdmi_dai_remove(struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = + snd_soc_component_get_drvdata(dai->component); + + hdmi_dai_proc_free(hcp); return 0; } @@ -875,11 +935,18 @@ static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp, static void plugged_cb(struct device *dev, bool plugged) { struct hdmi_codec_priv *hcp = dev_get_drvdata(dev); + int ret; if (plugged) { if (hcp->hcd.ops->get_eld) { hcp->hcd.ops->get_eld(dev->parent, hcp->hcd.data, hcp->eld, sizeof(hcp->eld)); + ret = snd_parse_eld(dev, &hcp->eld_parsed, + hcp->eld, sizeof(hcp->eld)); + if (ret < 0) + dev_dbg(dev, "Failed to parse ELD: %d\n", ret); + else + snd_show_eld(dev, &hcp->eld_parsed); } hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT); } else { @@ -926,6 +993,7 @@ static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai) static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = { .probe = hdmi_dai_probe, + .remove = hdmi_dai_remove, .startup = hdmi_codec_startup, .shutdown = hdmi_codec_shutdown, .hw_params = hdmi_codec_hw_params, @@ -995,7 +1063,7 @@ static int hdmi_of_xlate_dai_id(struct snd_soc_component *component, int ret = -ENOTSUPP; /* see snd_soc_get_dai_id() */ if (hcp->hcd.ops->get_dai_id) - ret = hcp->hcd.ops->get_dai_id(component, endpoint); + ret = hcp->hcd.ops->get_dai_id(component, endpoint, hcp->hcd.data); return ret; } diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c index 6217e611259f..e04af1b9ace8 100644 --- a/sound/soc/codecs/jz4760.c +++ b/sound/soc/codecs/jz4760.c @@ -314,37 +314,13 @@ static const struct snd_kcontrol_new jz4760_codec_snd_controls[] = { }; static const struct snd_kcontrol_new jz4760_codec_pcm_playback_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Volume", - .info = snd_soc_info_volsw, - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ - | SNDRV_CTL_ELEM_ACCESS_READWRITE, - .tlv.p = dac_tlv, - .get = snd_soc_dapm_get_volsw, - .put = snd_soc_dapm_put_volsw, - .private_value = SOC_DOUBLE_R_VALUE(JZ4760_CODEC_REG_GCR6, - JZ4760_CODEC_REG_GCR5, - REG_GCR_GAIN_OFFSET, - REG_GCR_GAIN_MAX, 1), - }, + SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4760_CODEC_REG_GCR6, JZ4760_CODEC_REG_GCR5, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, dac_tlv), }; static const struct snd_kcontrol_new jz4760_codec_hp_playback_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Volume", - .info = snd_soc_info_volsw, - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ - | SNDRV_CTL_ELEM_ACCESS_READWRITE, - .tlv.p = out_tlv, - .get = snd_soc_dapm_get_volsw, - .put = snd_soc_dapm_put_volsw, - .private_value = SOC_DOUBLE_R_VALUE(JZ4760_CODEC_REG_GCR2, - JZ4760_CODEC_REG_GCR1, - REG_GCR_GAIN_OFFSET, - REG_GCR_GAIN_MAX, 1), - }, + SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4760_CODEC_REG_GCR2, JZ4760_CODEC_REG_GCR1, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, out_tlv), }; static int hpout_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c index acb9eaa7ea1c..312202ab5cea 100644 --- a/sound/soc/codecs/jz4770.c +++ b/sound/soc/codecs/jz4770.c @@ -331,43 +331,15 @@ static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = { }; static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Volume", - .info = snd_soc_info_volsw, - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ - | SNDRV_CTL_ELEM_ACCESS_READWRITE, - .tlv.p = dac_tlv, - .get = snd_soc_dapm_get_volsw, - .put = snd_soc_dapm_put_volsw, - /* - * NOTE: DACR/DACL are inversed; the gain value written to DACR - * seems to affect the left channel, and the gain value written - * to DACL seems to affect the right channel. - */ - .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_DACR, - JZ4770_CODEC_REG_GCR_DACL, - REG_GCR_GAIN_OFFSET, - REG_GCR_GAIN_MAX, 1), - }, + SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4770_CODEC_REG_GCR_DACR, + JZ4770_CODEC_REG_GCR_DACL, REG_GCR_GAIN_OFFSET, + REG_GCR_GAIN_MAX, 1, dac_tlv), }; static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Volume", - .info = snd_soc_info_volsw, - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ - | SNDRV_CTL_ELEM_ACCESS_READWRITE, - .tlv.p = out_tlv, - .get = snd_soc_dapm_get_volsw, - .put = snd_soc_dapm_put_volsw, - /* HPR/HPL inversed for the same reason as above */ - .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_HPR, - JZ4770_CODEC_REG_GCR_HPL, - REG_GCR_GAIN_OFFSET, - REG_GCR_GAIN_MAX, 1), - }, + SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4770_CODEC_REG_GCR_HPR, + JZ4770_CODEC_REG_GCR_HPL, REG_GCR_GAIN_OFFSET, + REG_GCR_GAIN_MAX, 1, out_tlv), }; static int hpout_event(struct snd_soc_dapm_widget *w, diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c index 5e0bd0d24ed3..a3d6318c9050 100644 --- a/sound/soc/codecs/lochnagar-sc.c +++ b/sound/soc/codecs/lochnagar-sc.c @@ -129,12 +129,12 @@ static int lochnagar_sc_check_fmt(struct snd_soc_dai *dai, unsigned int fmt, static int lochnagar_sc_set_line_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBS_CFS); + return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBC_CFC); } static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBM_CFM); + return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBP_CFP); } static const struct snd_soc_dai_ops lochnagar_sc_line_ops = { diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index febbbe073962..45a6b83808b2 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3963,7 +3963,7 @@ static const struct of_device_id rx_macro_dt_match[] = { }; MODULE_DEVICE_TABLE(of, rx_macro_dt_match); -static int __maybe_unused rx_macro_runtime_suspend(struct device *dev) +static int rx_macro_runtime_suspend(struct device *dev) { struct rx_macro *rx = dev_get_drvdata(dev); @@ -3977,7 +3977,7 @@ static int __maybe_unused rx_macro_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused rx_macro_runtime_resume(struct device *dev) +static int rx_macro_runtime_resume(struct device *dev) { struct rx_macro *rx = dev_get_drvdata(dev); int ret; @@ -4012,7 +4012,7 @@ err_npl: } static const struct dev_pm_ops rx_macro_pm_ops = { - SET_RUNTIME_PM_OPS(rx_macro_runtime_suspend, rx_macro_runtime_resume, NULL) + RUNTIME_PM_OPS(rx_macro_runtime_suspend, rx_macro_runtime_resume, NULL) }; static struct platform_driver rx_macro_driver = { @@ -4020,7 +4020,7 @@ static struct platform_driver rx_macro_driver = { .name = "rx_macro", .of_match_table = rx_macro_dt_match, .suppress_bind_attrs = true, - .pm = &rx_macro_pm_ops, + .pm = pm_ptr(&rx_macro_pm_ops), }, .probe = rx_macro_probe, .remove = rx_macro_remove, diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index a134584acf90..27bae58f4072 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -2400,7 +2400,7 @@ static void tx_macro_remove(struct platform_device *pdev) lpass_macro_pds_exit(tx->pds); } -static int __maybe_unused tx_macro_runtime_suspend(struct device *dev) +static int tx_macro_runtime_suspend(struct device *dev) { struct tx_macro *tx = dev_get_drvdata(dev); @@ -2414,7 +2414,7 @@ static int __maybe_unused tx_macro_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tx_macro_runtime_resume(struct device *dev) +static int tx_macro_runtime_resume(struct device *dev) { struct tx_macro *tx = dev_get_drvdata(dev); int ret; @@ -2450,7 +2450,7 @@ err_npl: } static const struct dev_pm_ops tx_macro_pm_ops = { - SET_RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL) + RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL) }; static const struct tx_macro_data lpass_ver_9 = { @@ -2531,7 +2531,7 @@ static struct platform_driver tx_macro_driver = { .name = "tx_macro", .of_match_table = tx_macro_dt_match, .suppress_bind_attrs = true, - .pm = &tx_macro_pm_ops, + .pm = pm_ptr(&tx_macro_pm_ops), }, .probe = tx_macro_probe, .remove = tx_macro_remove, diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index c781da476240..74ada6e77526 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -1674,7 +1674,7 @@ static void va_macro_remove(struct platform_device *pdev) lpass_macro_pds_exit(va->pds); } -static int __maybe_unused va_macro_runtime_suspend(struct device *dev) +static int va_macro_runtime_suspend(struct device *dev) { struct va_macro *va = dev_get_drvdata(dev); @@ -1689,7 +1689,7 @@ static int __maybe_unused va_macro_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused va_macro_runtime_resume(struct device *dev) +static int va_macro_runtime_resume(struct device *dev) { struct va_macro *va = dev_get_drvdata(dev); int ret; @@ -1717,7 +1717,7 @@ static int __maybe_unused va_macro_runtime_resume(struct device *dev) static const struct dev_pm_ops va_macro_pm_ops = { - SET_RUNTIME_PM_OPS(va_macro_runtime_suspend, va_macro_runtime_resume, NULL) + RUNTIME_PM_OPS(va_macro_runtime_suspend, va_macro_runtime_resume, NULL) }; static const struct of_device_id va_macro_dt_match[] = { @@ -1735,7 +1735,7 @@ static struct platform_driver va_macro_driver = { .name = "va_macro", .of_match_table = va_macro_dt_match, .suppress_bind_attrs = true, - .pm = &va_macro_pm_ops, + .pm = pm_ptr(&va_macro_pm_ops), }, .probe = va_macro_probe, .remove = va_macro_remove, diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index c989d82d1d3c..b261fa373e65 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2900,7 +2900,7 @@ static void wsa_macro_remove(struct platform_device *pdev) clk_disable_unprepare(wsa->fsgen); } -static int __maybe_unused wsa_macro_runtime_suspend(struct device *dev) +static int wsa_macro_runtime_suspend(struct device *dev) { struct wsa_macro *wsa = dev_get_drvdata(dev); @@ -2914,7 +2914,7 @@ static int __maybe_unused wsa_macro_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused wsa_macro_runtime_resume(struct device *dev) +static int wsa_macro_runtime_resume(struct device *dev) { struct wsa_macro *wsa = dev_get_drvdata(dev); int ret; @@ -2950,7 +2950,7 @@ err_npl: } static const struct dev_pm_ops wsa_macro_pm_ops = { - SET_RUNTIME_PM_OPS(wsa_macro_runtime_suspend, wsa_macro_runtime_resume, NULL) + RUNTIME_PM_OPS(wsa_macro_runtime_suspend, wsa_macro_runtime_resume, NULL) }; static const struct of_device_id wsa_macro_dt_match[] = { @@ -2977,7 +2977,7 @@ static struct platform_driver wsa_macro_driver = { .driver = { .name = "wsa_macro", .of_match_table = wsa_macro_dt_match, - .pm = &wsa_macro_pm_ops, + .pm = pm_ptr(&wsa_macro_pm_ops), }, .probe = wsa_macro_probe, .remove = wsa_macro_remove, diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c index b24d6472ad5f..bc3470cf2c54 100644 --- a/sound/soc/codecs/madera.c +++ b/sound/soc/codecs/madera.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/tlv.h> @@ -2322,10 +2323,10 @@ int madera_out_ev(struct snd_soc_dapm_widget *w, case CS42L92: case CS47L92: case CS47L93: - out_up_delay = 6; + out_up_delay = 6000; break; default: - out_up_delay = 17; + out_up_delay = 17000; break; } @@ -2356,7 +2357,7 @@ int madera_out_ev(struct snd_soc_dapm_widget *w, case MADERA_OUT3R_ENA_SHIFT: priv->out_up_pending--; if (!priv->out_up_pending) { - msleep(priv->out_up_delay); + fsleep(priv->out_up_delay); priv->out_up_delay = 0; } break; @@ -2375,7 +2376,7 @@ int madera_out_ev(struct snd_soc_dapm_widget *w, case MADERA_OUT3L_ENA_SHIFT: case MADERA_OUT3R_ENA_SHIFT: priv->out_down_pending++; - priv->out_down_delay++; + priv->out_down_delay += 1000; break; default: break; @@ -2392,7 +2393,7 @@ int madera_out_ev(struct snd_soc_dapm_widget *w, case MADERA_OUT3R_ENA_SHIFT: priv->out_down_pending--; if (!priv->out_down_pending) { - msleep(priv->out_down_delay); + fsleep(priv->out_down_delay); priv->out_down_delay = 0; } break; @@ -2775,7 +2776,7 @@ static int madera_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) break; case SND_SOC_DAIFMT_DSP_B: if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != - SND_SOC_DAIFMT_CBM_CFM) { + SND_SOC_DAIFMT_CBP_CFP) { madera_aif_err(dai, "DSP_B not valid in slave mode\n"); return -EINVAL; } @@ -2786,7 +2787,7 @@ static int madera_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) break; case SND_SOC_DAIFMT_LEFT_J: if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != - SND_SOC_DAIFMT_CBM_CFM) { + SND_SOC_DAIFMT_CBP_CFP) { madera_aif_err(dai, "LEFT_J not valid in slave mode\n"); return -EINVAL; } @@ -2799,15 +2800,15 @@ static int madera_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: lrclk |= MADERA_AIF1TX_LRCLK_MSTR; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: bclk |= MADERA_AIF1_BCLK_MSTR; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: bclk |= MADERA_AIF1_BCLK_MSTR; lrclk |= MADERA_AIF1TX_LRCLK_MSTR; break; @@ -3965,7 +3966,7 @@ static int madera_enable_fll(struct madera_fll *fll) } madera_fll_dbg(fll, "Enabling FLL, initially %s\n", - already_enabled ? "enabled" : "disabled"); + str_enabled_disabled(already_enabled)); if (fll->fout < MADERA_FLL_MIN_FOUT || fll->fout > MADERA_FLL_MAX_FOUT) { @@ -4252,7 +4253,7 @@ static int madera_enable_fll_ao(struct madera_fll *fll, pm_runtime_get_sync(madera->dev); madera_fll_dbg(fll, "Enabling FLL_AO, initially %s\n", - already_enabled ? "enabled" : "disabled"); + str_enabled_disabled(already_enabled)); /* FLL_AO_HOLD must be set before configuring any registers */ regmap_update_bits(fll->madera->regmap, @@ -4576,7 +4577,7 @@ static int madera_fllhj_enable(struct madera_fll *fll) pm_runtime_get_sync(madera->dev); madera_fll_dbg(fll, "Enabling FLL, initially %s\n", - already_enabled ? "enabled" : "disabled"); + str_enabled_disabled(already_enabled)); /* FLLn_HOLD must be set before configuring any registers */ regmap_update_bits(fll->madera->regmap, diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 8915f5250695..37e61d8d4be6 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1731,7 +1731,6 @@ MODULE_DEVICE_TABLE(i2c, max98088_i2c_id); static int max98088_i2c_probe(struct i2c_client *i2c) { struct max98088_priv *max98088; - const struct i2c_device_id *id; max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv), GFP_KERNEL); @@ -1747,8 +1746,7 @@ static int max98088_i2c_probe(struct i2c_client *i2c) if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER) return PTR_ERR(max98088->mclk); - id = i2c_match_id(max98088_i2c_id, i2c); - max98088->devtype = id->driver_data; + max98088->devtype = (uintptr_t)i2c_get_match_data(i2c); i2c_set_clientdata(i2c, max98088); max98088->pdata = i2c->dev.platform_data; diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 2adf744c6526..22177c1ce160 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2543,8 +2543,6 @@ MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); static int max98090_i2c_probe(struct i2c_client *i2c) { struct max98090_priv *max98090; - const struct acpi_device_id *acpi_id; - kernel_ulong_t driver_data = 0; int ret; pr_debug("max98090_i2c_probe\n"); @@ -2554,21 +2552,7 @@ static int max98090_i2c_probe(struct i2c_client *i2c) if (max98090 == NULL) return -ENOMEM; - if (ACPI_HANDLE(&i2c->dev)) { - acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table, - &i2c->dev); - if (!acpi_id) { - dev_err(&i2c->dev, "No driver data\n"); - return -EINVAL; - } - driver_data = acpi_id->driver_data; - } else { - const struct i2c_device_id *i2c_id = - i2c_match_id(max98090_i2c_id, i2c); - driver_data = i2c_id->driver_data; - } - - max98090->devtype = driver_data; + max98090->devtype = (uintptr_t)i2c_get_match_data(i2c); i2c_set_clientdata(i2c, max98090); max98090->pdata = i2c->dev.platform_data; @@ -2620,7 +2604,6 @@ static void max98090_i2c_remove(struct i2c_client *client) max98090_i2c_shutdown(client); } -#ifdef CONFIG_PM static int max98090_runtime_resume(struct device *dev) { struct max98090_priv *max98090 = dev_get_drvdata(dev); @@ -2642,9 +2625,7 @@ static int max98090_runtime_suspend(struct device *dev) return 0; } -#endif -#ifdef CONFIG_PM_SLEEP static int max98090_resume(struct device *dev) { struct max98090_priv *max98090 = dev_get_drvdata(dev); @@ -2661,12 +2642,10 @@ static int max98090_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops max98090_pm = { - SET_RUNTIME_PM_OPS(max98090_runtime_suspend, - max98090_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume) + RUNTIME_PM_OPS(max98090_runtime_suspend, max98090_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume) }; #ifdef CONFIG_OF @@ -2689,7 +2668,7 @@ MODULE_DEVICE_TABLE(acpi, max98090_acpi_match); static struct i2c_driver max98090_i2c_driver = { .driver = { .name = "max98090", - .pm = &max98090_pm, + .pm = pm_ptr(&max98090_pm), .of_match_table = of_match_ptr(max98090_of_match), .acpi_match_table = ACPI_PTR(max98090_acpi_match), }, diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 7e525d49328d..cfb63fe69267 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -2115,7 +2115,6 @@ static int max98095_i2c_probe(struct i2c_client *i2c) { struct max98095_priv *max98095; int ret; - const struct i2c_device_id *id; max98095 = devm_kzalloc(&i2c->dev, sizeof(struct max98095_priv), GFP_KERNEL); @@ -2131,8 +2130,7 @@ static int max98095_i2c_probe(struct i2c_client *i2c) return ret; } - id = i2c_match_id(max98095_i2c_id, i2c); - max98095->devtype = id->driver_data; + max98095->devtype = (uintptr_t)i2c_get_match_data(i2c); i2c_set_clientdata(i2c, max98095); max98095->pdata = i2c->dev.platform_data; diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c index 1f7ff3dbcbbe..56c4ba1f3782 100644 --- a/sound/soc/codecs/max98373-i2c.c +++ b/sound/soc/codecs/max98373-i2c.c @@ -472,7 +472,6 @@ static struct snd_soc_dai_driver max98373_dai[] = { } }; -#ifdef CONFIG_PM_SLEEP static int max98373_suspend(struct device *dev) { struct max98373_priv *max98373 = dev_get_drvdata(dev); @@ -496,10 +495,9 @@ static int max98373_resume(struct device *dev) regcache_sync(max98373->regmap); return 0; } -#endif static const struct dev_pm_ops max98373_pm = { - SET_SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume) + SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume) }; static const struct regmap_config max98373_regmap = { @@ -605,7 +603,7 @@ static struct i2c_driver max98373_i2c_driver = { .name = "max98373", .of_match_table = of_match_ptr(max98373_of_match), .acpi_match_table = ACPI_PTR(max98373_acpi_match), - .pm = &max98373_pm, + .pm = pm_ptr(&max98373_pm), }, .probe = max98373_i2c_probe, .id_table = max98373_i2c_id, diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 26860882fd91..6088278e6503 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -246,7 +246,7 @@ static const struct regmap_config max98373_sdw_regmap = { }; /* Power management functions and structure */ -static __maybe_unused int max98373_suspend(struct device *dev) +static int max98373_suspend(struct device *dev) { struct max98373_priv *max98373 = dev_get_drvdata(dev); int i; @@ -262,7 +262,7 @@ static __maybe_unused int max98373_suspend(struct device *dev) #define MAX98373_PROBE_TIMEOUT 5000 -static __maybe_unused int max98373_resume(struct device *dev) +static int max98373_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct max98373_priv *max98373 = dev_get_drvdata(dev); @@ -292,8 +292,8 @@ regmap_sync: } static const struct dev_pm_ops max98373_pm = { - SET_SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume) - SET_RUNTIME_PM_OPS(max98373_suspend, max98373_resume, NULL) + SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume) + RUNTIME_PM_OPS(max98373_suspend, max98373_resume, NULL) }; static int max98373_read_prop(struct sdw_slave *slave) @@ -874,7 +874,7 @@ static struct sdw_driver max98373_sdw_driver = { .name = "max98373", .of_match_table = of_match_ptr(max98373_of_match), .acpi_match_table = ACPI_PTR(max98373_acpi_match), - .pm = &max98373_pm, + .pm = pm_ptr(&max98373_pm), }, .probe = max98373_sdw_probe, .remove = max98373_sdw_remove, diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 1bae253618fd..76296176f948 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -943,7 +943,6 @@ static int max98390_probe(struct snd_soc_component *component) return 0; } -#ifdef CONFIG_PM_SLEEP static int max98390_suspend(struct device *dev) { struct max98390_priv *max98390 = dev_get_drvdata(dev); @@ -967,10 +966,9 @@ static int max98390_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops max98390_pm = { - SET_SYSTEM_SLEEP_PM_OPS(max98390_suspend, max98390_resume) + SYSTEM_SLEEP_PM_OPS(max98390_suspend, max98390_resume) }; static const struct snd_soc_component_driver soc_codec_dev_max98390 = { @@ -1130,7 +1128,7 @@ static struct i2c_driver max98390_i2c_driver = { .name = "max98390", .of_match_table = of_match_ptr(max98390_of_match), .acpi_match_table = ACPI_PTR(max98390_acpi_match), - .pm = &max98390_pm, + .pm = pm_ptr(&max98390_pm), }, .probe = max98390_i2c_probe, .id_table = max98390_i2c_id, diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index e52bb2266fa1..c1888cd83dbc 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -1571,7 +1571,6 @@ static int max98396_probe(struct snd_soc_component *component) return 0; } -#ifdef CONFIG_PM_SLEEP static int max98396_suspend(struct device *dev) { struct max98396_priv *max98396 = dev_get_drvdata(dev); @@ -1616,10 +1615,9 @@ static int max98396_resume(struct device *dev) regcache_sync(max98396->regmap); return 0; } -#endif static const struct dev_pm_ops max98396_pm = { - SET_SYSTEM_SLEEP_PM_OPS(max98396_suspend, max98396_resume) + SYSTEM_SLEEP_PM_OPS(max98396_suspend, max98396_resume) }; static const struct snd_soc_component_driver soc_codec_dev_max98396 = { @@ -1904,7 +1902,7 @@ static struct i2c_driver max98396_i2c_driver = { .name = "max98396", .of_match_table = of_match_ptr(max98396_of_match), .acpi_match_table = ACPI_PTR(max98396_acpi_match), - .pm = &max98396_pm, + .pm = pm_ptr(&max98396_pm), }, .probe = max98396_i2c_probe, .id_table = max98396_i2c_id, diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c index 479ded22672e..adf5a898c6df 100644 --- a/sound/soc/codecs/max98520.c +++ b/sound/soc/codecs/max98520.c @@ -621,7 +621,7 @@ static int max98520_probe(struct snd_soc_component *component) return 0; } -static int __maybe_unused max98520_suspend(struct device *dev) +static int max98520_suspend(struct device *dev) { struct max98520_priv *max98520 = dev_get_drvdata(dev); @@ -630,7 +630,7 @@ static int __maybe_unused max98520_suspend(struct device *dev) return 0; } -static int __maybe_unused max98520_resume(struct device *dev) +static int max98520_resume(struct device *dev) { struct max98520_priv *max98520 = dev_get_drvdata(dev); @@ -641,7 +641,7 @@ static int __maybe_unused max98520_resume(struct device *dev) } static const struct dev_pm_ops max98520_pm = { - SET_SYSTEM_SLEEP_PM_OPS(max98520_suspend, max98520_resume) + SYSTEM_SLEEP_PM_OPS(max98520_suspend, max98520_resume) }; static const struct snd_soc_component_driver soc_codec_dev_max98520 = { @@ -752,7 +752,7 @@ static struct i2c_driver max98520_i2c_driver = { .driver = { .name = "max98520", .of_match_table = of_match_ptr(max98520_of_match), - .pm = &max98520_pm, + .pm = pm_ptr(&max98520_pm), }, .probe = max98520_i2c_probe, .id_table = max98520_i2c_id, diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 4015ed2c47ec..716d16daf7d7 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -539,7 +539,6 @@ static const struct snd_soc_component_driver max9860_component_driver = { .endianness = 1, }; -#ifdef CONFIG_PM static int max9860_suspend(struct device *dev) { struct max9860_priv *max9860 = dev_get_drvdata(dev); @@ -584,10 +583,9 @@ static int max9860_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops max9860_pm_ops = { - SET_RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL) + RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL) }; static int max9860_probe(struct i2c_client *i2c) @@ -729,7 +727,7 @@ static struct i2c_driver max9860_i2c_driver = { .driver = { .name = "max9860", .of_match_table = max9860_of_match, - .pm = &max9860_pm_ops, + .pm = pm_ptr(&max9860_pm_ops), }, }; diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 747aa6f1d54f..55cc18451a2d 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -731,7 +731,6 @@ static int max98927_probe(struct snd_soc_component *component) return 0; } -#ifdef CONFIG_PM_SLEEP static int max98927_suspend(struct device *dev) { struct max98927_priv *max98927 = dev_get_drvdata(dev); @@ -750,10 +749,9 @@ static int max98927_resume(struct device *dev) regcache_sync(max98927->regmap); return 0; } -#endif static const struct dev_pm_ops max98927_pm = { - SET_SYSTEM_SLEEP_PM_OPS(max98927_suspend, max98927_resume) + SYSTEM_SLEEP_PM_OPS(max98927_suspend, max98927_resume) }; static const struct snd_soc_component_driver soc_component_dev_max98927 = { @@ -902,7 +900,7 @@ static struct i2c_driver max98927_i2c_driver = { .name = "max98927", .of_match_table = of_match_ptr(max98927_of_match), .acpi_match_table = ACPI_PTR(max98927_acpi_match), - .pm = &max98927_pm, + .pm = pm_ptr(&max98927_pm), }, .probe = max98927_i2c_probe, .remove = max98927_i2c_remove, diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c index 9247b90d1b99..e033027fd4c7 100644 --- a/sound/soc/codecs/mt6358.c +++ b/sound/soc/codecs/mt6358.c @@ -162,47 +162,6 @@ static void capture_gpio_reset(struct mt6358_priv *priv) 0xf << 12, 0x0); } -/* use only when not govern by DAPM */ -static int mt6358_set_dcxo(struct mt6358_priv *priv, bool enable) -{ - regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, - 0x1 << RG_XO_AUDIO_EN_M_SFT, - (enable ? 1 : 0) << RG_XO_AUDIO_EN_M_SFT); - return 0; -} - -/* use only when not govern by DAPM */ -static int mt6358_set_clksq(struct mt6358_priv *priv, bool enable) -{ - /* audio clk source from internal dcxo */ - regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6, - RG_CLKSQ_IN_SEL_TEST_MASK_SFT, - 0x0); - - /* Enable/disable CLKSQ 26MHz */ - regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6, - RG_CLKSQ_EN_MASK_SFT, - (enable ? 1 : 0) << RG_CLKSQ_EN_SFT); - return 0; -} - -/* use only when not govern by DAPM */ -static int mt6358_set_aud_global_bias(struct mt6358_priv *priv, bool enable) -{ - regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, - RG_AUDGLB_PWRDN_VA28_MASK_SFT, - (enable ? 0 : 1) << RG_AUDGLB_PWRDN_VA28_SFT); - return 0; -} - -/* use only when not govern by DAPM */ -static int mt6358_set_topck(struct mt6358_priv *priv, bool enable) -{ - regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0, - 0x0066, enable ? 0x0 : 0x66); - return 0; -} - static int mt6358_mtkaif_tx_enable(struct mt6358_priv *priv) { switch (priv->mtkaif_protocol) { @@ -252,69 +211,6 @@ static int mt6358_mtkaif_tx_disable(struct mt6358_priv *priv) return 0; } -int mt6358_mtkaif_calibration_enable(struct snd_soc_component *cmpnt) -{ - struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); - - playback_gpio_set(priv); - capture_gpio_set(priv); - mt6358_mtkaif_tx_enable(priv); - - mt6358_set_dcxo(priv, true); - mt6358_set_aud_global_bias(priv, true); - mt6358_set_clksq(priv, true); - mt6358_set_topck(priv, true); - - /* set dat_miso_loopback on */ - regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, - RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT, - 1 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT); - regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, - RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT, - 1 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT); - return 0; -} -EXPORT_SYMBOL_GPL(mt6358_mtkaif_calibration_enable); - -int mt6358_mtkaif_calibration_disable(struct snd_soc_component *cmpnt) -{ - struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); - - /* set dat_miso_loopback off */ - regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, - RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT, - 0 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT); - regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, - RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT, - 0 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT); - - mt6358_set_topck(priv, false); - mt6358_set_clksq(priv, false); - mt6358_set_aud_global_bias(priv, false); - mt6358_set_dcxo(priv, false); - - mt6358_mtkaif_tx_disable(priv); - playback_gpio_reset(priv); - capture_gpio_reset(priv); - return 0; -} -EXPORT_SYMBOL_GPL(mt6358_mtkaif_calibration_disable); - -int mt6358_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt, - int phase_1, int phase_2) -{ - struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); - - regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, - RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT, - phase_1 << RG_AUD_PAD_TOP_PHASE_MODE_SFT); - regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, - RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT, - phase_2 << RG_AUD_PAD_TOP_PHASE_MODE2_SFT); - return 0; -} -EXPORT_SYMBOL_GPL(mt6358_set_mtkaif_calibration_phase); - /* dl pga gain */ enum { DL_GAIN_8DB = 0, diff --git a/sound/soc/codecs/mt6358.h b/sound/soc/codecs/mt6358.h index a5953315eaa2..b729c3899b7e 100644 --- a/sound/soc/codecs/mt6358.h +++ b/sound/soc/codecs/mt6358.h @@ -2307,8 +2307,4 @@ enum { /* set only during init */ int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt, int mtkaif_protocol); -int mt6358_mtkaif_calibration_enable(struct snd_soc_component *cmpnt); -int mt6358_mtkaif_calibration_disable(struct snd_soc_component *cmpnt); -int mt6358_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt, - int phase_1, int phase_2); #endif /* __MT6358_H__ */ diff --git a/sound/soc/codecs/mt6359-accdet.h b/sound/soc/codecs/mt6359-accdet.h index c234f2f4276a..78ada3a5bfae 100644 --- a/sound/soc/codecs/mt6359-accdet.h +++ b/sound/soc/codecs/mt6359-accdet.h @@ -123,6 +123,15 @@ struct mt6359_accdet { struct workqueue_struct *jd_workqueue; }; +#if IS_ENABLED(CONFIG_SND_SOC_MT6359_ACCDET) int mt6359_accdet_enable_jack_detect(struct snd_soc_component *component, struct snd_soc_jack *jack); +#else +static inline int +mt6359_accdet_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + return -EOPNOTSUPP; +} +#endif #endif diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c index 0b76a55664b0..f73120c6a6ce 100644 --- a/sound/soc/codecs/mt6359.c +++ b/sound/soc/codecs/mt6359.c @@ -2867,9 +2867,12 @@ static int mt6359_parse_dt(struct mt6359_priv *priv) struct device *dev = priv->dev; struct device_node *np; - np = of_get_child_by_name(dev->parent->of_node, "mt6359codec"); - if (!np) - return -EINVAL; + np = of_get_child_by_name(dev->parent->of_node, "audio-codec"); + if (!np) { + np = of_get_child_by_name(dev->parent->of_node, "mt6359codec"); + if (!np) + return -EINVAL; + } ret = of_property_read_u32(np, "mediatek,dmic-mode", &priv->dmic_one_wire_mode); diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index 39a57f643d81..d16bccebae52 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -529,7 +529,7 @@ static void mt6660_i2c_remove(struct i2c_client *client) mutex_destroy(&chip->io_lock); } -static int __maybe_unused mt6660_i2c_runtime_suspend(struct device *dev) +static int mt6660_i2c_runtime_suspend(struct device *dev) { struct mt6660_chip *chip = dev_get_drvdata(dev); @@ -538,7 +538,7 @@ static int __maybe_unused mt6660_i2c_runtime_suspend(struct device *dev) MT6660_REG_SYSTEM_CTRL, 0x01, 0x01); } -static int __maybe_unused mt6660_i2c_runtime_resume(struct device *dev) +static int mt6660_i2c_runtime_resume(struct device *dev) { struct mt6660_chip *chip = dev_get_drvdata(dev); @@ -548,8 +548,7 @@ static int __maybe_unused mt6660_i2c_runtime_resume(struct device *dev) } static const struct dev_pm_ops mt6660_dev_pm_ops = { - SET_RUNTIME_PM_OPS(mt6660_i2c_runtime_suspend, - mt6660_i2c_runtime_resume, NULL) + RUNTIME_PM_OPS(mt6660_i2c_runtime_suspend, mt6660_i2c_runtime_resume, NULL) }; static const struct of_device_id __maybe_unused mt6660_of_id[] = { @@ -568,7 +567,7 @@ static struct i2c_driver mt6660_i2c_driver = { .driver = { .name = "mt6660", .of_match_table = of_match_ptr(mt6660_of_id), - .pm = &mt6660_dev_pm_ops, + .pm = pm_ptr(&mt6660_dev_pm_ops), }, .probe = mt6660_i2c_probe, .remove = mt6660_i2c_remove, diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index 7e59448e7ac6..caf2edb23088 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -482,10 +482,10 @@ static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int ctrl1_val = 0, ctrl2_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ctrl2_val |= NAU8540_I2S_MS_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c index dc3aaca89919..6f432b992941 100644 --- a/sound/soc/codecs/nau8810.c +++ b/sound/soc/codecs/nau8810.c @@ -613,10 +613,10 @@ static int nau8810_set_dai_fmt(struct snd_soc_dai *codec_dai, u16 ctrl1_val = 0, ctrl2_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ctrl2_val |= NAU8810_CLKIO_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index fd4a96a12060..15d6f8d01f78 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -806,10 +806,10 @@ static int nau8822_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) dev_dbg(component->dev, "%s\n", __func__); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ctrl2_val |= 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: ctrl2_val &= ~1; break; default: diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 12540397fd4d..542bd22e6180 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -368,13 +368,13 @@ static const struct snd_kcontrol_new nau8824_snd_controls[] = { SOC_ENUM("DAC Oversampling Rate", nau8824_dac_oversampl_enum), SOC_SINGLE_TLV("Speaker Right DACR Volume", - NAU8824_REG_CLASSD_GAIN_1, 8, 0x1f, 0, spk_vol_tlv), + NAU8824_REG_CLASSD_GAIN_1, 8, 0x19, 0, spk_vol_tlv), SOC_SINGLE_TLV("Speaker Left DACL Volume", - NAU8824_REG_CLASSD_GAIN_2, 0, 0x1f, 0, spk_vol_tlv), + NAU8824_REG_CLASSD_GAIN_2, 0, 0x19, 0, spk_vol_tlv), SOC_SINGLE_TLV("Speaker Left DACR Volume", - NAU8824_REG_CLASSD_GAIN_1, 0, 0x1f, 0, spk_vol_tlv), + NAU8824_REG_CLASSD_GAIN_1, 0, 0x19, 0, spk_vol_tlv), SOC_SINGLE_TLV("Speaker Right DACL Volume", - NAU8824_REG_CLASSD_GAIN_2, 8, 0x1f, 0, spk_vol_tlv), + NAU8824_REG_CLASSD_GAIN_2, 8, 0x19, 0, spk_vol_tlv), SOC_SINGLE_TLV("Headphone Right DACR Volume", NAU8824_REG_ATT_PORT0, 8, 0x1f, 0, hp_vol_tlv), @@ -1159,10 +1159,10 @@ static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int ctrl1_val = 0, ctrl2_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ctrl2_val |= NAU8824_I2S_MS_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index bde25bc6909d..25b8b19e27ec 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -1356,10 +1356,10 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) unsigned int ctrl1_val = 0, ctrl2_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ctrl2_val |= NAU8825_I2S_MS_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/ntp8835.c b/sound/soc/codecs/ntp8835.c index 796e1410496f..2cc4c6395f55 100644 --- a/sound/soc/codecs/ntp8835.c +++ b/sound/soc/codecs/ntp8835.c @@ -454,7 +454,7 @@ static int ntp8835_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ntp8835_i2c_id[] = { - { "ntp8835", 0 }, + { "ntp8835" }, {} }; MODULE_DEVICE_TABLE(i2c, ntp8835_i2c_id); diff --git a/sound/soc/codecs/ntp8918.c b/sound/soc/codecs/ntp8918.c index 0493ab6acbe4..5593d48ef696 100644 --- a/sound/soc/codecs/ntp8918.c +++ b/sound/soc/codecs/ntp8918.c @@ -8,7 +8,6 @@ */ #include <linux/kernel.h> -#include <linux/clk.h> #include <linux/reset.h> #include <linux/i2c.h> #include <linux/regmap.h> @@ -371,7 +370,7 @@ static int ntp8918_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ntp8918_i2c_id[] = { - { "ntp8918", 0 }, + { "ntp8918" }, {} }; MODULE_DEVICE_TABLE(i2c, ntp8918_i2c_id); diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index fc152496d5dc..a1ec881d7084 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -9,7 +9,6 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/delay.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/of.h> diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c index a514ebd1b68a..a50f9f6e39c1 100644 --- a/sound/soc/codecs/pcm186x-i2c.c +++ b/sound/soc/codecs/pcm186x-i2c.c @@ -33,8 +33,7 @@ MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id); static int pcm186x_i2c_probe(struct i2c_client *i2c) { - const struct i2c_device_id *id = i2c_match_id(pcm186x_i2c_id, i2c); - const enum pcm186x_type type = (enum pcm186x_type)id->driver_data; + const enum pcm186x_type type = (uintptr_t)i2c_get_match_data(i2c); int irq = i2c->irq; struct regmap *regmap; diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index 09c6c1326833..d3d2e7f40170 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -14,7 +14,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/device.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/module.h> #include <sound/core.h> @@ -22,17 +22,22 @@ #include <sound/initval.h> #include <sound/soc.h> -#include "pcm3008.h" +struct pcm3008 { + struct gpio_desc *dem0_pin; + struct gpio_desc *dem1_pin; + struct gpio_desc *pdad_pin; + struct gpio_desc *pdda_pin; +}; static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct pcm3008_setup_data *setup = component->dev->platform_data; + struct pcm3008 *pcm = component->dev->platform_data; - gpio_set_value_cansleep(setup->pdda_pin, - SND_SOC_DAPM_EVENT_ON(event)); + gpiod_set_value_cansleep(pcm->pdda_pin, + SND_SOC_DAPM_EVENT_ON(event)); return 0; } @@ -42,10 +47,10 @@ static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w, int event) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct pcm3008_setup_data *setup = component->dev->platform_data; + struct pcm3008 *pcm = component->dev->platform_data; - gpio_set_value_cansleep(setup->pdad_pin, - SND_SOC_DAPM_EVENT_ON(event)); + gpiod_set_value_cansleep(pcm->pdad_pin, + SND_SOC_DAPM_EVENT_ON(event)); return 0; } @@ -106,11 +111,13 @@ static const struct snd_soc_component_driver soc_component_dev_pcm3008 = { static int pcm3008_codec_probe(struct platform_device *pdev) { - struct pcm3008_setup_data *setup = pdev->dev.platform_data; - int ret; + struct device *dev = &pdev->dev; + struct pcm3008 *pcm; - if (!setup) - return -EINVAL; + pcm = devm_kzalloc(dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + platform_set_drvdata(pdev, pcm); /* DEM1 DEM0 DE-EMPHASIS_MODE * Low Low De-emphasis 44.1 kHz ON @@ -120,30 +127,26 @@ static int pcm3008_codec_probe(struct platform_device *pdev) */ /* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */ - ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin, - GPIOF_OUT_INIT_HIGH, "codec_dem0"); - if (ret != 0) - return ret; + pcm->dem0_pin = devm_gpiod_get(dev, "dem0", GPIOD_OUT_HIGH); + if (IS_ERR(pcm->dem0_pin)) + return PTR_ERR(pcm->dem0_pin); /* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */ - ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin, - GPIOF_OUT_INIT_LOW, "codec_dem1"); - if (ret != 0) - return ret; + pcm->dem1_pin = devm_gpiod_get(dev, "dem1", GPIOD_OUT_LOW); + if (IS_ERR(pcm->dem1_pin)) + return PTR_ERR(pcm->dem1_pin); /* Configure PDAD GPIO. */ - ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin, - GPIOF_OUT_INIT_LOW, "codec_pdad"); - if (ret != 0) - return ret; + pcm->pdad_pin = devm_gpiod_get(dev, "pdad", GPIOD_OUT_LOW); + if (IS_ERR(pcm->pdad_pin)) + return PTR_ERR(pcm->pdad_pin); /* Configure PDDA GPIO. */ - ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin, - GPIOF_OUT_INIT_LOW, "codec_pdda"); - if (ret != 0) - return ret; + pcm->pdda_pin = devm_gpiod_get(dev, "pdda", GPIOD_OUT_LOW); + if (IS_ERR(pcm->pdda_pin)) + return PTR_ERR(pcm->pdda_pin); - return devm_snd_soc_register_component(&pdev->dev, + return devm_snd_soc_register_component(dev, &soc_component_dev_pcm3008, &pcm3008_dai, 1); } diff --git a/sound/soc/codecs/pcm3008.h b/sound/soc/codecs/pcm3008.h deleted file mode 100644 index f7f4fbbd89db..000000000000 --- a/sound/soc/codecs/pcm3008.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * PCM3008 ALSA SoC Layer - * - * Author: Hugo Villeneuve - * Copyright (C) 2008 Lyrtech inc - */ - -#ifndef __LINUX_SND_SOC_PCM3008_H -#define __LINUX_SND_SOC_PCM3008_H - -struct pcm3008_setup_data { - unsigned dem0_pin; - unsigned dem1_pin; - unsigned pdad_pin; - unsigned pdda_pin; -}; - -#endif diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c index 7052cc0c97d1..ff18c74b616c 100644 --- a/sound/soc/codecs/pcm3168a-i2c.c +++ b/sound/soc/codecs/pcm3168a-i2c.c @@ -10,6 +10,7 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <sound/soc.h> @@ -37,6 +38,13 @@ static const struct i2c_device_id pcm3168a_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id); +static const struct acpi_device_id pcm3168a_acpi_match[] = { + { "PCM3168A" }, + { "104C3168" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, pcm3168a_acpi_match); + static const struct of_device_id pcm3168a_of_match[] = { { .compatible = "ti,pcm3168a", }, { } @@ -49,8 +57,9 @@ static struct i2c_driver pcm3168a_i2c_driver = { .id_table = pcm3168a_i2c_id, .driver = { .name = "pcm3168a", + .acpi_match_table = pcm3168a_acpi_match, .of_match_table = pcm3168a_of_match, - .pm = &pcm3168a_pm_ops, + .pm = pm_ptr(&pcm3168a_pm_ops), }, }; module_i2c_driver(pcm3168a_i2c_driver); diff --git a/sound/soc/codecs/pcm3168a-spi.c b/sound/soc/codecs/pcm3168a-spi.c index b5b08046f545..0871338eacba 100644 --- a/sound/soc/codecs/pcm3168a-spi.c +++ b/sound/soc/codecs/pcm3168a-spi.c @@ -50,7 +50,7 @@ static struct spi_driver pcm3168a_spi_driver = { .driver = { .name = "pcm3168a", .of_match_table = pcm3168a_of_match, - .pm = &pcm3168a_pm_ops, + .pm = pm_ptr(&pcm3168a_pm_ops), }, }; module_spi_driver(pcm3168a_spi_driver); diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index fac0617ab95b..c8617a488b11 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -493,9 +493,9 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, } break; case 24: - if (provider_mode || (format == SND_SOC_DAIFMT_DSP_A) || - (format == SND_SOC_DAIFMT_DSP_B)) { - dev_err(component->dev, "24-bit slots not supported in provider mode, or consumer mode using DSP\n"); + if (!provider_mode && ((format == SND_SOC_DAIFMT_DSP_A) || + (format == SND_SOC_DAIFMT_DSP_B))) { + dev_err(component->dev, "24-bit slots not supported in consumer mode using DSP\n"); return -EINVAL; } break; @@ -743,7 +743,7 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst), "failed to acquire RST gpio\n"); - pcm3168a->scki = devm_clk_get(dev, "scki"); + pcm3168a->scki = devm_clk_get_optional(dev, "scki"); if (IS_ERR(pcm3168a->scki)) return dev_err_probe(dev, PTR_ERR(pcm3168a->scki), "failed to acquire clock 'scki'\n"); @@ -755,6 +755,9 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) } pcm3168a->sysclk = clk_get_rate(pcm3168a->scki); + /* Fallback to the default if no clk entry available. */ + if (!pcm3168a->sysclk) + pcm3168a->sysclk = 24576000; for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++) pcm3168a->supplies[i].supply = pcm3168a_supply_names[i]; @@ -846,7 +849,6 @@ void pcm3168a_remove(struct device *dev) } EXPORT_SYMBOL_GPL(pcm3168a_remove); -#ifdef CONFIG_PM static int pcm3168a_rt_resume(struct device *dev) { struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); @@ -902,12 +904,10 @@ static int pcm3168a_rt_suspend(struct device *dev) return 0; } -#endif -const struct dev_pm_ops pcm3168a_pm_ops = { - SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL) +EXPORT_GPL_DEV_PM_OPS(pcm3168a_pm_ops) = { + RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL) }; -EXPORT_SYMBOL_GPL(pcm3168a_pm_ops); MODULE_DESCRIPTION("PCM3168A codec driver"); MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 92bcf5179779..a1d849b0c50f 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -79,7 +79,7 @@ static struct i2c_driver pcm512x_i2c_driver = { .name = "pcm512x", .of_match_table = of_match_ptr(pcm512x_of_match), .acpi_match_table = ACPI_PTR(pcm512x_acpi_match), - .pm = &pcm512x_pm_ops, + .pm = pm_ptr(&pcm512x_pm_ops), }, }; diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index 6629b862f47d..92f7f78a4e20 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -58,7 +58,7 @@ static struct spi_driver pcm512x_spi_driver = { .driver = { .name = "pcm512x", .of_match_table = pcm512x_of_match, - .pm = &pcm512x_pm_ops, + .pm = pm_ptr(&pcm512x_pm_ops), }, }; diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index aa8edf87b743..007dfc0fa224 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -1725,7 +1725,6 @@ void pcm512x_remove(struct device *dev) } EXPORT_SYMBOL_GPL(pcm512x_remove); -#ifdef CONFIG_PM static int pcm512x_suspend(struct device *dev) { struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); @@ -1787,12 +1786,10 @@ static int pcm512x_resume(struct device *dev) return 0; } -#endif -const struct dev_pm_ops pcm512x_pm_ops = { - SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL) +EXPORT_GPL_DEV_PM_OPS(pcm512x_pm_ops) = { + RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL) }; -EXPORT_SYMBOL_GPL(pcm512x_pm_ops); MODULE_DESCRIPTION("ASoC PCM512x codec driver"); MODULE_AUTHOR("Mark Brown <broonie@kernel.org>"); diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index 5d99877f8839..b2bd2f172ae7 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -14,7 +14,7 @@ #include <linux/unaligned.h> #include <linux/firmware.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/of_irq.h> @@ -2035,10 +2035,8 @@ static const struct regmap_config pcmdevice_i2c_regmap = { static void pcmdevice_remove(struct pcmdevice_priv *pcm_dev) { - if (gpio_is_valid(pcm_dev->irq_info.gpio)) { - gpio_free(pcm_dev->irq_info.gpio); - free_irq(pcm_dev->irq_info.nmb, pcm_dev); - } + if (pcm_dev->irq) + free_irq(pcm_dev->irq, pcm_dev); mutex_destroy(&pcm_dev->codec_lock); } @@ -2059,7 +2057,6 @@ static char *str_to_upper(char *str) static int pcmdevice_i2c_probe(struct i2c_client *i2c) { - const struct i2c_device_id *id = i2c_match_id(pcmdevice_i2c_id, i2c); struct pcmdevice_priv *pcm_dev; struct device_node *np; unsigned int dev_addrs[PCMDEVICE_MAX_I2C_DEVICES]; @@ -2069,7 +2066,7 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c) if (!pcm_dev) return -ENOMEM; - pcm_dev->chip_id = (id != NULL) ? id->driver_data : 0; + pcm_dev->chip_id = (uintptr_t)i2c_get_match_data(i2c); pcm_dev->dev = &i2c->dev; pcm_dev->client = i2c; @@ -2110,7 +2107,7 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c) ndev = 1; dev_addrs[0] = i2c->addr; } - pcm_dev->irq_info.gpio = of_irq_get(np, 0); + pcm_dev->irq = of_irq_get(np, 0); for (i = 0; i < ndev; i++) pcm_dev->addr[i] = dev_addrs[i]; @@ -2133,22 +2130,10 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c) if (pcm_dev->chip_id == PCM1690) goto skip_interrupt; - if (gpio_is_valid(pcm_dev->irq_info.gpio)) { - dev_dbg(pcm_dev->dev, "irq-gpio = %d", pcm_dev->irq_info.gpio); - - ret = gpio_request(pcm_dev->irq_info.gpio, "PCMDEV-IRQ"); - if (!ret) { - int gpio = pcm_dev->irq_info.gpio; - - gpio_direction_input(gpio); - pcm_dev->irq_info.nmb = gpio_to_irq(gpio); - - } else - dev_err(pcm_dev->dev, "%s: GPIO %d request error\n", - __func__, pcm_dev->irq_info.gpio); + if (pcm_dev->irq) { + dev_dbg(pcm_dev->dev, "irq = %d", pcm_dev->irq); } else - dev_err(pcm_dev->dev, "Looking up irq-gpio failed %d\n", - pcm_dev->irq_info.gpio); + dev_err(pcm_dev->dev, "No irq provided\n"); skip_interrupt: ret = devm_snd_soc_register_component(&i2c->dev, diff --git a/sound/soc/codecs/pcm6240.h b/sound/soc/codecs/pcm6240.h index 1e125bb97286..2d8f9e798139 100644 --- a/sound/soc/codecs/pcm6240.h +++ b/sound/soc/codecs/pcm6240.h @@ -208,11 +208,6 @@ struct pcmdevice_regbin { struct pcmdevice_config_info **cfg_info; }; -struct pcmdevice_irqinfo { - int gpio; - int nmb; -}; - struct pcmdevice_priv { struct snd_soc_component *component; struct i2c_client *client; @@ -221,7 +216,7 @@ struct pcmdevice_priv { struct gpio_desc *hw_rst; struct regmap *regmap; struct pcmdevice_regbin regbin; - struct pcmdevice_irqinfo irq_info; + int irq; unsigned int addr[PCMDEVICE_MAX_I2C_DEVICES]; unsigned int chip_id; int cur_conf; diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index bb9ca6354ae1..a989cfe058f0 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -26,8 +26,7 @@ struct peb2466_lookup { unsigned int count; }; -#define PEB2466_TLV_SIZE (sizeof((unsigned int []){TLV_DB_SCALE_ITEM(0, 0, 0)}) / \ - sizeof(unsigned int)) +#define PEB2466_TLV_SIZE ARRAY_SIZE(((unsigned int[]){TLV_DB_SCALE_ITEM(0, 0, 0)})) struct peb2466_lkup_ctrl { int reg; diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c index 3c5b66357661..3c9957b00881 100644 --- a/sound/soc/codecs/rk817_codec.c +++ b/sound/soc/codecs/rk817_codec.c @@ -303,10 +303,10 @@ static int rk817_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int i2s_mst = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: i2s_mst |= RK817_I2S_MODE_SLV; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: i2s_mst |= RK817_I2S_MODE_MST; break; default: diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index a0e75b03e9dc..b84dd18ddde9 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -1671,7 +1671,7 @@ static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) snd_soc_dapm_mutex_lock(dapm); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT1011_I2S_TDM_MS_S; break; default: @@ -2192,15 +2192,15 @@ static const struct regmap_config rt1011_regmap = { #if defined(CONFIG_OF) static const struct of_device_id rt1011_of_match[] = { { .compatible = "realtek,rt1011", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt1011_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt1011_acpi_match[] = { - {"10EC1011", 0,}, - {}, + { "10EC1011" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt1011_acpi_match); #endif diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 0f806dde9c39..818b45226b72 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -753,10 +753,10 @@ static int rt1015_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0, reg_val2 = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: reg_val |= RT1015_TCON_TDM_MS_M; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT1015_TCON_TDM_MS_S; break; default: @@ -1105,15 +1105,15 @@ MODULE_DEVICE_TABLE(i2c, rt1015_i2c_id); #if defined(CONFIG_OF) static const struct of_device_id rt1015_of_match[] = { { .compatible = "realtek,rt1015", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt1015_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt1015_acpi_match[] = { - {"10EC1015", 0,}, - {}, + { "10EC1015" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt1015_acpi_match); #endif diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c index fed4da23cba2..9f86f071fca8 100644 --- a/sound/soc/codecs/rt1016.c +++ b/sound/soc/codecs/rt1016.c @@ -367,11 +367,11 @@ static int rt1016_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: reg_val |= RT1016_I2S_MS_M; rt1016->master = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT1016_I2S_MS_S; break; default: @@ -616,15 +616,15 @@ MODULE_DEVICE_TABLE(i2c, rt1016_i2c_id); #if defined(CONFIG_OF) static const struct of_device_id rt1016_of_match[] = { { .compatible = "realtek,rt1016", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt1016_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt1016_acpi_match[] = { - {"10EC1016", 0,}, - {}, + { "10EC1016" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt1016_acpi_match); #endif diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index 7c8103a0d562..88fc23a4999f 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -758,7 +758,7 @@ static const struct sdw_device_id rt1017_sdca_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt1017_sdca_id); -static int __maybe_unused rt1017_sdca_dev_suspend(struct device *dev) +static int rt1017_sdca_dev_suspend(struct device *dev) { struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); @@ -772,7 +772,7 @@ static int __maybe_unused rt1017_sdca_dev_suspend(struct device *dev) #define RT1017_PROBE_TIMEOUT 5000 -static int __maybe_unused rt1017_sdca_dev_resume(struct device *dev) +static int rt1017_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); @@ -802,14 +802,14 @@ regmap_sync: } static const struct dev_pm_ops rt1017_sdca_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume) - SET_RUNTIME_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume) + RUNTIME_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume, NULL) }; static struct sdw_driver rt1017_sdca_sdw_driver = { .driver = { .name = "rt1017-sdca", - .pm = &rt1017_sdca_pm, + .pm = pm_ptr(&rt1017_sdca_pm), }, .probe = rt1017_sdca_sdw_probe, .remove = rt1017_sdca_sdw_remove, diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c index d989d06a2614..86539c6f6cc1 100644 --- a/sound/soc/codecs/rt1019.c +++ b/sound/soc/codecs/rt1019.c @@ -547,14 +547,14 @@ MODULE_DEVICE_TABLE(i2c, rt1019_i2c_id); static const struct of_device_id rt1019_of_match[] __maybe_unused = { { .compatible = "realtek,rt1019", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt1019_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id rt1019_acpi_match[] = { - { "10EC1019", 0}, - { }, + { "10EC1019" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt1019_acpi_match); #endif diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index c2b55be8d165..26b7382f97ef 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -697,11 +697,11 @@ static int rt1305_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0, reg1_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: reg_val |= RT1305_SEL_I2S_OUT_MODE_M; rt1305->master = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT1305_SEL_I2S_OUT_MODE_S; rt1305->master = 0; break; @@ -966,16 +966,16 @@ static const struct regmap_config rt1305_regmap = { static const struct of_device_id rt1305_of_match[] = { { .compatible = "realtek,rt1305", }, { .compatible = "realtek,rt1306", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt1305_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt1305_acpi_match[] = { - {"10EC1305", 0,}, - {"10EC1306", 0,}, - {}, + { "10EC1305" }, + { "10EC1306" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt1305_acpi_match); #endif diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 563df483a466..ea708068f0e8 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -753,7 +753,7 @@ static const struct sdw_device_id rt1308_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt1308_id); -static int __maybe_unused rt1308_dev_suspend(struct device *dev) +static int rt1308_dev_suspend(struct device *dev) { struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); @@ -767,7 +767,7 @@ static int __maybe_unused rt1308_dev_suspend(struct device *dev) #define RT1308_PROBE_TIMEOUT 5000 -static int __maybe_unused rt1308_dev_resume(struct device *dev) +static int rt1308_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); @@ -797,14 +797,14 @@ regmap_sync: } static const struct dev_pm_ops rt1308_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume) - SET_RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume) + RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL) }; static struct sdw_driver rt1308_sdw_driver = { .driver = { .name = "rt1308", - .pm = &rt1308_pm, + .pm = pm_ptr(&rt1308_pm), }, .probe = rt1308_sdw_probe, .remove = rt1308_sdw_remove, diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c index b366338cea71..df50b38c24b9 100644 --- a/sound/soc/codecs/rt1308.c +++ b/sound/soc/codecs/rt1308.c @@ -523,7 +523,7 @@ static int rt1308_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0, reg1_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: rt1308->master = 0; break; default: @@ -781,15 +781,15 @@ static const struct regmap_config rt1308_regmap = { #ifdef CONFIG_OF static const struct of_device_id rt1308_of_match[] = { { .compatible = "realtek,rt1308", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, rt1308_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt1308_acpi_match[] = { - { "10EC1308", 0, }, - { }, + { "10EC1308" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt1308_acpi_match); #endif diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 22f1ed4e03f1..960b6c4f5a66 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -730,7 +730,7 @@ static const struct sdw_device_id rt1316_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt1316_id); -static int __maybe_unused rt1316_dev_suspend(struct device *dev) +static int rt1316_dev_suspend(struct device *dev) { struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev); @@ -744,7 +744,7 @@ static int __maybe_unused rt1316_dev_suspend(struct device *dev) #define RT1316_PROBE_TIMEOUT 5000 -static int __maybe_unused rt1316_dev_resume(struct device *dev) +static int rt1316_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev); @@ -774,14 +774,14 @@ regmap_sync: } static const struct dev_pm_ops rt1316_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume) - SET_RUNTIME_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume) + RUNTIME_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume, NULL) }; static struct sdw_driver rt1316_sdw_driver = { .driver = { .name = "rt1316-sdca", - .pm = &rt1316_pm, + .pm = pm_ptr(&rt1316_pm), }, .probe = rt1316_sdw_probe, .remove = rt1316_sdw_remove, diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index 319f71f5e60d..4eb636e0c9ed 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -807,7 +807,7 @@ static const struct sdw_device_id rt1318_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt1318_id); -static int __maybe_unused rt1318_dev_suspend(struct device *dev) +static int rt1318_dev_suspend(struct device *dev) { struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev); @@ -820,7 +820,7 @@ static int __maybe_unused rt1318_dev_suspend(struct device *dev) #define RT1318_PROBE_TIMEOUT 5000 -static int __maybe_unused rt1318_dev_resume(struct device *dev) +static int rt1318_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev); @@ -848,14 +848,14 @@ regmap_sync: } static const struct dev_pm_ops rt1318_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume) - SET_RUNTIME_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume) + RUNTIME_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume, NULL) }; static struct sdw_driver rt1318_sdw_driver = { .driver = { .name = "rt1318-sdca", - .pm = &rt1318_pm, + .pm = pm_ptr(&rt1318_pm), }, .probe = rt1318_sdw_probe, .remove = rt1318_sdw_remove, diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c index e12b1e96a53a..ae01b2ce630b 100644 --- a/sound/soc/codecs/rt1318.c +++ b/sound/soc/codecs/rt1318.c @@ -1147,14 +1147,14 @@ MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id); static const struct of_device_id rt1318_of_match[] = { { .compatible = "realtek,rt1318", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt1318_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id rt1318_acpi_match[] = { - { "10EC1318", 0}, - { }, + { "10EC1318" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt1318_acpi_match); #endif diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 3510c3819074..f51ba345a16e 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -535,6 +535,9 @@ static int rt1320_read_prop(struct sdw_slave *slave) /* set the timeout values */ prop->clk_stop_timeout = 64; + /* BIOS may set wake_capable. Make sure it is 0 as wake events are disabled. */ + prop->wake_capable = 0; + return 0; } @@ -1455,7 +1458,7 @@ static const struct sdw_device_id rt1320_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt1320_id); -static int __maybe_unused rt1320_dev_suspend(struct device *dev) +static int rt1320_dev_suspend(struct device *dev) { struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev); @@ -1469,7 +1472,7 @@ static int __maybe_unused rt1320_dev_suspend(struct device *dev) #define RT1320_PROBE_TIMEOUT 5000 -static int __maybe_unused rt1320_dev_resume(struct device *dev) +static int rt1320_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev); @@ -1498,14 +1501,14 @@ regmap_sync: } static const struct dev_pm_ops rt1320_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume) - SET_RUNTIME_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume) + RUNTIME_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume, NULL) }; static struct sdw_driver rt1320_sdw_driver = { .driver = { .name = "rt1320-sdca", - .pm = &rt1320_pm, + .pm = pm_ptr(&rt1320_pm), }, .probe = rt1320_sdw_probe, .remove = rt1320_sdw_remove, diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index bd61a257d7b5..2c055c45111f 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -706,12 +706,12 @@ static int rt274_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: snd_soc_component_update_bits(component, RT274_I2S_CTRL1, RT274_I2S_MODE_MASK, RT274_I2S_MODE_M); rt274->master = true; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: snd_soc_component_update_bits(component, RT274_I2S_CTRL1, RT274_I2S_MODE_MASK, RT274_I2S_MODE_S); rt274->master = false; @@ -1091,7 +1091,7 @@ static const struct regmap_config rt274_regmap = { #ifdef CONFIG_OF static const struct of_device_id rt274_of_match[] = { {.compatible = "realtek,rt274"}, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt274_of_match); #endif @@ -1104,9 +1104,9 @@ MODULE_DEVICE_TABLE(i2c, rt274_i2c_id); #ifdef CONFIG_ACPI static const struct acpi_device_id rt274_acpi_match[] = { - { "10EC0274", 0 }, - { "INT34C2", 0 }, - {}, + { "10EC0274" }, + { "INT34C2" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt274_acpi_match); #endif diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index d0f533120c33..fd8de8b49793 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -765,11 +765,11 @@ static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct snd_soc_component *component = dai->component; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: snd_soc_component_update_bits(component, RT286_I2S_CTRL1, 0x800, 0x800); break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: snd_soc_component_update_bits(component, RT286_I2S_CTRL1, 0x800, 0x0); break; @@ -1083,8 +1083,9 @@ MODULE_DEVICE_TABLE(i2c, rt286_i2c_id); #ifdef CONFIG_ACPI static const struct acpi_device_id rt286_acpi_match[] = { - { "INT343A", 0 }, - {}, + { "10EC0286" }, + { "INT343A" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt286_acpi_match); #endif diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 13aef6c5e91c..ee3d9291eea6 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -829,11 +829,11 @@ static int rt298_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct snd_soc_component *component = dai->component; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: snd_soc_component_update_bits(component, RT298_I2S_CTRL1, 0x800, 0x800); break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: snd_soc_component_update_bits(component, RT298_I2S_CTRL1, 0x800, 0x0); break; @@ -1144,8 +1144,9 @@ MODULE_DEVICE_TABLE(i2c, rt298_i2c_id); #ifdef CONFIG_ACPI static const struct acpi_device_id rt298_acpi_match[] = { - { "INT343A", 0 }, - {}, + { "10EC0298" }, + { "INT343A" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt298_acpi_match); #endif diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index f475c8cfadae..54d84581ec47 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -458,7 +458,7 @@ static int rt5514_spi_probe(struct spi_device *spi) return 0; } -static int __maybe_unused rt5514_suspend(struct device *dev) +static int rt5514_suspend(struct device *dev) { int irq = to_spi_device(dev)->irq; @@ -468,7 +468,7 @@ static int __maybe_unused rt5514_suspend(struct device *dev) return 0; } -static int __maybe_unused rt5514_resume(struct device *dev) +static int rt5514_resume(struct device *dev) { struct rt5514_dsp *rt5514_dsp = dev_get_drvdata(dev); int irq = to_spi_device(dev)->irq; @@ -490,7 +490,7 @@ static int __maybe_unused rt5514_resume(struct device *dev) } static const struct dev_pm_ops rt5514_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(rt5514_suspend, rt5514_resume) + SYSTEM_SLEEP_PM_OPS(rt5514_suspend, rt5514_resume) }; static const struct of_device_id rt5514_of_match[] = { @@ -502,7 +502,7 @@ MODULE_DEVICE_TABLE(of, rt5514_of_match); static struct spi_driver rt5514_spi_driver = { .driver = { .name = "rt5514", - .pm = &rt5514_pm_ops, + .pm = pm_ptr(&rt5514_pm_ops), .of_match_table = of_match_ptr(rt5514_of_match), }, .probe = rt5514_spi_probe, diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 2b3c0f9e178c..ab9d81c32be8 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1091,8 +1091,7 @@ static int rt5514_set_bias_level(struct snd_soc_component *component, static int rt5514_probe(struct snd_soc_component *component) { struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); - struct platform_device *pdev = container_of(component->dev, - struct platform_device, dev); + struct platform_device *pdev = to_platform_device(component->dev); rt5514->mclk = devm_clk_get_optional(component->dev, "mclk"); if (IS_ERR(rt5514->mclk)) @@ -1207,15 +1206,15 @@ 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 #ifdef CONFIG_ACPI static const struct acpi_device_id rt5514_acpi_match[] = { - { "10EC5514", 0}, - {}, + { "10EC5514" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5514_acpi_match); #endif @@ -1232,7 +1231,7 @@ static int rt5514_parse_dp(struct rt5514_priv *rt5514, struct device *dev) return 0; } -static __maybe_unused int rt5514_i2c_resume(struct device *dev) +static int rt5514_i2c_resume(struct device *dev) { struct rt5514_priv *rt5514 = dev_get_drvdata(dev); unsigned int val; @@ -1314,7 +1313,7 @@ static int rt5514_i2c_probe(struct i2c_client *i2c) } static const struct dev_pm_ops rt5514_i2_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(NULL, rt5514_i2c_resume) + SYSTEM_SLEEP_PM_OPS(NULL, rt5514_i2c_resume) }; static struct i2c_driver rt5514_i2c_driver = { @@ -1322,7 +1321,7 @@ static struct i2c_driver rt5514_i2c_driver = { .name = "rt5514", .acpi_match_table = ACPI_PTR(rt5514_acpi_match), .of_match_table = of_match_ptr(rt5514_of_match), - .pm = &rt5514_i2_pm_ops, + .pm = pm_ptr(&rt5514_i2_pm_ops), }, .probe = rt5514_i2c_probe, .id_table = rt5514_i2c_id, diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index 34461c462009..1ec486707ff9 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1015,10 +1015,10 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5616->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT5616_I2S_MS_S; rt5616->master[dai->id] = 0; break; diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 12df0c4f2097..d523477c5102 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1411,10 +1411,10 @@ static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, dev_dbg(component->dev, "enter %s\n", __func__); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5631->master = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: iface |= RT5631_SDP_MODE_SEL_SLAVE; rt5631->master = 0; break; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 855139348edb..21a18012b4c0 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -1773,10 +1773,10 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) int dai_sel; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5640->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT5640_I2S_MS_S; rt5640->master[dai->id] = 0; break; @@ -2963,19 +2963,19 @@ MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id); static const struct of_device_id rt5640_of_match[] = { { .compatible = "realtek,rt5639", }, { .compatible = "realtek,rt5640", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt5640_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt5640_acpi_match[] = { - { "INT33CA", 0 }, - { "10EC3276", 0 }, - { "10EC5640", 0 }, - { "10EC5642", 0 }, - { "INTCCFFD", 0 }, - { }, + { "10EC3276" }, + { "10EC5640" }, + { "10EC5642" }, + { "INT33CA" }, + { "INTCCFFD" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match); #endif diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 51187b1e0ed2..343e3bcef0ca 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2841,10 +2841,10 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5645->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT5645_I2S_MS_S; rt5645->master[dai->id] = 0; break; @@ -3660,12 +3660,12 @@ MODULE_DEVICE_TABLE(of, rt5645_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id rt5645_acpi_match[] = { - { "10EC5645", 0 }, - { "10EC5648", 0 }, - { "10EC5650", 0 }, - { "10EC5640", 0 }, - { "10EC3270", 0 }, - {}, + { "10EC3270" }, + { "10EC5640" }, + { "10EC5645" }, + { "10EC5648" }, + { "10EC5650" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match); #endif @@ -4314,7 +4314,7 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c) gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0); } -static int __maybe_unused rt5645_sys_suspend(struct device *dev) +static int rt5645_sys_suspend(struct device *dev) { struct rt5645_priv *rt5645 = dev_get_drvdata(dev); @@ -4327,7 +4327,7 @@ static int __maybe_unused rt5645_sys_suspend(struct device *dev) return 0; } -static int __maybe_unused rt5645_sys_resume(struct device *dev) +static int rt5645_sys_resume(struct device *dev) { struct rt5645_priv *rt5645 = dev_get_drvdata(dev); @@ -4342,7 +4342,7 @@ static int __maybe_unused rt5645_sys_resume(struct device *dev) } static const struct dev_pm_ops rt5645_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt5645_sys_suspend, rt5645_sys_resume) + SYSTEM_SLEEP_PM_OPS(rt5645_sys_suspend, rt5645_sys_resume) }; static struct i2c_driver rt5645_i2c_driver = { @@ -4350,7 +4350,7 @@ static struct i2c_driver rt5645_i2c_driver = { .name = "rt5645", .of_match_table = of_match_ptr(rt5645_of_match), .acpi_match_table = ACPI_PTR(rt5645_acpi_match), - .pm = &rt5645_pm, + .pm = pm_ptr(&rt5645_pm), }, .probe = rt5645_i2c_probe, .remove = rt5645_i2c_remove, diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 00421a1f54bf..9eeeba8cd6ff 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -1352,10 +1352,10 @@ static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5651->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT5651_I2S_MS_S; rt5651->master[dai->id] = 0; break; @@ -2184,16 +2184,16 @@ static const struct regmap_config rt5651_regmap = { #if defined(CONFIG_OF) static const struct of_device_id rt5651_of_match[] = { { .compatible = "realtek,rt5651", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt5651_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt5651_acpi_match[] = { - { "10EC5651", 0 }, - { "10EC5640", 0 }, - { }, + { "10EC5640" }, + { "10EC5651" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5651_acpi_match); #endif diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index a2652fa6e1d7..31b47db7b4f7 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -3363,10 +3363,10 @@ static int rt5659_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5659->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT5659_I2S_MS_S; rt5659->master[dai->id] = 0; break; @@ -4315,16 +4315,16 @@ static void rt5659_i2c_shutdown(struct i2c_client *client) 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 const struct acpi_device_id rt5659_acpi_match[] = { - { "10EC5658", 0, }, - { "10EC5659", 0, }, - { }, + { "10EC5658" }, + { "10EC5659" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5659_acpi_match); #endif diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 3ac41d2c279b..82b92e83be4c 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -905,11 +905,11 @@ static int rt5660_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5660->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT5660_I2S_MS_S; rt5660->master[dai->id] = 0; break; @@ -1232,16 +1232,16 @@ MODULE_DEVICE_TABLE(i2c, rt5660_i2c_id); #ifdef CONFIG_OF static const struct of_device_id rt5660_of_match[] = { { .compatible = "realtek,rt5660", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt5660_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt5660_acpi_match[] = { - { "10EC5660", 0 }, - { "10EC3277", 0 }, - { }, + { "10EC3277" }, + { "10EC5660" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5660_acpi_match); #endif diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 9d32debd3689..45057562c0c8 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -2814,9 +2814,9 @@ static int rt5663_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT5663_I2S_MS_S; break; default: @@ -3315,15 +3315,15 @@ MODULE_DEVICE_TABLE(i2c, rt5663_i2c_id); #if defined(CONFIG_OF) static const struct of_device_id rt5663_of_match[] = { { .compatible = "realtek,rt5663", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt5663_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt5663_acpi_match[] = { - { "10EC5663", 0}, - {}, + { "10EC5663" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5663_acpi_match); #endif diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 47df14ba5278..e0d1991cffdb 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4219,10 +4219,10 @@ static int rt5665_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5665->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT5665_I2S_MS_S; rt5665->master[dai->id] = 0; break; @@ -4949,16 +4949,16 @@ static void rt5665_i2c_shutdown(struct i2c_client *client) static const struct of_device_id rt5665_of_match[] = { {.compatible = "realtek,rt5665"}, {.compatible = "realtek,rt5666"}, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt5665_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt5665_acpi_match[] = { - {"10EC5665", 0,}, - {"10EC5666", 0,}, - {}, + { "10EC5665" }, + { "10EC5666" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5665_acpi_match); #endif diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index 494ca3ce9b96..f626453f332b 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2010,10 +2010,10 @@ static int rt5668_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0, tdm_ctrl = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5668->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: rt5668->master[dai->id] = 0; break; default: @@ -2598,15 +2598,15 @@ static void rt5668_i2c_shutdown(struct i2c_client *client) #ifdef CONFIG_OF static const struct of_device_id rt5668_of_match[] = { {.compatible = "realtek,rt5668b"}, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt5668_of_match); #endif #ifdef CONFIG_ACPI static const struct acpi_device_id rt5668_acpi_match[] = { - {"10EC5668", 0,}, - {}, + { "10EC5668" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5668_acpi_match); #endif diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 30bf96c35b58..efd26082f19a 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2439,10 +2439,10 @@ static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5670->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT5670_I2S_MS_S; rt5670->master[dai->id] = 0; break; @@ -2880,10 +2880,10 @@ MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id); #ifdef CONFIG_ACPI static const struct acpi_device_id rt5670_acpi_match[] = { - { "10EC5670", 0}, - { "10EC5672", 0}, - { "10EC5640", 0}, /* quirk */ - { }, + { "10EC5640" }, /* quirk */ + { "10EC5670" }, + { "10EC5672" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match); #endif diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c index d91a2184f67c..abe0a5a95770 100644 --- a/sound/soc/codecs/rt5677-spi.c +++ b/sound/soc/codecs/rt5677-spi.c @@ -617,7 +617,8 @@ static int rt5677_spi_probe(struct spi_device *spi) #ifdef CONFIG_ACPI static const struct acpi_device_id rt5677_spi_acpi_id[] = { - { "RT5677AA", 0 }, + { "10EC5677" }, + { "RT5677AA" }, { } }; MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id); diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 0e70a3ab42b5..6e4774148566 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4377,10 +4377,10 @@ static int rt5677_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5677->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg_val |= RT5677_I2S_MS_S; rt5677->master[dai->id] = 0; break; @@ -5201,6 +5201,7 @@ static const struct of_device_id rt5677_of_match[] = { MODULE_DEVICE_TABLE(of, rt5677_of_match); static const struct acpi_device_id rt5677_acpi_match[] = { + { "10EC5677", RT5677 }, { "RT5677CE", RT5677 }, { } }; diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index ff9e14fad0cd..bba987308e15 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -186,6 +186,12 @@ static int rt5682_i2c_probe(struct i2c_client *i2c) return -ENODEV; } + regmap_read(rt5682->regmap, RT5682_INT_DEVICE_ID, &val); + if (val == 0x6956) { + dev_dbg(&i2c->dev, "ALC5682I-VE device\n"); + rt5682->ve_ic = true; + } + mutex_init(&rt5682->calibrate_mutex); rt5682_calibrate(rt5682); @@ -307,13 +313,13 @@ static void rt5682_i2c_remove(struct i2c_client *client) static const struct of_device_id rt5682_of_match[] = { {.compatible = "realtek,rt5682i"}, - {}, + { } }; MODULE_DEVICE_TABLE(of, rt5682_of_match); static const struct acpi_device_id rt5682_acpi_match[] = { - {"10EC5682", 0,}, - {}, + { "10EC5682" }, + { } }; MODULE_DEVICE_TABLE(acpi, rt5682_acpi_match); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 5edf11e136b4..aa229894129b 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -709,7 +709,7 @@ static const struct sdw_device_id rt5682_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt5682_id); -static int __maybe_unused rt5682_dev_suspend(struct device *dev) +static int rt5682_dev_suspend(struct device *dev) { struct rt5682_priv *rt5682 = dev_get_drvdata(dev); @@ -725,7 +725,7 @@ static int __maybe_unused rt5682_dev_suspend(struct device *dev) return 0; } -static int __maybe_unused rt5682_dev_system_suspend(struct device *dev) +static int rt5682_dev_system_suspend(struct device *dev) { struct rt5682_priv *rt5682 = dev_get_drvdata(dev); struct sdw_slave *slave = dev_to_sdw_dev(dev); @@ -753,7 +753,7 @@ static int __maybe_unused rt5682_dev_system_suspend(struct device *dev) return rt5682_dev_suspend(dev); } -static int __maybe_unused rt5682_dev_resume(struct device *dev) +static int rt5682_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt5682_priv *rt5682 = dev_get_drvdata(dev); @@ -791,14 +791,14 @@ regmap_sync: } static const struct dev_pm_ops rt5682_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt5682_dev_system_suspend, rt5682_dev_resume) - SET_RUNTIME_PM_OPS(rt5682_dev_suspend, rt5682_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt5682_dev_system_suspend, rt5682_dev_resume) + RUNTIME_PM_OPS(rt5682_dev_suspend, rt5682_dev_resume, NULL) }; static struct sdw_driver rt5682_sdw_driver = { .driver = { .name = "rt5682", - .pm = &rt5682_pm, + .pm = pm_ptr(&rt5682_pm), }, .probe = rt5682_sdw_probe, .remove = rt5682_sdw_remove, diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index aa163ec40862..7c88370e2dee 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -395,6 +395,7 @@ bool rt5682_volatile_register(struct device *dev, unsigned int reg) case RT5682_4BTN_IL_CMD_1: case RT5682_AJD1_CTRL: case RT5682_HP_CALIB_CTRL_1: + case RT5682_INT_DEVICE_ID: case RT5682_DEVICE_ID: case RT5682_I2C_MODE: case RT5682_HP_CALIB_CTRL_10: @@ -419,6 +420,7 @@ bool rt5682_readable_register(struct device *dev, unsigned int reg) { switch (reg) { case RT5682_RESET: + case RT5682_INT_DEVICE_ID: case RT5682_VERSION_ID: case RT5682_VENDOR_ID: case RT5682_DEVICE_ID: @@ -2223,10 +2225,10 @@ static int rt5682_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0, tdm_ctrl = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5682->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: rt5682->master[dai->id] = 0; break; default: @@ -3139,7 +3141,10 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0100); regmap_write(rt5682->regmap, RT5682_HP_IMP_SENS_CTRL_19, 0x3800); regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000); - regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7005); + if (rt5682->ve_ic) + regmap_write(rt5682->regmap, RT5682_CHOP_ADC, 0x7005); + else + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7005); regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c); regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321); @@ -3168,7 +3173,10 @@ void rt5682_calibrate(struct rt5682_priv *rt5682) regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x0000); regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000); regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000); - regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); + if (rt5682->ve_ic) + regmap_write(rt5682->regmap, RT5682_CHOP_ADC, 0x2005); + else + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c); diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index b2d9e87af259..de43a5d99403 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -22,6 +22,7 @@ /* Info */ #define RT5682_RESET 0x0000 +#define RT5682_INT_DEVICE_ID 0x00f9 #define RT5682_VERSION_ID 0x00fd #define RT5682_VENDOR_ID 0x00fe #define RT5682_DEVICE_ID 0x00ff @@ -1446,6 +1447,7 @@ struct rt5682_priv { bool hw_init; bool first_hw_init; bool is_sdw; + bool ve_ic; #ifdef CONFIG_COMMON_CLK struct clk_hw dai_clks_hw[RT5682_DAI_NUM_CLKS]; diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index ce2e88e066f3..73c4b3c31f8c 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -2132,10 +2132,10 @@ static int rt5682s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int reg_val = 0, tdm_ctrl = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: rt5682s->master[dai->id] = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: rt5682s->master[dai->id] = 0; break; default: diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 24cb895b759f..44543c0da177 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -475,7 +475,7 @@ static const struct sdw_device_id rt700_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt700_id); -static int __maybe_unused rt700_dev_suspend(struct device *dev) +static int rt700_dev_suspend(struct device *dev) { struct rt700_priv *rt700 = dev_get_drvdata(dev); @@ -490,7 +490,7 @@ static int __maybe_unused rt700_dev_suspend(struct device *dev) return 0; } -static int __maybe_unused rt700_dev_system_suspend(struct device *dev) +static int rt700_dev_system_suspend(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt700_priv *rt700 = dev_get_drvdata(dev); @@ -520,7 +520,7 @@ static int __maybe_unused rt700_dev_system_suspend(struct device *dev) #define RT700_PROBE_TIMEOUT 5000 -static int __maybe_unused rt700_dev_resume(struct device *dev) +static int rt700_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt700_priv *rt700 = dev_get_drvdata(dev); @@ -551,14 +551,14 @@ regmap_sync: } static const struct dev_pm_ops rt700_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt700_dev_system_suspend, rt700_dev_resume) - SET_RUNTIME_PM_OPS(rt700_dev_suspend, rt700_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt700_dev_system_suspend, rt700_dev_resume) + RUNTIME_PM_OPS(rt700_dev_suspend, rt700_dev_resume, NULL) }; static struct sdw_driver rt700_sdw_driver = { .driver = { .name = "rt700", - .pm = &rt700_pm, + .pm = pm_ptr(&rt700_pm), }, .probe = rt700_sdw_probe, .remove = rt700_sdw_remove, diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index f5933d2e085e..adf05f64259b 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -380,7 +380,7 @@ static const struct sdw_device_id rt711_sdca_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt711_sdca_id); -static int __maybe_unused rt711_sdca_dev_suspend(struct device *dev) +static int rt711_sdca_dev_suspend(struct device *dev) { struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev); @@ -396,7 +396,7 @@ static int __maybe_unused rt711_sdca_dev_suspend(struct device *dev) return 0; } -static int __maybe_unused rt711_sdca_dev_system_suspend(struct device *dev) +static int rt711_sdca_dev_system_suspend(struct device *dev) { struct rt711_sdca_priv *rt711_sdca = dev_get_drvdata(dev); struct sdw_slave *slave = dev_to_sdw_dev(dev); @@ -428,7 +428,7 @@ static int __maybe_unused rt711_sdca_dev_system_suspend(struct device *dev) #define RT711_PROBE_TIMEOUT 5000 -static int __maybe_unused rt711_sdca_dev_resume(struct device *dev) +static int rt711_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev); @@ -467,14 +467,14 @@ regmap_sync: } static const struct dev_pm_ops rt711_sdca_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt711_sdca_dev_system_suspend, rt711_sdca_dev_resume) - SET_RUNTIME_PM_OPS(rt711_sdca_dev_suspend, rt711_sdca_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt711_sdca_dev_system_suspend, rt711_sdca_dev_resume) + RUNTIME_PM_OPS(rt711_sdca_dev_suspend, rt711_sdca_dev_resume, NULL) }; static struct sdw_driver rt711_sdca_sdw_driver = { .driver = { .name = "rt711-sdca", - .pm = &rt711_sdca_pm, + .pm = pm_ptr(&rt711_sdca_pm), }, .probe = rt711_sdca_sdw_probe, .remove = rt711_sdca_sdw_remove, diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index dfda6bb5c6f8..93a5a89a96b1 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -482,7 +482,7 @@ static const struct sdw_device_id rt711_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt711_id); -static int __maybe_unused rt711_dev_suspend(struct device *dev) +static int rt711_dev_suspend(struct device *dev) { struct rt711_priv *rt711 = dev_get_drvdata(dev); @@ -498,7 +498,7 @@ static int __maybe_unused rt711_dev_suspend(struct device *dev) return 0; } -static int __maybe_unused rt711_dev_system_suspend(struct device *dev) +static int rt711_dev_system_suspend(struct device *dev) { struct rt711_priv *rt711 = dev_get_drvdata(dev); struct sdw_slave *slave = dev_to_sdw_dev(dev); @@ -528,7 +528,7 @@ static int __maybe_unused rt711_dev_system_suspend(struct device *dev) #define RT711_PROBE_TIMEOUT 5000 -static int __maybe_unused rt711_dev_resume(struct device *dev) +static int rt711_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt711_priv *rt711 = dev_get_drvdata(dev); @@ -564,14 +564,14 @@ regmap_sync: } static const struct dev_pm_ops rt711_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt711_dev_system_suspend, rt711_dev_resume) - SET_RUNTIME_PM_OPS(rt711_dev_suspend, rt711_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt711_dev_system_suspend, rt711_dev_resume) + RUNTIME_PM_OPS(rt711_dev_suspend, rt711_dev_resume, NULL) }; static struct sdw_driver rt711_sdw_driver = { .driver = { .name = "rt711", - .pm = &rt711_pm, + .pm = pm_ptr(&rt711_pm), }, .probe = rt711_sdw_probe, .remove = rt711_sdw_remove, diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index ee5435f3a80a..db011da63bd9 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -884,7 +884,7 @@ static const struct sdw_device_id rt712_sdca_dmic_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt712_sdca_dmic_id); -static int __maybe_unused rt712_sdca_dmic_dev_suspend(struct device *dev) +static int rt712_sdca_dmic_dev_suspend(struct device *dev) { struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev); @@ -897,7 +897,7 @@ static int __maybe_unused rt712_sdca_dmic_dev_suspend(struct device *dev) return 0; } -static int __maybe_unused rt712_sdca_dmic_dev_system_suspend(struct device *dev) +static int rt712_sdca_dmic_dev_system_suspend(struct device *dev) { struct rt712_sdca_dmic_priv *rt712_sdca = dev_get_drvdata(dev); @@ -909,7 +909,7 @@ static int __maybe_unused rt712_sdca_dmic_dev_system_suspend(struct device *dev) #define RT712_PROBE_TIMEOUT 5000 -static int __maybe_unused rt712_sdca_dmic_dev_resume(struct device *dev) +static int rt712_sdca_dmic_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev); @@ -941,8 +941,8 @@ regmap_sync: } static const struct dev_pm_ops rt712_sdca_dmic_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt712_sdca_dmic_dev_system_suspend, rt712_sdca_dmic_dev_resume) - SET_RUNTIME_PM_OPS(rt712_sdca_dmic_dev_suspend, rt712_sdca_dmic_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt712_sdca_dmic_dev_system_suspend, rt712_sdca_dmic_dev_resume) + RUNTIME_PM_OPS(rt712_sdca_dmic_dev_suspend, rt712_sdca_dmic_dev_resume, NULL) }; @@ -978,7 +978,7 @@ static int rt712_sdca_dmic_sdw_remove(struct sdw_slave *slave) static struct sdw_driver rt712_sdca_dmic_sdw_driver = { .driver = { .name = "rt712-sdca-dmic", - .pm = &rt712_sdca_dmic_pm, + .pm = pm_ptr(&rt712_sdca_dmic_pm), }, .probe = rt712_sdca_dmic_sdw_probe, .remove = rt712_sdca_dmic_sdw_remove, diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index b584a3f854b8..ea07131edfa2 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -400,7 +400,7 @@ static const struct sdw_device_id rt712_sdca_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt712_sdca_id); -static int __maybe_unused rt712_sdca_dev_suspend(struct device *dev) +static int rt712_sdca_dev_suspend(struct device *dev) { struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); @@ -416,7 +416,7 @@ static int __maybe_unused rt712_sdca_dev_suspend(struct device *dev) return 0; } -static int __maybe_unused rt712_sdca_dev_system_suspend(struct device *dev) +static int rt712_sdca_dev_system_suspend(struct device *dev) { struct rt712_sdca_priv *rt712_sdca = dev_get_drvdata(dev); struct sdw_slave *slave = dev_to_sdw_dev(dev); @@ -448,7 +448,7 @@ static int __maybe_unused rt712_sdca_dev_system_suspend(struct device *dev) #define RT712_PROBE_TIMEOUT 5000 -static int __maybe_unused rt712_sdca_dev_resume(struct device *dev) +static int rt712_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); @@ -488,14 +488,14 @@ regmap_sync: } static const struct dev_pm_ops rt712_sdca_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt712_sdca_dev_system_suspend, rt712_sdca_dev_resume) - SET_RUNTIME_PM_OPS(rt712_sdca_dev_suspend, rt712_sdca_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt712_sdca_dev_system_suspend, rt712_sdca_dev_resume) + RUNTIME_PM_OPS(rt712_sdca_dev_suspend, rt712_sdca_dev_resume, NULL) }; static struct sdw_driver rt712_sdca_sdw_driver = { .driver = { .name = "rt712-sdca", - .pm = &rt712_sdca_pm, + .pm = pm_ptr(&rt712_sdca_pm), }, .probe = rt712_sdca_sdw_probe, .remove = rt712_sdca_sdw_remove, diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 78dbf9eed494..19d99b9d4ab2 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -652,6 +652,61 @@ static int rt712_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol, return 1; } +static int rt712_sdca_set_fu05_playback_ctl(struct rt712_sdca_priv *rt712) +{ + int err; + unsigned int ch_01, ch_02; + + ch_01 = (rt712->fu05_dapm_mute || rt712->fu05_mixer_l_mute) ? 0x01 : 0x00; + ch_02 = (rt712->fu05_dapm_mute || rt712->fu05_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, + RT712_SDCA_CTL_FU_MUTE, CH_01), ch_01); + if (err < 0) + return err; + + err = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, + RT712_SDCA_CTL_FU_MUTE, CH_02), ch_02); + if (err < 0) + return err; + + return 0; +} + +static int rt712_sdca_fu05_playback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt712->fu05_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt712->fu05_mixer_r_mute; + return 0; +} + +static int rt712_sdca_fu05_playback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + int err; + + if (rt712->fu05_mixer_l_mute == !ucontrol->value.integer.value[0] && + rt712->fu05_mixer_r_mute == !ucontrol->value.integer.value[1]) + return 0; + + rt712->fu05_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt712->fu05_mixer_r_mute = !ucontrol->value.integer.value[1]; + + err = rt712_sdca_set_fu05_playback_ctl(rt712); + if (err < 0) + return err; + + return 1; +} + static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0); @@ -674,6 +729,8 @@ static const struct snd_kcontrol_new rt712_sdca_controls[] = { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 8, 3, 0, rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, boost_vol_tlv), + SOC_DOUBLE_EXT("FU05 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt712_sdca_fu05_playback_get, rt712_sdca_fu05_playback_put), }; static const struct snd_kcontrol_new rt712_sdca_spk_controls[] = { @@ -766,28 +823,15 @@ static int rt712_sdca_fu05_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); - unsigned char unmute = 0x0, mute = 0x1; switch (event) { case SND_SOC_DAPM_POST_PMU: - regmap_write(rt712->regmap, - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_01), - unmute); - regmap_write(rt712->regmap, - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_02), - unmute); + rt712->fu05_dapm_mute = false; + rt712_sdca_set_fu05_playback_ctl(rt712); break; case SND_SOC_DAPM_PRE_PMD: - regmap_write(rt712->regmap, - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_01), - mute); - regmap_write(rt712->regmap, - SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, - RT712_SDCA_CTL_FU_MUTE, CH_02), - mute); + rt712->fu05_dapm_mute = true; + rt712_sdca_set_fu05_playback_ctl(rt712); break; } return 0; @@ -1640,6 +1684,8 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, rt712->fu1e_dapm_mute = true; rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] = rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true; + rt712->fu05_dapm_mute = true; + rt712->fu05_mixer_l_mute = rt712->fu05_mixer_r_mute = false; /* JD source uses JD1 in default */ rt712->jd_src = RT712_JD1; diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index a08491496d90..7ab7d5feb50a 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -42,6 +42,9 @@ struct rt712_sdca_priv { bool fu0f_mixer_r_mute; bool fu1e_dapm_mute; bool fu1e_mixer_mute[4]; + bool fu05_dapm_mute; + bool fu05_mixer_l_mute; + bool fu05_mixer_r_mute; }; struct rt712_dmic_kctrl_priv { diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index c8dabb9b16b5..ce7d8955efc3 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -205,7 +205,7 @@ static const struct sdw_device_id rt715_sdca_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt715_sdca_id); -static int __maybe_unused rt715_dev_suspend(struct device *dev) +static int rt715_dev_suspend(struct device *dev) { struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); @@ -222,7 +222,7 @@ static int __maybe_unused rt715_dev_suspend(struct device *dev) #define RT715_PROBE_TIMEOUT 5000 -static int __maybe_unused rt715_dev_resume(struct device *dev) +static int rt715_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); @@ -263,14 +263,14 @@ regmap_sync: } static const struct dev_pm_ops rt715_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) - SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) + RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) }; static struct sdw_driver rt715_sdw_driver = { .driver = { .name = "rt715-sdca", - .pm = &rt715_pm, + .pm = pm_ptr(&rt715_pm), }, .probe = rt715_sdca_sdw_probe, .remove = rt715_sdca_sdw_remove, diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 7e10fd913812..7fb02654c16b 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -427,14 +427,6 @@ static int rt715_sdca_fu_info(struct snd_kcontrol *kcontrol, .private_value = RT715_SDCA_PR_VALUE(reg_base, xcount, xmax, \ xshift, xinvert)} -#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ - xhandler_get, xhandler_put) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .info = snd_soc_info_volsw, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ - xmax, xinvert) } - #define RT715_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\ xhandler_put, tlv_array, xcount, xmax) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index ec255ada44e0..a3df4bbedf86 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -372,47 +372,6 @@ static const struct regmap_config rt715_sdw_regmap = { .use_single_write = true, }; -int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, - unsigned int *sdw_addr_h, unsigned int *sdw_data_h, - unsigned int *sdw_addr_l, unsigned int *sdw_data_l) -{ - unsigned int offset_h, offset_l, e_verb; - - if (((verb & 0xff) != 0) || verb == 0xf00) { /* 12 bits command */ - if (verb == 0x7ff) /* special case */ - offset_h = 0; - else - offset_h = 0x3000; - - if (verb & 0x800) /* get command */ - e_verb = (verb - 0xf00) | 0x80; - else /* set command */ - e_verb = (verb - 0x700); - - *sdw_data_h = payload; /* 7 bits payload */ - *sdw_addr_l = *sdw_data_l = 0; - } else { /* 4 bits command */ - if ((verb & 0x800) == 0x800) { /* read */ - offset_h = 0x9000; - offset_l = 0xa000; - } else { /* write */ - offset_h = 0x7000; - offset_l = 0x8000; - } - e_verb = verb >> 8; - *sdw_data_h = (payload >> 8); /* 16 bits payload [15:8] */ - *sdw_addr_l = (e_verb << 8) | nid | 0x80; /* 0x80: valid bit */ - *sdw_addr_l += offset_l; - *sdw_data_l = payload & 0xff; - } - - *sdw_addr_h = (e_verb << 8) | nid; - *sdw_addr_h += offset_h; - - return 0; -} -EXPORT_SYMBOL(hda_to_sdw); - static int rt715_update_status(struct sdw_slave *slave, enum sdw_slave_status status) { @@ -526,7 +485,7 @@ static const struct sdw_device_id rt715_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt715_id); -static int __maybe_unused rt715_dev_suspend(struct device *dev) +static int rt715_dev_suspend(struct device *dev) { struct rt715_priv *rt715 = dev_get_drvdata(dev); @@ -540,7 +499,7 @@ static int __maybe_unused rt715_dev_suspend(struct device *dev) #define RT715_PROBE_TIMEOUT 5000 -static int __maybe_unused rt715_dev_resume(struct device *dev) +static int rt715_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt715_priv *rt715 = dev_get_drvdata(dev); @@ -571,14 +530,14 @@ regmap_sync: } static const struct dev_pm_ops rt715_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) - SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) + RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) }; static struct sdw_driver rt715_sdw_driver = { .driver = { .name = "rt715", - .pm = &rt715_pm, + .pm = pm_ptr(&rt715_pm), }, .probe = rt715_sdw_probe, .remove = rt715_sdw_remove, diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 299c9b12377c..2cf461852091 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -486,14 +486,6 @@ static int rt715_vol_info(struct snd_kcontrol *kcontrol, return 0; } -#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ - xhandler_get, xhandler_put) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .info = snd_soc_info_volsw, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ - xmax, xinvert) } - #define RT715_MAIN_SWITCH_EXT(xname, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .info = rt715_switch_info, \ diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h index 6e37bf64e12f..a0c56aa1003a 100644 --- a/sound/soc/codecs/rt715.h +++ b/sound/soc/codecs/rt715.h @@ -220,8 +220,5 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave); int rt715_init(struct device *dev, struct regmap *sdw_regmap, struct regmap *regmap, struct sdw_slave *slave); -int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, - unsigned int *sdw_addr_h, unsigned int *sdw_data_h, - unsigned int *sdw_addr_l, unsigned int *sdw_data_l); int rt715_clock_config(struct device *dev); #endif /* __RT715_H__ */ diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index c71453da088a..582b47d69278 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -437,7 +437,7 @@ static const struct sdw_device_id rt721_sdca_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt721_sdca_id); -static int __maybe_unused rt721_sdca_dev_suspend(struct device *dev) +static int rt721_sdca_dev_suspend(struct device *dev) { struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev); @@ -453,7 +453,7 @@ static int __maybe_unused rt721_sdca_dev_suspend(struct device *dev) return 0; } -static int __maybe_unused rt721_sdca_dev_system_suspend(struct device *dev) +static int rt721_sdca_dev_system_suspend(struct device *dev) { struct rt721_sdca_priv *rt721_sdca = dev_get_drvdata(dev); struct sdw_slave *slave = dev_to_sdw_dev(dev); @@ -485,7 +485,7 @@ static int __maybe_unused rt721_sdca_dev_system_suspend(struct device *dev) #define RT721_PROBE_TIMEOUT 5000 -static int __maybe_unused rt721_sdca_dev_resume(struct device *dev) +static int rt721_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev); @@ -524,15 +524,15 @@ regmap_sync: } static const struct dev_pm_ops rt721_sdca_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt721_sdca_dev_system_suspend, rt721_sdca_dev_resume) - SET_RUNTIME_PM_OPS(rt721_sdca_dev_suspend, rt721_sdca_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt721_sdca_dev_system_suspend, rt721_sdca_dev_resume) + RUNTIME_PM_OPS(rt721_sdca_dev_suspend, rt721_sdca_dev_resume, NULL) }; static struct sdw_driver rt721_sdca_sdw_driver = { .driver = { .name = "rt721-sdca", .owner = THIS_MODULE, - .pm = &rt721_sdca_pm, + .pm = pm_ptr(&rt721_sdca_pm), }, .probe = rt721_sdca_sdw_probe, .remove = rt721_sdca_sdw_remove, diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index 25fc13687bc8..11e2e8f68a98 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -16,7 +16,7 @@ #include "rt722-sdca.h" #include "rt722-sdca-sdw.h" -static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg) +static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) { switch (reg) { case 0x2f01 ... 0x2f0a: @@ -28,36 +28,52 @@ static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg) 0): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, 0): - case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, - 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, - RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): - case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: - return true; - default: - return false; - } -} - -static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case 0x2f01: - case 0x2f54: - case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU03, RT722_SDCA_CTL_SELECTED_MODE, 0): - case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, - 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, - RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_L) ... + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D, + RT722_SDCA_CTL_SELECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_MUTE, CH_L) ... + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_MUTE, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, + RT722_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, + RT722_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_MUTE, CH_01) ... + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_MUTE, CH_04): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, + RT722_SDCA_CTL_VENDOR_DEF, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, + RT722_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_L) ... + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, + RT722_SDCA_CTL_VENDOR_DEF, CH_08): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, + RT722_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: - return true; - default: - return false; - } -} - -static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int reg) -{ - switch (reg) { + return 1; case 0x2000000 ... 0x2000024: case 0x2000029 ... 0x200004a: case 0x2000051 ... 0x2000052: @@ -74,6 +90,7 @@ static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int re case 0x5600000 ... 0x5600007: case 0x5700000 ... 0x5700004: case 0x5800000 ... 0x5800004: + case 0x5810000: case 0x5b00003: case 0x5c00011: case 0x5d00006: @@ -81,11 +98,16 @@ static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int re case 0x5f00030: case 0x6100000 ... 0x6100051: case 0x6100055 ... 0x6100057: + case 0x6100060: case 0x6100062: case 0x6100064 ... 0x6100065: case 0x6100067: case 0x6100070 ... 0x610007c: case 0x6100080: + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, + CH_01) ... + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, + CH_04): case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, CH_01): case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, @@ -108,15 +130,32 @@ static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int re RT722_SDCA_CTL_FU_CH_GAIN, CH_L): case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, CH_R): - return true; + return 2; default: - return false; + return 0; } } -static bool rt722_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +static struct regmap_sdw_mbq_cfg rt722_mbq_config = { + .mbq_size = rt722_sdca_mbq_size, +}; + +static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg) +{ + return rt722_sdca_mbq_size(dev, reg) > 0; +} + +static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { + case 0x2f01: + case 0x2f54: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, + 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: case 0x2000000: case 0x200000d: case 0x2000019: @@ -135,7 +174,7 @@ static bool rt722_sdca_mbq_volatile_register(struct device *dev, unsigned int re static const struct regmap_config rt722_sdca_regmap = { .reg_bits = 32, - .val_bits = 8, + .val_bits = 16, .readable_reg = rt722_sdca_readable_register, .volatile_reg = rt722_sdca_volatile_register, .max_register = 0x44ffffff, @@ -146,20 +185,6 @@ static const struct regmap_config rt722_sdca_regmap = { .use_single_write = true, }; -static const struct regmap_config rt722_sdca_mbq_regmap = { - .name = "sdw-mbq", - .reg_bits = 32, - .val_bits = 16, - .readable_reg = rt722_sdca_mbq_readable_register, - .volatile_reg = rt722_sdca_mbq_volatile_register, - .max_register = 0x41000312, - .reg_defaults = rt722_sdca_mbq_defaults, - .num_reg_defaults = ARRAY_SIZE(rt722_sdca_mbq_defaults), - .cache_type = REGCACHE_MAPLE, - .use_single_read = true, - .use_single_write = true, -}; - static int rt722_sdca_update_status(struct sdw_slave *slave, enum sdw_slave_status status) { @@ -203,6 +228,8 @@ static int rt722_sdca_read_prop(struct sdw_slave *slave) unsigned long addr; struct sdw_dpn_prop *dpn; + sdw_slave_read_lane_mapping(slave); + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; @@ -369,18 +396,14 @@ static const struct sdw_slave_ops rt722_sdca_slave_ops = { static int rt722_sdca_sdw_probe(struct sdw_slave *slave, const struct sdw_device_id *id) { - struct regmap *regmap, *mbq_regmap; + struct regmap *regmap; /* Regmap Initialization */ - mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt722_sdca_mbq_regmap); - if (IS_ERR(mbq_regmap)) - return PTR_ERR(mbq_regmap); - - regmap = devm_regmap_init_sdw(slave, &rt722_sdca_regmap); + regmap = devm_regmap_init_sdw_mbq_cfg(slave, &rt722_sdca_regmap, &rt722_mbq_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); - return rt722_sdca_init(&slave->dev, regmap, mbq_regmap, slave); + return rt722_sdca_init(&slave->dev, regmap, slave); } static int rt722_sdca_sdw_remove(struct sdw_slave *slave) @@ -407,7 +430,7 @@ static const struct sdw_device_id rt722_sdca_id[] = { }; MODULE_DEVICE_TABLE(sdw, rt722_sdca_id); -static int __maybe_unused rt722_sdca_dev_suspend(struct device *dev) +static int rt722_sdca_dev_suspend(struct device *dev) { struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); @@ -418,12 +441,11 @@ static int __maybe_unused rt722_sdca_dev_suspend(struct device *dev) cancel_delayed_work_sync(&rt722->jack_btn_check_work); regcache_cache_only(rt722->regmap, true); - regcache_cache_only(rt722->mbq_regmap, true); return 0; } -static int __maybe_unused rt722_sdca_dev_system_suspend(struct device *dev) +static int rt722_sdca_dev_system_suspend(struct device *dev) { struct rt722_sdca_priv *rt722_sdca = dev_get_drvdata(dev); struct sdw_slave *slave = dev_to_sdw_dev(dev); @@ -455,7 +477,7 @@ static int __maybe_unused rt722_sdca_dev_system_suspend(struct device *dev) #define RT722_PROBE_TIMEOUT 5000 -static int __maybe_unused rt722_sdca_dev_resume(struct device *dev) +static int rt722_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); @@ -488,20 +510,18 @@ regmap_sync: slave->unattach_request = 0; regcache_cache_only(rt722->regmap, false); regcache_sync(rt722->regmap); - regcache_cache_only(rt722->mbq_regmap, false); - regcache_sync(rt722->mbq_regmap); return 0; } static const struct dev_pm_ops rt722_sdca_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt722_sdca_dev_system_suspend, rt722_sdca_dev_resume) - SET_RUNTIME_PM_OPS(rt722_sdca_dev_suspend, rt722_sdca_dev_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rt722_sdca_dev_system_suspend, rt722_sdca_dev_resume) + RUNTIME_PM_OPS(rt722_sdca_dev_suspend, rt722_sdca_dev_resume, NULL) }; static struct sdw_driver rt722_sdca_sdw_driver = { .driver = { .name = "rt722-sdca", - .pm = &rt722_sdca_pm, + .pm = pm_ptr(&rt722_sdca_pm), }, .probe = rt722_sdca_sdw_probe, .remove = rt722_sdca_sdw_remove, diff --git a/sound/soc/codecs/rt722-sdca-sdw.h b/sound/soc/codecs/rt722-sdca-sdw.h index 5b43e86f75d1..80b014456940 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.h +++ b/sound/soc/codecs/rt722-sdca-sdw.h @@ -31,50 +31,9 @@ static const struct reg_default rt722_sdca_reg_defaults[] = { { 0x2f5b, 0x07 }, { 0x2f5c, 0x27 }, { 0x2f5d, 0x07 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, - 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, - 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE, - 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE, - 0), 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_L), - 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_R), - 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_L), - 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_R), - 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, - 0), 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_01), - 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_02), - 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_03), - 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_04), - 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0), - 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0), - 0x00 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), - 0x09 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_L), - 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_R), - 0x01 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0), - 0x03 }, - { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, -}; - -static const struct reg_default rt722_sdca_mbq_defaults[] = { { 0x200003c, 0xc214 }, { 0x2000046, 0x8004 }, + { 0x5810000, 0x702d }, { 0x6100006, 0x0005 }, { 0x6100010, 0x2630 }, { 0x6100011, 0x152f }, @@ -86,27 +45,34 @@ static const struct reg_default rt722_sdca_mbq_defaults[] = { { 0x6100028, 0x2a2a }, { 0x6100029, 0x4141 }, { 0x6100055, 0x0000 }, - { 0x5810000, 0x702d }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE, + 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE, + 0), 0x03 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, CH_L), 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, CH_R), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, - CH_01), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, - CH_02), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, - CH_03), 0x0000 }, - { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, - CH_04), 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_02), @@ -115,10 +81,41 @@ static const struct reg_default rt722_sdca_mbq_defaults[] = { 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_01), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_02), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_03), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_04), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0), + 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0), + 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0), + 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, }; #endif /* __RT722_SDW_H__ */ diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index e17a142d03b9..f093ce841b3f 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -25,11 +25,13 @@ #include "rt722-sdca.h" +#define RT722_NID_ADDR(nid, reg) ((nid) << 20 | (reg)) + int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, unsigned int nid, unsigned int reg, unsigned int value) { - struct regmap *regmap = rt722->mbq_regmap; - unsigned int addr = (nid << 20) | reg; + struct regmap *regmap = rt722->regmap; + unsigned int addr = RT722_NID_ADDR(nid, reg); int ret; ret = regmap_write(regmap, addr, value); @@ -45,8 +47,8 @@ int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, unsigned int nid, unsigned int reg, unsigned int *value) { int ret; - struct regmap *regmap = rt722->mbq_regmap; - unsigned int addr = (nid << 20) | reg; + struct regmap *regmap = rt722->regmap; + unsigned int addr = RT722_NID_ADDR(nid, reg); ret = regmap_read(regmap, addr, value); if (ret < 0) @@ -361,8 +363,8 @@ static int rt722_sdca_set_gain_put(struct snd_kcontrol *kcontrol, strstr(ucontrol->id.name, "FU0F Capture Volume")) adc_vol_flag = 1; - regmap_read(rt722->mbq_regmap, mc->reg, &lvalue); - regmap_read(rt722->mbq_regmap, mc->rreg, &rvalue); + regmap_read(rt722->regmap, mc->reg, &lvalue); + regmap_read(rt722->regmap, mc->rreg, &rvalue); /* L Channel */ gain_l_val = ucontrol->value.integer.value[0]; @@ -402,13 +404,13 @@ static int rt722_sdca_set_gain_put(struct snd_kcontrol *kcontrol, return 0; /* Lch*/ - regmap_write(rt722->mbq_regmap, mc->reg, gain_l_val); + regmap_write(rt722->regmap, mc->reg, gain_l_val); /* Rch */ - regmap_write(rt722->mbq_regmap, mc->rreg, gain_r_val); + regmap_write(rt722->regmap, mc->rreg, gain_r_val); - regmap_read(rt722->mbq_regmap, mc->reg, &read_l); - regmap_read(rt722->mbq_regmap, mc->rreg, &read_r); + regmap_read(rt722->regmap, mc->reg, &read_l); + regmap_read(rt722->regmap, mc->rreg, &read_r); if (read_r == gain_r_val && read_l == gain_l_val) return changed; @@ -431,8 +433,8 @@ static int rt722_sdca_set_gain_get(struct snd_kcontrol *kcontrol, strstr(ucontrol->id.name, "FU0F Capture Volume")) adc_vol_flag = 1; - regmap_read(rt722->mbq_regmap, mc->reg, &read_l); - regmap_read(rt722->mbq_regmap, mc->rreg, &read_r); + regmap_read(rt722->regmap, mc->reg, &read_l); + regmap_read(rt722->regmap, mc->rreg, &read_r); if (mc->shift == 8) /* boost gain */ ctl_l = read_l / tendB; @@ -604,7 +606,7 @@ static int rt722_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, /* check all channels */ for (i = 0; i < p->count; i++) { - regmap_read(rt722->mbq_regmap, p->reg_base + i, ®value); + regmap_read(rt722->regmap, p->reg_base + i, ®value); if (!adc_vol_flag) /* boost gain */ ctl = regvalue / boost_step; @@ -637,7 +639,7 @@ static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, /* check all channels */ for (i = 0; i < p->count; i++) { - regmap_read(rt722->mbq_regmap, p->reg_base + i, ®value[i]); + regmap_read(rt722->regmap, p->reg_base + i, ®value[i]); gain_val[i] = ucontrol->value.integer.value[i]; if (gain_val[i] > p->max) @@ -658,7 +660,7 @@ static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, return 0; for (i = 0; i < p->count; i++) { - err = regmap_write(rt722->mbq_regmap, p->reg_base + i, gain_val[i]); + err = regmap_write(rt722->regmap, p->reg_base + i, gain_val[i]); if (err < 0) dev_err(&rt722->slave->dev, "%s: %#08x can't be set\n", __func__, p->reg_base + i); @@ -739,77 +741,6 @@ static const struct snd_kcontrol_new rt722_sdca_controls[] = { 4, 3, boost_vol_tlv), }; -static int rt722_sdca_adc_mux_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = - snd_soc_dapm_kcontrol_component(kcontrol); - struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); - unsigned int val = 0, mask_sft; - - if (strstr(ucontrol->id.name, "ADC 22 Mux")) - mask_sft = 12; - else if (strstr(ucontrol->id.name, "ADC 24 Mux")) - mask_sft = 4; - else if (strstr(ucontrol->id.name, "ADC 25 Mux")) - mask_sft = 0; - else - return -EINVAL; - - rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL, - RT722_HDA_LEGACY_MUX_CTL0, &val); - - ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7; - - return 0; -} - -static int rt722_sdca_adc_mux_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = - snd_soc_dapm_kcontrol_component(kcontrol); - struct snd_soc_dapm_context *dapm = - snd_soc_dapm_kcontrol_dapm(kcontrol); - struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int *item = ucontrol->value.enumerated.item; - unsigned int val, val2 = 0, change, mask_sft; - - if (item[0] >= e->items) - return -EINVAL; - - if (strstr(ucontrol->id.name, "ADC 22 Mux")) - mask_sft = 12; - else if (strstr(ucontrol->id.name, "ADC 24 Mux")) - mask_sft = 4; - else if (strstr(ucontrol->id.name, "ADC 25 Mux")) - mask_sft = 0; - else - return -EINVAL; - - val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - - rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL, - RT722_HDA_LEGACY_MUX_CTL0, &val2); - val2 = (0x7 << mask_sft) & val2; - - if (val == val2) - change = 0; - else - change = 1; - - if (change) - rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, - RT722_HDA_LEGACY_MUX_CTL0, 0x7 << mask_sft, - val << mask_sft); - - snd_soc_dapm_mux_update_power(dapm, kcontrol, - item[0], e, NULL); - - return change; -} - static const char * const adc22_mux_text[] = { "MIC2", "LINE1", @@ -821,26 +752,26 @@ static const char * const adc07_10_mux_text[] = { "DMIC2", }; -static SOC_ENUM_SINGLE_DECL( - rt722_adc22_enum, SND_SOC_NOPM, 0, adc22_mux_text); +static SOC_ENUM_SINGLE_DECL(rt722_adc22_enum, + RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0), + 12, adc22_mux_text); -static SOC_ENUM_SINGLE_DECL( - rt722_adc24_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); +static SOC_ENUM_SINGLE_DECL(rt722_adc24_enum, + RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0), + 4, adc07_10_mux_text); -static SOC_ENUM_SINGLE_DECL( - rt722_adc25_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); +static SOC_ENUM_SINGLE_DECL(rt722_adc25_enum, + RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0), + 0, adc07_10_mux_text); static const struct snd_kcontrol_new rt722_sdca_adc22_mux = - SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt722_adc22_enum, - rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + SOC_DAPM_ENUM("ADC 22 Mux", rt722_adc22_enum); static const struct snd_kcontrol_new rt722_sdca_adc24_mux = - SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt722_adc24_enum, - rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + SOC_DAPM_ENUM("ADC 24 Mux", rt722_adc24_enum); static const struct snd_kcontrol_new rt722_sdca_adc25_mux = - SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt722_adc25_enum, - rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + SOC_DAPM_ENUM("ADC 25 Mux", rt722_adc25_enum); static int rt722_sdca_fu42_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -1335,8 +1266,7 @@ static struct snd_soc_dai_driver rt722_sdca_dai[] = { } }; -int rt722_sdca_init(struct device *dev, struct regmap *regmap, - struct regmap *mbq_regmap, struct sdw_slave *slave) +int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave) { struct rt722_sdca_priv *rt722; @@ -1347,7 +1277,6 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, dev_set_drvdata(dev, rt722); rt722->slave = slave; rt722->regmap = regmap; - rt722->mbq_regmap = mbq_regmap; mutex_init(&rt722->calibrate_mutex); mutex_init(&rt722->disable_irq_lock); @@ -1521,8 +1450,6 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt722->first_hw_init) { regcache_cache_only(rt722->regmap, false); regcache_cache_bypass(rt722->regmap, true); - regcache_cache_only(rt722->mbq_regmap, false); - regcache_cache_bypass(rt722->mbq_regmap, true); } else { /* * PM runtime is only enabled when a Slave reports as Attached @@ -1550,8 +1477,6 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt722->first_hw_init) { regcache_cache_bypass(rt722->regmap, false); regcache_mark_dirty(rt722->regmap); - regcache_cache_bypass(rt722->mbq_regmap, false); - regcache_mark_dirty(rt722->mbq_regmap); } else rt722->first_hw_init = true; diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h index 2464361a7958..04c3b4232ef3 100644 --- a/sound/soc/codecs/rt722-sdca.h +++ b/sound/soc/codecs/rt722-sdca.h @@ -17,7 +17,6 @@ struct rt722_sdca_priv { struct regmap *regmap; - struct regmap *mbq_regmap; struct snd_soc_component *component; struct sdw_slave *slave; struct sdw_bus_params params; @@ -229,8 +228,7 @@ enum rt722_sdca_jd_src { }; int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave); -int rt722_sdca_init(struct device *dev, struct regmap *regmap, - struct regmap *mbq_regmap, struct sdw_slave *slave); +int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave); int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, unsigned int nid, unsigned int reg, unsigned int value); int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c index 733a7d130a95..97f56af25577 100644 --- a/sound/soc/codecs/rt9120.c +++ b/sound/soc/codecs/rt9120.c @@ -590,7 +590,7 @@ static void rt9120_remove(struct i2c_client *i2c) pm_runtime_set_suspended(&i2c->dev); } -static int __maybe_unused rt9120_runtime_suspend(struct device *dev) +static int rt9120_runtime_suspend(struct device *dev) { struct rt9120_data *data = dev_get_drvdata(dev); @@ -603,7 +603,7 @@ static int __maybe_unused rt9120_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused rt9120_runtime_resume(struct device *dev) +static int rt9120_runtime_resume(struct device *dev) { struct rt9120_data *data = dev_get_drvdata(dev); @@ -618,7 +618,7 @@ static int __maybe_unused rt9120_runtime_resume(struct device *dev) } static const struct dev_pm_ops rt9120_pm_ops = { - SET_RUNTIME_PM_OPS(rt9120_runtime_suspend, rt9120_runtime_resume, NULL) + RUNTIME_PM_OPS(rt9120_runtime_suspend, rt9120_runtime_resume, NULL) }; static const struct of_device_id __maybe_unused rt9120_device_table[] = { @@ -631,7 +631,7 @@ static struct i2c_driver rt9120_driver = { .driver = { .name = "rt9120", .of_match_table = rt9120_device_table, - .pm = &rt9120_pm_ops, + .pm = pm_ptr(&rt9120_pm_ops), }, .probe = rt9120_probe, .remove = rt9120_remove, diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c index aa3eadecd974..391cc03d687f 100644 --- a/sound/soc/codecs/rtq9128.c +++ b/sound/soc/codecs/rtq9128.c @@ -729,7 +729,7 @@ static int rtq9128_probe(struct i2c_client *i2c) return devm_snd_soc_register_component(dev, &rtq9128_comp_driver, &rtq9128_dai, 1); } -static int __maybe_unused rtq9128_pm_runtime_suspend(struct device *dev) +static int rtq9128_pm_runtime_suspend(struct device *dev) { struct rtq9128_data *data = dev_get_drvdata(dev); struct regmap *regmap = dev_get_regmap(dev, NULL); @@ -746,7 +746,7 @@ static int __maybe_unused rtq9128_pm_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused rtq9128_pm_runtime_resume(struct device *dev) +static int rtq9128_pm_runtime_resume(struct device *dev) { struct rtq9128_data *data = dev_get_drvdata(dev); struct regmap *regmap = dev_get_regmap(dev, NULL); @@ -764,8 +764,8 @@ static int __maybe_unused rtq9128_pm_runtime_resume(struct device *dev) return regcache_sync(regmap); } -static const struct dev_pm_ops __maybe_unused rtq9128_pm_ops = { - SET_RUNTIME_PM_OPS(rtq9128_pm_runtime_suspend, rtq9128_pm_runtime_resume, NULL) +static const struct dev_pm_ops rtq9128_pm_ops = { + RUNTIME_PM_OPS(rtq9128_pm_runtime_suspend, rtq9128_pm_runtime_resume, NULL) }; static const struct of_device_id rtq9128_device_table[] = { diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 7aa89e34657e..2cc8efe3d896 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -806,9 +806,9 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) * - clock and frame master */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: i2sctl |= SGTL5000_I2S_MASTER; sgtl5000->master = 1; break; diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c index f2cea6186d98..f5c303d4bb62 100644 --- a/sound/soc/codecs/sma1307.c +++ b/sound/soc/codecs/sma1307.c @@ -1019,14 +1019,9 @@ static const struct snd_kcontrol_new sma1307_aif_out1_source_control = { .private_value = (unsigned long)&sma1307_aif_out_source_enum }; -static const struct snd_kcontrol_new sma1307_sdo_control = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Switch", - .info = snd_soc_info_volsw, - .get = sma1307_dapm_sdo_enable_get, - .put = sma1307_dapm_sdo_enable_put, - .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, 0, 1, 0, 0) -}; +static const struct snd_kcontrol_new sma1307_sdo_control = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, 0, 1, 0, + sma1307_dapm_sdo_enable_get, sma1307_dapm_sdo_enable_put); static const struct snd_kcontrol_new sma1307_enable_control = SOC_DAPM_SINGLE("Switch", SMA1307_00_SYSTEM_CTRL, 0, 1, 0); @@ -1728,6 +1723,11 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil } data = kzalloc(fw->size, GFP_KERNEL); + if (!data) { + release_firmware(fw); + sma1307->set.status = false; + return; + } size = fw->size >> 2; memcpy(data, fw->data, fw->size); @@ -1741,6 +1741,12 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil sma1307->set.header = devm_kzalloc(sma1307->dev, sma1307->set.header_size, GFP_KERNEL); + if (!sma1307->set.header) { + kfree(data); + sma1307->set.status = false; + return; + } + memcpy(sma1307->set.header, data, sma1307->set.header_size * sizeof(int)); @@ -1756,6 +1762,13 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil sma1307->set.def = devm_kzalloc(sma1307->dev, sma1307->set.def_size * sizeof(int), GFP_KERNEL); + if (!sma1307->set.def) { + kfree(data); + kfree(sma1307->set.header); + sma1307->set.status = false; + return; + } + memcpy(sma1307->set.def, &data[sma1307->set.header_size], sma1307->set.def_size * sizeof(int)); @@ -1768,6 +1781,16 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil = devm_kzalloc(sma1307->dev, sma1307->set.mode_size * 2 * sizeof(int), GFP_KERNEL); + if (!sma1307->set.mode_set[i]) { + kfree(data); + kfree(sma1307->set.header); + kfree(sma1307->set.def); + for (int j = 0; j < i; j++) + kfree(sma1307->set.mode_set[j]); + sma1307->set.status = false; + return; + } + for (int j = 0; j < sma1307->set.mode_size; j++) { sma1307->set.mode_set[i][2 * j] = data[offset + ((num_mode + 1) * j)]; @@ -2011,8 +2034,8 @@ static void sma1307_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id sma1307_i2c_id[] = { - { "sma1307a", 0 }, - { "sma1307aq", 0 }, + { "sma1307a" }, + { "sma1307aq" }, { } }; diff --git a/sound/soc/codecs/src4xxx.c b/sound/soc/codecs/src4xxx.c index db4e280dd055..5a3489475225 100644 --- a/sound/soc/codecs/src4xxx.c +++ b/sound/soc/codecs/src4xxx.c @@ -158,11 +158,11 @@ static int src4xxx_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int ctrl; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ctrl = SRC4XXX_BUS_MASTER; src4xxx->master[dai->id] = true; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: ctrl = 0; src4xxx->master[dai->id] = false; break; diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c index 596096466cd4..49c74cba17c7 100644 --- a/sound/soc/codecs/ssm2602-i2c.c +++ b/sound/soc/codecs/ssm2602-i2c.c @@ -13,8 +13,6 @@ #include "ssm2602.h" -static const struct i2c_device_id ssm2602_i2c_id[]; - /* * ssm2602 2 wire address is determined by GPIO5 * state during powerup. @@ -23,8 +21,7 @@ static const struct i2c_device_id ssm2602_i2c_id[]; */ static int ssm2602_i2c_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_match_id(ssm2602_i2c_id, client); - return ssm2602_probe(&client->dev, id->driver_data, + return ssm2602_probe(&client->dev, (uintptr_t)i2c_get_match_data(client), devm_regmap_init_i2c(client, &ssm2602_regmap_config)); } diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 684d52ec6600..b56dd279d90a 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -138,7 +138,6 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = { {"ASI OUT", NULL, "DMIC"} }; -#ifdef CONFIG_PM static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown) { u8 cfg1_reg = 0; @@ -152,7 +151,6 @@ static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown) snd_soc_component_update_bits(tas2552->component, TAS2552_CFG_1, TAS2552_SWS, cfg1_reg); } -#endif static int tas2552_setup_pll(struct snd_soc_component *component, struct snd_pcm_hw_params *params) @@ -480,7 +478,6 @@ static int tas2552_mute(struct snd_soc_dai *dai, int mute, int direction) return 0; } -#ifdef CONFIG_PM static int tas2552_runtime_suspend(struct device *dev) { struct tas2552_data *tas2552 = dev_get_drvdata(dev); @@ -508,11 +505,9 @@ static int tas2552_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops tas2552_pm = { - SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume, - NULL) + RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume, NULL) }; static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = { @@ -768,7 +763,7 @@ static struct i2c_driver tas2552_i2c_driver = { .driver = { .name = "tas2552", .of_match_table = of_match_ptr(tas2552_of_match), - .pm = &tas2552_pm, + .pm = pm_ptr(&tas2552_pm), }, .probe = tas2552_probe, .remove = tas2552_i2c_remove, diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 54561ae598b8..8e00dcc09d0c 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -513,17 +513,9 @@ static const struct snd_kcontrol_new vsense_switch = static const struct snd_kcontrol_new tas2562_snd_controls[] = { SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 1, 0x1c, 0, tas2562_dac_tlv), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Volume Control", - .index = 0, - .tlv.p = dvc_tlv, - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_soc_info_volsw, - .get = tas2562_volume_control_get, - .put = tas2562_volume_control_put, - .private_value = SOC_SINGLE_VALUE(TAS2562_DVC_CFG1, 0, 110, 0, 0), - }, + SOC_SINGLE_EXT_TLV("Digital Volume Control", TAS2562_DVC_CFG1, 0, 110, 0, + tas2562_volume_control_get, tas2562_volume_control_put, + dvc_tlv), }; static const struct snd_soc_dapm_widget tas2110_dapm_widgets[] = { @@ -731,16 +723,14 @@ static int tas2562_probe(struct i2c_client *client) struct device *dev = &client->dev; struct tas2562_data *data; int ret; - const struct i2c_device_id *id; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - id = i2c_match_id(tas2562_id, client); data->client = client; data->dev = &client->dev; - data->model_id = id->driver_data; + data->model_id = (uintptr_t)i2c_get_match_data(client); tas2562_parse_dt(data); diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index d482cd194c08..08aa7ee34256 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -14,6 +14,7 @@ #include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/slab.h> #include <sound/soc.h> #include <sound/pcm.h> @@ -23,6 +24,11 @@ #include "tas2764.h" +enum tas2764_devid { + DEVID_TAS2764 = 0, + DEVID_SN012776 = 1 +}; + struct tas2764_priv { struct snd_soc_component *component; struct gpio_desc *reset_gpio; @@ -30,7 +36,8 @@ struct tas2764_priv { struct regmap *regmap; struct device *dev; int irq; - + enum tas2764_devid devid; + int v_sense_slot; int i_sense_slot; @@ -142,6 +149,8 @@ static int tas2764_codec_suspend(struct snd_soc_component *component) regcache_cache_only(tas2764->regmap, true); regcache_mark_dirty(tas2764->regmap); + usleep_range(6000, 7000); + return 0; } @@ -180,33 +189,6 @@ static SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new tas2764_asi1_mux = SOC_DAPM_ENUM("ASI1 Source", tas2764_ASI1_src_enum); -static int tas2764_dac_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); - struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); - int ret; - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - tas2764->dac_powered = true; - ret = tas2764_update_pwr_ctrl(tas2764); - break; - case SND_SOC_DAPM_PRE_PMD: - tas2764->dac_powered = false; - ret = tas2764_update_pwr_ctrl(tas2764); - break; - default: - dev_err(tas2764->dev, "Unsupported event\n"); - return -EINVAL; - } - - if (ret < 0) - return ret; - - return 0; -} - static const struct snd_kcontrol_new isense_switch = SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, 1, 1); static const struct snd_kcontrol_new vsense_switch = @@ -219,8 +201,7 @@ static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = { 1, &isense_switch), SND_SOC_DAPM_SWITCH("VSENSE", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, 1, &vsense_switch), - SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2764_dac_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_OUTPUT("OUT"), SND_SOC_DAPM_SIGGEN("VMON"), SND_SOC_DAPM_SIGGEN("IMON") @@ -241,9 +222,34 @@ static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(dai->component); + int ret; + + if (!mute) { + tas2764->dac_powered = true; + ret = tas2764_update_pwr_ctrl(tas2764); + if (ret) + return ret; + } tas2764->unmuted = !mute; - return tas2764_update_pwr_ctrl(tas2764); + ret = tas2764_update_pwr_ctrl(tas2764); + if (ret) + return ret; + + if (mute) { + /* Wait for ramp-down */ + usleep_range(6000, 7000); + + tas2764->dac_powered = false; + ret = tas2764_update_pwr_ctrl(tas2764); + if (ret) + return ret; + + /* Wait a bit after shutdown */ + usleep_range(2000, 3000); + } + + return 0; } static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth) @@ -365,7 +371,7 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_component *component = dai->component; struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); - u8 tdm_rx_start_slot = 0, asi_cfg_0 = 0, asi_cfg_1 = 0; + u8 tdm_rx_start_slot = 0, asi_cfg_0 = 0, asi_cfg_1 = 0, asi_cfg_4 = 0; int ret; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -374,12 +380,14 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) fallthrough; case SND_SOC_DAIFMT_NB_NF: asi_cfg_1 = TAS2764_TDM_CFG1_RX_RISING; + asi_cfg_4 = TAS2764_TDM_CFG4_TX_FALLING; break; case SND_SOC_DAIFMT_IB_IF: asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START; fallthrough; case SND_SOC_DAIFMT_IB_NF: asi_cfg_1 = TAS2764_TDM_CFG1_RX_FALLING; + asi_cfg_4 = TAS2764_TDM_CFG4_TX_RISING; break; } @@ -389,6 +397,12 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) if (ret < 0) return ret; + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG4, + TAS2764_TDM_CFG4_TX_MASK, + asi_cfg_4); + if (ret < 0) + return ret; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START; @@ -526,10 +540,16 @@ static struct snd_soc_dai_driver tas2764_dai_driver[] = { }, }; +static uint8_t sn012776_bop_presets[] = { + 0x01, 0x32, 0x02, 0x22, 0x83, 0x2d, 0x80, 0x02, 0x06, + 0x32, 0x46, 0x30, 0x02, 0x06, 0x38, 0x40, 0x30, 0x02, + 0x06, 0x3e, 0x37, 0x30, 0xff, 0xe6 +}; + static int tas2764_codec_probe(struct snd_soc_component *component) { struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); - int ret; + int ret, i; tas2764->component = component; @@ -578,6 +598,27 @@ static int tas2764_codec_probe(struct snd_soc_component *component) if (ret < 0) return ret; + switch (tas2764->devid) { + case DEVID_SN012776: + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_BOP_SRC, + TAS2764_PWR_CTRL_BOP_SRC); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(sn012776_bop_presets); i++) { + ret = snd_soc_component_write(component, + TAS2764_BOP_CFG0 + i, + sn012776_bop_presets[i]); + + if (ret < 0) + return ret; + } + break; + default: + break; + } + return 0; } @@ -593,12 +634,21 @@ static SOC_ENUM_SINGLE_DECL( tas2764_hpf_enum, TAS2764_DC_BLK0, TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT, tas2764_hpf_texts); +static const char * const tas2764_oce_texts[] = { + "Disable", "Retry", +}; + +static SOC_ENUM_SINGLE_DECL( + tas2764_oce_enum, TAS2764_MISC_CFG1, + TAS2764_MISC_CFG1_OCE_RETRY_SHIFT, tas2764_oce_texts); + static const struct snd_kcontrol_new tas2764_snd_controls[] = { SOC_SINGLE_TLV("Speaker Volume", TAS2764_DVC, 0, TAS2764_DVC_MAX, 1, tas2764_playback_volume), SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 1, 0x14, 0, tas2764_digital_tlv), SOC_ENUM("HPF Corner Frequency", tas2764_hpf_enum), + SOC_ENUM("OCE Handling", tas2764_oce_enum), }; static const struct snd_soc_component_driver soc_component_driver_tas2764 = { @@ -626,6 +676,7 @@ static const struct reg_default tas2764_reg_defaults[] = { { TAS2764_TDM_CFG2, 0x0a }, { TAS2764_TDM_CFG3, 0x10 }, { TAS2764_TDM_CFG5, 0x42 }, + { TAS2764_INT_CLK_CFG, 0x19 }, }; static const struct regmap_range_cfg tas2764_regmap_ranges[] = { @@ -643,6 +694,7 @@ static const struct regmap_range_cfg tas2764_regmap_ranges[] = { static bool tas2764_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { + case TAS2764_SW_RST: case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4: case TAS2764_INT_CLK_CFG: return true; @@ -707,6 +759,8 @@ static int tas2764_i2c_probe(struct i2c_client *client) if (!tas2764) return -ENOMEM; + tas2764->devid = (enum tas2764_devid)of_device_get_match_data(&client->dev); + tas2764->dev = &client->dev; tas2764->irq = client->irq; i2c_set_clientdata(client, tas2764); @@ -743,7 +797,8 @@ MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id); #if defined(CONFIG_OF) static const struct of_device_id tas2764_of_match[] = { - { .compatible = "ti,tas2764" }, + { .compatible = "ti,tas2764", .data = (void *)DEVID_TAS2764 }, + { .compatible = "ti,sn012776", .data = (void *)DEVID_SN012776 }, {}, }; MODULE_DEVICE_TABLE(of, tas2764_of_match); diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h index 168af772a898..3251dc0106e0 100644 --- a/sound/soc/codecs/tas2764.h +++ b/sound/soc/codecs/tas2764.h @@ -25,10 +25,11 @@ /* Power Control */ #define TAS2764_PWR_CTRL TAS2764_REG(0X0, 0x02) -#define TAS2764_PWR_CTRL_MASK GENMASK(1, 0) +#define TAS2764_PWR_CTRL_MASK GENMASK(2, 0) #define TAS2764_PWR_CTRL_ACTIVE 0x0 #define TAS2764_PWR_CTRL_MUTE BIT(0) #define TAS2764_PWR_CTRL_SHUTDOWN BIT(1) +#define TAS2764_PWR_CTRL_BOP_SRC BIT(7) #define TAS2764_VSENSE_POWER_EN 3 #define TAS2764_ISENSE_POWER_EN 4 @@ -43,6 +44,10 @@ #define TAS2764_CHNL_0 TAS2764_REG(0X0, 0x03) +/* Miscellaneous */ +#define TAS2764_MISC_CFG1 TAS2764_REG(0x0, 0x06) +#define TAS2764_MISC_CFG1_OCE_RETRY_SHIFT 5 + /* TDM Configuration Reg0 */ #define TAS2764_TDM_CFG0 TAS2764_REG(0X0, 0x08) #define TAS2764_TDM_CFG0_SMP_MASK BIT(5) @@ -79,6 +84,12 @@ #define TAS2764_TDM_CFG3_RXS_SHIFT 0x4 #define TAS2764_TDM_CFG3_MASK GENMASK(3, 0) +/* TDM Configuration Reg4 */ +#define TAS2764_TDM_CFG4 TAS2764_REG(0X0, 0x0d) +#define TAS2764_TDM_CFG4_TX_MASK BIT(0) +#define TAS2764_TDM_CFG4_TX_RISING 0x0 +#define TAS2764_TDM_CFG4_TX_FALLING BIT(0) + /* TDM Configuration Reg5 */ #define TAS2764_TDM_CFG5 TAS2764_REG(0X0, 0x0e) #define TAS2764_TDM_CFG5_VSNS_MASK BIT(6) @@ -110,4 +121,6 @@ #define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c) #define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2) +#define TAS2764_BOP_CFG0 TAS2764_REG(0X0, 0x1d) + #endif /* __TAS2764__ */ diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 9f93b230652a..7f219df8be70 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -189,6 +189,31 @@ static int tas2770_mute(struct snd_soc_dai *dai, int mute, int direction) return tas2770_update_pwr_ctrl(tas2770); } +static int tas2770_set_ivsense_transmit(struct tas2770_priv *tas2770, + int i_slot, int v_slot) +{ + struct snd_soc_component *component = tas2770->component; + int ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG5, + TAS2770_TDM_CFG_REG5_VSNS_MASK | + TAS2770_TDM_CFG_REG5_50_MASK, + TAS2770_TDM_CFG_REG5_VSNS_ENABLE | + v_slot); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG6, + TAS2770_TDM_CFG_REG6_ISNS_MASK | + TAS2770_TDM_CFG_REG6_50_MASK, + TAS2770_TDM_CFG_REG6_ISNS_ENABLE | + i_slot); + if (ret < 0) + return ret; + + return 0; +} + static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) { int ret; @@ -199,19 +224,16 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, TAS2770_TDM_CFG_REG2_RXW_MASK, TAS2770_TDM_CFG_REG2_RXW_16BITS); - tas2770->v_sense_slot = tas2770->i_sense_slot + 2; break; case SNDRV_PCM_FORMAT_S24_LE: ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, TAS2770_TDM_CFG_REG2_RXW_MASK, TAS2770_TDM_CFG_REG2_RXW_24BITS); - tas2770->v_sense_slot = tas2770->i_sense_slot + 4; break; case SNDRV_PCM_FORMAT_S32_LE: ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, TAS2770_TDM_CFG_REG2_RXW_MASK, TAS2770_TDM_CFG_REG2_RXW_32BITS); - tas2770->v_sense_slot = tas2770->i_sense_slot + 4; break; default: @@ -221,22 +243,6 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG5, - TAS2770_TDM_CFG_REG5_VSNS_MASK | - TAS2770_TDM_CFG_REG5_50_MASK, - TAS2770_TDM_CFG_REG5_VSNS_ENABLE | - tas2770->v_sense_slot); - if (ret < 0) - return ret; - - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG6, - TAS2770_TDM_CFG_REG6_ISNS_MASK | - TAS2770_TDM_CFG_REG6_50_MASK, - TAS2770_TDM_CFG_REG6_ISNS_ENABLE | - tas2770->i_sense_slot); - if (ret < 0) - return ret; - return 0; } @@ -491,6 +497,7 @@ static int tas2770_codec_probe(struct snd_soc_component *component) { struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + int ret; tas2770->component = component; @@ -502,11 +509,19 @@ static int tas2770_codec_probe(struct snd_soc_component *component) tas2770_reset(tas2770); regmap_reinit_cache(tas2770->regmap, &tas2770_i2c_regmap); + if (tas2770->i_sense_slot != -1 && tas2770->v_sense_slot != -1) { + ret = tas2770_set_ivsense_transmit(tas2770, tas2770->i_sense_slot, + tas2770->v_sense_slot); + + if (ret < 0) + return ret; + } + return 0; } static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0); -static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0); +static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -10050, 50, 0); static const struct snd_kcontrol_new tas2770_snd_controls[] = { SOC_SINGLE_TLV("Speaker Playback Volume", TAS2770_PLAY_CFG_REG2, @@ -629,7 +644,7 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) dev_info(tas2770->dev, "Property %s is missing setting default slot\n", "ti,imon-slot-no"); - tas2770->i_sense_slot = 0; + tas2770->i_sense_slot = -1; } rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", @@ -638,7 +653,7 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) dev_info(tas2770->dev, "Property %s is missing setting default slot\n", "ti,vmon-slot-no"); - tas2770->v_sense_slot = 2; + tas2770->v_sense_slot = -1; } tas2770->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 61d9c220b6a4..13a197468193 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -2,9 +2,10 @@ // // tas2781-fmwlib.c -- TASDEVICE firmware support // -// Copyright 2023 - 2024 Texas Instruments, Inc. +// Copyright 2023 - 2025 Texas Instruments, Inc. // // Author: Shenghao Ding <shenghao-ding@ti.com> +// Author: Baojun Xu <baojun.xu@ti.com> #include <linux/crc8.h> #include <linux/firmware.h> @@ -389,10 +390,10 @@ static unsigned char map_dev_idx(struct tasdevice_fw *tas_fmw, int i, n = ARRAY_SIZE(non_ppc3_mapping_table); unsigned char dev_idx = 0; - if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781) { + if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781_BASIC_MIN) { p = (struct blktyp_devidx_map *)ppc3_tas2781_mapping_table; n = ARRAY_SIZE(ppc3_tas2781_mapping_table); - } else if (fw_fixed_hdr->ppcver >= PPC3_VERSION) { + } else if (fw_fixed_hdr->ppcver >= PPC3_VERSION_BASE) { p = (struct blktyp_devidx_map *)ppc3_mapping_table; n = ARRAY_SIZE(ppc3_mapping_table); } @@ -559,6 +560,124 @@ out: return offset; } +static void fct_param_address_parser(struct cali_reg *r, + struct tasdevice_fw *tas_fmw, const unsigned char *data) +{ + struct fct_param_address *p = &tas_fmw->fct_par_addr; + unsigned int i; + + /* + * Calibration parameters locations and data schema in dsp firmware. + * The number of items are flexible, but not more than 20. The dsp tool + * will reseve 20*24-byte space for fct params. In some cases, the + * number of fct param is less than 20, the data will be saved from the + * beginning, the rest part will be stuffed with zero. + * + * fct_param_num (not more than 20) + * for (i = 0; i < fct_param_num; i++) { + * Alias of fct param (20 bytes) + * Book (1 byte) + * Page (1 byte) + * Offset (1 byte) + * CoeffLength (1 byte) = 0x1 + * } + * if (20 - fct_param_num) + * 24*(20 - fct_param_num) pieces of '0' as stuffing + * + * As follow: + * umg_SsmKEGCye = Book, Page, Offset, CoeffLength + * iks_E0 = Book, Page, Offset, CoeffLength + * yep_LsqM0 = Book, Page, Offset, CoeffLength + * oyz_U0_ujx = Book, Page, Offset, CoeffLength + * iks_GC_GMgq = Book, Page, Offset, CoeffLength + * gou_Yao = Book, Page, Offset, CoeffLength + * kgd_Wsc_Qsbp = Book, Page, Offset, CoeffLength + * yec_CqseSsqs = Book, Page, Offset, CoeffLength + * iks_SogkGgog2 = Book, Page, Offset, CoeffLength + * yec_Sae_Y = Book, Page, Offset, CoeffLength + * Re_Int = Book, Page, Offset, CoeffLength + * SigFlag = Book, Page, Offset, CoeffLength + * a1_Int = Book, Page, Offset, CoeffLength + * a2_Int = Book, Page, Offset, CoeffLength + */ + for (i = 0; i < 20; i++) { + const unsigned char *dat = &data[24 * i]; + + /* + * check whether current fct param is empty. + */ + if (dat[23] != 1) + break; + + if (!strncmp(dat, "umg_SsmKEGCye", 20)) + r->pow_reg = TASDEVICE_REG(dat[20], dat[21], dat[22]); + /* high 32-bit of real-time spk impedance */ + else if (!strncmp(dat, "iks_E0", 20)) + r->r0_reg = TASDEVICE_REG(dat[20], dat[21], dat[22]); + /* inverse of real-time spk impedance */ + else if (!strncmp(dat, "yep_LsqM0", 20)) + r->invr0_reg = + TASDEVICE_REG(dat[20], dat[21], dat[22]); + /* low 32-bit of real-time spk impedance */ + else if (!strncmp(dat, "oyz_U0_ujx", 20)) + r->r0_low_reg = + TASDEVICE_REG(dat[20], dat[21], dat[22]); + /* Delta Thermal Limit */ + else if (!strncmp(dat, "iks_GC_GMgq", 20)) + r->tlimit_reg = + TASDEVICE_REG(dat[20], dat[21], dat[22]); + /* Thermal data for PG 1.0 device */ + else if (!strncmp(dat, "gou_Yao", 20)) + memcpy(p->thr, &dat[20], 3); + /* Pilot tone enable flag, usually the sine wave */ + else if (!strncmp(dat, "kgd_Wsc_Qsbp", 20)) + memcpy(p->plt_flg, &dat[20], 3); + /* Pilot tone gain for calibration */ + else if (!strncmp(dat, "yec_CqseSsqs", 20)) + memcpy(p->sin_gn, &dat[20], 3); + /* Pilot tone gain for calibration, useless in PG 2.0 */ + else if (!strncmp(dat, "iks_SogkGgog2", 20)) + memcpy(p->sin_gn2, &dat[20], 3); + /* Thermal data for PG 2.0 device */ + else if (!strncmp(dat, "yec_Sae_Y", 20)) + memcpy(p->thr2, &dat[20], 3); + /* Spk Equivalent Resistance in fixed-point format */ + else if (!strncmp(dat, "Re_Int", 20)) + memcpy(p->r0_reg, &dat[20], 3); + /* Check whether the spk connection is open */ + else if (!strncmp(dat, "SigFlag", 20)) + memcpy(p->tf_reg, &dat[20], 3); + /* check spk resonant frequency */ + else if (!strncmp(dat, "a1_Int", 20)) + memcpy(p->a1_reg, &dat[20], 3); + /* check spk resonant frequency */ + else if (!strncmp(dat, "a2_Int", 20)) + memcpy(p->a2_reg, &dat[20], 3); + } +} + +static int fw_parse_fct_param_address(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct calidata *cali_data = &tas_priv->cali_data; + struct cali_reg *r = &cali_data->cali_reg_array; + const unsigned char *data = fmw->data; + + if (offset + 520 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + return -EINVAL; + } + + /* skip reserved part */ + offset += 40; + + fct_param_address_parser(r, tas_fmw, &data[offset]); + + offset += 480; + + return offset; +} + static int fw_parse_variable_header_kernel( struct tasdevice_priv *tas_priv, const struct firmware *fmw, int offset) @@ -1496,7 +1615,7 @@ static int tasdev_block_chksum(struct tasdevice_priv *tas_priv, unsigned int nr_value; int ret; - ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_I2CChecksum, + ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_CHECKSUM_REG, &nr_value); if (ret < 0) { dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn); @@ -1540,7 +1659,7 @@ static int tasdev_load_blk(struct tasdevice_priv *tas_priv, while (block->nr_retry > 0) { if (block->is_pchksum_present) { ret = tasdevice_dev_write(tas_priv, chn, - TASDEVICE_I2CChecksum, 0); + TASDEVICE_CHECKSUM_REG, 0); if (ret < 0) break; } @@ -1686,13 +1805,29 @@ static int tasdevice_load_block(struct tasdevice_priv *tas_priv, return rc; } +static void dspbin_type_check(struct tasdevice_priv *tas_priv, + unsigned int ppcver) +{ + if (ppcver >= PPC3_VERSION_TAS2781_ALPHA_MIN) { + if (ppcver >= PPC3_VERSION_TAS2781_BETA_MIN) + tas_priv->dspbin_typ = TASDEV_BETA; + else if (ppcver >= PPC3_VERSION_TAS2781_BASIC_MIN) + tas_priv->dspbin_typ = TASDEV_BASIC; + else + tas_priv->dspbin_typ = TASDEV_ALPHA; + } + if (tas_priv->dspbin_typ != TASDEV_BASIC) + tas_priv->fw_parse_fct_param_address = + fw_parse_fct_param_address; +} + static int dspfw_default_callback(struct tasdevice_priv *tas_priv, unsigned int drv_ver, unsigned int ppcver) { int rc = 0; if (drv_ver == 0x100) { - if (ppcver >= PPC3_VERSION) { + if (ppcver >= PPC3_VERSION_BASE) { tas_priv->fw_parse_variable_header = fw_parse_variable_header_kernel; tas_priv->fw_parse_program_data = @@ -1701,6 +1836,7 @@ static int dspfw_default_callback(struct tasdevice_priv *tas_priv, fw_parse_configuration_data_kernel; tas_priv->tasdevice_load_block = tasdevice_load_block_kernel; + dspbin_type_check(tas_priv, ppcver); } else { switch (ppcver) { case 0x00: @@ -1716,7 +1852,7 @@ static int dspfw_default_callback(struct tasdevice_priv *tas_priv, default: dev_err(tas_priv->dev, "%s: PPCVer must be 0x0 or 0x%02x", - __func__, PPC3_VERSION); + __func__, PPC3_VERSION_BASE); dev_err(tas_priv->dev, " Current:0x%02x\n", ppcver); rc = -EINVAL; @@ -1952,28 +2088,25 @@ static int tasdevice_dspfw_ready(const struct firmware *fmw, struct tasdevice_fw_fixed_hdr *fw_fixed_hdr; struct tasdevice_fw *tas_fmw; int offset = 0; - int ret = 0; + int ret; if (!fmw || !fmw->data) { dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n", __func__, tas_priv->coef_binaryname); - ret = -EINVAL; - goto out; + return -EINVAL; } tas_priv->fmw = kzalloc(sizeof(struct tasdevice_fw), GFP_KERNEL); - if (!tas_priv->fmw) { - ret = -ENOMEM; - goto out; - } + if (!tas_priv->fmw) + return -ENOMEM; + tas_fmw = tas_priv->fmw; tas_fmw->dev = tas_priv->dev; offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset); - if (offset == -EINVAL) { - ret = -EINVAL; - goto out; - } + if (offset == -EINVAL) + return -EINVAL; + fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr); /* Support different versions of firmware */ switch (fw_fixed_hdr->drv_ver) { @@ -2006,28 +2139,32 @@ static int tasdevice_dspfw_ready(const struct firmware *fmw, ret = dspfw_default_callback(tas_priv, fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver); if (ret) - goto out; + return ret; break; } offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset); - if (offset < 0) { - ret = offset; - goto out; - } + if (offset < 0) + return offset; + offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw, offset); - if (offset < 0) { - ret = offset; - goto out; - } + if (offset < 0) + return offset; + offset = tas_priv->fw_parse_configuration_data(tas_priv, tas_fmw, fmw, offset); if (offset < 0) - ret = offset; + return offset; -out: - return ret; + if (tas_priv->fw_parse_fct_param_address) { + offset = tas_priv->fw_parse_fct_param_address(tas_priv, + tas_fmw, fmw, offset); + if (offset < 0) + return offset; + } + + return 0; } int tasdevice_dsp_parser(void *context) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 728bf78ae71f..b950914b7d48 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -2,7 +2,7 @@ // // ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier // -// Copyright (C) 2022 - 2024 Texas Instruments Incorporated +// Copyright (C) 2022 - 2025 Texas Instruments Incorporated // https://www.ti.com // // The TAS2563/TAS2781 driver implements a flexible and configurable @@ -349,13 +349,31 @@ static int calib_data_get(struct tasdevice_priv *tas_priv, int reg, return rc; } +static int partial_cali_data_update(int *reg, int j) +{ + switch (tas2781_cali_start_reg[j].reg) { + case 0: + return reg[0]; + case TAS2781_PRM_PLT_FLAG_REG: + return reg[1]; + case TAS2781_PRM_SINEGAIN_REG: + return reg[2]; + case TAS2781_PRM_SINEGAIN2_REG: + return reg[3]; + default: + return 0; + } +} + static void sngl_calib_start(struct tasdevice_priv *tas_priv, int i, int *reg, unsigned char *dat) { struct tasdevice *tasdev = tas_priv->tasdevice; struct bulk_reg_val *p = tasdev[i].cali_data_backup; + struct bulk_reg_val *t = &tasdev[i].alp_cali_bckp; const int sum = ARRAY_SIZE(tas2781_cali_start_reg); - int j; + unsigned char val[4]; + int j, r; if (p == NULL) return; @@ -370,30 +388,23 @@ static void sngl_calib_start(struct tasdevice_priv *tas_priv, int i, tasdevice_dev_read(tas_priv, i, p[j].reg, (int *)&p[j].val[0]); } else { - switch (tas2781_cali_start_reg[j].reg) { - case 0: { - if (!reg[0]) - continue; - p[j].reg = reg[0]; + if (!tas_priv->dspbin_typ) { + r = partial_cali_data_update(reg, j); + if (r) + p[j].reg = r; } - break; - case TAS2781_PRM_PLT_FLAG_REG: - p[j].reg = reg[1]; - break; - case TAS2781_PRM_SINEGAIN_REG: - p[j].reg = reg[2]; - break; - case TAS2781_PRM_SINEGAIN2_REG: - p[j].reg = reg[3]; - break; - } - tasdevice_dev_bulk_read(tas_priv, i, p[j].reg, - p[j].val, 4); + + if (p[j].reg) + tasdevice_dev_bulk_read(tas_priv, i, p[j].reg, + p[j].val, 4); } } + if (tas_priv->dspbin_typ == TASDEV_ALPHA) + tasdevice_dev_bulk_read(tas_priv, i, t->reg, t->val, 4); + /* Update the setting for calibration */ - for (j = 0; j < sum - 2; j++) { + for (j = 0; j < sum - 4; j++) { if (p[j].val_len == 1) { if (p[j].is_locked) tasdevice_dev_write(tas_priv, i, @@ -401,17 +412,33 @@ static void sngl_calib_start(struct tasdevice_priv *tas_priv, int i, TAS2781_TEST_PAGE_UNLOCK); tasdevice_dev_write(tas_priv, i, p[j].reg, tas2781_cali_start_reg[j].val[0]); - } else { - if (!p[j].reg) - continue; - tasdevice_dev_bulk_write(tas_priv, i, p[j].reg, - (unsigned char *) - tas2781_cali_start_reg[j].val, 4); } } - tasdevice_dev_bulk_write(tas_priv, i, p[j].reg, &dat[1], 4); - tasdevice_dev_bulk_write(tas_priv, i, p[j + 1].reg, &dat[5], 4); + if (tas_priv->dspbin_typ == TASDEV_ALPHA) { + val[0] = 0x00; + val[1] = 0x00; + val[2] = 0x21; + val[3] = 0x8e; + } else { + val[0] = tas2781_cali_start_reg[j].val[0]; + val[1] = tas2781_cali_start_reg[j].val[1]; + val[2] = tas2781_cali_start_reg[j].val[2]; + val[3] = tas2781_cali_start_reg[j].val[3]; + } + tasdevice_dev_bulk_write(tas_priv, i, p[j].reg, val, 4); + tasdevice_dev_bulk_write(tas_priv, i, p[j + 1].reg, + (unsigned char *)tas2781_cali_start_reg[j + 1].val, 4); + tasdevice_dev_bulk_write(tas_priv, i, p[j + 2].reg, &dat[1], 4); + tasdevice_dev_bulk_write(tas_priv, i, p[j + 3].reg, &dat[5], 4); + if (tas_priv->dspbin_typ == TASDEV_ALPHA) { + val[0] = 0x00; + val[1] = 0x00; + val[2] = 0x2a; + val[3] = 0x0b; + + tasdevice_dev_bulk_read(tas_priv, i, t->reg, val, 4); + } } static int tas2781_calib_start_put(struct snd_kcontrol *kcontrol, @@ -452,14 +479,15 @@ static int tas2781_calib_start_put(struct snd_kcontrol *kcontrol, return 1; } -static void tas2781_calib_stop_put(struct tasdevice_priv *tas_priv) +static void tas2781_calib_stop_put(struct tasdevice_priv *priv) { const int sum = ARRAY_SIZE(tas2781_cali_start_reg); int i, j; - for (i = 0; i < tas_priv->ndev; i++) { - struct tasdevice *tasdev = tas_priv->tasdevice; + for (i = 0; i < priv->ndev; i++) { + struct tasdevice *tasdev = priv->tasdevice; struct bulk_reg_val *p = tasdev[i].cali_data_backup; + struct bulk_reg_val *t = &tasdev[i].alp_cali_bckp; if (p == NULL) continue; @@ -467,18 +495,21 @@ static void tas2781_calib_stop_put(struct tasdevice_priv *tas_priv) for (j = 0; j < sum; j++) { if (p[j].val_len == 1) { if (p[j].is_locked) - tasdevice_dev_write(tas_priv, i, + tasdevice_dev_write(priv, i, TAS2781_TEST_UNLOCK_REG, TAS2781_TEST_PAGE_UNLOCK); - tasdevice_dev_write(tas_priv, i, p[j].reg, + tasdevice_dev_write(priv, i, p[j].reg, p[j].val[0]); } else { if (!p[j].reg) continue; - tasdevice_dev_bulk_write(tas_priv, i, p[j].reg, + tasdevice_dev_bulk_write(priv, i, p[j].reg, p[j].val, 4); } } + + if (priv->dspbin_typ == TASDEV_ALPHA) + tasdevice_dev_bulk_write(priv, i, t->reg, t->val, 4); } } @@ -489,14 +520,11 @@ static int tas2563_calib_start_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp); const int sum = ARRAY_SIZE(tas2563_cali_start_reg); - int rc = 1; int i, j; guard(mutex)(&tas_priv->codec_lock); - if (tas_priv->chip_id != TAS2563) { - rc = -1; - goto out; - } + if (tas_priv->chip_id != TAS2563) + return -1; for (i = 0; i < tas_priv->ndev; i++) { struct tasdevice *tasdev = tas_priv->tasdevice; @@ -523,8 +551,8 @@ static int tas2563_calib_start_put(struct snd_kcontrol *kcontrol, q[j].val, 4); } } -out: - return rc; + + return 1; } static void tas2563_calib_stop_put(struct tasdevice_priv *tas_priv) @@ -576,7 +604,7 @@ static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol, struct cali_reg *p = &cali_data->cali_reg_array; unsigned char *src = ucontrol->value.bytes.data; unsigned char *dst = cali_data->data; - int rc = 1, i = 0; + int i = 0; int j; guard(mutex)(&priv->codec_lock); @@ -593,19 +621,23 @@ static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol, i += 2; priv->is_user_space_calidata = true; - p->r0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); - i += 3; - p->r0_low_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); - i += 3; - p->invr0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); - i += 3; - p->pow_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); - i += 3; - p->tlimit_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); - i += 3; + if (priv->dspbin_typ == TASDEV_BASIC) { + p->r0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); + i += 3; + p->r0_low_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); + i += 3; + p->invr0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); + i += 3; + p->pow_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); + i += 3; + p->tlimit_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]); + i += 3; + } else { + i += 15; + } memcpy(dst, &src[i], cali_data->total_sz); - return rc; + return 1; } static int tas2781_latch_reg_get(struct snd_kcontrol *kcontrol, @@ -649,12 +681,19 @@ static int tasdev_tf_data_get(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *bytes_ext = (struct soc_bytes_ext *) kcontrol->private_value; unsigned char *dst = ucontrol->value.bytes.data; - unsigned int reg; + unsigned int reg = TAS2781_RUNTIME_RE_REG_TF; + + if (tas_priv->chip_id == TAS2781) { + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct fct_param_address *p = &(tas_fmw->fct_par_addr); - if (tas_priv->chip_id == TAS2781) reg = TAS2781_RUNTIME_RE_REG_TF; - else + if (tas_priv->dspbin_typ) + reg = TASDEVICE_REG(p->tf_reg[0], p->tf_reg[1], + p->tf_reg[2]); + } else { reg = TAS2563_RUNTIME_RE_REG_TF; + } guard(mutex)(&tas_priv->codec_lock); dst[0] = bytes_ext->max; @@ -669,12 +708,19 @@ static int tasdev_re_data_get(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *bytes_ext = (struct soc_bytes_ext *) kcontrol->private_value; unsigned char *dst = ucontrol->value.bytes.data; - unsigned int reg; + unsigned int reg = TAS2781_RUNTIME_RE_REG; - if (tas_priv->chip_id == TAS2781) - reg = TAS2781_RUNTIME_RE_REG; - else + if (tas_priv->chip_id == TAS2781) { + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct fct_param_address *p = &(tas_fmw->fct_par_addr); + + if (tas_priv->dspbin_typ) + reg = TASDEVICE_REG(p->r0_reg[0], p->r0_reg[1], + p->r0_reg[2]); + } else { reg = TAS2563_RUNTIME_RE_REG; + } + guard(mutex)(&tas_priv->codec_lock); dst[0] = bytes_ext->max; return calib_data_get(tas_priv, reg, &dst[1]); @@ -708,11 +754,16 @@ static int tasdev_XMA1_data_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp); + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct fct_param_address *p = &(tas_fmw->fct_par_addr); struct soc_bytes_ext *bytes_ext = (struct soc_bytes_ext *) kcontrol->private_value; unsigned char *dst = ucontrol->value.bytes.data; unsigned int reg = TASDEVICE_XM_A1_REG; + if (tas_priv->dspbin_typ) + reg = TASDEVICE_REG(p->a1_reg[0], p->a1_reg[1], p->a1_reg[2]); + guard(mutex)(&tas_priv->codec_lock); dst[0] = bytes_ext->max; return calib_data_get(tas_priv, reg, &dst[1]); @@ -723,11 +774,16 @@ static int tasdev_XMA2_data_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp); + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct fct_param_address *p = &(tas_fmw->fct_par_addr); struct soc_bytes_ext *bytes_ext = (struct soc_bytes_ext *) kcontrol->private_value; unsigned char *dst = ucontrol->value.bytes.data; unsigned int reg = TASDEVICE_XM_A2_REG; + if (tas_priv->dspbin_typ) + reg = TASDEVICE_REG(p->a2_reg[0], p->a2_reg[1], p->a2_reg[2]); + guard(mutex)(&tas_priv->codec_lock); dst[0] = bytes_ext->max; return calib_data_get(tas_priv, reg, &dst[1]); @@ -1115,25 +1171,21 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv) char *conf_name, *prog_name; int nr_controls = 4; int mix_index = 0; - int ret; /* Alloc kcontrol via devm_kzalloc, which don't manually * free the kcontrol */ dsp_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, sizeof(dsp_ctrls[0]), GFP_KERNEL); - if (!dsp_ctrls) { - ret = -ENOMEM; - goto out; - } + if (!dsp_ctrls) + return -ENOMEM; /* Create mixer items for selecting the active Program and Config */ prog_name = devm_kstrdup(tas_priv->dev, "Speaker Program Id", GFP_KERNEL); - if (!prog_name) { - ret = -ENOMEM; - goto out; - } + if (!prog_name) + return -ENOMEM; + dsp_ctrls[mix_index].name = prog_name; dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; dsp_ctrls[mix_index].info = tasdevice_info_programs; @@ -1143,10 +1195,9 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv) conf_name = devm_kstrdup(tas_priv->dev, "Speaker Config Id", GFP_KERNEL); - if (!conf_name) { - ret = -ENOMEM; - goto out; - } + if (!conf_name) + return -ENOMEM; + dsp_ctrls[mix_index].name = conf_name; dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; dsp_ctrls[mix_index].info = tasdevice_info_configurations; @@ -1156,10 +1207,9 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv) active_dev_num = devm_kstrdup(tas_priv->dev, "Activate Tasdevice Num", GFP_KERNEL); - if (!active_dev_num) { - ret = -ENOMEM; - goto out; - } + if (!active_dev_num) + return -ENOMEM; + dsp_ctrls[mix_index].name = active_dev_num; dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; dsp_ctrls[mix_index].info = tasdevice_info_active_num; @@ -1168,27 +1218,64 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv) mix_index++; chip_id = devm_kstrdup(tas_priv->dev, "Tasdevice Chip Id", GFP_KERNEL); - if (!chip_id) { - ret = -ENOMEM; - goto out; - } + if (!chip_id) + return -ENOMEM; + dsp_ctrls[mix_index].name = chip_id; dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; dsp_ctrls[mix_index].info = tasdevice_info_chip_id; dsp_ctrls[mix_index].get = tasdevice_get_chip_id; mix_index++; - ret = snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls, + return snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls, nr_controls < mix_index ? nr_controls : mix_index); +} -out: - return ret; +static void cali_reg_update(struct bulk_reg_val *p, + struct fct_param_address *t) +{ + const int sum = ARRAY_SIZE(tas2781_cali_start_reg); + int reg, j; + + for (j = 0; j < sum; j++) { + switch (tas2781_cali_start_reg[j].reg) { + case 0: + reg = TASDEVICE_REG(t->thr[0], t->thr[1], t->thr[2]); + break; + case TAS2781_PRM_PLT_FLAG_REG: + reg = TASDEVICE_REG(t->plt_flg[0], t->plt_flg[1], + t->plt_flg[2]); + break; + case TAS2781_PRM_SINEGAIN_REG: + reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1], + t->sin_gn[2]); + break; + case TAS2781_PRM_SINEGAIN2_REG: + reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1], + t->sin_gn[2]); + break; + default: + reg = 0; + break; + } + if (reg) + p[j].reg = reg; + } +} + +static void alpa_cali_update(struct bulk_reg_val *p, + struct fct_param_address *t) +{ + p->is_locked = false; + p->reg = TASDEVICE_REG(t->thr2[0], t->thr2[1], t->thr2[2]); + p->val_len = 4; } static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) { struct calidata *cali_data = &priv->cali_data; struct tasdevice *tasdev = priv->tasdevice; + struct tasdevice_fw *fmw = priv->fmw; struct soc_bytes_ext *ext_cali_data; struct snd_kcontrol_new *cali_ctrls; unsigned int nctrls; @@ -1204,14 +1291,25 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) } if (priv->chip_id == TAS2781) { + struct fct_param_address *t = &(fmw->fct_par_addr); + cali_ctrls = (struct snd_kcontrol_new *)tas2781_cali_controls; nctrls = ARRAY_SIZE(tas2781_cali_controls); for (i = 0; i < priv->ndev; i++) { - tasdev[i].cali_data_backup = + struct bulk_reg_val *p; + + p = tasdev[i].cali_data_backup = kmemdup(tas2781_cali_start_reg, sizeof(tas2781_cali_start_reg), GFP_KERNEL); if (!tasdev[i].cali_data_backup) return -ENOMEM; + if (priv->dspbin_typ) { + cali_reg_update(p, t); + if (priv->dspbin_typ == TASDEV_ALPHA) { + p = &tasdev[i].alp_cali_bckp; + alpa_cali_update(p, t); + } + } } } else { cali_ctrls = (struct snd_kcontrol_new *)tas2563_cali_controls; @@ -1273,8 +1371,6 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv) (cali_data->cali_dat_sz_per_dev + 1) + 1 + 15 + 1; priv->cali_data.total_sz = priv->ndev * (cali_data->cali_dat_sz_per_dev + 1); - priv->cali_data.data = devm_kzalloc(priv->dev, - ext_cali_data->max, GFP_KERNEL); cali_ctrls[i].name = cali_name; cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; cali_ctrls[i].info = snd_soc_bytes_info_ext; @@ -1469,7 +1565,6 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream, unsigned int slot_width; unsigned int fsrate; int bclk_rate; - int rc = 0; fsrate = params_rate(params); switch (fsrate) { @@ -1479,8 +1574,7 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream, default: dev_err(tas_priv->dev, "%s: incorrect sample rate = %u\n", __func__, fsrate); - rc = -EINVAL; - goto out; + return -EINVAL; } slot_width = params_width(params); @@ -1493,20 +1587,17 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream, default: dev_err(tas_priv->dev, "%s: incorrect slot width = %u\n", __func__, slot_width); - rc = -EINVAL; - goto out; + return -EINVAL; } bclk_rate = snd_soc_params_to_bclk(params); if (bclk_rate < 0) { dev_err(tas_priv->dev, "%s: incorrect bclk rate = %d\n", __func__, bclk_rate); - rc = bclk_rate; - goto out; + return bclk_rate; } -out: - return rc; + return 0; } static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai, @@ -1663,7 +1754,6 @@ static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) static int tasdevice_i2c_probe(struct i2c_client *i2c) { - const struct i2c_device_id *id = i2c_match_id(tasdevice_id, i2c); const struct acpi_device_id *acpi_id; struct tasdevice_priv *tas_priv; int ret; @@ -1685,7 +1775,7 @@ static int tasdevice_i2c_probe(struct i2c_client *i2c) tas_priv->chip_id = acpi_id->driver_data; tas_priv->isacpi = true; } else { - tas_priv->chip_id = id ? id->driver_data : 0; + tas_priv->chip_id = (uintptr_t)i2c_get_match_data(i2c); tas_priv->isacpi = false; } diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c index 6dd6c0896eff..f0361822061f 100644 --- a/sound/soc/codecs/tas5720.c +++ b/sound/soc/codecs/tas5720.c @@ -43,7 +43,6 @@ static const char * const tas5720_supply_names[] = { struct tas5720_data { struct snd_soc_component *component; struct regmap *regmap; - struct i2c_client *tas5720_client; enum tas572x_type devtype; struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES]; struct delayed_work fault_check_work; @@ -729,7 +728,6 @@ static int tas5720_probe(struct i2c_client *client) struct device *dev = &client->dev; struct tas5720_data *data; const struct regmap_config *regmap_config; - const struct i2c_device_id *id; int ret; int i; @@ -737,11 +735,9 @@ static int tas5720_probe(struct i2c_client *client) if (!data) return -ENOMEM; - id = i2c_match_id(tas5720_id, client); - data->tas5720_client = client; - data->devtype = id->driver_data; + data->devtype = (uintptr_t)i2c_get_match_data(client); - switch (id->driver_data) { + switch (data->devtype) { case TAS5720: regmap_config = &tas5720_regmap_config; break; @@ -774,7 +770,7 @@ static int tas5720_probe(struct i2c_client *client) dev_set_drvdata(dev, data); - switch (id->driver_data) { + switch (data->devtype) { case TAS5720: ret = devm_snd_soc_register_component(&client->dev, &soc_component_dev_tas5720, diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index 868e8a91e05b..191e067ed1c9 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1401,7 +1401,6 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct adc3xxx *adc3xxx = NULL; - const struct i2c_device_id *id; int ret; adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL); @@ -1466,8 +1465,7 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, adc3xxx); - id = i2c_match_id(adc3xxx_i2c_id, i2c); - adc3xxx->type = id->driver_data; + adc3xxx->type = (uintptr_t)i2c_get_match_data(i2c); /* Reset codec chip */ gpiod_set_value_cansleep(adc3xxx->rst_pin, 1); @@ -1495,8 +1493,7 @@ static void adc3xxx_i2c_remove(struct i2c_client *client) { struct adc3xxx *adc3xxx = i2c_get_clientdata(client); - if (adc3xxx->mclk) - clk_disable_unprepare(adc3xxx->mclk); + clk_disable_unprepare(adc3xxx->mclk); adc3xxx_free_gpio(adc3xxx); snd_soc_unregister_component(&client->dev); } diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index d81ab9c25c29..4b3f9128ec37 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1736,12 +1736,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c) { struct aic31xx_priv *aic31xx; unsigned int micbias_value = MICBIAS_2_0V; - const struct i2c_device_id *id = i2c_match_id(aic31xx_i2c_id, i2c); int i, ret; - dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__, - id->name, (int)id->driver_data); - aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL); if (!aic31xx) return -ENOMEM; @@ -1758,7 +1754,7 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c) aic31xx->dev = &i2c->dev; aic31xx->irq = i2c->irq; - aic31xx->codec_type = id->driver_data; + aic31xx->codec_type = (uintptr_t)i2c_get_match_data(i2c); dev_set_drvdata(aic31xx->dev, aic31xx); diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c index bb33fd3dfb4f..0b585925c1ac 100644 --- a/sound/soc/codecs/tlv320aic3x-i2c.c +++ b/sound/soc/codecs/tlv320aic3x-i2c.c @@ -31,14 +31,13 @@ static int aic3x_i2c_probe(struct i2c_client *i2c) { struct regmap *regmap; struct regmap_config config; - const struct i2c_device_id *id = i2c_match_id(aic3x_i2c_id, i2c); config = aic3x_regmap; config.reg_bits = 8; config.val_bits = 8; regmap = devm_regmap_init_i2c(i2c, &config); - return aic3x_probe(&i2c->dev, regmap, id->driver_data); + return aic3x_probe(&i2c->dev, regmap, (uintptr_t)i2c_get_match_data(i2c)); } static void aic3x_i2c_remove(struct i2c_client *i2c) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index fa46f51d4341..423b9264a205 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1477,10 +1477,8 @@ static int dac33_i2c_probe(struct i2c_client *client) if (dac33 == NULL) return -ENOMEM; - dac33->reg_cache = devm_kmemdup(&client->dev, - dac33_reg, - ARRAY_SIZE(dac33_reg) * sizeof(u8), - GFP_KERNEL); + dac33->reg_cache = devm_kmemdup_array(&client->dev, dac33_reg, ARRAY_SIZE(dac33_reg), + sizeof(dac33_reg[0]), GFP_KERNEL); if (!dac33->reg_cache) return -ENOMEM; diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 5bc486283fde..b5472fa1bdda 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -222,7 +222,6 @@ static int tpa6130a2_probe(struct i2c_client *client) struct tpa6130a2_data *data; struct tpa6130a2_platform_data *pdata = client->dev.platform_data; struct device_node *np = client->dev.of_node; - const struct i2c_device_id *id; const char *regulator; unsigned int version; int ret; @@ -251,8 +250,7 @@ static int tpa6130a2_probe(struct i2c_client *client) i2c_set_clientdata(client, data); - id = i2c_match_id(tpa6130a2_id, client); - data->id = id->driver_data; + data->id = (uintptr_t)i2c_get_match_data(client); if (data->power_gpio >= 0) { ret = devm_gpio_request(dev, data->power_gpio, diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index b9eb59e3bfa0..5ce0db9326fd 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -399,7 +399,6 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c) return 0; } -#ifdef CONFIG_PM_SLEEP static int ts3a227e_suspend(struct device *dev) { struct ts3a227e *ts3a227e = dev_get_drvdata(dev); @@ -419,10 +418,9 @@ static int ts3a227e_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops ts3a227e_pm = { - SET_SYSTEM_SLEEP_PM_OPS(ts3a227e_suspend, ts3a227e_resume) + SYSTEM_SLEEP_PM_OPS(ts3a227e_suspend, ts3a227e_resume) }; static const struct i2c_device_id ts3a227e_i2c_ids[] = { @@ -450,7 +448,7 @@ MODULE_DEVICE_TABLE(acpi, ts3a227e_acpi_match); static struct i2c_driver ts3a227e_driver = { .driver = { .name = "ts3a227e", - .pm = &ts3a227e_pm, + .pm = pm_ptr(&ts3a227e_pm), .of_match_table = of_match_ptr(ts3a227e_of_match), .acpi_match_table = ACPI_PTR(ts3a227e_acpi_match), }, diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c index 850e5de9271e..da2f3cb1cd13 100644 --- a/sound/soc/codecs/tscs454.c +++ b/sound/soc/codecs/tscs454.c @@ -10,6 +10,7 @@ #include <linux/i2c.h> #include <linux/err.h> #include <linux/string.h> +#include <linux/string_choices.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/mutex.h> @@ -737,9 +738,7 @@ static int pll_power_event(struct snd_soc_dapm_widget *w, ret = snd_soc_component_update_bits(component, R_PLLCTL, msk, val); if (ret < 0) { dev_err(component->dev, "Failed to %s PLL %d (%d)\n", - enable ? "enable" : "disable", - pll1 ? 1 : 2, - ret); + str_enable_disable(enable), pll1 ? 1 : 2, ret); return ret; } diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index e3782762139f..609886461805 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -2049,7 +2049,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, case SND_SOC_DAIFMT_CBP_CFP: format &= ~(TWL4030_VIF_SLAVE_EN); break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: format |= TWL4030_VIF_SLAVE_EN; break; default: diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c index 3d49a7869948..b0b29012842d 100644 --- a/sound/soc/codecs/uda1342.c +++ b/sound/soc/codecs/uda1342.c @@ -319,7 +319,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(uda1342_pm_ops, uda1342_suspend, uda1342_resume, NULL); static const struct i2c_device_id uda1342_i2c_id[] = { - { "uda1342", 0 }, + { "uda1342" }, { } }; MODULE_DEVICE_TABLE(i2c, uda1342_i2c_id); diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 4f8fdd574585..c179d865b938 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -766,10 +766,8 @@ static int uda1380_i2c_probe(struct i2c_client *i2c) return ret; } - uda1380->reg_cache = devm_kmemdup(&i2c->dev, - uda1380_reg, - ARRAY_SIZE(uda1380_reg) * sizeof(u16), - GFP_KERNEL); + uda1380->reg_cache = devm_kmemdup_array(&i2c->dev, uda1380_reg, ARRAY_SIZE(uda1380_reg), + sizeof(uda1380_reg[0]), GFP_KERNEL); if (!uda1380->reg_cache) return -ENOMEM; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index a2521e16c099..7cef43bb2a88 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -159,6 +159,8 @@ {"AMIC MUX" #id, "ADC5", "ADC5"}, \ {"AMIC MUX" #id, "ADC6", "ADC6"} +#define NUM_CODEC_DAIS 7 + enum { WCD9335_RX0 = 0, WCD9335_RX1, diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 910852eb9698..dd0cda394bf1 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -23,6 +23,8 @@ #include "wcd-clsh-v2.h" #include "wcd-mbhc-v2.h" +#include <dt-bindings/sound/qcom,wcd934x.h> + #define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) @@ -307,6 +309,7 @@ {"SLIM TX" #id, NULL, "CDC_IF TX" #id " MUX"} #define WCD934X_MAX_MICBIAS MIC_BIAS_4 +#define NUM_CODEC_DAIS 9 enum { SIDO_SOURCE_INTERNAL, @@ -435,19 +438,6 @@ enum { }; enum { - AIF1_PB = 0, - AIF1_CAP, - AIF2_PB, - AIF2_CAP, - AIF3_PB, - AIF3_CAP, - AIF4_PB, - AIF4_VIFEED, - AIF4_MAD_TX, - NUM_CODEC_DAIS, -}; - -enum { INTn_1_INP_SEL_ZERO = 0, INTn_1_INP_SEL_DEC0, INTn_1_INP_SEL_DEC1, diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c index 0c33f7f3dc25..1bfe7383b311 100644 --- a/sound/soc/codecs/wcd937x-sdw.c +++ b/sound/soc/codecs/wcd937x-sdw.c @@ -19,7 +19,7 @@ #include <sound/soc.h> #include "wcd937x.h" -static const struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { +static struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD937X_HPH_L, WCD937X_HPH_PORT, BIT(0)), WCD_SDW_CH(WCD937X_HPH_R, WCD937X_HPH_PORT, BIT(1)), WCD_SDW_CH(WCD937X_CLSH, WCD937X_CLSH_PORT, BIT(0)), @@ -30,7 +30,7 @@ static const struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = { WCD_SDW_CH(WCD937X_DSD_R, WCD937X_DSD_PORT, BIT(1)), }; -static const struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = { +static struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = { WCD_SDW_CH(WCD937X_ADC1, WCD937X_ADC_1_PORT, BIT(0)), WCD_SDW_CH(WCD937X_ADC2, WCD937X_ADC_2_3_PORT, BIT(0)), WCD_SDW_CH(WCD937X_ADC3, WCD937X_ADC_2_3_PORT, BIT(0)), @@ -1019,14 +1019,16 @@ static int wcd9370_probe(struct sdw_slave *pdev, { struct device *dev = &pdev->dev; struct wcd937x_sdw_priv *wcd; - int ret; + u8 master_ch_mask[WCD937X_MAX_SWR_CH_IDS]; + int master_ch_mask_size = 0; + int ret, i; wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); if (!wcd) return -ENOMEM; /* Port map index starts at 0, however the data port for this codec start at index 1 */ - if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) { + if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) { wcd->is_tx = true; ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping", &pdev->m_port_map[1], @@ -1048,10 +1050,36 @@ static int wcd9370_probe(struct sdw_slave *pdev, SDW_SCP_INT1_PARITY; pdev->prop.lane_control_support = true; pdev->prop.simple_clk_stop_capable = true; + + memset(master_ch_mask, 0, WCD937X_MAX_SWR_CH_IDS); + if (wcd->is_tx) { - pdev->prop.source_ports = GENMASK(WCD937X_MAX_TX_SWR_PORTS - 1, 0); + master_ch_mask_size = of_property_count_u8_elems(dev->of_node, + "qcom,tx-channel-mapping"); + + if (master_ch_mask_size) + ret = of_property_read_u8_array(dev->of_node, "qcom,tx-channel-mapping", + master_ch_mask, master_ch_mask_size); + } else { + master_ch_mask_size = of_property_count_u8_elems(dev->of_node, + "qcom,rx-channel-mapping"); + + if (master_ch_mask_size) + ret = of_property_read_u8_array(dev->of_node, "qcom,rx-channel-mapping", + master_ch_mask, master_ch_mask_size); + } + + if (ret < 0) + dev_info(dev, "Static channel mapping not specified using device channel maps\n"); + + if (wcd->is_tx) { + pdev->prop.source_ports = GENMASK(WCD937X_MAX_TX_SWR_PORTS, 0); pdev->prop.src_dpn_prop = wcd937x_dpn_prop; wcd->ch_info = &wcd937x_sdw_tx_ch_info[0]; + + for (i = 0; i < master_ch_mask_size; i++) + wcd->ch_info[i].master_ch_mask = WCD937X_SWRM_CH_MASK(master_ch_mask[i]); + pdev->prop.wake_capable = true; wcd->regmap = devm_regmap_init_sdw(pdev, &wcd937x_regmap_config); @@ -1065,6 +1093,9 @@ static int wcd9370_probe(struct sdw_slave *pdev, pdev->prop.sink_ports = GENMASK(WCD937X_MAX_SWR_PORTS - 1, 0); pdev->prop.sink_dpn_prop = wcd937x_dpn_prop; wcd->ch_info = &wcd937x_sdw_rx_ch_info[0]; + + for (i = 0; i < master_ch_mask_size; i++) + wcd->ch_info[i].master_ch_mask = WCD937X_SWRM_CH_MASK(master_ch_mask[i]); } @@ -1093,7 +1124,7 @@ static const struct sdw_device_id wcd9370_slave_id[] = { }; MODULE_DEVICE_TABLE(sdw, wcd9370_slave_id); -static int __maybe_unused wcd937x_sdw_runtime_suspend(struct device *dev) +static int wcd937x_sdw_runtime_suspend(struct device *dev) { struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev); @@ -1105,7 +1136,7 @@ static int __maybe_unused wcd937x_sdw_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused wcd937x_sdw_runtime_resume(struct device *dev) +static int wcd937x_sdw_runtime_resume(struct device *dev) { struct wcd937x_sdw_priv *wcd = dev_get_drvdata(dev); @@ -1118,7 +1149,7 @@ static int __maybe_unused wcd937x_sdw_runtime_resume(struct device *dev) } static const struct dev_pm_ops wcd937x_sdw_pm_ops = { - SET_RUNTIME_PM_OPS(wcd937x_sdw_runtime_suspend, wcd937x_sdw_runtime_resume, NULL) + RUNTIME_PM_OPS(wcd937x_sdw_runtime_suspend, wcd937x_sdw_runtime_resume, NULL) }; static struct sdw_driver wcd9370_codec_driver = { @@ -1128,7 +1159,7 @@ static struct sdw_driver wcd9370_codec_driver = { .id_table = wcd9370_slave_id, .driver = { .name = "wcd9370-codec", - .pm = &wcd937x_sdw_pm_ops, + .pm = pm_ptr(&wcd937x_sdw_pm_ops), } }; module_sdw_driver(wcd9370_codec_driver); diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index c9d5e67bf66e..dd2045a5d26d 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -1197,13 +1197,21 @@ static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch const struct wcd937x_sdw_ch_info *ch_info = &wcd->ch_info[ch_id]; u8 port_num = ch_info->port_num; u8 ch_mask = ch_info->ch_mask; + u8 mstr_port_num, mstr_ch_mask; + struct sdw_slave *sdev = wcd->sdev; port_config->num = port_num; - if (enable) + mstr_port_num = sdev->m_port_map[port_num]; + mstr_ch_mask = ch_info->master_ch_mask; + + if (enable) { port_config->ch_mask |= ch_mask; - else + wcd->master_channel_map[mstr_port_num] |= mstr_ch_mask; + } else { port_config->ch_mask &= ~ch_mask; + wcd->master_channel_map[mstr_port_num] &= ~mstr_ch_mask; + } return 0; } @@ -2472,7 +2480,7 @@ static const struct irq_domain_ops wcd_domain_ops = { static int wcd937x_irq_init(struct wcd937x_priv *wcd, struct device *dev) { - wcd->virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL); + wcd->virq = irq_domain_create_linear(NULL, 1, &wcd_domain_ops, NULL); if (!(wcd->virq)) { dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); return -EINVAL; @@ -2563,6 +2571,7 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) ARRAY_SIZE(wcd9375_dapm_widgets)); if (ret < 0) { dev_err(component->dev, "Failed to add snd_ctls\n"); + wcd_clsh_ctrl_free(wcd937x->clsh_info); return ret; } @@ -2570,6 +2579,7 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) ARRAY_SIZE(wcd9375_audio_map)); if (ret < 0) { dev_err(component->dev, "Failed to add routes\n"); + wcd_clsh_ctrl_free(wcd937x->clsh_info); return ret; } } @@ -2689,10 +2699,51 @@ static int wcd937x_codec_set_sdw_stream(struct snd_soc_dai *dai, return 0; } +static int wcd937x_get_channel_map(const struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev); + struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id]; + int i; + + switch (dai->id) { + case AIF1_PB: + if (!rx_slot || !rx_num) { + dev_err(dai->dev, "Invalid rx_slot %p or rx_num %p\n", + rx_slot, rx_num); + return -EINVAL; + } + + for (i = 0; i < SDW_MAX_PORTS; i++) + rx_slot[i] = wcd->master_channel_map[i]; + + *rx_num = i; + break; + case AIF1_CAP: + if (!tx_slot || !tx_num) { + dev_err(dai->dev, "Invalid tx_slot %p or tx_num %p\n", + tx_slot, tx_num); + return -EINVAL; + } + + for (i = 0; i < SDW_MAX_PORTS; i++) + tx_slot[i] = wcd->master_channel_map[i]; + + *tx_num = i; + break; + default: + break; + } + + return 0; +} + static const struct snd_soc_dai_ops wcd937x_sdw_dai_ops = { .hw_params = wcd937x_codec_hw_params, .hw_free = wcd937x_codec_free, .set_stream = wcd937x_codec_set_sdw_stream, + .get_channel_map = wcd937x_get_channel_map, }; static struct snd_soc_dai_driver wcd937x_dais[] = { diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h index 4afa48dcaf74..4ef57c496c37 100644 --- a/sound/soc/codecs/wcd937x.h +++ b/sound/soc/codecs/wcd937x.h @@ -489,6 +489,7 @@ #define WCD937X_MAX_MICBIAS 3 #define WCD937X_MAX_BULK_SUPPLY 4 #define WCD937X_MAX_SWR_CH_IDS 15 +#define WCD937X_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1) enum wcd937x_tx_sdw_ports { WCD937X_ADC_1_PORT = 1, @@ -510,12 +511,14 @@ enum wcd937x_rx_sdw_ports { struct wcd937x_sdw_ch_info { int port_num; unsigned int ch_mask; + unsigned int master_ch_mask; }; #define WCD_SDW_CH(id, pn, cmask) \ [id] = { \ .port_num = pn, \ .ch_mask = cmask, \ + .master_ch_mask = cmask, \ } struct wcd937x_priv; @@ -524,9 +527,11 @@ struct wcd937x_sdw_priv { struct sdw_stream_config sconfig; struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS]; - const struct wcd937x_sdw_ch_info *ch_info; + struct wcd937x_sdw_ch_info *ch_info; bool port_enable[WCD937X_MAX_SWR_CH_IDS]; + unsigned int master_channel_map[SDW_MAX_PORTS]; int active_ports; + int num_ports; bool is_tx; struct wcd937x_priv *wcd937x; struct irq_domain *slave_irq; diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 7da8a10bd0a9..cabddadc90ef 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1229,7 +1229,7 @@ static int wcd9380_probe(struct sdw_slave *pdev, * Port map index starts with 0, however the data port for this codec * are from index 1 */ - if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) { + if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) { wcd->is_tx = true; ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping", &pdev->m_port_map[1], @@ -1311,7 +1311,7 @@ static const struct sdw_device_id wcd9380_slave_id[] = { }; MODULE_DEVICE_TABLE(sdw, wcd9380_slave_id); -static int __maybe_unused wcd938x_sdw_runtime_suspend(struct device *dev) +static int wcd938x_sdw_runtime_suspend(struct device *dev) { struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev); @@ -1323,7 +1323,7 @@ static int __maybe_unused wcd938x_sdw_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused wcd938x_sdw_runtime_resume(struct device *dev) +static int wcd938x_sdw_runtime_resume(struct device *dev) { struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev); @@ -1338,7 +1338,7 @@ static int __maybe_unused wcd938x_sdw_runtime_resume(struct device *dev) } static const struct dev_pm_ops wcd938x_sdw_pm_ops = { - SET_RUNTIME_PM_OPS(wcd938x_sdw_runtime_suspend, wcd938x_sdw_runtime_resume, NULL) + RUNTIME_PM_OPS(wcd938x_sdw_runtime_suspend, wcd938x_sdw_runtime_resume, NULL) }; @@ -1349,7 +1349,7 @@ static struct sdw_driver wcd9380_codec_driver = { .id_table = wcd9380_slave_id, .driver = { .name = "wcd9380-codec", - .pm = &wcd938x_sdw_pm_ops, + .pm = pm_ptr(&wcd938x_sdw_pm_ops), } }; module_sdw_driver(wcd9380_codec_driver); diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index f2a4f3262bdb..1ae498c32391 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -70,13 +70,8 @@ #define WCD_MBHC_HS_V_MAX 1600 #define WCD938X_EAR_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ - SNDRV_CTL_ELEM_ACCESS_READWRITE,\ - .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ - .put = wcd938x_ear_pa_put_gain, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } + SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, snd_soc_get_volsw, \ + wcd938x_ear_pa_put_gain, tlv_array) enum { WCD9380 = 0, @@ -3030,7 +3025,7 @@ static const struct irq_domain_ops wcd_domain_ops = { static int wcd938x_irq_init(struct wcd938x_priv *wcd, struct device *dev) { - wcd->virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL); + wcd->virq = irq_domain_create_linear(NULL, 1, &wcd_domain_ops, NULL); if (!(wcd->virq)) { dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); return -EINVAL; diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c index fca95777a75a..f7a9323a9fea 100644 --- a/sound/soc/codecs/wcd939x-sdw.c +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -1429,7 +1429,7 @@ static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) * Port map index starts with 0, however the data port for this codec * are from index 1 */ - if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) { + if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) { wcd->is_tx = true; ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping", @@ -1507,7 +1507,7 @@ static const struct sdw_device_id wcd9390_slave_id[] = { }; MODULE_DEVICE_TABLE(sdw, wcd9390_slave_id); -static int __maybe_unused wcd939x_sdw_runtime_suspend(struct device *dev) +static int wcd939x_sdw_runtime_suspend(struct device *dev) { struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); @@ -1519,7 +1519,7 @@ static int __maybe_unused wcd939x_sdw_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused wcd939x_sdw_runtime_resume(struct device *dev) +static int wcd939x_sdw_runtime_resume(struct device *dev) { struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); @@ -1532,7 +1532,7 @@ static int __maybe_unused wcd939x_sdw_runtime_resume(struct device *dev) } static const struct dev_pm_ops wcd939x_sdw_pm_ops = { - SET_RUNTIME_PM_OPS(wcd939x_sdw_runtime_suspend, wcd939x_sdw_runtime_resume, NULL) + RUNTIME_PM_OPS(wcd939x_sdw_runtime_suspend, wcd939x_sdw_runtime_resume, NULL) }; static struct sdw_driver wcd9390_codec_driver = { @@ -1542,7 +1542,7 @@ static struct sdw_driver wcd9390_codec_driver = { .id_table = wcd9390_slave_id, .driver = { .name = "wcd9390-codec", - .pm = &wcd939x_sdw_pm_ops, + .pm = pm_ptr(&wcd939x_sdw_pm_ops), } }; module_sdw_driver(wcd9390_codec_driver); diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 4a417a92514d..0a87a79772da 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -2975,7 +2975,7 @@ static const struct irq_domain_ops wcd_domain_ops = { static int wcd939x_irq_init(struct wcd939x_priv *wcd, struct device *dev) { - wcd->virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL); + wcd->virq = irq_domain_create_linear(NULL, 1, &wcd_domain_ops, NULL); if (!(wcd->virq)) { dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); return -EINVAL; diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index edd2cb185c42..9e67fbfc2cca 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -920,7 +920,7 @@ static int wm0010_spi_probe(struct spi_device *spi) if (ret) { dev_err(wm0010->dev, "Failed to set IRQ %d as wake source: %d\n", irq, ret); - return ret; + goto free_irq; } if (spi->max_speed_hz) @@ -932,9 +932,18 @@ static int wm0010_spi_probe(struct spi_device *spi) &soc_component_dev_wm0010, wm0010_dai, ARRAY_SIZE(wm0010_dai)); if (ret < 0) - return ret; + goto disable_irq_wake; return 0; + +disable_irq_wake: + irq_set_irq_wake(wm0010->irq, 0); + +free_irq: + if (wm0010->irq) + free_irq(wm0010->irq, wm0010); + + return ret; } static void wm0010_spi_remove(struct spi_device *spi) diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 841247173d98..87418c838ca0 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1576,15 +1576,15 @@ static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: lrclk |= WM2200_AIF1TX_LRCLK_MSTR; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: bclk |= WM2200_AIF1_BCLK_MSTR; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: lrclk |= WM2200_AIF1TX_LRCLK_MSTR; bclk |= WM2200_AIF1_BCLK_MSTR; break; @@ -2429,7 +2429,6 @@ static void wm2200_i2c_remove(struct i2c_client *i2c) wm2200->core_supplies); } -#ifdef CONFIG_PM static int wm2200_runtime_suspend(struct device *dev) { struct wm2200_priv *wm2200 = dev_get_drvdata(dev); @@ -2466,11 +2465,9 @@ static int wm2200_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops wm2200_pm = { - SET_RUNTIME_PM_OPS(wm2200_runtime_suspend, wm2200_runtime_resume, - NULL) + RUNTIME_PM_OPS(wm2200_runtime_suspend, wm2200_runtime_resume, NULL) }; static const struct i2c_device_id wm2200_i2c_id[] = { @@ -2482,7 +2479,7 @@ MODULE_DEVICE_TABLE(i2c, wm2200_i2c_id); static struct i2c_driver wm2200_i2c_driver = { .driver = { .name = "wm2200", - .pm = &wm2200_pm, + .pm = pm_ptr(&wm2200_pm), }, .probe = wm2200_i2c_probe, .remove = wm2200_i2c_remove, diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 11bbc94a282c..d9e5762324df 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1303,15 +1303,15 @@ static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: lrclk |= WM5100_AIF1TX_LRCLK_MSTR; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: bclk |= WM5100_AIF1_BCLK_MSTR; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: lrclk |= WM5100_AIF1TX_LRCLK_MSTR; bclk |= WM5100_AIF1_BCLK_MSTR; break; @@ -2625,7 +2625,6 @@ static void wm5100_i2c_remove(struct i2c_client *i2c) gpiod_set_value_cansleep(wm5100->ldo_ena, 0); } -#ifdef CONFIG_PM static int wm5100_runtime_suspend(struct device *dev) { struct wm5100_priv *wm5100 = dev_get_drvdata(dev); @@ -2662,11 +2661,9 @@ static int wm5100_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops wm5100_pm = { - SET_RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume, - NULL) + RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume, NULL) }; static const struct i2c_device_id wm5100_i2c_id[] = { @@ -2678,7 +2675,7 @@ MODULE_DEVICE_TABLE(i2c, wm5100_i2c_id); static struct i2c_driver wm5100_i2c_driver = { .driver = { .name = "wm5100", - .pm = &wm5100_pm, + .pm = pm_ptr(&wm5100_pm), }, .probe = wm5100_i2c_probe, .remove = wm5100_i2c_remove, diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 502196253d42..212eca675f27 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -302,7 +302,7 @@ static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w) } else { wseq = wm5110_no_dre_left_enable; nregs = ARRAY_SIZE(wm5110_no_dre_left_enable); - priv->out_up_delay += 10; + priv->out_up_delay += 10000; } break; case ARIZONA_OUT1R_ENA_SHIFT: @@ -312,7 +312,7 @@ static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w) } else { wseq = wm5110_no_dre_right_enable; nregs = ARRAY_SIZE(wm5110_no_dre_right_enable); - priv->out_up_delay += 10; + priv->out_up_delay += 10000; } break; default: @@ -338,7 +338,7 @@ static int wm5110_hp_pre_disable(struct snd_soc_dapm_widget *w) snd_soc_component_update_bits(component, ARIZONA_SPARE_TRIGGERS, ARIZONA_WS_TRG1, 0); - priv->out_down_delay += 27; + priv->out_down_delay += 27000; } break; case ARIZONA_OUT1R_ENA_SHIFT: @@ -350,7 +350,7 @@ static int wm5110_hp_pre_disable(struct snd_soc_dapm_widget *w) snd_soc_component_update_bits(component, ARIZONA_SPARE_TRIGGERS, ARIZONA_WS_TRG2, 0); - priv->out_down_delay += 27; + priv->out_down_delay += 27000; } break; default: @@ -477,7 +477,7 @@ static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol, */ snd_soc_dapm_mutex_lock(dapm); - ret = snd_soc_get_volsw_range(kcontrol, ucontrol); + ret = snd_soc_get_volsw(kcontrol, ucontrol); snd_soc_dapm_mutex_unlock(dapm); @@ -497,7 +497,7 @@ static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol, */ snd_soc_dapm_mutex_lock(dapm); - ret = snd_soc_put_volsw_range(kcontrol, ucontrol); + ret = snd_soc_put_volsw(kcontrol, ucontrol); snd_soc_dapm_mutex_unlock(dapm); diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 66bd281095e1..b1fe6f4e0c10 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -846,12 +846,12 @@ static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: master |= WM8350_BCLK_MSTR; dac_lrc |= WM8350_DACLRC_ENA; adc_lrc |= WM8350_ADCLRC_ENA; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 19ce839f6ef7..5ad6d5b63ffc 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -986,10 +986,10 @@ static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: audio3 &= ~WM8400_AIF_MSTR1; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: audio3 |= WM8400_AIF_MSTR1; break; default: diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 4a31d6f89502..79adbcc90d4a 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -389,10 +389,10 @@ static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: clk |= 0x0001; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 138eba7e577a..6671e13c320c 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -264,10 +264,10 @@ static int wm8523_set_dai_fmt(struct snd_soc_dai *codec_dai, WM8523_FMT_MASK | WM8523_AIF_MSTR_MASK); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aifctrl1 |= WM8523_AIF_MSTR; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8524.c b/sound/soc/codecs/wm8524.c index fa9942a08927..403e513f3fa8 100644 --- a/sound/soc/codecs/wm8524.c +++ b/sound/soc/codecs/wm8524.c @@ -139,7 +139,7 @@ static int wm8524_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) SND_SOC_DAIFMT_MASTER_MASK); if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS)) { + SND_SOC_DAIFMT_CBC_CFC)) { dev_err(codec_dai->dev, "Invalid DAI format\n"); return -EINVAL; } diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 73a8edc797fb..ba4a08456e78 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -614,10 +614,10 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: aifa &= ~WM8580_AIF_MS; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aifa |= WM8580_AIF_MS; break; default: diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index a1c99bbf5aa1..481088987742 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -243,10 +243,10 @@ static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: iface |= 0x0040; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 2cbd6b189416..ea0a588da40f 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -128,7 +128,7 @@ static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai, /* The hardware only support full slave mode */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index efdc242c2ede..f7e48f27649d 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -404,10 +404,10 @@ static int wm8737_set_dai_fmt(struct snd_soc_dai *codec_dai, u16 af = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: af |= WM8737_MS; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 4863d6ac461b..4dfbb33edb09 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -308,7 +308,7 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, /* check master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index cae97fa3bcb0..312be0721b5d 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -522,10 +522,10 @@ static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: iface = 0x0040; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 38b76b7275e5..43cc368cf3f3 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -963,12 +963,12 @@ static int wm8753_pcm_set_dai_fmt(struct snd_soc_component *component, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ioctl |= 0x2; fallthrough; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: voice |= 0x0040; break; default: @@ -1089,12 +1089,12 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_component *component, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ioctl |= 0x1; fallthrough; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: hifi |= 0x0040; break; default: diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c index 38376b605201..5685c3bb5555 100644 --- a/sound/soc/codecs/wm8770.c +++ b/sound/soc/codecs/wm8770.c @@ -349,10 +349,10 @@ static int wm8770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) component = dai->component; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: master = 0x100; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: master = 0; break; default: diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 166e00fcd11d..7b73c825aed4 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -182,9 +182,9 @@ static int wm8776_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) iface = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: master = 0; break; default: diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c index e80dad87219b..3380d7301b17 100644 --- a/sound/soc/codecs/wm8804-i2c.c +++ b/sound/soc/codecs/wm8804-i2c.c @@ -56,7 +56,7 @@ MODULE_DEVICE_TABLE(acpi, wm8804_acpi_match); static struct i2c_driver wm8804_i2c_driver = { .driver = { .name = "wm8804", - .pm = &wm8804_pm, + .pm = pm_ptr(&wm8804_pm), .of_match_table = of_match_ptr(wm8804_of_match), .acpi_match_table = ACPI_PTR(wm8804_acpi_match), }, diff --git a/sound/soc/codecs/wm8804-spi.c b/sound/soc/codecs/wm8804-spi.c index 628568724c20..cf74abfb1a2c 100644 --- a/sound/soc/codecs/wm8804-spi.c +++ b/sound/soc/codecs/wm8804-spi.c @@ -38,7 +38,7 @@ MODULE_DEVICE_TABLE(of, wm8804_of_match); static struct spi_driver wm8804_spi_driver = { .driver = { .name = "wm8804", - .pm = &wm8804_pm, + .pm = pm_ptr(&wm8804_pm), .of_match_table = wm8804_of_match, }, .probe = wm8804_spi_probe, diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index cfa78e4d8b73..48700cc25cb0 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -243,10 +243,10 @@ static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) snd_soc_component_update_bits(component, WM8804_AIFRX, 0x3, format); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: master = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: master = 0; break; default: @@ -680,7 +680,6 @@ void wm8804_remove(struct device *dev) } EXPORT_SYMBOL_GPL(wm8804_remove); -#if IS_ENABLED(CONFIG_PM) static int wm8804_runtime_resume(struct device *dev) { struct wm8804_priv *wm8804 = dev_get_drvdata(dev); @@ -713,12 +712,10 @@ static int wm8804_runtime_suspend(struct device *dev) return 0; } -#endif -const struct dev_pm_ops wm8804_pm = { - SET_RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL) +EXPORT_GPL_DEV_PM_OPS(wm8804_pm) = { + RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL) }; -EXPORT_SYMBOL_GPL(wm8804_pm); MODULE_DESCRIPTION("ASoC WM8804 driver"); MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index e44fdf97796f..6fb25588ca81 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -867,22 +867,22 @@ static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR; aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR; aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR; break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR; aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR; aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR; aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR; aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR; aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR; aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index c643b5377d3a..03902909f27e 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1229,15 +1229,15 @@ static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai, WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: aif1 |= WM8903_LRCLK_DIR; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aif1 |= WM8903_LRCLK_DIR | WM8903_BCLK_DIR; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: aif1 |= WM8903_BCLK_DIR; break; default: diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 829bf055622a..1de09ea646cf 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -844,6 +844,26 @@ static int out_pga_event(struct snd_soc_dapm_widget *w, return 0; } +static const char * const dmic_text[] = { + "DMIC1", "DMIC2" +}; + +static SOC_ENUM_SINGLE_DECL(dmic_enum, WM8904_DIGITAL_MICROPHONE_0, + WM8904_DMIC_SRC_SHIFT, dmic_text); + +static const struct snd_kcontrol_new dmic_mux = + SOC_DAPM_ENUM("DMIC Mux", dmic_enum); + +static const char * const cin_text[] = { + "ADC", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL(cin_enum, WM8904_DIGITAL_MICROPHONE_0, + WM8904_DMIC_ENA_SHIFT, cin_text); + +static const struct snd_kcontrol_new cin_mux = + SOC_DAPM_ENUM("Capture Input", cin_enum); + static const char *input_mode_text[] = { "Single-Ended", "Differential Line", "Differential Mic" }; @@ -963,6 +983,15 @@ SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0), }; +static const struct snd_soc_dapm_widget wm8904_dmic_dapm_widgets[] = { +SND_SOC_DAPM_MUX("DMIC Mux", SND_SOC_NOPM, 0, 0, &dmic_mux), +}; + +static const struct snd_soc_dapm_widget wm8904_cin_dapm_widgets[] = { +SND_SOC_DAPM_MUX("Left Capture Input", SND_SOC_NOPM, 0, 0, &cin_mux), +SND_SOC_DAPM_MUX("Right Capture Input", SND_SOC_NOPM, 0, 0, &cin_mux), +}; + static const struct snd_soc_dapm_widget wm8904_dac_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0), @@ -1101,12 +1130,45 @@ static const struct snd_soc_dapm_route adc_intercon[] = { { "AIFOUTR", NULL, "AIFOUTR Mux" }, { "ADCL", NULL, "CLK_DSP" }, - { "ADCL", NULL, "Left Capture PGA" }, - { "ADCR", NULL, "CLK_DSP" }, +}; + +/* No DMICs, always connect PGAs */ +static const struct snd_soc_dapm_route cin_nodmic_con[] = { + { "ADCL", NULL, "Left Capture PGA" }, { "ADCR", NULL, "Right Capture PGA" }, }; +/* DMIC system in use: mux between ADC and DMICDAT1, 2 or both */ +static const struct snd_soc_dapm_route cin_adc_dmic_con[] = { + { "Left Capture Input", "ADC", "Left Capture PGA" }, + { "Right Capture Input", "ADC", "Right Capture PGA" }, + + { "ADCL", NULL, "Left Capture Input" }, + { "ADCR", NULL, "Right Capture Input" }, +}; + +/* IN1L as DMICDAT1 */ +static const struct snd_soc_dapm_route cin_dmic1_con[] = { + { "Left Capture Input", "DMIC", "IN1L" }, + { "Right Capture Input", "DMIC", "IN1L" }, +}; + +/* IN1R as DMICDAT2 */ +static const struct snd_soc_dapm_route cin_dmic2_con[] = { + { "Left Capture Input", "DMIC", "IN1R" }, + { "Right Capture Input", "DMIC", "IN1R" }, +}; + +/* DMICDAT1 and DMICDAT2: mux between them, ADC still used for IN2 and IN3 */ +static const struct snd_soc_dapm_route cin_2dmics_con[] = { + { "DMIC Mux", "DMIC1", "IN1L" }, + { "DMIC Mux", "DMIC2", "IN1R" }, + + { "Left Capture Input", "DMIC", "DMIC Mux" }, + { "Right Capture Input", "DMIC", "DMIC Mux" }, +}; + static const struct snd_soc_dapm_route dac_intercon[] = { { "DACL Mux", "Left", "AIFINL" }, { "DACL Mux", "Right", "AIFINR" }, @@ -1424,15 +1486,15 @@ static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int aif3 = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: aif3 |= WM8904_LRCLK_DIR; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: aif1 |= WM8904_BCLK_DIR; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aif1 |= WM8904_BCLK_DIR; aif3 |= WM8904_LRCLK_DIR; break; @@ -2050,18 +2112,70 @@ static void wm8904_handle_retune_mobile_pdata(struct snd_soc_component *componen "Failed to add ReTune Mobile control: %d\n", ret); } +static void wm8904_handle_dmic_pdata(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + struct wm8904_pdata *pdata = wm8904->pdata; + unsigned int dmic_src; + + if (!pdata->in1l_as_dmicdat1 && !pdata->in1r_as_dmicdat2) { + snd_soc_dapm_add_routes(dapm, cin_nodmic_con, + ARRAY_SIZE(cin_nodmic_con)); + snd_soc_component_update_bits(component, WM8904_DIGITAL_MICROPHONE_0, + WM8904_DMIC_ENA_MASK, 0); + return; + } + + /* Need a control and routing to switch between DMIC and ADC */ + snd_soc_dapm_new_controls(dapm, wm8904_cin_dapm_widgets, + ARRAY_SIZE(wm8904_cin_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, cin_adc_dmic_con, + ARRAY_SIZE(cin_adc_dmic_con)); + + if (pdata->in1l_as_dmicdat1 && pdata->in1r_as_dmicdat2) { + /* Need a control and routing to mux between DMICDAT1 and 2 */ + dev_dbg(component->dev, "DMICDAT1 and DMICDAT2 in use\n"); + snd_soc_dapm_new_controls(dapm, wm8904_dmic_dapm_widgets, + ARRAY_SIZE(wm8904_dmic_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, cin_2dmics_con, + ARRAY_SIZE(cin_2dmics_con)); + return; + } + + /* Either DMICDAT1 or DMICDAT2 is in use, not both */ + if (pdata->in1l_as_dmicdat1) { + dmic_src = 0; + snd_soc_dapm_add_routes(dapm, cin_dmic1_con, + ARRAY_SIZE(cin_dmic1_con)); + } else { + dmic_src = 1; + snd_soc_dapm_add_routes(dapm, cin_dmic2_con, + ARRAY_SIZE(cin_dmic2_con)); + } + dev_dbg(component->dev, "DMIC_SRC (0 or 1): %d\n", dmic_src); + snd_soc_component_update_bits(component, WM8904_DIGITAL_MICROPHONE_0, + WM8904_DMIC_SRC_MASK, + dmic_src << WM8904_DMIC_SRC_SHIFT); +} + static void wm8904_handle_pdata(struct snd_soc_component *component) { + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); struct wm8904_pdata *pdata = wm8904->pdata; int ret, i; if (!pdata) { + snd_soc_dapm_add_routes(dapm, cin_nodmic_con, + ARRAY_SIZE(cin_nodmic_con)); snd_soc_add_component_controls(component, wm8904_eq_controls, - ARRAY_SIZE(wm8904_eq_controls)); + ARRAY_SIZE(wm8904_eq_controls)); return; } + wm8904_handle_dmic_pdata(component); + dev_dbg(component->dev, "%d DRC configurations\n", pdata->num_drc_cfgs); if (pdata->num_drc_cfgs) { @@ -2117,10 +2231,11 @@ static int wm8904_probe(struct snd_soc_component *component) return -EINVAL; } - wm8904_handle_pdata(component); - wm8904_add_widgets(component); + /* This can add dependent widgets, so it is done after add_widgets */ + wm8904_handle_pdata(component); + return 0; } @@ -2168,6 +2283,184 @@ static const struct of_device_id wm8904_of_match[] = { MODULE_DEVICE_TABLE(of, wm8904_of_match); #endif +/** + * wm8904_read_cfg_reg_arr() - Reads a subarray from a DT u16 array + * + * @np: pointer to the device_node struct + * @regs_property: DT property of interest + * @size: size of subarrays within the array + * @idx: index of the subarray of interest + * @out: output + * + * Helper to read a subarray from a DT uint16-array, + * divided into equally sized arrays of size `size` + * + * Subset starts at `idx * size` and is of size `size` + * + * Return: 0 on success, negative error code otherwise + */ +static int wm8904_read_cfg_reg_arr(const struct device_node *np, + const char * const regs_property, + int size, int idx, + u16 * const out) +{ + int i, offset, ret; + + offset = idx * size; + + for (i = 0; i < size; i++) { + ret = of_property_read_u16_index(np, regs_property, i + offset, &out[i]); + if (ret) + return ret; + } + return 0; +} + +static int wm8904_parse_retune_cfg_regs(const struct device_node *np, + struct wm8904_pdata *pdata, int cfg_idx) +{ + return wm8904_read_cfg_reg_arr(np, "wlf,retune-mobile-cfg-regs", + WM8904_EQ_REGS, cfg_idx, + &pdata->retune_mobile_cfgs[cfg_idx].regs[0]); +} + +static int wm8904_parse_drc_cfg_regs(const struct device_node *np, + struct wm8904_pdata *pdata, int cfg_idx) +{ + return wm8904_read_cfg_reg_arr(np, "wlf,drc-cfg-regs", + WM8904_DRC_REGS, cfg_idx, + &pdata->drc_cfgs[cfg_idx].regs[0]); +} + +static int wm8904_parse_drc_cfg_from_of(struct i2c_client *i2c, + struct wm8904_pdata *pdata) +{ + const struct device_node *np = i2c->dev.of_node; + int i, n_cfgs; + + n_cfgs = of_property_count_strings(np, "wlf,drc-cfg-names"); + if (n_cfgs == -EINVAL) + return 0; + + if (n_cfgs <= 0) { + dev_err(&i2c->dev, "Could not get wlf,drc-cfg-names length: %d", + n_cfgs); + return n_cfgs; + } + + pdata->drc_cfgs = devm_kzalloc(&i2c->dev, + n_cfgs * sizeof(struct wm8904_drc_cfg), + GFP_KERNEL); + if (!pdata->drc_cfgs) + return -ENOMEM; + + for (i = 0; i < n_cfgs; i++) { + if (wm8904_parse_drc_cfg_regs(np, pdata, i)) { + dev_err(&i2c->dev, + "Invalid 'wlf,drc-cfg-regs[%i,:]'\n", i); + return -EINVAL; + } + + if (of_property_read_string_index(np, "wlf,drc-cfg-names", i, + &pdata->drc_cfgs[i].name)) { + dev_err(&i2c->dev, + "Invalid 'wlf,drc-cfg-names[%i]'\n", i); + return -EINVAL; + } + } + + pdata->num_drc_cfgs = n_cfgs; + return 0; +} + +static int wm8904_parse_retune_cfg_from_of(struct i2c_client *i2c, + struct wm8904_pdata *pdata) +{ + const struct device_node *np = i2c->dev.of_node; + int i, n_cfgs; + + n_cfgs = of_property_count_strings(np, "wlf,retune-mobile-cfg-names"); + if (n_cfgs == -EINVAL) + return 0; + + if (n_cfgs <= 0) { + dev_err(&i2c->dev, + "Could not get wlf,retune-mobile-cfg-names length: %d", + n_cfgs); + return n_cfgs; + } + + pdata->retune_mobile_cfgs = devm_kzalloc(&i2c->dev, + n_cfgs * sizeof(struct wm8904_retune_mobile_cfg), + GFP_KERNEL); + if (!pdata->retune_mobile_cfgs) + return -ENOMEM; + + for (i = 0; i < n_cfgs; i++) { + if (wm8904_parse_retune_cfg_regs(np, pdata, i)) { + dev_err(&i2c->dev, + "Invalid 'wlf,retune-mobile-cfg-regs[%i,:]'\n", i); + return -EINVAL; + } + + if (of_property_read_u32_index(np, "wlf,retune-mobile-cfg-hz", i, + &pdata->retune_mobile_cfgs[i].rate)) { + dev_err(&i2c->dev, + "Invalid 'wlf,retune-mobile-cfg-hz[%i]'\n", i); + return -EINVAL; + } + + if (of_property_read_string_index(np, "wlf,retune-mobile-cfg-names", i, + &pdata->retune_mobile_cfgs[i].name)) { + dev_err(&i2c->dev, + "Invalid 'wlf,retune-mobile-cfg-names[%i]'\n", i); + return -EINVAL; + } + } + + pdata->num_retune_mobile_cfgs = n_cfgs; + return 0; +} + +static int wm8904_set_pdata_from_of(struct i2c_client *i2c, + struct wm8904_priv *wm8904) +{ + const struct device_node *np = i2c->dev.of_node; + struct wm8904_pdata *pdata; + int ret, i; + + pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->in1l_as_dmicdat1 = + of_property_read_bool(np, "wlf,in1l-as-dmicdat1"); + + pdata->in1r_as_dmicdat2 = + of_property_read_bool(np, "wlf,in1r-as-dmicdat2"); + + /* If absent, default to 0xFFFF for GPIO config (i.e.: don't set) */ + for (i = 0; i < WM8904_GPIO_REGS; i++) + pdata->gpio_cfg[i] = 0xFFFF; + + of_property_read_u32_array(np, "wlf,gpio-cfg", pdata->gpio_cfg, + ARRAY_SIZE(pdata->gpio_cfg)); + + of_property_read_u32_array(np, "wlf,micbias-cfg", pdata->mic_cfg, + ARRAY_SIZE(pdata->mic_cfg)); + + ret = wm8904_parse_drc_cfg_from_of(i2c, pdata); + if (ret) + return ret; + + ret = wm8904_parse_retune_cfg_from_of(i2c, pdata); + if (ret) + return ret; + + wm8904->pdata = pdata; + return 0; +} + static const struct i2c_device_id wm8904_i2c_id[]; static int wm8904_i2c_probe(struct i2c_client *i2c) @@ -2196,22 +2489,20 @@ static int wm8904_i2c_probe(struct i2c_client *i2c) return ret; } - if (i2c->dev.of_node) { - const struct of_device_id *match; + wm8904->devtype = (uintptr_t)i2c_get_match_data(i2c); - match = of_match_node(wm8904_of_match, i2c->dev.of_node); - if (match == NULL) - return -EINVAL; - wm8904->devtype = (uintptr_t)match->data; + i2c_set_clientdata(i2c, wm8904); + + if (i2c->dev.of_node) { + ret = wm8904_set_pdata_from_of(i2c, wm8904); + if (ret) { + dev_err(&i2c->dev, "Failed to set platform data from of: %d\n", ret); + return ret; + } } else { - const struct i2c_device_id *id = - i2c_match_id(wm8904_i2c_id, i2c); - wm8904->devtype = id->driver_data; + wm8904->pdata = i2c->dev.platform_data; } - i2c_set_clientdata(i2c, wm8904); - wm8904->pdata = i2c->dev.platform_data; - for (i = 0; i < ARRAY_SIZE(wm8904->supplies); i++) wm8904->supplies[i].supply = wm8904_supply_names[i]; @@ -2281,7 +2572,8 @@ static int wm8904_i2c_probe(struct i2c_client *i2c) /* Apply configuration from the platform data. */ if (wm8904->pdata) { for (i = 0; i < WM8904_GPIO_REGS; i++) { - if (!wm8904->pdata->gpio_cfg[i]) + /* 0xFFFF in this config means "don't touch" */ + if (wm8904->pdata->gpio_cfg[i] == 0xffff) continue; regmap_update_bits(wm8904->regmap, diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 8a532f7d750c..401ee20897b1 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -343,10 +343,10 @@ static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, u16 clk = snd_soc_component_read(component, WM8940_CLOCK) & 0x1fe; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: clk |= 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index bae52a8a2e11..bca83410b432 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -671,9 +671,9 @@ static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) u16 aif = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aif |= WM8955_MS; break; default: diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 00858b9c9568..e6525b4cedfb 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -540,10 +540,10 @@ static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: iface |= 0x0040; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index d1c731e25777..1ec7c5e8fd69 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -627,10 +627,10 @@ static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) WM8961_MS | WM8961_FORMAT_MASK); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aif |= WM8961_MS; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 08d164ce3e49..68f746626c33 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2762,10 +2762,10 @@ static int wm8962_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aif0 |= WM8962_MSTR; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; @@ -3850,7 +3850,6 @@ static void wm8962_i2c_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); } -#ifdef CONFIG_PM static int wm8962_runtime_resume(struct device *dev) { struct wm8962_priv *wm8962 = dev_get_drvdata(dev); @@ -3930,11 +3929,10 @@ static int wm8962_runtime_suspend(struct device *dev) return 0; } -#endif static const struct dev_pm_ops wm8962_pm = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(wm8962_runtime_suspend, wm8962_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(wm8962_runtime_suspend, wm8962_runtime_resume, NULL) }; static const struct i2c_device_id wm8962_i2c_id[] = { @@ -3953,7 +3951,7 @@ static struct i2c_driver wm8962_i2c_driver = { .driver = { .name = "wm8962", .of_match_table = wm8962_of_match, - .pm = &wm8962_pm, + .pm = pm_ptr(&wm8962_pm), }, .probe = wm8962_i2c_probe, .remove = wm8962_i2c_remove, diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index b97c7d5bd4e7..62dcddeb78b3 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -452,10 +452,10 @@ static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: iface = 0x0040; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 0ee3655cad01..bdf437a5403f 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -454,10 +454,10 @@ static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: clk |= 0x0001; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index 40d22b36b7a9..8c45ba6fc4c3 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -660,10 +660,10 @@ static int wm8978_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: clk |= 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: clk &= ~1; break; default: diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 252b4a6cac04..6a83afe6400b 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -594,10 +594,10 @@ static int wm8983_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) WM8983_FMT_MASK, format << WM8983_FMT_SHIFT); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: master = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: master = 0; break; default: diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 8606e0752a60..2e2d07193c41 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -688,10 +688,10 @@ static int wm8985_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) WM8985_FMT_MASK, format << WM8985_FMT_SHIFT); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: master = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: master = 0; break; default: @@ -1166,12 +1166,10 @@ static struct spi_driver wm8985_spi_driver = { #endif #if IS_ENABLED(CONFIG_I2C) -static const struct i2c_device_id wm8985_i2c_id[]; static int wm8985_i2c_probe(struct i2c_client *i2c) { struct wm8985_priv *wm8985; - const struct i2c_device_id *id = i2c_match_id(wm8985_i2c_id, i2c); int ret; wm8985 = devm_kzalloc(&i2c->dev, sizeof *wm8985, GFP_KERNEL); @@ -1180,7 +1178,7 @@ static int wm8985_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, wm8985); - wm8985->dev_type = id->driver_data; + wm8985->dev_type = (uintptr_t)i2c_get_match_data(i2c); wm8985->regmap = devm_regmap_init_i2c(i2c, &wm8985_regmap); if (IS_ERR(wm8985->regmap)) { diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index f0e9d6e38dc0..cf6d642b7bfe 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -589,10 +589,10 @@ static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: iface = 0x0040; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 573bd3d487ba..89df406bf552 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -897,10 +897,10 @@ static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: audio3 &= ~WM8990_AIF_MSTR1; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: audio3 |= WM8990_AIF_MSTR1; break; default: diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 3bd9b362051b..c3dd44c1dd0c 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -958,10 +958,10 @@ static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai, /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: audio3 &= ~WM8991_AIF_MSTR1; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: audio3 |= WM8991_AIF_MSTR1; break; default: diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index f257980f9b56..9be4f6cadba3 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1098,18 +1098,18 @@ static int wm8993_set_dai_fmt(struct snd_soc_dai *dai, aif4 &= ~WM8993_LRCLK_DIR; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: wm8993->master = 0; break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: aif4 |= WM8993_LRCLK_DIR; wm8993->master = 1; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: aif1 |= WM8993_BCLK_DIR; wm8993->master = 1; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aif1 |= WM8993_BCLK_DIR; aif4 |= WM8993_LRCLK_DIR; wm8993->master = 1; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index a4abe6e53bfc..240ec1bed234 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2783,9 +2783,9 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ms = WM8994_AIF1_MSTR; break; default: @@ -4662,7 +4662,6 @@ static void wm8994_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static int wm8994_suspend(struct device *dev) { struct wm8994_priv *wm8994 = dev_get_drvdata(dev); @@ -4687,16 +4686,15 @@ static int wm8994_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops wm8994_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(wm8994_suspend, wm8994_resume) + SYSTEM_SLEEP_PM_OPS(wm8994_suspend, wm8994_resume) }; static struct platform_driver wm8994_codec_driver = { .driver = { .name = "wm8994-codec", - .pm = &wm8994_pm_ops, + .pm = pm_ptr(&wm8994_pm_ops), }, .probe = wm8994_probe, .remove = wm8994_remove, diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 1f9a9b636935..a88170a3ed91 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -1448,9 +1448,9 @@ static int wm8995_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) master = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: master = WM8995_AIF1_MSTR; break; default: diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 5c06cea09bd1..c2af8d7ecdd8 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -1672,16 +1672,16 @@ static int wm8996_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR; lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: bclk |= WM8996_AIF1_BCLK_MSTR; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: bclk |= WM8996_AIF1_BCLK_MSTR; lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR; lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR; diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index cb9d040b34d6..94d8571360c4 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -907,18 +907,18 @@ static int wm9081_set_dai_fmt(struct snd_soc_dai *dai, WM9081_BCLK_DIR | WM9081_LRCLK_DIR | WM9081_AIF_FMT_MASK); switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: wm9081->master = 0; break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: aif2 |= WM9081_LRCLK_DIR; wm9081->master = 1; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: aif2 |= WM9081_BCLK_DIR; wm9081->master = 1; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: aif2 |= WM9081_LRCLK_DIR | WM9081_BCLK_DIR; wm9081->master = 1; break; diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index e63921de0c37..8ff4b5f13b3a 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -275,13 +275,9 @@ static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol, return 0; } -#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_volsw, \ - .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \ - .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \ - (xmixer << 8) | xshift, 1, 0, 0) \ -} +#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) \ + SOC_SINGLE_EXT(xname, SND_SOC_NOPM, ((xmixer) << 8) | (xshift), \ + 1, 0, wm9712_hp_mixer_get, wm9712_hp_mixer_put) /* Left Headphone Mixers */ static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = { diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 64b69316e4c7..5f1b0f5c1a58 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -284,13 +284,9 @@ static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol, return 0; } -#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_volsw, \ - .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \ - .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \ - xshift, xmixer, 1, 0, 0) \ -} +#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) \ + SOC_DOUBLE_EXT(xname, SND_SOC_NOPM, xshift, xmixer, 1, 0, \ + wm9713_hp_mixer_get, wm9713_hp_mixer_put) /* Left Headphone Mixers */ static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = { @@ -944,19 +940,19 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai, /* clock masters */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: reg |= 0x4000; gpio |= 0x0010; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: reg |= 0x6000; gpio |= 0x0018; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: reg |= 0x2000; gpio |= 0x001a; break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: gpio |= 0x0012; break; } diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 0c881846f485..196ddb224e6d 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -14,6 +14,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/mfd/wm8994/registers.h> +#include <linux/string_choices.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -674,7 +675,7 @@ void wm_hubs_update_class_w(struct snd_soc_component *component) if (hubs->check_class_w_digital && !hubs->check_class_w_digital(component)) enable = false; - dev_vdbg(component->dev, "Class W %s\n", enable ? "enabled" : "disabled"); + dev_vdbg(component->dev, "Class W %s\n", str_enabled_disabled(enable)); snd_soc_component_update_bits(component, WM8993_CLASS_W_0, WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable); diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index dd2d6661adc7..6627d2da3722 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -199,13 +199,8 @@ #define WSA881X_PROBE_TIMEOUT 1000 #define WSA881X_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ - SNDRV_CTL_ELEM_ACCESS_READWRITE,\ - .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ - .put = wsa881x_put_pa_gain, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } + SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \ + snd_soc_get_volsw, wsa881x_put_pa_gain, tlv_array) static struct reg_default wsa881x_defaults[] = { { WSA881X_CHIP_ID0, 0x00 }, @@ -1174,7 +1169,7 @@ static int wsa881x_probe(struct sdw_slave *pdev, ARRAY_SIZE(wsa881x_dais)); } -static int __maybe_unused wsa881x_runtime_suspend(struct device *dev) +static int wsa881x_runtime_suspend(struct device *dev) { struct regmap *regmap = dev_get_regmap(dev, NULL); struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); @@ -1187,7 +1182,7 @@ static int __maybe_unused wsa881x_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused wsa881x_runtime_resume(struct device *dev) +static int wsa881x_runtime_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct regmap *regmap = dev_get_regmap(dev, NULL); @@ -1211,7 +1206,7 @@ static int __maybe_unused wsa881x_runtime_resume(struct device *dev) } static const struct dev_pm_ops wsa881x_pm_ops = { - SET_RUNTIME_PM_OPS(wsa881x_runtime_suspend, wsa881x_runtime_resume, NULL) + RUNTIME_PM_OPS(wsa881x_runtime_suspend, wsa881x_runtime_resume, NULL) }; static const struct sdw_device_id wsa881x_slave_id[] = { @@ -1227,7 +1222,7 @@ static struct sdw_driver wsa881x_codec_driver = { .id_table = wsa881x_slave_id, .driver = { .name = "wsa881x-codec", - .pm = &wsa881x_pm_ops, + .pm = pm_ptr(&wsa881x_pm_ops), } }; module_sdw_driver(wsa881x_codec_driver); diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index 47da5674d7c9..d259e1d4d83d 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -6,6 +6,7 @@ #include <linux/bitops.h> #include <linux/device.h> #include <linux/gpio/consumer.h> +#include <linux/hwmon.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> @@ -156,8 +157,28 @@ #define WSA883X_PA_FSM_ERR_COND (WSA883X_DIG_CTRL_BASE + 0x0014) #define WSA883X_PA_FSM_MSK (WSA883X_DIG_CTRL_BASE + 0x0015) #define WSA883X_PA_FSM_BYP (WSA883X_DIG_CTRL_BASE + 0x0016) +#define WSA883X_PA_FSM_BYP_DC_CAL_EN_MASK 0x01 +#define WSA883X_PA_FSM_BYP_DC_CAL_EN_SHIFT 0 +#define WSA883X_PA_FSM_BYP_CLK_WD_EN_MASK 0x02 +#define WSA883X_PA_FSM_BYP_CLK_WD_EN_SHIFT 1 +#define WSA883X_PA_FSM_BYP_BG_EN_MASK 0x04 +#define WSA883X_PA_FSM_BYP_BG_EN_SHIFT 2 +#define WSA883X_PA_FSM_BYP_BOOST_EN_MASK 0x08 +#define WSA883X_PA_FSM_BYP_BOOST_EN_SHIFT 3 +#define WSA883X_PA_FSM_BYP_PA_EN_MASK 0x10 +#define WSA883X_PA_FSM_BYP_PA_EN_SHIFT 4 +#define WSA883X_PA_FSM_BYP_D_UNMUTE_MASK 0x20 +#define WSA883X_PA_FSM_BYP_D_UNMUTE_SHIFT 5 +#define WSA883X_PA_FSM_BYP_SPKR_PROT_EN_MASK 0x40 +#define WSA883X_PA_FSM_BYP_SPKR_PROT_EN_SHIFT 6 +#define WSA883X_PA_FSM_BYP_TSADC_EN_MASK 0x80 +#define WSA883X_PA_FSM_BYP_TSADC_EN_SHIFT 7 #define WSA883X_PA_FSM_DBG (WSA883X_DIG_CTRL_BASE + 0x0017) #define WSA883X_TADC_VALUE_CTL (WSA883X_DIG_CTRL_BASE + 0x0020) +#define WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK 0x01 +#define WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_SHIFT 0 +#define WSA883X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_MASK 0x02 +#define WSA883X_TADC_VALUE_CTL_VBAT_VALUE_RD_EN_SHIFT 1 #define WSA883X_TEMP_DETECT_CTL (WSA883X_DIG_CTRL_BASE + 0x0021) #define WSA883X_TEMP_MSB (WSA883X_DIG_CTRL_BASE + 0x0022) #define WSA883X_TEMP_LSB (WSA883X_DIG_CTRL_BASE + 0x0023) @@ -427,6 +448,17 @@ SNDRV_PCM_FMTBIT_S24_LE |\ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) +/* Two-point trimming for temperature calibration */ +#define WSA883X_T1_TEMP -10L +#define WSA883X_T2_TEMP 150L + +/* + * Device will report senseless data in many cases, so discard any measurements + * outside of valid range. + */ +#define WSA883X_LOW_TEMP_THRESHOLD 5 +#define WSA883X_HIGH_TEMP_THRESHOLD 45 + struct wsa883x_priv { struct regmap *regmap; struct device *dev; @@ -441,6 +473,13 @@ struct wsa883x_priv { int active_ports; int dev_mode; int comp_offset; + /* + * Protects temperature reading code (related to speaker protection) and + * fields: temperature and pa_on. + */ + struct mutex sp_lock; + unsigned int temperature; + bool pa_on; }; enum { @@ -1186,6 +1225,10 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: + mutex_lock(&wsa883x->sp_lock); + wsa883x->pa_on = true; + mutex_unlock(&wsa883x->sp_lock); + switch (wsa883x->dev_mode) { case RECEIVER: snd_soc_component_write_field(component, WSA883X_CDC_PATH_MODE, @@ -1235,6 +1278,9 @@ static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, WSA883X_GLOBAL_PA_EN_MASK, 0); snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, WSA883X_PDM_EN_MASK, 0); + mutex_lock(&wsa883x->sp_lock); + wsa883x->pa_on = false; + mutex_unlock(&wsa883x->sp_lock); break; } return 0; @@ -1367,6 +1413,140 @@ static struct snd_soc_dai_driver wsa883x_dais[] = { }, }; +static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp) +{ + unsigned int d1_msb = 0, d1_lsb = 0, d2_msb = 0, d2_lsb = 0; + unsigned int dmeas_msb = 0, dmeas_lsb = 0; + int d1, d2, dmeas; + unsigned int mask; + int ret, range; + long val; + + guard(mutex)(&wsa883x->sp_lock); + + if (wsa883x->pa_on) { + /* + * Reading temperature is possible only when Power Amplifier is + * off. Report last cached data. + */ + *temp = wsa883x->temperature * 1000; + return 0; + } + + ret = pm_runtime_resume_and_get(wsa883x->dev); + if (ret < 0) + return ret; + + mask = WSA883X_PA_FSM_BYP_DC_CAL_EN_MASK | + WSA883X_PA_FSM_BYP_CLK_WD_EN_MASK | + WSA883X_PA_FSM_BYP_BG_EN_MASK | + WSA883X_PA_FSM_BYP_D_UNMUTE_MASK | + WSA883X_PA_FSM_BYP_SPKR_PROT_EN_MASK | + WSA883X_PA_FSM_BYP_TSADC_EN_MASK; + + /* + * Here and further do not care about read or update failures. + * For example, before turning the amplifier on for the first + * time, reading WSA883X_TEMP_DIN_MSB will always return 0. + * Instead, check if returned value is within reasonable + * thresholds. + */ + regmap_update_bits(wsa883x->regmap, WSA883X_PA_FSM_BYP, mask, mask); + + regmap_update_bits(wsa883x->regmap, WSA883X_TADC_VALUE_CTL, + WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, + FIELD_PREP(WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x0)); + + regmap_read(wsa883x->regmap, WSA883X_TEMP_MSB, &dmeas_msb); + regmap_read(wsa883x->regmap, WSA883X_TEMP_LSB, &dmeas_lsb); + + regmap_update_bits(wsa883x->regmap, WSA883X_TADC_VALUE_CTL, + WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, + FIELD_PREP(WSA883X_TADC_VALUE_CTL_TEMP_VALUE_RD_EN_MASK, 0x1)); + + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_1, &d1_msb); + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_2, &d1_lsb); + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_3, &d2_msb); + regmap_read(wsa883x->regmap, WSA883X_OTP_REG_4, &d2_lsb); + + regmap_update_bits(wsa883x->regmap, WSA883X_PA_FSM_BYP, mask, 0x0); + + dmeas = (((dmeas_msb & 0xff) << 0x8) | (dmeas_lsb & 0xff)) >> 0x6; + d1 = (((d1_msb & 0xff) << 0x8) | (d1_lsb & 0xff)) >> 0x6; + d2 = (((d2_msb & 0xff) << 0x8) | (d2_lsb & 0xff)) >> 0x6; + + if (d1 == d2) { + /* Incorrect data in OTP? */ + ret = -EINVAL; + goto out; + } + + val = WSA883X_T1_TEMP + (((dmeas - d1) * (WSA883X_T2_TEMP - WSA883X_T1_TEMP)) / (d2 - d1)); + range = WSA883X_HIGH_TEMP_THRESHOLD - WSA883X_LOW_TEMP_THRESHOLD; + if (in_range(val, WSA883X_LOW_TEMP_THRESHOLD, range)) { + wsa883x->temperature = val; + *temp = val * 1000; + ret = 0; + } else { + ret = -EAGAIN; + } +out: + pm_runtime_mark_last_busy(wsa883x->dev); + pm_runtime_put_autosuspend(wsa883x->dev); + + return ret; +} + +static umode_t wsa883x_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + break; + } + + return 0; +} + +static int wsa883x_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + int ret; + + switch (attr) { + case hwmon_temp_input: + ret = wsa883x_get_temp(dev_get_drvdata(dev), temp); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static const struct hwmon_channel_info *const wsa883x_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops wsa883x_hwmon_ops = { + .is_visible = wsa883x_hwmon_is_visible, + .read = wsa883x_hwmon_read, +}; + +static const struct hwmon_chip_info wsa883x_hwmon_chip_info = { + .ops = &wsa883x_hwmon_ops, + .info = wsa883x_hwmon_info, +}; + static int wsa883x_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) { @@ -1402,6 +1582,7 @@ static int wsa883x_probe(struct sdw_slave *pdev, wsa883x->sconfig.bps = 1; wsa883x->sconfig.direction = SDW_DATA_DIR_RX; wsa883x->sconfig.type = SDW_STREAM_PDM; + mutex_init(&wsa883x->sp_lock); /** * Port map index starts with 0, however the data port for this codec @@ -1424,6 +1605,19 @@ static int wsa883x_probe(struct sdw_slave *pdev, "regmap_init failed\n"); goto err; } + + if (IS_REACHABLE(CONFIG_HWMON)) { + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(dev, "wsa883x", + wsa883x, + &wsa883x_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon)) + return dev_err_probe(dev, PTR_ERR(hwmon), + "Failed to register hwmon sensor\n"); + } + pm_runtime_set_autosuspend_delay(dev, 3000); pm_runtime_use_autosuspend(dev); pm_runtime_mark_last_busy(dev); @@ -1442,7 +1636,7 @@ err: } -static int __maybe_unused wsa883x_runtime_suspend(struct device *dev) +static int wsa883x_runtime_suspend(struct device *dev) { struct regmap *regmap = dev_get_regmap(dev, NULL); @@ -1452,7 +1646,7 @@ static int __maybe_unused wsa883x_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused wsa883x_runtime_resume(struct device *dev) +static int wsa883x_runtime_resume(struct device *dev) { struct regmap *regmap = dev_get_regmap(dev, NULL); @@ -1463,7 +1657,7 @@ static int __maybe_unused wsa883x_runtime_resume(struct device *dev) } static const struct dev_pm_ops wsa883x_pm_ops = { - SET_RUNTIME_PM_OPS(wsa883x_runtime_suspend, wsa883x_runtime_resume, NULL) + RUNTIME_PM_OPS(wsa883x_runtime_suspend, wsa883x_runtime_resume, NULL) }; static const struct sdw_device_id wsa883x_swr_id[] = { @@ -1476,7 +1670,7 @@ MODULE_DEVICE_TABLE(sdw, wsa883x_swr_id); static struct sdw_driver wsa883x_codec_driver = { .driver = { .name = "wsa883x-codec", - .pm = &wsa883x_pm_ops, + .pm = pm_ptr(&wsa883x_pm_ops), .suppress_bind_attrs = true, }, .probe = wsa883x_probe, diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c index 86df5152c547..8051483aa1ac 100644 --- a/sound/soc/codecs/wsa884x.c +++ b/sound/soc/codecs/wsa884x.c @@ -1875,7 +1875,7 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp) * Reading temperature is possible only when Power Amplifier is * off. Report last cached data. */ - *temp = wsa884x->temperature; + *temp = wsa884x->temperature * 1000; return 0; } @@ -1934,7 +1934,7 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp) if ((val > WSA884X_LOW_TEMP_THRESHOLD) && (val < WSA884X_HIGH_TEMP_THRESHOLD)) { wsa884x->temperature = val; - *temp = val; + *temp = val * 1000; ret = 0; } else { ret = -EAGAIN; @@ -2136,7 +2136,7 @@ static int wsa884x_probe(struct sdw_slave *pdev, ARRAY_SIZE(wsa884x_dais)); } -static int __maybe_unused wsa884x_runtime_suspend(struct device *dev) +static int wsa884x_runtime_suspend(struct device *dev) { struct regmap *regmap = dev_get_regmap(dev, NULL); @@ -2146,7 +2146,7 @@ static int __maybe_unused wsa884x_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused wsa884x_runtime_resume(struct device *dev) +static int wsa884x_runtime_resume(struct device *dev) { struct regmap *regmap = dev_get_regmap(dev, NULL); @@ -2157,7 +2157,7 @@ static int __maybe_unused wsa884x_runtime_resume(struct device *dev) } static const struct dev_pm_ops wsa884x_pm_ops = { - SET_RUNTIME_PM_OPS(wsa884x_runtime_suspend, wsa884x_runtime_resume, NULL) + RUNTIME_PM_OPS(wsa884x_runtime_suspend, wsa884x_runtime_resume, NULL) }; static const struct sdw_device_id wsa884x_swr_id[] = { @@ -2169,7 +2169,7 @@ MODULE_DEVICE_TABLE(sdw, wsa884x_swr_id); static struct sdw_driver wsa884x_codec_driver = { .driver = { .name = "wsa884x-codec", - .pm = &wsa884x_pm_ops, + .pm = pm_ptr(&wsa884x_pm_ops), }, .probe = wsa884x_probe, .ops = &wsa884x_slave_ops, diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 57b789d7fbed..4c4171bb3fbb 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -478,7 +478,6 @@ static const struct snd_soc_dai_ops dw_i2s_dai_ops = { .set_tdm_slot = dw_i2s_set_tdm_slot, }; -#ifdef CONFIG_PM static int dw_i2s_runtime_suspend(struct device *dev) { struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev); @@ -501,6 +500,7 @@ static int dw_i2s_runtime_resume(struct device *dev) return 0; } +#ifdef CONFIG_PM static int dw_i2s_suspend(struct snd_soc_component *component) { struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component); @@ -1084,7 +1084,7 @@ MODULE_DEVICE_TABLE(of, dw_i2s_of_match); #endif static const struct dev_pm_ops dwc_pm_ops = { - SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL) + RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL) }; static struct platform_driver dw_i2s_driver = { @@ -1093,7 +1093,7 @@ static struct platform_driver dw_i2s_driver = { .driver = { .name = "designware-i2s", .of_match_table = of_match_ptr(dw_i2s_of_match), - .pm = &dwc_pm_ops, + .pm = pm_ptr(&dwc_pm_ops), }, }; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 678540b78280..c4cf3cff58de 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -6,8 +6,11 @@ comment "Common SoC Audio options for Freescale CPUs:" config SND_SOC_FSL_ASRC tristate "Asynchronous Sample Rate Converter (ASRC) module support" depends on HAS_DMA + select DMA_SHARED_BUFFER select REGMAP_MMIO select SND_SOC_GENERIC_DMAENGINE_PCM + select SND_COMPRESS_ACCEL + select SND_COMPRESS_OFFLOAD help Say Y if you want to add Asynchronous Sample Rate Converter (ASRC) support for the Freescale CPUs. diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index ad97244b5cc3..d656a9ab54e3 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o # Freescale SSI/DMA/SAI/SPDIF Support snd-soc-fsl-audmix-y := fsl_audmix.o snd-soc-fsl-asoc-card-y := fsl-asoc-card.o -snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o +snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o fsl_asrc_m2m.o snd-soc-fsl-lpc3xxx-y := lpc3xxx-pcm.o lpc3xxx-i2s.o snd-soc-fsl-sai-y := fsl_sai.o snd-soc-fsl-ssi-y := fsl_ssi.o diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 02e1594e8223..ab583b432c60 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -759,7 +759,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { codec_dai_name[0] = "tlv320dac31xx-hifi"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; priv->dai_link[1].playback_only = 1; priv->dai_link[2].playback_only = 1; priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; @@ -818,7 +818,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK; priv->codec_priv[0].fll_id = NAU8822_CLK_PLL; priv->codec_priv[0].pll_id = NAU8822_CLK_PLL; - priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; if (codec_dev[0]) priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) { @@ -932,7 +932,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) if (!asrc_pdev) priv->card.num_dapm_routes /= 2; - if (of_property_read_bool(np, "audio-routing")) { + if (of_property_present(np, "audio-routing")) { ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing"); if (ret) { dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index bd5c46d763c0..677529916dc0 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1063,6 +1063,139 @@ static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index) return REG_ASRDx(dir, index); } +/* Get sample numbers in FIFO */ +static unsigned int fsl_asrc_get_output_fifo_size(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc = pair->asrc; + enum asrc_pair_index index = pair->index; + u32 val; + + regmap_read(asrc->regmap, REG_ASRFST(index), &val); + + val &= ASRFSTi_OUTPUT_FIFO_MASK; + + return val >> ASRFSTi_OUTPUT_FIFO_SHIFT; +} + +static int fsl_asrc_m2m_prepare(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc_pair_priv *pair_priv = pair->private; + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &asrc->pdev->dev; + struct asrc_config config; + int ret; + + /* fill config */ + config.pair = pair->index; + config.channel_num = pair->channels; + config.input_sample_rate = pair->rate[IN]; + config.output_sample_rate = pair->rate[OUT]; + config.input_format = pair->sample_format[IN]; + config.output_format = pair->sample_format[OUT]; + config.inclk = INCLK_NONE; + config.outclk = OUTCLK_ASRCK1_CLK; + + pair_priv->config = &config; + ret = fsl_asrc_config_pair(pair, true); + if (ret) { + dev_err(dev, "failed to config pair: %d\n", ret); + return ret; + } + + pair->first_convert = 1; + + return 0; +} + +static int fsl_asrc_m2m_start(struct fsl_asrc_pair *pair) +{ + if (pair->first_convert) { + fsl_asrc_start_pair(pair); + pair->first_convert = 0; + } + /* + * Clear DMA request during the stall state of ASRC: + * During STALL state, the remaining in input fifo would never be + * smaller than the input threshold while the output fifo would not + * be bigger than output one. Thus the DMA request would be cleared. + */ + fsl_asrc_set_watermarks(pair, ASRC_FIFO_THRESHOLD_MIN, + ASRC_FIFO_THRESHOLD_MAX); + + /* Update the real input threshold to raise DMA request */ + fsl_asrc_set_watermarks(pair, ASRC_M2M_INPUTFIFO_WML, + ASRC_M2M_OUTPUTFIFO_WML); + + return 0; +} + +static int fsl_asrc_m2m_stop(struct fsl_asrc_pair *pair) +{ + if (!pair->first_convert) { + fsl_asrc_stop_pair(pair); + pair->first_convert = 1; + } + + return 0; +} + +/* calculate capture data length according to output data length and sample rate */ +static int fsl_asrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length) +{ + unsigned int in_width, out_width; + unsigned int channels = pair->channels; + unsigned int in_samples, out_samples; + unsigned int out_length; + + in_width = snd_pcm_format_physical_width(pair->sample_format[IN]) / 8; + out_width = snd_pcm_format_physical_width(pair->sample_format[OUT]) / 8; + + in_samples = input_buffer_length / in_width / channels; + out_samples = pair->rate[OUT] * in_samples / pair->rate[IN]; + out_length = (out_samples - ASRC_OUTPUT_LAST_SAMPLE) * out_width * channels; + + return out_length; +} + +static int fsl_asrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc = pair->asrc; + struct fsl_asrc_priv *asrc_priv = asrc->private; + int wml = (dir == IN) ? ASRC_M2M_INPUTFIFO_WML : ASRC_M2M_OUTPUTFIFO_WML; + + if (!asrc_priv->soc->use_edma) + return wml * pair->channels; + else + return 1; +} + +static int fsl_asrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap) +{ + cap->fmt_in = FSL_ASRC_FORMATS; + cap->fmt_out = FSL_ASRC_FORMATS | SNDRV_PCM_FMTBIT_S8; + + cap->rate_in = supported_asrc_rate; + cap->rate_in_count = ARRAY_SIZE(supported_asrc_rate); + cap->rate_out = supported_asrc_rate; + cap->rate_out_count = ARRAY_SIZE(supported_asrc_rate); + cap->chan_min = 1; + cap->chan_max = 10; + + return 0; +} + +static int fsl_asrc_m2m_pair_resume(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc = pair->asrc; + int i; + + for (i = 0; i < pair->channels * 4; i++) + regmap_write(asrc->regmap, REG_ASRDI(pair->index), 0); + + pair->first_convert = 1; + return 0; +} + static int fsl_asrc_runtime_resume(struct device *dev); static int fsl_asrc_runtime_suspend(struct device *dev); @@ -1147,6 +1280,15 @@ static int fsl_asrc_probe(struct platform_device *pdev) asrc->get_fifo_addr = fsl_asrc_get_fifo_addr; asrc->pair_priv_size = sizeof(struct fsl_asrc_pair_priv); + asrc->m2m_prepare = fsl_asrc_m2m_prepare; + asrc->m2m_start = fsl_asrc_m2m_start; + asrc->m2m_stop = fsl_asrc_m2m_stop; + asrc->get_output_fifo_size = fsl_asrc_get_output_fifo_size; + asrc->m2m_calc_out_len = fsl_asrc_m2m_calc_out_len; + asrc->m2m_get_maxburst = fsl_asrc_m2m_get_maxburst; + asrc->m2m_pair_resume = fsl_asrc_m2m_pair_resume; + asrc->m2m_get_cap = fsl_asrc_m2m_get_cap; + if (of_device_is_compatible(np, "fsl,imx35-asrc")) { asrc_priv->clk_map[IN] = input_clk_map_imx35; asrc_priv->clk_map[OUT] = output_clk_map_imx35; @@ -1242,6 +1384,12 @@ static int fsl_asrc_probe(struct platform_device *pdev) goto err_pm_get_sync; } + ret = fsl_asrc_m2m_init(asrc); + if (ret) { + dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); + return ret; + } + return 0; err_pm_get_sync: @@ -1254,6 +1402,10 @@ err_pm_disable: static void fsl_asrc_remove(struct platform_device *pdev) { + struct fsl_asrc *asrc = dev_get_drvdata(&pdev->dev); + + fsl_asrc_m2m_exit(asrc); + pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) fsl_asrc_runtime_suspend(&pdev->dev); @@ -1355,10 +1507,29 @@ static int fsl_asrc_runtime_suspend(struct device *dev) return 0; } +static int fsl_asrc_suspend(struct device *dev) +{ + struct fsl_asrc *asrc = dev_get_drvdata(dev); + int ret; + + fsl_asrc_m2m_suspend(asrc); + ret = pm_runtime_force_suspend(dev); + return ret; +} + +static int fsl_asrc_resume(struct device *dev) +{ + struct fsl_asrc *asrc = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + fsl_asrc_m2m_resume(asrc); + return ret; +} + static const struct dev_pm_ops fsl_asrc_pm = { - SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume) }; static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = { @@ -1396,7 +1567,7 @@ static struct platform_driver fsl_asrc_driver = { .driver = { .name = "fsl-asrc", .of_match_table = fsl_asrc_ids, - .pm = &fsl_asrc_pm, + .pm = pm_ptr(&fsl_asrc_pm), }, }; module_platform_driver(fsl_asrc_driver); diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h index 86d2422ad606..1c492eb237f5 100644 --- a/sound/soc/fsl/fsl_asrc.h +++ b/sound/soc/fsl/fsl_asrc.h @@ -12,6 +12,8 @@ #include "fsl_asrc_common.h" +#define ASRC_M2M_INPUTFIFO_WML 0x4 +#define ASRC_M2M_OUTPUTFIFO_WML 0x2 #define ASRC_DMA_BUFFER_NUM 2 #define ASRC_INPUTFIFO_THRESHOLD 32 #define ASRC_OUTPUTFIFO_THRESHOLD 32 diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h index 7e1c13ca37f1..0cd595b0f629 100644 --- a/sound/soc/fsl/fsl_asrc_common.h +++ b/sound/soc/fsl/fsl_asrc_common.h @@ -22,6 +22,26 @@ enum asrc_pair_index { #define PAIR_CTX_NUM 0x4 /** + * struct fsl_asrc_m2m_cap - capability data + * @fmt_in: input sample format + * @fmt_out: output sample format + * @chan_min: minimum channel number + * @chan_max: maximum channel number + * @rate_in: minimum rate + * @rate_out: maximum rete + */ +struct fsl_asrc_m2m_cap { + u64 fmt_in; + u64 fmt_out; + int chan_min; + int chan_max; + const unsigned int *rate_in; + int rate_in_count; + const unsigned int *rate_out; + int rate_out_count; +}; + +/** * fsl_asrc_pair: ASRC Pair common data * * @asrc: pointer to its parent module @@ -34,6 +54,14 @@ enum asrc_pair_index { * @pos: hardware pointer position * @req_dma_chan: flag to release dev_to_dev chan * @private: pair private area + * @complete: dma task complete + * @sample_format: format of m2m + * @rate: rate of m2m + * @buf_len: buffer length of m2m + * @dma_buffer: buffer pointers + * @first_convert: start of conversion + * @ratio_mod_flag: flag for new ratio modifier + * @ratio_mod: ratio modification */ struct fsl_asrc_pair { struct fsl_asrc *asrc; @@ -49,6 +77,16 @@ struct fsl_asrc_pair { bool req_dma_chan; void *private; + + /* used for m2m */ + struct completion complete[2]; + snd_pcm_format_t sample_format[2]; + unsigned int rate[2]; + unsigned int buf_len[2]; + struct snd_dma_buffer dma_buffer[2]; + unsigned int first_convert; + bool ratio_mod_flag; + unsigned int ratio_mod; }; /** @@ -62,6 +100,7 @@ struct fsl_asrc_pair { * @mem_clk: clock source to access register * @ipg_clk: clock source to drive peripheral * @spba_clk: SPBA clock (optional, depending on SoC design) + * @card: compress sound card * @lock: spin lock for resource protection * @pair: pair pointers * @channel_avail: non-occupied channel numbers @@ -72,6 +111,17 @@ struct fsl_asrc_pair { * @request_pair: function pointer * @release_pair: function pointer * @get_fifo_addr: function pointer + * @m2m_get_cap: function pointer + * @m2m_prepare: function pointer + * @m2m_start: function pointer + * @m2m_unprepare: function pointer + * @m2m_stop: function pointer + * @m2m_calc_out_len: function pointer + * @m2m_get_maxburst: function pointer + * @m2m_pair_suspend: function pointer + * @m2m_pair_resume: function pointer + * @m2m_set_ratio_mod: function pointer + * @get_output_fifo_size: function pointer * @pair_priv_size: size of pair private struct. * @private: private data structure */ @@ -84,6 +134,7 @@ struct fsl_asrc { struct clk *mem_clk; struct clk *ipg_clk; struct clk *spba_clk; + struct snd_card *card; spinlock_t lock; /* spin lock for resource protection */ struct fsl_asrc_pair *pair[PAIR_CTX_NUM]; @@ -97,6 +148,20 @@ struct fsl_asrc { int (*request_pair)(int channels, struct fsl_asrc_pair *pair); void (*release_pair)(struct fsl_asrc_pair *pair); int (*get_fifo_addr)(u8 dir, enum asrc_pair_index index); + int (*m2m_get_cap)(struct fsl_asrc_m2m_cap *cap); + + int (*m2m_prepare)(struct fsl_asrc_pair *pair); + int (*m2m_start)(struct fsl_asrc_pair *pair); + int (*m2m_unprepare)(struct fsl_asrc_pair *pair); + int (*m2m_stop)(struct fsl_asrc_pair *pair); + + int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length); + int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair); + int (*m2m_pair_suspend)(struct fsl_asrc_pair *pair); + int (*m2m_pair_resume)(struct fsl_asrc_pair *pair); + int (*m2m_set_ratio_mod)(struct fsl_asrc_pair *pair, int val); + + unsigned int (*get_output_fifo_size)(struct fsl_asrc_pair *pair); size_t pair_priv_size; void *private; @@ -105,4 +170,9 @@ struct fsl_asrc { #define DRV_NAME "fsl-asrc-dai" extern struct snd_soc_component_driver fsl_asrc_component; +int fsl_asrc_m2m_init(struct fsl_asrc *asrc); +void fsl_asrc_m2m_exit(struct fsl_asrc *asrc); +int fsl_asrc_m2m_resume(struct fsl_asrc *asrc); +int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc); + #endif /* _FSL_ASRC_COMMON_H */ diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c new file mode 100644 index 000000000000..f46881f71e43 --- /dev/null +++ b/sound/soc/fsl/fsl_asrc_m2m.c @@ -0,0 +1,729 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2014-2016 Freescale Semiconductor, Inc. +// Copyright (C) 2019-2024 NXP +// +// Freescale ASRC Memory to Memory (M2M) driver + +#include <linux/dma/imx-dma.h> +#include <linux/dma-buf.h> +#include <linux/dma-mapping.h> +#include <linux/pm_runtime.h> +#include <sound/asound.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> + +#include "fsl_asrc_common.h" + +#define DIR_STR(dir) (dir) == IN ? "in" : "out" + +#define ASRC_xPUT_DMA_CALLBACK(dir) \ + (((dir) == IN) ? asrc_input_dma_callback \ + : asrc_output_dma_callback) + +/* Maximum output and capture buffer size */ +#define ASRC_M2M_BUFFER_SIZE (512 * 1024) + +/* Maximum output and capture period size */ +#define ASRC_M2M_PERIOD_SIZE (48 * 1024) + +/* dma complete callback */ +static void asrc_input_dma_callback(void *data) +{ + struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data; + + complete(&pair->complete[IN]); +} + +/* dma complete callback */ +static void asrc_output_dma_callback(void *data) +{ + struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data; + + complete(&pair->complete[OUT]); +} + +/** + *asrc_read_last_fifo: read all the remaining data from FIFO + *@pair: Structure pointer of fsl_asrc_pair + *@dma_vaddr: virtual address of capture buffer + *@length: payload length of capture buffer + */ +static void asrc_read_last_fifo(struct fsl_asrc_pair *pair, void *dma_vaddr, u32 *length) +{ + struct fsl_asrc *asrc = pair->asrc; + enum asrc_pair_index index = pair->index; + u32 i, reg, size, t_size = 0, width; + u32 *reg32 = NULL; + u16 *reg16 = NULL; + u8 *reg24 = NULL; + + width = snd_pcm_format_physical_width(pair->sample_format[OUT]); + if (width == 32) + reg32 = dma_vaddr + *length; + else if (width == 16) + reg16 = dma_vaddr + *length; + else + reg24 = dma_vaddr + *length; +retry: + size = asrc->get_output_fifo_size(pair); + if (size + *length > ASRC_M2M_BUFFER_SIZE) + goto end; + + for (i = 0; i < size * pair->channels; i++) { + regmap_read(asrc->regmap, asrc->get_fifo_addr(OUT, index), ®); + if (reg32) { + *reg32++ = reg; + } else if (reg16) { + *reg16++ = (u16)reg; + } else { + *reg24++ = (u8)reg; + *reg24++ = (u8)(reg >> 8); + *reg24++ = (u8)(reg >> 16); + } + } + t_size += size; + + /* In case there is data left in FIFO */ + if (size) + goto retry; +end: + /* Update payload length */ + if (reg32) + *length += t_size * pair->channels * 4; + else if (reg16) + *length += t_size * pair->channels * 2; + else + *length += t_size * pair->channels * 3; +} + +/* config dma channel */ +static int asrc_dmaconfig(struct fsl_asrc_pair *pair, + struct dma_chan *chan, + u32 dma_addr, dma_addr_t buf_addr, u32 buf_len, + int dir, int width) +{ + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &asrc->pdev->dev; + struct dma_slave_config slave_config; + enum dma_slave_buswidth buswidth; + unsigned int sg_len, max_period_size; + struct scatterlist *sg; + int ret, i; + + switch (width) { + case 8: + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 16: + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 24: + buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES; + break; + case 32: + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + dev_err(dev, "invalid word width\n"); + return -EINVAL; + } + + memset(&slave_config, 0, sizeof(slave_config)); + if (dir == IN) { + slave_config.direction = DMA_MEM_TO_DEV; + slave_config.dst_addr = dma_addr; + slave_config.dst_addr_width = buswidth; + slave_config.dst_maxburst = asrc->m2m_get_maxburst(IN, pair); + } else { + slave_config.direction = DMA_DEV_TO_MEM; + slave_config.src_addr = dma_addr; + slave_config.src_addr_width = buswidth; + slave_config.src_maxburst = asrc->m2m_get_maxburst(OUT, pair); + } + + ret = dmaengine_slave_config(chan, &slave_config); + if (ret) { + dev_err(dev, "failed to config dmaengine for %s task: %d\n", + DIR_STR(dir), ret); + return -EINVAL; + } + + max_period_size = rounddown(ASRC_M2M_PERIOD_SIZE, width * pair->channels / 8); + /* scatter gather mode */ + sg_len = buf_len / max_period_size; + if (buf_len % max_period_size) + sg_len += 1; + + sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL); + if (!sg) + return -ENOMEM; + + sg_init_table(sg, sg_len); + for (i = 0; i < (sg_len - 1); i++) { + sg_dma_address(&sg[i]) = buf_addr + i * max_period_size; + sg_dma_len(&sg[i]) = max_period_size; + } + sg_dma_address(&sg[i]) = buf_addr + i * max_period_size; + sg_dma_len(&sg[i]) = buf_len - i * max_period_size; + + pair->desc[dir] = dmaengine_prep_slave_sg(chan, sg, sg_len, + slave_config.direction, + DMA_PREP_INTERRUPT); + kfree(sg); + if (!pair->desc[dir]) { + dev_err(dev, "failed to prepare dmaengine for %s task\n", DIR_STR(dir)); + return -EINVAL; + } + + pair->desc[dir]->callback = ASRC_xPUT_DMA_CALLBACK(dir); + pair->desc[dir]->callback_param = pair; + + return 0; +} + +/* main function of converter */ +static int asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task_runtime *task) +{ + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &asrc->pdev->dev; + enum asrc_pair_index index = pair->index; + struct snd_dma_buffer *src_buf, *dst_buf; + unsigned int in_buf_len; + unsigned int out_dma_len; + unsigned int width; + u32 fifo_addr; + int ret = 0; + + /* set ratio mod */ + if (asrc->m2m_set_ratio_mod) { + if (pair->ratio_mod_flag) { + asrc->m2m_set_ratio_mod(pair, pair->ratio_mod); + pair->ratio_mod_flag = false; + } + } + + src_buf = &pair->dma_buffer[IN]; + dst_buf = &pair->dma_buffer[OUT]; + + width = snd_pcm_format_physical_width(pair->sample_format[IN]); + fifo_addr = asrc->paddr + asrc->get_fifo_addr(IN, index); + + in_buf_len = task->input_size; + + if (in_buf_len < width * pair->channels / 8 || + in_buf_len > ASRC_M2M_BUFFER_SIZE || + in_buf_len % (width * pair->channels / 8)) { + dev_err(dev, "out buffer size is error: [%d]\n", in_buf_len); + ret = -EINVAL; + goto end; + } + + /* dma config for output dma channel */ + ret = asrc_dmaconfig(pair, + pair->dma_chan[IN], + fifo_addr, + src_buf->addr, + in_buf_len, IN, width); + if (ret) { + dev_err(dev, "out dma config error\n"); + goto end; + } + + width = snd_pcm_format_physical_width(pair->sample_format[OUT]); + fifo_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index); + out_dma_len = asrc->m2m_calc_out_len(pair, in_buf_len); + if (out_dma_len > 0 && out_dma_len <= ASRC_M2M_BUFFER_SIZE) { + /* dma config for capture dma channel */ + ret = asrc_dmaconfig(pair, + pair->dma_chan[OUT], + fifo_addr, + dst_buf->addr, + out_dma_len, OUT, width); + if (ret) { + dev_err(dev, "cap dma config error\n"); + goto end; + } + } else if (out_dma_len > ASRC_M2M_BUFFER_SIZE) { + dev_err(dev, "cap buffer size error\n"); + ret = -EINVAL; + goto end; + } + + reinit_completion(&pair->complete[IN]); + reinit_completion(&pair->complete[OUT]); + + /* Submit DMA request */ + dmaengine_submit(pair->desc[IN]); + dma_async_issue_pending(pair->desc[IN]->chan); + if (out_dma_len > 0) { + dmaengine_submit(pair->desc[OUT]); + dma_async_issue_pending(pair->desc[OUT]->chan); + } + + asrc->m2m_start(pair); + + if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) { + dev_err(dev, "out DMA task timeout\n"); + ret = -ETIMEDOUT; + goto end; + } + + if (out_dma_len > 0) { + if (!wait_for_completion_interruptible_timeout(&pair->complete[OUT], 10 * HZ)) { + dev_err(dev, "cap DMA task timeout\n"); + ret = -ETIMEDOUT; + goto end; + } + } + + /* read the last words from FIFO */ + asrc_read_last_fifo(pair, dst_buf->area, &out_dma_len); + /* update payload length for capture */ + task->output_size = out_dma_len; +end: + return ret; +} + +static int fsl_asrc_m2m_comp_open(struct snd_compr_stream *stream) +{ + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct device *dev = &asrc->pdev->dev; + struct fsl_asrc_pair *pair; + int size, ret; + + pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL); + if (!pair) + return -ENOMEM; + + pair->private = (void *)pair + sizeof(struct fsl_asrc_pair); + pair->asrc = asrc; + + init_completion(&pair->complete[IN]); + init_completion(&pair->complete[OUT]); + + runtime->private_data = pair; + + size = ASRC_M2M_BUFFER_SIZE; + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[IN]); + if (ret) + goto error_alloc_in_buf; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[OUT]); + if (ret) + goto error_alloc_out_buf; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to power up asrc\n"); + goto err_pm_runtime; + } + + return 0; + +err_pm_runtime: + snd_dma_free_pages(&pair->dma_buffer[OUT]); +error_alloc_out_buf: + snd_dma_free_pages(&pair->dma_buffer[IN]); +error_alloc_in_buf: + kfree(pair); + return ret; +} + +static int fsl_asrc_m2m_comp_release(struct snd_compr_stream *stream) +{ + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + struct device *dev = &asrc->pdev->dev; + + pm_runtime_put_sync(dev); + + snd_dma_free_pages(&pair->dma_buffer[IN]); + snd_dma_free_pages(&pair->dma_buffer[OUT]); + + kfree(runtime->private_data); + + return 0; +} + +static int fsl_asrc_m2m_comp_set_params(struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + struct fsl_asrc_m2m_cap cap; + int ret, i; + + ret = asrc->m2m_get_cap(&cap); + if (ret) + return -EINVAL; + + if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.format) & cap.fmt_in) + pair->sample_format[IN] = (__force snd_pcm_format_t)params->codec.format; + else + return -EINVAL; + + if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.pcm_format) & cap.fmt_out) + pair->sample_format[OUT] = (__force snd_pcm_format_t)params->codec.pcm_format; + else + return -EINVAL; + + /* check input rate is in scope */ + for (i = 0; i < cap.rate_in_count; i++) + if (params->codec.sample_rate == cap.rate_in[i]) { + pair->rate[IN] = params->codec.sample_rate; + break; + } + if (i == cap.rate_in_count) + return -EINVAL; + + /* check output rate is in scope */ + for (i = 0; i < cap.rate_out_count; i++) + if (params->codec.options.src_d.out_sample_rate == cap.rate_out[i]) { + pair->rate[OUT] = params->codec.options.src_d.out_sample_rate; + break; + } + if (i == cap.rate_out_count) + return -EINVAL; + + if (params->codec.ch_in != params->codec.ch_out || + params->codec.ch_in < cap.chan_min || + params->codec.ch_in > cap.chan_max) + return -EINVAL; + + pair->channels = params->codec.ch_in; + pair->buf_len[IN] = params->buffer.fragment_size; + pair->buf_len[OUT] = params->buffer.fragment_size; + + return 0; +} + +static int fsl_asrc_m2m_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct snd_dma_buffer *dmab = dmabuf->priv; + + return snd_dma_buffer_mmap(dmab, vma); +} + +static struct sg_table *fsl_asrc_m2m_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct snd_dma_buffer *dmab = attachment->dmabuf->priv; + struct sg_table *sgt; + + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return NULL; + + if (dma_get_sgtable(attachment->dev, sgt, dmab->area, dmab->addr, dmab->bytes) < 0) + goto free; + + if (dma_map_sgtable(attachment->dev, sgt, direction, 0)) + goto free; + + return sgt; + +free: + sg_free_table(sgt); + kfree(sgt); + return NULL; +} + +static void fsl_asrc_m2m_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ + dma_unmap_sgtable(attachment->dev, table, direction, 0); +} + +static void fsl_asrc_m2m_release(struct dma_buf *dmabuf) +{ + /* buffer is released by fsl_asrc_m2m_comp_release() */ +} + +static const struct dma_buf_ops fsl_asrc_m2m_dma_buf_ops = { + .mmap = fsl_asrc_m2m_mmap, + .map_dma_buf = fsl_asrc_m2m_map_dma_buf, + .unmap_dma_buf = fsl_asrc_m2m_unmap_dma_buf, + .release = fsl_asrc_m2m_release, +}; + +static int fsl_asrc_m2m_comp_task_create(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + DEFINE_DMA_BUF_EXPORT_INFO(exp_info_in); + DEFINE_DMA_BUF_EXPORT_INFO(exp_info_out); + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + struct device *dev = &asrc->pdev->dev; + int ret; + + exp_info_in.ops = &fsl_asrc_m2m_dma_buf_ops; + exp_info_in.size = ASRC_M2M_BUFFER_SIZE; + exp_info_in.flags = O_RDWR; + exp_info_in.priv = &pair->dma_buffer[IN]; + task->input = dma_buf_export(&exp_info_in); + if (IS_ERR(task->input)) { + ret = PTR_ERR(task->input); + return ret; + } + + exp_info_out.ops = &fsl_asrc_m2m_dma_buf_ops; + exp_info_out.size = ASRC_M2M_BUFFER_SIZE; + exp_info_out.flags = O_RDWR; + exp_info_out.priv = &pair->dma_buffer[OUT]; + task->output = dma_buf_export(&exp_info_out); + if (IS_ERR(task->output)) { + ret = PTR_ERR(task->output); + return ret; + } + + /* Request asrc pair/context */ + ret = asrc->request_pair(pair->channels, pair); + if (ret) { + dev_err(dev, "failed to request pair: %d\n", ret); + goto err_request_pair; + } + + ret = asrc->m2m_prepare(pair); + if (ret) { + dev_err(dev, "failed to start pair part one: %d\n", ret); + goto err_start_part_one; + } + + /* Request dma channels */ + pair->dma_chan[IN] = asrc->get_dma_channel(pair, IN); + if (!pair->dma_chan[IN]) { + dev_err(dev, "[ctx%d] failed to get input DMA channel\n", pair->index); + ret = -EBUSY; + goto err_dma_channel_in; + } + + pair->dma_chan[OUT] = asrc->get_dma_channel(pair, OUT); + if (!pair->dma_chan[OUT]) { + dev_err(dev, "[ctx%d] failed to get output DMA channel\n", pair->index); + ret = -EBUSY; + goto err_dma_channel_out; + } + + return 0; + +err_dma_channel_out: + dma_release_channel(pair->dma_chan[IN]); +err_dma_channel_in: + if (asrc->m2m_unprepare) + asrc->m2m_unprepare(pair); +err_start_part_one: + asrc->release_pair(pair); +err_request_pair: + return ret; +} + +static int fsl_asrc_m2m_comp_task_start(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + + return asrc_m2m_device_run(pair, task); +} + +static int fsl_asrc_m2m_comp_task_stop(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + return 0; +} + +static int fsl_asrc_m2m_comp_task_free(struct snd_compr_stream *stream, + struct snd_compr_task_runtime *task) +{ + struct fsl_asrc *asrc = stream->private_data; + struct snd_compr_runtime *runtime = stream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + + /* Stop & release pair/context */ + if (asrc->m2m_stop) + asrc->m2m_stop(pair); + + if (asrc->m2m_unprepare) + asrc->m2m_unprepare(pair); + asrc->release_pair(pair); + + /* Release dma channel */ + if (pair->dma_chan[IN]) + dma_release_channel(pair->dma_chan[IN]); + if (pair->dma_chan[OUT]) + dma_release_channel(pair->dma_chan[OUT]); + + return 0; +} + +static int fsl_asrc_m2m_get_caps(struct snd_compr_stream *cstream, + struct snd_compr_caps *caps) +{ + caps->num_codecs = 1; + caps->min_fragment_size = 4096; + caps->max_fragment_size = 4096; + caps->min_fragments = 1; + caps->max_fragments = 1; + caps->codecs[0] = SND_AUDIOCODEC_PCM; + + return 0; +} + +static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc, + struct snd_compr_codec_caps *codec) +{ + struct fsl_asrc_m2m_cap cap; + snd_pcm_format_t k; + int j = 0; + int ret; + + ret = asrc->m2m_get_cap(&cap); + if (ret) + return -EINVAL; + + pcm_for_each_format(k) { + if (pcm_format_to_bits(k) & cap.fmt_in) { + codec->descriptor[j].max_ch = cap.chan_max; + memcpy(codec->descriptor[j].sample_rates, + cap.rate_in, + cap.rate_in_count * sizeof(__u32)); + codec->descriptor[j].num_sample_rates = cap.rate_in_count; + codec->descriptor[j].formats = (__force __u32)k; + codec->descriptor[j].pcm_formats = cap.fmt_out; + codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0]; + codec->descriptor[j].src.out_sample_rate_max = + cap.rate_out[cap.rate_out_count - 1]; + j++; + } + } + + codec->codec = SND_AUDIOCODEC_PCM; + codec->num_descriptors = j; + return 0; +} + +static int fsl_asrc_m2m_get_codec_caps(struct snd_compr_stream *stream, + struct snd_compr_codec_caps *codec) +{ + struct fsl_asrc *asrc = stream->private_data; + + return fsl_asrc_m2m_fill_codec_caps(asrc, codec); +} + +static struct snd_compr_ops fsl_asrc_m2m_compr_ops = { + .open = fsl_asrc_m2m_comp_open, + .free = fsl_asrc_m2m_comp_release, + .set_params = fsl_asrc_m2m_comp_set_params, + .get_caps = fsl_asrc_m2m_get_caps, + .get_codec_caps = fsl_asrc_m2m_get_codec_caps, + .task_create = fsl_asrc_m2m_comp_task_create, + .task_start = fsl_asrc_m2m_comp_task_start, + .task_stop = fsl_asrc_m2m_comp_task_stop, + .task_free = fsl_asrc_m2m_comp_task_free, +}; + +int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc) +{ + struct fsl_asrc_pair *pair; + int i; + + for (i = 0; i < PAIR_CTX_NUM; i++) { + pair = asrc->pair[i]; + if (!pair || !pair->dma_buffer[IN].area || !pair->dma_buffer[OUT].area) + continue; + if (!completion_done(&pair->complete[IN])) { + if (pair->dma_chan[IN]) + dmaengine_terminate_all(pair->dma_chan[IN]); + asrc_input_dma_callback((void *)pair); + } + if (!completion_done(&pair->complete[OUT])) { + if (pair->dma_chan[OUT]) + dmaengine_terminate_all(pair->dma_chan[OUT]); + asrc_output_dma_callback((void *)pair); + } + + if (asrc->m2m_pair_suspend) + asrc->m2m_pair_suspend(pair); + } + + return 0; +} +EXPORT_SYMBOL_GPL(fsl_asrc_m2m_suspend); + +int fsl_asrc_m2m_resume(struct fsl_asrc *asrc) +{ + struct fsl_asrc_pair *pair; + int i; + + for (i = 0; i < PAIR_CTX_NUM; i++) { + pair = asrc->pair[i]; + if (!pair) + continue; + if (asrc->m2m_pair_resume) + asrc->m2m_pair_resume(pair); + } + + return 0; +} +EXPORT_SYMBOL_GPL(fsl_asrc_m2m_resume); + +int fsl_asrc_m2m_init(struct fsl_asrc *asrc) +{ + struct device *dev = &asrc->pdev->dev; + struct snd_card *card; + struct snd_compr *compr; + int ret; + + ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (ret < 0) + return ret; + + strscpy(card->driver, "fsl-asrc-m2m", sizeof(card->driver)); + strscpy(card->shortname, "ASRC-M2M", sizeof(card->shortname)); + strscpy(card->longname, "ASRC-M2M", sizeof(card->shortname)); + + asrc->card = card; + + compr = devm_kzalloc(dev, sizeof(*compr), GFP_KERNEL); + if (!compr) { + ret = -ENOMEM; + goto err; + } + + compr->ops = &fsl_asrc_m2m_compr_ops; + compr->private_data = asrc; + + ret = snd_compress_new(card, 0, SND_COMPRESS_ACCEL, "ASRC M2M", compr); + if (ret < 0) + goto err; + + ret = snd_card_register(card); + if (ret < 0) + goto err; + + return 0; +err: + snd_card_free(card); + return ret; +} +EXPORT_SYMBOL_GPL(fsl_asrc_m2m_init); + +void fsl_asrc_m2m_exit(struct fsl_asrc *asrc) +{ + struct snd_card *card = asrc->card; + + snd_card_free(card); +} +EXPORT_SYMBOL_GPL(fsl_asrc_m2m_exit); + +MODULE_IMPORT_NS("DMA_BUF"); +MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>"); +MODULE_DESCRIPTION("Freescale ASRC M2M driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c index bde642318835..da401561e2de 100644 --- a/sound/soc/fsl/fsl_aud2htx.c +++ b/sound/soc/fsl/fsl_aud2htx.c @@ -290,8 +290,7 @@ static int fsl_aud2htx_runtime_resume(struct device *dev) static const struct dev_pm_ops fsl_aud2htx_pm_ops = { RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend, fsl_aud2htx_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver fsl_aud2htx_driver = { diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index 3cd9a66b70a1..7981d598ba13 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -488,11 +488,17 @@ static int fsl_audmix_probe(struct platform_device *pdev) goto err_disable_pm; } - priv->pdev = platform_device_register_data(dev, "imx-audmix", 0, NULL, 0); - if (IS_ERR(priv->pdev)) { - ret = PTR_ERR(priv->pdev); - dev_err(dev, "failed to register platform: %d\n", ret); - goto err_disable_pm; + /* + * If dais property exist, then register the imx-audmix card driver. + * otherwise, it should be linked by audio graph card. + */ + if (of_find_property(pdev->dev.of_node, "dais", NULL)) { + priv->pdev = platform_device_register_data(dev, "imx-audmix", 0, NULL, 0); + if (IS_ERR(priv->pdev)) { + ret = PTR_ERR(priv->pdev); + dev_err(dev, "failed to register platform: %d\n", ret); + goto err_disable_pm; + } } return 0; diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index d22f0621c465..f404a39009e1 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1861,6 +1861,224 @@ static int fsl_easrc_get_fifo_addr(u8 dir, enum asrc_pair_index index) return REG_EASRC_FIFO(dir, index); } +/* Get sample numbers in FIFO */ +static unsigned int fsl_easrc_get_output_fifo_size(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc = pair->asrc; + enum asrc_pair_index index = pair->index; + u32 val; + + regmap_read(asrc->regmap, REG_EASRC_SFS(index), &val); + val &= EASRC_SFS_NSGO_MASK; + + return val >> EASRC_SFS_NSGO_SHIFT; +} + +static int fsl_easrc_m2m_prepare(struct fsl_asrc_pair *pair) +{ + struct fsl_easrc_ctx_priv *ctx_priv = pair->private; + struct fsl_asrc *asrc = pair->asrc; + struct device *dev = &asrc->pdev->dev; + int ret; + + ctx_priv->in_params.sample_rate = pair->rate[IN]; + ctx_priv->in_params.sample_format = pair->sample_format[IN]; + ctx_priv->out_params.sample_rate = pair->rate[OUT]; + ctx_priv->out_params.sample_format = pair->sample_format[OUT]; + + ctx_priv->in_params.fifo_wtmk = FSL_EASRC_INPUTFIFO_WML; + ctx_priv->out_params.fifo_wtmk = FSL_EASRC_OUTPUTFIFO_WML; + /* Fill the right half of the re-sampler with zeros */ + ctx_priv->rs_init_mode = 0x2; + /* Zero fill the right half of the prefilter */ + ctx_priv->pf_init_mode = 0x2; + + ret = fsl_easrc_set_ctx_format(pair, + &ctx_priv->in_params.sample_format, + &ctx_priv->out_params.sample_format); + if (ret) { + dev_err(dev, "failed to set context format: %d\n", ret); + return ret; + } + + ret = fsl_easrc_config_context(asrc, pair->index); + if (ret) { + dev_err(dev, "failed to config context %d\n", ret); + return ret; + } + + ctx_priv->in_params.iterations = 1; + ctx_priv->in_params.group_len = pair->channels; + ctx_priv->in_params.access_len = pair->channels; + ctx_priv->out_params.iterations = 1; + ctx_priv->out_params.group_len = pair->channels; + ctx_priv->out_params.access_len = pair->channels; + + ret = fsl_easrc_set_ctx_organziation(pair); + if (ret) { + dev_err(dev, "failed to set fifo organization\n"); + return ret; + } + + /* The context start flag */ + pair->first_convert = 1; + return 0; +} + +static int fsl_easrc_m2m_start(struct fsl_asrc_pair *pair) +{ + /* start context once */ + if (pair->first_convert) { + fsl_easrc_start_context(pair); + pair->first_convert = 0; + } + + return 0; +} + +static int fsl_easrc_m2m_stop(struct fsl_asrc_pair *pair) +{ + /* Stop pair/context */ + if (!pair->first_convert) { + fsl_easrc_stop_context(pair); + pair->first_convert = 1; + } + + return 0; +} + +/* calculate capture data length according to output data length and sample rate */ +static int fsl_easrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length) +{ + struct fsl_asrc *easrc = pair->asrc; + struct fsl_easrc_priv *easrc_priv = easrc->private; + struct fsl_easrc_ctx_priv *ctx_priv = pair->private; + unsigned int in_rate = ctx_priv->in_params.norm_rate; + unsigned int out_rate = ctx_priv->out_params.norm_rate; + unsigned int channels = pair->channels; + unsigned int in_samples, out_samples; + unsigned int in_width, out_width; + unsigned int out_length; + unsigned int frac_bits; + u64 val1, val2; + + switch (easrc_priv->rs_num_taps) { + case EASRC_RS_32_TAPS: + /* integer bits = 5; */ + frac_bits = 39; + break; + case EASRC_RS_64_TAPS: + /* integer bits = 6; */ + frac_bits = 38; + break; + case EASRC_RS_128_TAPS: + /* integer bits = 7; */ + frac_bits = 37; + break; + default: + return -EINVAL; + } + + val1 = (u64)in_rate << frac_bits; + do_div(val1, out_rate); + val1 += (s64)ctx_priv->ratio_mod << (frac_bits - 31); + + in_width = snd_pcm_format_physical_width(ctx_priv->in_params.sample_format) / 8; + out_width = snd_pcm_format_physical_width(ctx_priv->out_params.sample_format) / 8; + + ctx_priv->in_filled_len += input_buffer_length; + if (ctx_priv->in_filled_len <= ctx_priv->in_filled_sample * in_width * channels) { + out_length = 0; + } else { + in_samples = ctx_priv->in_filled_len / (in_width * channels) - + ctx_priv->in_filled_sample; + + /* right shift 12 bit to make ratio in 32bit space */ + val2 = (u64)in_samples << (frac_bits - 12); + val1 = val1 >> 12; + do_div(val2, val1); + out_samples = val2; + + out_length = out_samples * out_width * channels; + ctx_priv->in_filled_len = ctx_priv->in_filled_sample * in_width * channels; + } + + return out_length; +} + +static int fsl_easrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair) +{ + struct fsl_easrc_ctx_priv *ctx_priv = pair->private; + + if (dir == IN) + return ctx_priv->in_params.fifo_wtmk * pair->channels; + else + return ctx_priv->out_params.fifo_wtmk * pair->channels; +} + +static int fsl_easrc_m2m_pair_suspend(struct fsl_asrc_pair *pair) +{ + fsl_easrc_stop_context(pair); + + return 0; +} + +static int fsl_easrc_m2m_pair_resume(struct fsl_asrc_pair *pair) +{ + struct fsl_easrc_ctx_priv *ctx_priv = pair->private; + + pair->first_convert = 1; + ctx_priv->in_filled_len = 0; + + return 0; +} + +/* val is Q31 */ +static int fsl_easrc_m2m_set_ratio_mod(struct fsl_asrc_pair *pair, int val) +{ + struct fsl_easrc_ctx_priv *ctx_priv = pair->private; + struct fsl_asrc *easrc = pair->asrc; + struct fsl_easrc_priv *easrc_priv = easrc->private; + unsigned int frac_bits; + + ctx_priv->ratio_mod += val; + + switch (easrc_priv->rs_num_taps) { + case EASRC_RS_32_TAPS: + /* integer bits = 5; */ + frac_bits = 39; + break; + case EASRC_RS_64_TAPS: + /* integer bits = 6; */ + frac_bits = 38; + break; + case EASRC_RS_128_TAPS: + /* integer bits = 7; */ + frac_bits = 37; + break; + default: + return -EINVAL; + } + + val <<= (frac_bits - 31); + regmap_write(easrc->regmap, REG_EASRC_RUC(pair->index), EASRC_RSUC_RS_RM(val)); + + return 0; +} + +static int fsl_easrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap) +{ + cap->fmt_in = FSL_EASRC_FORMATS; + cap->fmt_out = FSL_EASRC_FORMATS | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + cap->rate_in = easrc_rates; + cap->rate_in_count = ARRAY_SIZE(easrc_rates); + cap->rate_out = easrc_rates; + cap->rate_out_count = ARRAY_SIZE(easrc_rates); + cap->chan_min = 1; + cap->chan_max = 32; + return 0; +} + static const struct of_device_id fsl_easrc_dt_ids[] = { { .compatible = "fsl,imx8mn-easrc",}, {} @@ -1926,6 +2144,16 @@ static int fsl_easrc_probe(struct platform_device *pdev) easrc->release_pair = fsl_easrc_release_context; easrc->get_fifo_addr = fsl_easrc_get_fifo_addr; easrc->pair_priv_size = sizeof(struct fsl_easrc_ctx_priv); + easrc->m2m_prepare = fsl_easrc_m2m_prepare; + easrc->m2m_start = fsl_easrc_m2m_start; + easrc->m2m_stop = fsl_easrc_m2m_stop; + easrc->get_output_fifo_size = fsl_easrc_get_output_fifo_size; + easrc->m2m_calc_out_len = fsl_easrc_m2m_calc_out_len; + easrc->m2m_get_maxburst = fsl_easrc_m2m_get_maxburst; + easrc->m2m_pair_suspend = fsl_easrc_m2m_pair_suspend; + easrc->m2m_pair_resume = fsl_easrc_m2m_pair_resume; + easrc->m2m_set_ratio_mod = fsl_easrc_m2m_set_ratio_mod; + easrc->m2m_get_cap = fsl_easrc_m2m_get_cap; easrc_priv->rs_num_taps = EASRC_RS_32_TAPS; easrc_priv->const_coeff = 0x3FF0000000000000; @@ -1976,6 +2204,12 @@ static int fsl_easrc_probe(struct platform_device *pdev) goto err_pm_disable; } + ret = fsl_asrc_m2m_init(easrc); + if (ret) { + dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); + return ret; + } + return 0; err_pm_disable: @@ -1985,6 +2219,10 @@ err_pm_disable: static void fsl_easrc_remove(struct platform_device *pdev) { + struct fsl_asrc *easrc = dev_get_drvdata(&pdev->dev); + + fsl_asrc_m2m_exit(easrc); + pm_runtime_disable(&pdev->dev); } @@ -2085,10 +2323,29 @@ disable_mem_clk: return ret; } +static int fsl_easrc_suspend(struct device *dev) +{ + struct fsl_asrc *easrc = dev_get_drvdata(dev); + int ret; + + fsl_asrc_m2m_suspend(easrc); + ret = pm_runtime_force_suspend(dev); + return ret; +} + +static int fsl_easrc_resume(struct device *dev) +{ + struct fsl_asrc *easrc = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + fsl_asrc_m2m_resume(easrc); + return ret; +} + static const struct dev_pm_ops fsl_easrc_pm_ops = { RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, fsl_easrc_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + SYSTEM_SLEEP_PM_OPS(fsl_easrc_suspend, fsl_easrc_resume) }; static struct platform_driver fsl_easrc_driver = { diff --git a/sound/soc/fsl/fsl_easrc.h b/sound/soc/fsl/fsl_easrc.h index 7c70dac52713..c9f770862662 100644 --- a/sound/soc/fsl/fsl_easrc.h +++ b/sound/soc/fsl/fsl_easrc.h @@ -601,6 +601,8 @@ struct fsl_easrc_slot { * @out_missed_sample: sample missed in output * @st1_addexp: exponent added for stage1 * @st2_addexp: exponent added for stage2 + * @ratio_mod: update ratio + * @in_filled_len: input filled length */ struct fsl_easrc_ctx_priv { struct fsl_easrc_io_params in_params; @@ -618,6 +620,8 @@ struct fsl_easrc_ctx_priv { int out_missed_sample; int st1_addexp; int st2_addexp; + int ratio_mod; + unsigned int in_filled_len; }; /** diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 0b247f16a163..cde0b0c6c1ef 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -1189,11 +1189,8 @@ static int fsl_esai_runtime_suspend(struct device *dev) } static const struct dev_pm_ops fsl_esai_pm_ops = { - SET_RUNTIME_PM_OPS(fsl_esai_runtime_suspend, - fsl_esai_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(fsl_esai_runtime_suspend, fsl_esai_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver fsl_esai_driver = { @@ -1201,7 +1198,7 @@ static struct platform_driver fsl_esai_driver = { .remove = fsl_esai_remove, .driver = { .name = "fsl-esai-dai", - .pm = &fsl_esai_pm_ops, + .pm = pm_ptr(&fsl_esai_pm_ops), .of_match_table = fsl_esai_dt_ids, }, }; diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 8c15389c9a04..aabd90a8b3ec 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -19,6 +19,7 @@ #include <linux/dma/imx-dma.h> #include <sound/dmaengine_pcm.h> #include <sound/pcm.h> +#include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/tlv.h> #include <sound/core.h> @@ -35,6 +36,15 @@ #define MICFIL_AUDIO_PLL2 1 #define MICFIL_CLK_EXT3 2 +static const unsigned int fsl_micfil_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, +}; + +static const struct snd_pcm_hw_constraint_list fsl_micfil_rate_constraints = { + .count = ARRAY_SIZE(fsl_micfil_rates), + .list = fsl_micfil_rates, +}; + enum quality { QUALITY_HIGH, QUALITY_MEDIUM, @@ -69,6 +79,7 @@ struct fsl_micfil { struct fsl_micfil_verid verid; struct fsl_micfil_param param; bool mclk_flag; /* mclk enable flag */ + bool dec_bypass; }; struct fsl_micfil_soc_data { @@ -80,6 +91,7 @@ struct fsl_micfil_soc_data { bool use_verid; bool volume_sx; u64 formats; + int fifo_offset; }; static struct fsl_micfil_soc_data fsl_micfil_imx8mm = { @@ -89,6 +101,7 @@ static struct fsl_micfil_soc_data fsl_micfil_imx8mm = { .dataline = 0xf, .formats = SNDRV_PCM_FMTBIT_S16_LE, .volume_sx = true, + .fifo_offset = 0, }; static struct fsl_micfil_soc_data fsl_micfil_imx8mp = { @@ -98,6 +111,7 @@ static struct fsl_micfil_soc_data fsl_micfil_imx8mp = { .dataline = 0xf, .formats = SNDRV_PCM_FMTBIT_S32_LE, .volume_sx = false, + .fifo_offset = 0, }; static struct fsl_micfil_soc_data fsl_micfil_imx93 = { @@ -109,12 +123,26 @@ static struct fsl_micfil_soc_data fsl_micfil_imx93 = { .use_edma = true, .use_verid = true, .volume_sx = false, + .fifo_offset = 0, +}; + +static struct fsl_micfil_soc_data fsl_micfil_imx943 = { + .imx = true, + .fifos = 8, + .fifo_depth = 32, + .dataline = 0xf, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U32_BE, + .use_edma = true, + .use_verid = true, + .volume_sx = false, + .fifo_offset = -4, }; static const struct of_device_id fsl_micfil_dt_ids[] = { { .compatible = "fsl,imx8mm-micfil", .data = &fsl_micfil_imx8mm }, { .compatible = "fsl,imx8mp-micfil", .data = &fsl_micfil_imx8mp }, { .compatible = "fsl,imx93-micfil", .data = &fsl_micfil_imx93 }, + { .compatible = "fsl,imx943-micfil", .data = &fsl_micfil_imx943 }, {} }; MODULE_DEVICE_TABLE(of, fsl_micfil_dt_ids); @@ -157,6 +185,8 @@ static int micfil_set_quality(struct fsl_micfil *micfil) case QUALITY_VLOW2: qsel = MICFIL_QSEL_VLOW2_QUALITY; break; + default: + return -EINVAL; } return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, @@ -486,29 +516,12 @@ static int fsl_micfil_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); - unsigned int rates[MICFIL_NUM_RATES] = {8000, 11025, 16000, 22050, 32000, 44100, 48000}; - int i, j, k = 0; - u64 clk_rate; if (!micfil) { dev_err(dai->dev, "micfil dai priv_data not set\n"); return -EINVAL; } - micfil->constraint_rates.list = micfil->constraint_rates_list; - micfil->constraint_rates.count = 0; - - for (j = 0; j < MICFIL_NUM_RATES; j++) { - for (i = 0; i < MICFIL_CLK_SRC_NUM; i++) { - clk_rate = clk_get_rate(micfil->clk_src[i]); - if (clk_rate != 0 && do_div(clk_rate, rates[j]) == 0) { - micfil->constraint_rates_list[k++] = rates[j]; - micfil->constraint_rates.count++; - break; - } - } - } - if (micfil->constraint_rates.count > 0) snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, @@ -713,14 +726,14 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd, if (ret) return ret; - if (micfil->vad_enabled) + if (micfil->vad_enabled && !micfil->dec_bypass) fsl_micfil_hwvad_enable(micfil); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (micfil->vad_enabled) + if (micfil->vad_enabled && !micfil->dec_bypass) fsl_micfil_hwvad_disable(micfil); /* Disable the module */ @@ -767,8 +780,9 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, { struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai); unsigned int channels = params_channels(params); + snd_pcm_format_t format = params_format(params); unsigned int rate = params_rate(params); - int clk_div = 8; + int clk_div = 8, mclk_rate, div_multiply_k; int osr = MICFIL_OSR_DEFAULT; int ret; @@ -790,7 +804,39 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, micfil->mclk_flag = true; - ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8); + /* floor(K * CLKDIV) */ + switch (micfil->quality) { + case QUALITY_HIGH: + div_multiply_k = clk_div >> 1; + break; + case QUALITY_LOW: + case QUALITY_VLOW1: + div_multiply_k = clk_div << 1; + break; + case QUALITY_VLOW2: + div_multiply_k = clk_div << 2; + break; + case QUALITY_MEDIUM: + case QUALITY_VLOW0: + default: + div_multiply_k = clk_div; + break; + } + + if (format == SNDRV_PCM_FORMAT_DSD_U32_BE) { + micfil->dec_bypass = true; + /* + * According to equation 29 in RM: + * MCLK_CLK_ROOT = PDM CLK rate * 2 * floor(K * CLKDIV) + * PDM CLK rate = rate * physical bit width (32) + */ + mclk_rate = rate * div_multiply_k * 32 * 2; + } else { + micfil->dec_bypass = false; + mclk_rate = rate * clk_div * osr * 8; + } + + ret = clk_set_rate(micfil->mclk, mclk_rate); if (ret) return ret; @@ -798,10 +844,14 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, if (ret) return ret; + regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, + MICFIL_CTRL2_DEC_BYPASS, + micfil->dec_bypass ? MICFIL_CTRL2_DEC_BYPASS : 0); + ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2, MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR, FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) | - FIELD_PREP(MICFIL_CTRL2_CICOSR, 16 - osr)); + FIELD_PREP(MICFIL_CTRL2_CICOSR, 32 - osr)); /* Configure CIC OSR in VADCICOSR */ regmap_update_bits(micfil->regmap, REG_MICFIL_VAD0_CTRL1, @@ -940,9 +990,39 @@ static const struct reg_default fsl_micfil_reg_defaults[] = { {REG_MICFIL_VAD0_ZCD, 0x00000004}, }; +static const struct reg_default fsl_micfil_reg_defaults_v2[] = { + {REG_MICFIL_CTRL1, 0x00000000}, + {REG_MICFIL_CTRL2, 0x00000000}, + {REG_MICFIL_STAT, 0x00000000}, + {REG_MICFIL_FIFO_CTRL, 0x0000001F}, + {REG_MICFIL_FIFO_STAT, 0x00000000}, + {REG_MICFIL_DATACH0 - 0x4, 0x00000000}, + {REG_MICFIL_DATACH1 - 0x4, 0x00000000}, + {REG_MICFIL_DATACH2 - 0x4, 0x00000000}, + {REG_MICFIL_DATACH3 - 0x4, 0x00000000}, + {REG_MICFIL_DATACH4 - 0x4, 0x00000000}, + {REG_MICFIL_DATACH5 - 0x4, 0x00000000}, + {REG_MICFIL_DATACH6 - 0x4, 0x00000000}, + {REG_MICFIL_DATACH7 - 0x4, 0x00000000}, + {REG_MICFIL_DC_CTRL, 0x00000000}, + {REG_MICFIL_OUT_CTRL, 0x00000000}, + {REG_MICFIL_OUT_STAT, 0x00000000}, + {REG_MICFIL_VAD0_CTRL1, 0x00000000}, + {REG_MICFIL_VAD0_CTRL2, 0x000A0000}, + {REG_MICFIL_VAD0_STAT, 0x00000000}, + {REG_MICFIL_VAD0_SCONFIG, 0x00000000}, + {REG_MICFIL_VAD0_NCONFIG, 0x80000000}, + {REG_MICFIL_VAD0_NDATA, 0x00000000}, + {REG_MICFIL_VAD0_ZCD, 0x00000004}, +}; + static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg) { struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ofs = micfil->soc->fifo_offset; + + if (reg >= (REG_MICFIL_DATACH0 + ofs) && reg <= (REG_MICFIL_DATACH7 + ofs)) + return true; switch (reg) { case REG_MICFIL_CTRL1: @@ -950,14 +1030,6 @@ static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg) case REG_MICFIL_STAT: case REG_MICFIL_FIFO_CTRL: case REG_MICFIL_FIFO_STAT: - case REG_MICFIL_DATACH0: - case REG_MICFIL_DATACH1: - case REG_MICFIL_DATACH2: - case REG_MICFIL_DATACH3: - case REG_MICFIL_DATACH4: - case REG_MICFIL_DATACH5: - case REG_MICFIL_DATACH6: - case REG_MICFIL_DATACH7: case REG_MICFIL_DC_CTRL: case REG_MICFIL_OUT_CTRL: case REG_MICFIL_OUT_STAT: @@ -1011,17 +1083,15 @@ static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg) static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg) { + struct fsl_micfil *micfil = dev_get_drvdata(dev); + int ofs = micfil->soc->fifo_offset; + + if (reg >= (REG_MICFIL_DATACH0 + ofs) && reg <= (REG_MICFIL_DATACH7 + ofs)) + return true; + switch (reg) { case REG_MICFIL_STAT: case REG_MICFIL_FIFO_STAT: - case REG_MICFIL_DATACH0: - case REG_MICFIL_DATACH1: - case REG_MICFIL_DATACH2: - case REG_MICFIL_DATACH3: - case REG_MICFIL_DATACH4: - case REG_MICFIL_DATACH5: - case REG_MICFIL_DATACH6: - case REG_MICFIL_DATACH7: case REG_MICFIL_OUT_STAT: case REG_MICFIL_VERID: case REG_MICFIL_PARAM: @@ -1047,6 +1117,20 @@ static const struct regmap_config fsl_micfil_regmap_config = { .cache_type = REGCACHE_MAPLE, }; +static const struct regmap_config fsl_micfil_regmap_config_v2 = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = REG_MICFIL_VAD0_ZCD, + .reg_defaults = fsl_micfil_reg_defaults_v2, + .num_reg_defaults = ARRAY_SIZE(fsl_micfil_reg_defaults_v2), + .readable_reg = fsl_micfil_readable_reg, + .volatile_reg = fsl_micfil_volatile_reg, + .writeable_reg = fsl_micfil_writeable_reg, + .cache_type = REGCACHE_MAPLE, +}; + /* END OF REGMAP */ static irqreturn_t micfil_isr(int irq, void *devid) @@ -1239,14 +1323,26 @@ static int fsl_micfil_probe(struct platform_device *pdev) if (IS_ERR(micfil->clk_src[MICFIL_CLK_EXT3])) micfil->clk_src[MICFIL_CLK_EXT3] = NULL; + fsl_asoc_constrain_rates(&micfil->constraint_rates, + &fsl_micfil_rate_constraints, + micfil->clk_src[MICFIL_AUDIO_PLL1], + micfil->clk_src[MICFIL_AUDIO_PLL2], + micfil->clk_src[MICFIL_CLK_EXT3], + micfil->constraint_rates_list); + /* init regmap */ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) return PTR_ERR(regs); - micfil->regmap = devm_regmap_init_mmio(&pdev->dev, - regs, - &fsl_micfil_regmap_config); + if (of_device_is_compatible(np, "fsl,imx943-micfil")) + micfil->regmap = devm_regmap_init_mmio(&pdev->dev, + regs, + &fsl_micfil_regmap_config_v2); + else + micfil->regmap = devm_regmap_init_mmio(&pdev->dev, + regs, + &fsl_micfil_regmap_config); if (IS_ERR(micfil->regmap)) { dev_err(&pdev->dev, "failed to init MICFIL regmap: %ld\n", PTR_ERR(micfil->regmap)); @@ -1315,7 +1411,7 @@ static int fsl_micfil_probe(struct platform_device *pdev) } micfil->dma_params_rx.chan_name = "rx"; - micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0; + micfil->dma_params_rx.addr = res->start + REG_MICFIL_DATACH0 + micfil->soc->fifo_offset; micfil->dma_params_rx.maxburst = MICFIL_DMA_MAXBURST_RX; platform_set_drvdata(pdev, micfil); @@ -1416,11 +1512,8 @@ static int fsl_micfil_runtime_resume(struct device *dev) } static const struct dev_pm_ops fsl_micfil_pm_ops = { - SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, - fsl_micfil_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, fsl_micfil_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver fsl_micfil_driver = { @@ -1428,7 +1521,7 @@ static struct platform_driver fsl_micfil_driver = { .remove = fsl_micfil_remove, .driver = { .name = "fsl-micfil-dai", - .pm = &fsl_micfil_pm_ops, + .pm = pm_ptr(&fsl_micfil_pm_ops), .of_match_table = fsl_micfil_dt_ids, }, }; diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h index b7798a7cbf2a..fdfe4e7125bc 100644 --- a/sound/soc/fsl/fsl_micfil.h +++ b/sound/soc/fsl/fsl_micfil.h @@ -53,6 +53,7 @@ #define MICFIL_CTRL1_CHEN(ch) BIT(ch) /* MICFIL Control Register 2 -- REG_MICFILL_CTRL2 0x04 */ +#define MICFIL_CTRL2_DEC_BYPASS BIT(31) #define MICFIL_CTRL2_QSEL_SHIFT 25 #define MICFIL_CTRL2_QSEL GENMASK(27, 25) #define MICFIL_QSEL_MEDIUM_QUALITY 0 @@ -62,7 +63,7 @@ #define MICFIL_QSEL_VLOW1_QUALITY 5 #define MICFIL_QSEL_VLOW2_QUALITY 4 -#define MICFIL_CTRL2_CICOSR GENMASK(19, 16) +#define MICFIL_CTRL2_CICOSR GENMASK(20, 16) #define MICFIL_CTRL2_CLKDIV GENMASK(7, 0) /* MICFIL Status Register -- REG_MICFIL_STAT 0x08 */ diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c index 0513e9e8402e..e34e5ea98de5 100644 --- a/sound/soc/fsl/fsl_mqs.c +++ b/sound/soc/fsl/fsl_mqs.c @@ -410,12 +410,40 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx95_netc_data = { .div_shift = 9, }; +static const struct fsl_mqs_soc_data fsl_mqs_imx943_aon_data = { + .type = TYPE_REG_SM, + .ctrl_off = 0x88, + .en_mask = BIT(1), + .en_shift = 1, + .rst_mask = BIT(2), + .rst_shift = 2, + .osr_mask = BIT(3), + .osr_shift = 3, + .div_mask = GENMASK(15, 8), + .div_shift = 8, +}; + +static const struct fsl_mqs_soc_data fsl_mqs_imx943_wakeup_data = { + .type = TYPE_REG_GPR, + .ctrl_off = 0x10, + .en_mask = BIT(1), + .en_shift = 1, + .rst_mask = BIT(2), + .rst_shift = 2, + .osr_mask = BIT(3), + .osr_shift = 3, + .div_mask = GENMASK(15, 8), + .div_shift = 8, +}; + static const struct of_device_id fsl_mqs_dt_ids[] = { { .compatible = "fsl,imx8qm-mqs", .data = &fsl_mqs_imx8qm_data }, { .compatible = "fsl,imx6sx-mqs", .data = &fsl_mqs_imx6sx_data }, { .compatible = "fsl,imx93-mqs", .data = &fsl_mqs_imx93_data }, { .compatible = "fsl,imx95-aonmix-mqs", .data = &fsl_mqs_imx95_aon_data }, { .compatible = "fsl,imx95-netcmix-mqs", .data = &fsl_mqs_imx95_netc_data }, + { .compatible = "fsl,imx943-aonmix-mqs", .data = &fsl_mqs_imx943_aon_data }, + { .compatible = "fsl,imx943-wakeupmix-mqs", .data = &fsl_mqs_imx943_wakeup_data }, {} }; MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids); diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index e257b8adafe0..b2979290c973 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -892,7 +892,7 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * qmc_soc_dai_driver->playback.channels_max = count > 1 ? count : nb_tx_ts; } qmc_soc_dai_driver->playback.formats = qmc_audio_formats(nb_tx_ts, - count > 1 ? true : false); + count > 1); qmc_soc_dai_driver->capture.channels_min = 0; qmc_soc_dai_driver->capture.channels_max = 0; @@ -901,7 +901,7 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * qmc_soc_dai_driver->capture.channels_max = count > 1 ? count : nb_rx_ts; } qmc_soc_dai_driver->capture.formats = qmc_audio_formats(nb_rx_ts, - count > 1 ? true : false); + count > 1); qmc_soc_dai_driver->playback.rates = snd_pcm_rate_to_rate_bit(tx_fs_rate); qmc_soc_dai_driver->playback.rate_min = tx_fs_rate; diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 634168d2bb6e..ed2b4780c470 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -885,7 +885,7 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream, sai->dma_params_rx.maxburst); ret = snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints); + SNDRV_PCM_HW_PARAM_RATE, &sai->constraint_rates); return ret; } @@ -994,10 +994,10 @@ static struct snd_soc_dai_driver fsl_sai_dai_template[] = { { .name = "sai-tx", .playback = { - .stream_name = "CPU-Playback", + .stream_name = "SAI-Playback", .channels_min = 1, .channels_max = 32, - .rate_min = 8000, + .rate_min = 8000, .rate_max = 2822400, .rates = SNDRV_PCM_RATE_KNOT, .formats = FSL_SAI_FORMATS, @@ -1007,7 +1007,7 @@ static struct snd_soc_dai_driver fsl_sai_dai_template[] = { { .name = "sai-rx", .capture = { - .stream_name = "CPU-Capture", + .stream_name = "SAI-Capture", .channels_min = 1, .channels_max = 32, .rate_min = 8000, @@ -1442,6 +1442,11 @@ static int fsl_sai_probe(struct platform_device *pdev) fsl_asoc_get_pll_clocks(&pdev->dev, &sai->pll8k_clk, &sai->pll11k_clk); + fsl_asoc_constrain_rates(&sai->constraint_rates, + &fsl_sai_rate_constraints, + sai->pll8k_clk, sai->pll11k_clk, NULL, + sai->constraint_rates_list); + /* Use Multi FIFO mode depending on the support from SDMA script */ ret = of_property_read_u32_array(np, "dmas", dmas, 4); if (!sai->soc_data->use_edma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI) @@ -1812,10 +1817,8 @@ disable_bus_clk: } static const struct dev_pm_ops fsl_sai_pm_ops = { - SET_RUNTIME_PM_OPS(fsl_sai_runtime_suspend, - fsl_sai_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(fsl_sai_runtime_suspend, fsl_sai_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver fsl_sai_driver = { @@ -1823,7 +1826,7 @@ static struct platform_driver fsl_sai_driver = { .remove = fsl_sai_remove, .driver = { .name = "fsl-sai", - .pm = &fsl_sai_pm_ops, + .pm = pm_ptr(&fsl_sai_pm_ops), .of_match_table = fsl_sai_ids, }, }; diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 9c4d19fe22c6..0e25e2fc7ce0 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -9,6 +9,7 @@ #include <linux/dma/imx-dma.h> #include <sound/dmaengine_pcm.h> +#define FAL_SAI_NUM_RATES 20 #define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ @@ -309,6 +310,8 @@ struct fsl_sai { struct pinctrl *pinctrl; struct pinctrl_state *pins_state; struct sdma_peripheral_config audio_config[2]; + struct snd_pcm_hw_constraint_list constraint_rates; + unsigned int constraint_rates_list[FAL_SAI_NUM_RATES]; }; #define TX 1 diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index a5ab27c2f711..d69a6b9795bf 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -152,6 +152,51 @@ void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk, } EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks); +/** + * fsl_asoc_constrain_rates - constrain rates according to clocks + * + * @target_constr: target constraint + * @original_constr: original constraint + * @pll8k_clk: PLL clock pointer for 8kHz + * @pll11k_clk: PLL clock pointer for 11kHz + * @ext_clk: External clock pointer + * @target_rates: target rates array + * + * This function constrain rates according to clocks + */ +void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, + const struct snd_pcm_hw_constraint_list *original_constr, + struct clk *pll8k_clk, struct clk *pll11k_clk, + struct clk *ext_clk, int *target_rates) +{ + int i, j, k = 0; + u64 clk_rate[3]; + + *target_constr = *original_constr; + if (pll8k_clk || pll11k_clk || ext_clk) { + target_constr->list = target_rates; + target_constr->count = 0; + for (i = 0; i < original_constr->count; i++) { + clk_rate[0] = clk_get_rate(pll8k_clk); + clk_rate[1] = clk_get_rate(pll11k_clk); + clk_rate[2] = clk_get_rate(ext_clk); + for (j = 0; j < 3; j++) { + if (clk_rate[j] != 0 && + do_div(clk_rate[j], original_constr->list[i]) == 0) { + target_rates[k++] = original_constr->list[i]; + target_constr->count++; + break; + } + } + } + + /* protection for if there is no proper rate found*/ + if (!target_constr->count) + *target_constr = *original_constr; + } +} +EXPORT_SYMBOL(fsl_asoc_constrain_rates); + MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); MODULE_DESCRIPTION("Freescale ASoC utility code"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h index 4d5f3d93bc81..21b25a11ecda 100644 --- a/sound/soc/fsl/fsl_utils.h +++ b/sound/soc/fsl/fsl_utils.h @@ -26,4 +26,9 @@ void fsl_asoc_get_pll_clocks(struct device *dev, struct clk **pll8k_clk, void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk, struct clk *pll8k_clk, struct clk *pll11k_clk, u64 ratio); + +void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr, + const struct snd_pcm_hw_constraint_list *original_constr, + struct clk *pll8k_clk, struct clk *pll11k_clk, + struct clk *ext_clk, int *target_rates); #endif /* _FSL_UTILS_H */ diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 9c184ab73468..83aea341c1b6 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -19,6 +19,7 @@ #include "imx-pcm.h" #define FSL_XCVR_CAPDS_SIZE 256 +#define SPDIF_NUM_RATES 7 enum fsl_xcvr_pll_verison { PLL_MX8MP, @@ -37,6 +38,8 @@ struct fsl_xcvr { const struct fsl_xcvr_soc_data *soc_data; struct platform_device *pdev; struct regmap *regmap; + struct regmap *regmap_phy; + struct regmap *regmap_pll; struct clk *ipg_clk; struct clk *pll_ipg_clk; struct clk *phy_clk; @@ -55,6 +58,8 @@ struct fsl_xcvr { u8 cap_ds[FSL_XCVR_CAPDS_SIZE]; struct work_struct work_rst; spinlock_t lock; /* Protect hw_reset and trigger */ + struct snd_pcm_hw_constraint_list spdif_constr_rates; + u32 spdif_constr_rates_list[SPDIF_NUM_RATES]; }; static const struct fsl_xcvr_pll_conf { @@ -257,7 +262,7 @@ static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy) idx = BIT(phy ? 26 : 24); tidx = BIT(phy ? 27 : 25); - regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF); + regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF | FSL_XCVR_PHY_AI_CTRL_AI_RWB); regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, reg); regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_WDATA, data); regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx); @@ -271,6 +276,59 @@ static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy) return ret; } +static int fsl_xcvr_ai_read(struct fsl_xcvr *xcvr, u8 reg, u32 *data, bool phy) +{ + struct device *dev = &xcvr->pdev->dev; + u32 val, idx, tidx; + int ret; + + idx = BIT(phy ? 26 : 24); + tidx = BIT(phy ? 27 : 25); + + regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF | FSL_XCVR_PHY_AI_CTRL_AI_RWB); + regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, reg | FSL_XCVR_PHY_AI_CTRL_AI_RWB); + regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx); + + ret = regmap_read_poll_timeout(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL, val, + (val & idx) == ((val & tidx) >> 1), + 10, 10000); + if (ret) + dev_err(dev, "AI timeout: failed to read %s reg 0x%02x\n", + phy ? "PHY" : "PLL", reg); + + regmap_read(xcvr->regmap, FSL_XCVR_PHY_AI_RDATA, data); + + return ret; +} + +static int fsl_xcvr_phy_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct fsl_xcvr *xcvr = context; + + return fsl_xcvr_ai_read(xcvr, reg, val, 1); +} + +static int fsl_xcvr_phy_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct fsl_xcvr *xcvr = context; + + return fsl_xcvr_ai_write(xcvr, reg, val, 1); +} + +static int fsl_xcvr_pll_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct fsl_xcvr *xcvr = context; + + return fsl_xcvr_ai_read(xcvr, reg, val, 0); +} + +static int fsl_xcvr_pll_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct fsl_xcvr *xcvr = context; + + return fsl_xcvr_ai_write(xcvr, reg, val, 0); +} + static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx) { struct device *dev = &xcvr->pdev->dev; @@ -303,55 +361,55 @@ static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx) switch (xcvr->soc_data->pll_ver) { case PLL_MX8MP: /* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_BANDGAP_SET, - FSL_XCVR_PLL_BANDGAP_EN_VBG, 0); + regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_BANDGAP, + FSL_XCVR_PLL_BANDGAP_EN_VBG); /* PLL: CTRL0: DIV_INTEGER */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi, 0); + regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi); /* PLL: NUMERATOR: MFN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn, 0); + regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn); /* PLL: DENOMINATOR: MFD */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd, 0); + regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd); /* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP, 0); + regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0, + FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP); udelay(25); /* PLL: CTRL0: Clear Hold Ring Off */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_CLR, - FSL_XCVR_PLL_CTRL0_HROFF, 0); + regmap_clear_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0, + FSL_XCVR_PLL_CTRL0_HROFF); udelay(100); if (tx) { /* TX is enabled for SPDIF only */ /* PLL: POSTDIV: PDIV0 */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, - FSL_XCVR_PLL_PDIVx(log2, 0), 0); + regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 0)); /* PLL: CTRL_SET: CLKMUX0_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_CM0_EN, 0); + regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0, + FSL_XCVR_PLL_CTRL0_CM0_EN); } else if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC RX */ /* PLL: POSTDIV: PDIV1 */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, - FSL_XCVR_PLL_PDIVx(log2, 1), 0); + regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 1)); /* PLL: CTRL_SET: CLKMUX1_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_CM1_EN, 0); + regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0, + FSL_XCVR_PLL_CTRL0_CM1_EN); } else { /* SPDIF / ARC RX */ /* PLL: POSTDIV: PDIV2 */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, - FSL_XCVR_PLL_PDIVx(log2, 2), 0); + regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 2)); /* PLL: CTRL_SET: CLKMUX2_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, - FSL_XCVR_PLL_CTRL0_CM2_EN, 0); + regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0, + FSL_XCVR_PLL_CTRL0_CM2_EN); } break; case PLL_MX95: val = fsl_xcvr_pll_cfg[i].mfi << FSL_XCVR_GP_PLL_DIV_MFI_SHIFT | div; - fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_DIV, val, 0); + regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_DIV, val); val = fsl_xcvr_pll_cfg[i].mfn << FSL_XCVR_GP_PLL_NUMERATOR_MFN_SHIFT; - fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_NUMERATOR, val, 0); - fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_DENOMINATOR, - fsl_xcvr_pll_cfg[i].mfd, 0); + regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_NUMERATOR, val); + regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_DENOMINATOR, + fsl_xcvr_pll_cfg[i].mfd); val = FSL_XCVR_GP_PLL_CTRL_POWERUP | FSL_XCVR_GP_PLL_CTRL_CLKMUX_EN; - fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_CTRL, val, 0); + regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_CTRL, val); break; default: dev_err(dev, "Error for PLL version %d\n", xcvr->soc_data->pll_ver); @@ -360,22 +418,22 @@ static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx) if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC mode */ /* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET, - FSL_XCVR_PHY_CTRL_TSDIFF_OE | - FSL_XCVR_PHY_CTRL_PHY_EN, 1); + regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL, + FSL_XCVR_PHY_CTRL_TSDIFF_OE | + FSL_XCVR_PHY_CTRL_PHY_EN); /* PHY: CTRL2_SET: EARC_TX_MODE */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL2_SET, - FSL_XCVR_PHY_CTRL2_EARC_TXMS, 1); + regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL2, + FSL_XCVR_PHY_CTRL2_EARC_TXMS); } else if (!tx) { /* SPDIF / ARC RX mode */ if (xcvr->mode == FSL_XCVR_MODE_SPDIF) /* PHY: CTRL_SET: SPDIF_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET, - FSL_XCVR_PHY_CTRL_SPDIF_EN, 1); + regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL, + FSL_XCVR_PHY_CTRL_SPDIF_EN); else /* PHY: CTRL_SET: ARC RX setup */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET, - FSL_XCVR_PHY_CTRL_PHY_EN | - FSL_XCVR_PHY_CTRL_RX_CM_EN | - fsl_xcvr_phy_arc_cfg[xcvr->arc_mode], 1); + regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL, + FSL_XCVR_PHY_CTRL_PHY_EN | + FSL_XCVR_PHY_CTRL_RX_CM_EN | + fsl_xcvr_phy_arc_cfg[xcvr->arc_mode]); } dev_dbg(dev, "PLL Fexp: %u, Fout: %u, mfi: %u, mfn: %u, mfd: %d, div: %u, pdiv0: %u\n", @@ -416,17 +474,17 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq) if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC mode */ /* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET, - FSL_XCVR_PHY_CTRL_TSDIFF_OE | - FSL_XCVR_PHY_CTRL_PHY_EN, 1); + regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL, + FSL_XCVR_PHY_CTRL_TSDIFF_OE | + FSL_XCVR_PHY_CTRL_PHY_EN); /* PHY: CTRL2_SET: EARC_TX_MODE */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL2_SET, - FSL_XCVR_PHY_CTRL2_EARC_TXMS, 1); + regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL2, + FSL_XCVR_PHY_CTRL2_EARC_TXMS); } else { /* SPDIF mode */ /* PHY: CTRL_SET: TX_CLK_AUD_SS | SPDIF_EN */ - fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET, - FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS | - FSL_XCVR_PHY_CTRL_SPDIF_EN, 1); + regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL, + FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS | + FSL_XCVR_PHY_CTRL_SPDIF_EN); } dev_dbg(dev, "PLL Fexp: %u\n", freq); @@ -448,7 +506,7 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream, switch (xcvr->mode) { case FSL_XCVR_MODE_SPDIF: if (xcvr->soc_data->spdif_only && tx) { - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL_SET, + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL, FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM, FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM); if (ret < 0) { @@ -466,8 +524,8 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream, return ret; } - ret = regmap_write(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL_SET, - FSL_XCVR_TX_DPTH_CTRL_FRM_FMT); + ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL, + FSL_XCVR_TX_DPTH_CTRL_FRM_FMT); if (ret < 0) { dev_err(dai->dev, "Failed to set TX_DPTH: %d\n", ret); return ret; @@ -484,11 +542,11 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream, * Clear RX FIFO, flip RX FIFO bits, * disable eARC related HW mode detects */ - ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_SET, - FSL_XCVR_RX_DPTH_CTRL_STORE_FMT | - FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO | - FSL_XCVR_RX_DPTH_CTRL_COMP | - FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL); + ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL, + FSL_XCVR_RX_DPTH_CTRL_STORE_FMT | + FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO | + FSL_XCVR_RX_DPTH_CTRL_COMP | + FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL); if (ret < 0) { dev_err(dai->dev, "Failed to set RX_DPTH: %d\n", ret); return ret; @@ -505,18 +563,18 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream, case FSL_XCVR_MODE_EARC: if (!tx) { /** Clear RX FIFO, flip RX FIFO bits */ - ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_SET, - FSL_XCVR_RX_DPTH_CTRL_STORE_FMT | - FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO); + ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL, + FSL_XCVR_RX_DPTH_CTRL_STORE_FMT | + FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO); if (ret < 0) { dev_err(dai->dev, "Failed to set RX_DPTH: %d\n", ret); return ret; } /** Enable eARC related HW mode detects */ - ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_CLR, - FSL_XCVR_RX_DPTH_CTRL_COMP | - FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL); + ret = regmap_clear_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL, + FSL_XCVR_RX_DPTH_CTRL_COMP | + FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL); if (ret < 0) { dev_err(dai->dev, "Failed to clr TX_DPTH: %d\n", ret); return ret; @@ -585,8 +643,12 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream, switch (xcvr->mode) { case FSL_XCVR_MODE_SPDIF: case FSL_XCVR_MODE_ARC: - ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr, - &fsl_xcvr_spdif_rates_constr); + if (xcvr->soc_data->spdif_only && tx) + ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr, + &xcvr->spdif_constr_rates); + else + ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr, + &fsl_xcvr_spdif_rates_constr); break; case FSL_XCVR_MODE_EARC: ret = fsl_xcvr_constr(substream, &fsl_xcvr_earc_channels_constr, @@ -696,9 +758,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, } fallthrough; case FSL_XCVR_MODE_SPDIF: - ret = regmap_write(xcvr->regmap, - FSL_XCVR_TX_DPTH_CTRL_SET, - FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); + ret = regmap_set_bits(xcvr->regmap, + FSL_XCVR_TX_DPTH_CTRL, + FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); if (ret < 0) { dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret); goto release_lock; @@ -754,9 +816,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, if (tx) { switch (xcvr->mode) { case FSL_XCVR_MODE_SPDIF: - ret = regmap_write(xcvr->regmap, - FSL_XCVR_TX_DPTH_CTRL_CLR, - FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); + ret = regmap_clear_bits(xcvr->regmap, + FSL_XCVR_TX_DPTH_CTRL, + FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); if (ret < 0) { dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret); goto release_lock; @@ -1169,6 +1231,7 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg) case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET: case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR: case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG: + case FSL_XCVR_TX_DPTH_CTRL: case FSL_XCVR_TX_DPTH_CTRL_SET: case FSL_XCVR_TX_DPTH_CTRL_CLR: case FSL_XCVR_TX_DPTH_CTRL_TOG: @@ -1190,7 +1253,49 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg) static bool fsl_xcvr_volatile_reg(struct device *dev, unsigned int reg) { - return fsl_xcvr_readable_reg(dev, reg); + switch (reg) { + case FSL_XCVR_EXT_STATUS: + case FSL_XCVR_EXT_ISR: + case FSL_XCVR_EXT_ISR_SET: + case FSL_XCVR_EXT_ISR_CLR: + case FSL_XCVR_EXT_ISR_TOG: + case FSL_XCVR_ISR: + case FSL_XCVR_ISR_SET: + case FSL_XCVR_ISR_CLR: + case FSL_XCVR_ISR_TOG: + case FSL_XCVR_PHY_AI_CTRL: + case FSL_XCVR_PHY_AI_CTRL_SET: + case FSL_XCVR_PHY_AI_CTRL_CLR: + case FSL_XCVR_PHY_AI_CTRL_TOG: + case FSL_XCVR_PHY_AI_RDATA: + case FSL_XCVR_RX_CS_DATA_0: + case FSL_XCVR_RX_CS_DATA_1: + case FSL_XCVR_RX_CS_DATA_2: + case FSL_XCVR_RX_CS_DATA_3: + case FSL_XCVR_RX_CS_DATA_4: + case FSL_XCVR_RX_CS_DATA_5: + case FSL_XCVR_RX_DPTH_CNTR_CTRL: + case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET: + case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR: + case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG: + case FSL_XCVR_RX_DPTH_TSCR: + case FSL_XCVR_RX_DPTH_BCR: + case FSL_XCVR_RX_DPTH_BCTR: + case FSL_XCVR_RX_DPTH_BCRR: + case FSL_XCVR_TX_DPTH_CNTR_CTRL: + case FSL_XCVR_TX_DPTH_CNTR_CTRL_SET: + case FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR: + case FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG: + case FSL_XCVR_TX_DPTH_TSCR: + case FSL_XCVR_TX_DPTH_BCR: + case FSL_XCVR_TX_DPTH_BCTR: + case FSL_XCVR_TX_DPTH_BCRR: + case FSL_XCVR_DEBUG_REG_0: + case FSL_XCVR_DEBUG_REG_1: + return true; + default: + return false; + } } static const struct regmap_config fsl_xcvr_regmap_cfg = { @@ -1206,6 +1311,49 @@ static const struct regmap_config fsl_xcvr_regmap_cfg = { .cache_type = REGCACHE_FLAT, }; +static const struct reg_default fsl_xcvr_phy_reg_defaults[] = { + { FSL_XCVR_PHY_CTRL, 0x58200804 }, + { FSL_XCVR_PHY_STATUS, 0x00000000 }, + { FSL_XCVR_PHY_ANALOG_TRIM, 0x00260F13 }, + { FSL_XCVR_PHY_SLEW_RATE_TRIM, 0x00000411 }, + { FSL_XCVR_PHY_DATA_TEST_DELAY, 0x00990000 }, + { FSL_XCVR_PHY_TEST_CTRL, 0x00000000 }, + { FSL_XCVR_PHY_DIFF_CDR_CTRL, 0x016D0009 }, + { FSL_XCVR_PHY_CTRL2, 0x80000000 }, +}; + +static const struct regmap_config fsl_xcvr_regmap_phy_cfg = { + .reg_bits = 8, + .reg_stride = 4, + .val_bits = 32, + .max_register = FSL_XCVR_PHY_CTRL2_TOG, + .reg_defaults = fsl_xcvr_phy_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_xcvr_phy_reg_defaults), + .cache_type = REGCACHE_FLAT, + .reg_read = fsl_xcvr_phy_reg_read, + .reg_write = fsl_xcvr_phy_reg_write, +}; + +static const struct regmap_config fsl_xcvr_regmap_pllv0_cfg = { + .reg_bits = 8, + .reg_stride = 4, + .val_bits = 32, + .max_register = FSL_XCVR_PLL_STAT0_TOG, + .cache_type = REGCACHE_FLAT, + .reg_read = fsl_xcvr_pll_reg_read, + .reg_write = fsl_xcvr_pll_reg_write, +}; + +static const struct regmap_config fsl_xcvr_regmap_pllv1_cfg = { + .reg_bits = 8, + .reg_stride = 4, + .val_bits = 32, + .max_register = FSL_XCVR_GP_PLL_STATUS_TOG, + .cache_type = REGCACHE_FLAT, + .reg_read = fsl_xcvr_pll_reg_read, + .reg_write = fsl_xcvr_pll_reg_write, +}; + static void reset_rx_work(struct work_struct *work) { struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst); @@ -1405,6 +1553,15 @@ static int fsl_xcvr_probe(struct platform_device *pdev) fsl_asoc_get_pll_clocks(dev, &xcvr->pll8k_clk, &xcvr->pll11k_clk); + if (xcvr->soc_data->spdif_only) { + if (!(xcvr->pll8k_clk || xcvr->pll11k_clk)) + xcvr->pll8k_clk = xcvr->phy_clk; + fsl_asoc_constrain_rates(&xcvr->spdif_constr_rates, + &fsl_xcvr_spdif_rates_constr, + xcvr->pll8k_clk, xcvr->pll11k_clk, NULL, + xcvr->spdif_constr_rates_list); + } + xcvr->ram_addr = devm_platform_ioremap_resource_byname(pdev, "ram"); if (IS_ERR(xcvr->ram_addr)) return PTR_ERR(xcvr->ram_addr); @@ -1421,6 +1578,40 @@ static int fsl_xcvr_probe(struct platform_device *pdev) return PTR_ERR(xcvr->regmap); } + if (xcvr->soc_data->use_phy) { + xcvr->regmap_phy = devm_regmap_init(dev, NULL, xcvr, + &fsl_xcvr_regmap_phy_cfg); + if (IS_ERR(xcvr->regmap_phy)) { + dev_err(dev, "failed to init XCVR PHY regmap: %ld\n", + PTR_ERR(xcvr->regmap_phy)); + return PTR_ERR(xcvr->regmap_phy); + } + + switch (xcvr->soc_data->pll_ver) { + case PLL_MX8MP: + xcvr->regmap_pll = devm_regmap_init(dev, NULL, xcvr, + &fsl_xcvr_regmap_pllv0_cfg); + if (IS_ERR(xcvr->regmap_pll)) { + dev_err(dev, "failed to init XCVR PLL regmap: %ld\n", + PTR_ERR(xcvr->regmap_pll)); + return PTR_ERR(xcvr->regmap_pll); + } + break; + case PLL_MX95: + xcvr->regmap_pll = devm_regmap_init(dev, NULL, xcvr, + &fsl_xcvr_regmap_pllv1_cfg); + if (IS_ERR(xcvr->regmap_pll)) { + dev_err(dev, "failed to init XCVR PLL regmap: %ld\n", + PTR_ERR(xcvr->regmap_pll)); + return PTR_ERR(xcvr->regmap_pll); + } + break; + default: + dev_err(dev, "Error for PLL version %d\n", xcvr->soc_data->pll_ver); + return -EINVAL; + } + } + xcvr->reset = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(xcvr->reset)) { dev_err(dev, "failed to get XCVR reset control\n"); @@ -1454,6 +1645,10 @@ static int fsl_xcvr_probe(struct platform_device *pdev) platform_set_drvdata(pdev, xcvr); pm_runtime_enable(dev); regcache_cache_only(xcvr->regmap, true); + if (xcvr->soc_data->use_phy) { + regcache_cache_only(xcvr->regmap_phy, true); + regcache_cache_only(xcvr->regmap_pll, true); + } /* * Register platform component before registering cpu dai for there @@ -1492,7 +1687,8 @@ static int fsl_xcvr_runtime_suspend(struct device *dev) struct fsl_xcvr *xcvr = dev_get_drvdata(dev); int ret; - if (!xcvr->soc_data->spdif_only) { + if (!xcvr->soc_data->spdif_only && + xcvr->mode == FSL_XCVR_MODE_EARC) { /* Assert M0+ reset */ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, FSL_XCVR_EXT_CTRL_CORE_RESET, @@ -1502,6 +1698,10 @@ static int fsl_xcvr_runtime_suspend(struct device *dev) } regcache_cache_only(xcvr->regmap, true); + if (xcvr->soc_data->use_phy) { + regcache_cache_only(xcvr->regmap_phy, true); + regcache_cache_only(xcvr->regmap_pll, true); + } clk_disable_unprepare(xcvr->spba_clk); clk_disable_unprepare(xcvr->phy_clk); @@ -1546,6 +1746,12 @@ static int fsl_xcvr_runtime_resume(struct device *dev) goto stop_phy_clk; } + ret = reset_control_deassert(xcvr->reset); + if (ret) { + dev_err(dev, "failed to deassert M0+ reset.\n"); + goto stop_spba_clk; + } + regcache_cache_only(xcvr->regmap, false); regcache_mark_dirty(xcvr->regmap); ret = regcache_sync(xcvr->regmap); @@ -1555,31 +1761,49 @@ static int fsl_xcvr_runtime_resume(struct device *dev) goto stop_spba_clk; } - if (xcvr->soc_data->spdif_only) - return 0; + if (xcvr->soc_data->use_phy) { + ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, + FSL_XCVR_PHY_AI_CTRL_AI_RESETN); + if (ret < 0) { + dev_err(dev, "Error while release PHY reset: %d\n", ret); + goto stop_spba_clk; + } - ret = reset_control_deassert(xcvr->reset); - if (ret) { - dev_err(dev, "failed to deassert M0+ reset.\n"); - goto stop_spba_clk; - } + regcache_cache_only(xcvr->regmap_phy, false); + regcache_mark_dirty(xcvr->regmap_phy); + ret = regcache_sync(xcvr->regmap_phy); + if (ret) { + dev_err(dev, "failed to sync phy regcache.\n"); + goto stop_spba_clk; + } - ret = fsl_xcvr_load_firmware(xcvr); - if (ret) { - dev_err(dev, "failed to load firmware.\n"); - goto stop_spba_clk; + regcache_cache_only(xcvr->regmap_pll, false); + regcache_mark_dirty(xcvr->regmap_pll); + ret = regcache_sync(xcvr->regmap_pll); + if (ret) { + dev_err(dev, "failed to sync pll regcache.\n"); + goto stop_spba_clk; + } } - /* Release M0+ reset */ - ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, - FSL_XCVR_EXT_CTRL_CORE_RESET, 0); - if (ret < 0) { - dev_err(dev, "M0+ core release failed: %d\n", ret); - goto stop_spba_clk; - } + if (xcvr->mode == FSL_XCVR_MODE_EARC) { + ret = fsl_xcvr_load_firmware(xcvr); + if (ret) { + dev_err(dev, "failed to load firmware.\n"); + goto stop_spba_clk; + } + + /* Release M0+ reset */ + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, + FSL_XCVR_EXT_CTRL_CORE_RESET, 0); + if (ret < 0) { + dev_err(dev, "M0+ core release failed: %d\n", ret); + goto stop_spba_clk; + } - /* Let M0+ core complete firmware initialization */ - msleep(50); + /* Let M0+ core complete firmware initialization */ + msleep(50); + } return 0; @@ -1597,8 +1821,7 @@ stop_ipg_clk: static const struct dev_pm_ops fsl_xcvr_pm_ops = { RUNTIME_PM_OPS(fsl_xcvr_runtime_suspend, fsl_xcvr_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver fsl_xcvr_driver = { diff --git a/sound/soc/fsl/fsl_xcvr.h b/sound/soc/fsl/fsl_xcvr.h index c72cb05184df..dade3945cc0c 100644 --- a/sound/soc/fsl/fsl_xcvr.h +++ b/sound/soc/fsl/fsl_xcvr.h @@ -234,6 +234,7 @@ #define FSL_XCVR_TX_DPTH_CTRL_TM_NO_PRE_BME GENMASK(31, 30) #define FSL_XCVR_PHY_AI_CTRL_AI_RESETN BIT(15) +#define FSL_XCVR_PHY_AI_CTRL_AI_RWB BIT(31) #define FSL_XCVR_PLL_CTRL0 0x00 #define FSL_XCVR_PLL_CTRL0_SET 0x04 @@ -241,13 +242,25 @@ #define FSL_XCVR_PLL_NUM 0x20 #define FSL_XCVR_PLL_DEN 0x30 #define FSL_XCVR_PLL_PDIV 0x40 +#define FSL_XCVR_PLL_BANDGAP 0x50 #define FSL_XCVR_PLL_BANDGAP_SET 0x54 +#define FSL_XCVR_PLL_STAT0 0x60 +#define FSL_XCVR_PLL_STAT0_TOG 0x6c + #define FSL_XCVR_PHY_CTRL 0x00 #define FSL_XCVR_PHY_CTRL_SET 0x04 #define FSL_XCVR_PHY_CTRL_CLR 0x08 +#define FSL_XCVR_PHY_CTRL_TOG 0x0c +#define FSL_XCVR_PHY_STATUS 0x10 +#define FSL_XCVR_PHY_ANALOG_TRIM 0x20 +#define FSL_XCVR_PHY_SLEW_RATE_TRIM 0x30 +#define FSL_XCVR_PHY_DATA_TEST_DELAY 0x40 +#define FSL_XCVR_PHY_TEST_CTRL 0x50 +#define FSL_XCVR_PHY_DIFF_CDR_CTRL 0x60 #define FSL_XCVR_PHY_CTRL2 0x70 #define FSL_XCVR_PHY_CTRL2_SET 0x74 #define FSL_XCVR_PHY_CTRL2_CLR 0x78 +#define FSL_XCVR_PHY_CTRL2_TOG 0x7c #define FSL_XCVR_PLL_BANDGAP_EN_VBG BIT(0) #define FSL_XCVR_PLL_CTRL0_HROFF BIT(13) diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index 231400661c90..dac5d4ddacd6 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -23,7 +23,6 @@ struct imx_audmix { struct snd_soc_card card; struct platform_device *audmix_pdev; struct platform_device *out_pdev; - struct clk *cpu_mclk; int num_dai; struct snd_soc_dai_link *dai; int num_dai_conf; @@ -32,34 +31,11 @@ struct imx_audmix { struct snd_soc_dapm_route *dapm_routes; }; -static const u32 imx_audmix_rates[] = { - 8000, 12000, 16000, 24000, 32000, 48000, 64000, 96000, -}; - -static const struct snd_pcm_hw_constraint_list imx_audmix_rate_constraints = { - .count = ARRAY_SIZE(imx_audmix_rates), - .list = imx_audmix_rates, -}; - static int imx_audmix_fe_startup(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct imx_audmix *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_pcm_runtime *runtime = substream->runtime; - struct device *dev = rtd->card->dev; - unsigned long clk_rate = clk_get_rate(priv->cpu_mclk); int ret; - if (clk_rate % 24576000 == 0) { - ret = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &imx_audmix_rate_constraints); - if (ret < 0) - return ret; - } else { - dev_warn(dev, "mclk may be not supported %lu\n", clk_rate); - } - ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 1, 8); if (ret < 0) @@ -143,8 +119,8 @@ static const struct snd_soc_ops imx_audmix_be_ops = { static const char *name[][3] = { {"HiFi-AUDMIX-FE-0", "HiFi-AUDMIX-FE-1", "HiFi-AUDMIX-FE-2"}, {"sai-tx", "sai-tx", "sai-rx"}, - {"AUDMIX-Playback-0", "AUDMIX-Playback-1", "CPU-Capture"}, - {"CPU-Playback", "CPU-Playback", "AUDMIX-Capture-0"}, + {"AUDMIX-Playback-0", "AUDMIX-Playback-1", "SAI-Capture"}, + {"SAI-Playback", "SAI-Playback", "AUDMIX-Capture-0"}, }; static int imx_audmix_probe(struct platform_device *pdev) @@ -323,13 +299,6 @@ static int imx_audmix_probe(struct platform_device *pdev) } put_device(&cpu_pdev->dev); - priv->cpu_mclk = devm_clk_get(&cpu_pdev->dev, "mclk1"); - if (IS_ERR(priv->cpu_mclk)) { - ret = PTR_ERR(priv->cpu_mclk); - dev_err(&cpu_pdev->dev, "failed to get DAI mclk1: %d\n", ret); - return ret; - } - priv->audmix_pdev = audmix_pdev; priv->out_pdev = cpu_pdev; diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index 43e14f2eca8d..cc2918ee2cf5 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -237,7 +237,7 @@ static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, child); continue; } - if (!of_property_read_bool(child, "fsl,port-config")) { + if (!of_property_present(child, "fsl,port-config")) { dev_warn(&pdev->dev, "child node \"%pOF\" does not have property fsl,port-config\n", child); continue; diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index 95a57fda0250..905294682996 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -518,6 +518,7 @@ static int imx_card_parse_of(struct imx_card_data *data) struct snd_soc_dai_link *link; struct dai_link_data *link_data; struct of_phandle_args args; + bool playback_only, capture_only; int ret, num_links; u32 asrc_fmt = 0; u32 width; @@ -529,7 +530,7 @@ static int imx_card_parse_of(struct imx_card_data *data) } /* DAPM routes */ - if (of_property_read_bool(dev->of_node, "audio-routing")) { + if (of_property_present(dev->of_node, "audio-routing")) { ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); if (ret) return ret; @@ -679,6 +680,10 @@ static int imx_card_parse_of(struct imx_card_data *data) link->ops = &imx_aif_ops; } + graph_util_parse_link_direction(np, &playback_only, &capture_only); + link->playback_only = playback_only; + link->capture_only = capture_only; + /* Get dai fmt */ ret = simple_util_parse_daifmt(dev, np, codec, NULL, &link->dai_fmt); diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 3391430e4253..83de3ae33691 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -185,8 +185,7 @@ static int snd_imx_open(struct snd_soc_component *component, atomic_set(&iprtd->playing, 0); atomic_set(&iprtd->capturing, 0); - hrtimer_init(&iprtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - iprtd->hrt.function = snd_hrtimer_callback; + hrtimer_setup(&iprtd->hrt, snd_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ret = snd_pcm_hw_constraint_integer(substream->runtime, SNDRV_PCM_HW_PARAM_PERIODS); diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index ce98d2288193..7cd3aa4c8706 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -218,7 +218,7 @@ static int imx_rpmsg_probe(struct platform_device *pdev) if (ret) goto fail; - if (of_property_read_bool(np, "audio-routing")) { + if (of_property_present(np, "audio-routing")) { ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); if (ret) { dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 7655425a3deb..a8a3bad3df00 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -20,6 +20,13 @@ #define DPCM_SELECTABLE 1 +#define graph_ret(priv, ret) _graph_ret(priv, __func__, ret) +static inline int _graph_ret(struct simple_util_priv *priv, + const char *func, int ret) +{ + return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func); +} + #define ep_to_port(ep) of_get_parent(ep) static struct device_node *port_to_ports(struct device_node *port) { @@ -81,18 +88,14 @@ static void graph_parse_convert(struct device *dev, struct simple_util_data *adata) { struct device_node *top = dev->of_node; - struct device_node *port = ep_to_port(ep); - struct device_node *ports = port_to_ports(port); - struct device_node *node = of_graph_get_port_parent(ep); + struct device_node *port __free(device_node) = ep_to_port(ep); + struct device_node *ports __free(device_node) = port_to_ports(port); + struct device_node *node __free(device_node) = of_graph_get_port_parent(ep); simple_util_parse_convert(top, NULL, adata); simple_util_parse_convert(ports, NULL, adata); simple_util_parse_convert(port, NULL, adata); simple_util_parse_convert(ep, NULL, adata); - - of_node_put(port); - of_node_put(ports); - of_node_put(node); } static int graph_parse_node(struct simple_util_priv *priv, @@ -115,19 +118,17 @@ static int graph_parse_node(struct simple_util_priv *priv, dai = simple_props_to_dai_codec(dai_props, 0); } - ret = graph_util_parse_dai(dev, ep, dlc, cpu); + ret = graph_util_parse_dai(priv, ep, dlc, cpu); if (ret < 0) - return ret; + goto end; ret = simple_util_parse_tdm(ep, dai); if (ret < 0) - return ret; + goto end; ret = simple_util_parse_clk(dev, ep, dai, dlc); - if (ret < 0) - return ret; - - return 0; +end: + return graph_ret(priv, ret); } static int graph_link_init(struct simple_util_priv *priv, @@ -140,10 +141,10 @@ static int graph_link_init(struct simple_util_priv *priv, struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); - struct device_node *port_cpu = ep_to_port(ep_cpu); - struct device_node *port_codec = ep_to_port(ep_codec); - struct device_node *ports_cpu = port_to_ports(port_cpu); - struct device_node *ports_codec = port_to_ports(port_codec); + struct device_node *port_cpu __free(device_node) = ep_to_port(ep_cpu); + struct device_node *port_codec __free(device_node) = ep_to_port(ep_codec); + struct device_node *ports_cpu __free(device_node) = port_to_ports(port_cpu); + struct device_node *ports_codec __free(device_node) = port_to_ports(port_codec); enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT; enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT; bool playback_only = 0, capture_only = 0; @@ -152,7 +153,7 @@ static int graph_link_init(struct simple_util_priv *priv, ret = simple_util_parse_daifmt(dev, ep_cpu, ep_codec, NULL, &dai_link->dai_fmt); if (ret < 0) - goto init_end; + goto end; graph_util_parse_link_direction(top, &playback_only, &capture_only); graph_util_parse_link_direction(port_cpu, &playback_only, &capture_only); @@ -187,14 +188,9 @@ static int graph_link_init(struct simple_util_priv *priv, if (priv->ops) dai_link->ops = priv->ops; - ret = simple_util_set_dailink_name(dev, dai_link, name); -init_end: - of_node_put(ports_cpu); - of_node_put(ports_codec); - of_node_put(port_cpu); - of_node_put(port_codec); - - return ret; + ret = simple_util_set_dailink_name(priv, dai_link, name); +end: + return graph_ret(priv, ret); } static int graph_dai_link_of_dpcm(struct simple_util_priv *priv, @@ -226,7 +222,7 @@ static int graph_dai_link_of_dpcm(struct simple_util_priv *priv, ret = graph_parse_node(priv, cpu_ep, li, &is_single_links); if (ret) - return ret; + goto end; snprintf(dai_name, sizeof(dai_name), "fe.%pOFP.%s", cpus->of_node, cpus->dai_name); @@ -250,8 +246,6 @@ static int graph_dai_link_of_dpcm(struct simple_util_priv *priv, } else { struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, 0); struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0); - struct device_node *port; - struct device_node *ports; /* CPU is dummy */ @@ -261,20 +255,18 @@ static int graph_dai_link_of_dpcm(struct simple_util_priv *priv, ret = graph_parse_node(priv, codec_ep, li, NULL); if (ret < 0) - return ret; + goto end; snprintf(dai_name, sizeof(dai_name), "be.%pOFP.%s", codecs->of_node, codecs->dai_name); /* check "prefix" from top node */ - port = ep_to_port(ep); - ports = port_to_ports(port); + struct device_node *port __free(device_node) = ep_to_port(ep); + struct device_node *ports __free(device_node) = port_to_ports(port); + snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, "prefix"); snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, "prefix"); snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, "prefix"); - - of_node_put(ports); - of_node_put(port); } graph_parse_convert(dev, ep, &dai_props->adata); @@ -282,8 +274,8 @@ static int graph_dai_link_of_dpcm(struct simple_util_priv *priv, ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name); li->link++; - - return ret; +end: + return graph_ret(priv, ret); } static int graph_dai_link_of(struct simple_util_priv *priv, @@ -303,11 +295,11 @@ static int graph_dai_link_of(struct simple_util_priv *priv, ret = graph_parse_node(priv, cpu_ep, li, &is_single_links); if (ret < 0) - return ret; + goto end; ret = graph_parse_node(priv, codec_ep, li, NULL); if (ret < 0) - return ret; + goto end; snprintf(dai_name, sizeof(dai_name), "%s-%s", cpus->dai_name, codecs->dai_name); @@ -317,11 +309,11 @@ static int graph_dai_link_of(struct simple_util_priv *priv, ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name); if (ret < 0) - return ret; + goto end; li->link++; - - return 0; +end: + return graph_ret(priv, ret); } static inline bool parse_as_dpcm_link(struct simple_util_priv *priv, @@ -361,8 +353,6 @@ static int __graph_for_each_link(struct simple_util_priv *priv, struct device *dev = simple_priv_to_dev(priv); struct device_node *node = dev->of_node; struct device_node *cpu_port; - struct device_node *codec_ep; - struct device_node *codec_port; struct device_node *codec_port_old = NULL; struct simple_util_data adata; int rc, ret = 0; @@ -374,8 +364,8 @@ static int __graph_for_each_link(struct simple_util_priv *priv, /* loop for all CPU endpoint */ for_each_of_graph_port_endpoint(cpu_port, cpu_ep) { /* get codec */ - codec_ep = of_graph_get_remote_endpoint(cpu_ep); - codec_port = ep_to_port(codec_ep); + struct device_node *codec_ep __free(device_node) = of_graph_get_remote_endpoint(cpu_ep); + struct device_node *codec_port __free(device_node) = ep_to_port(codec_ep); /* get convert-xxx property */ memset(&adata, 0, sizeof(adata)); @@ -399,17 +389,14 @@ static int __graph_for_each_link(struct simple_util_priv *priv, ret = func_noml(priv, cpu_ep, codec_ep, li); } - of_node_put(codec_ep); - of_node_put(codec_port); - if (ret < 0) - return ret; + goto end; codec_port_old = codec_port; } } - - return 0; +end: + return graph_ret(priv, ret); } static int graph_for_each_link(struct simple_util_priv *priv, @@ -442,7 +429,7 @@ static int graph_for_each_link(struct simple_util_priv *priv, break; } - return ret; + return graph_ret(priv, ret); } static int graph_count_noml(struct simple_util_priv *priv, @@ -451,11 +438,10 @@ static int graph_count_noml(struct simple_util_priv *priv, struct link_info *li) { struct device *dev = simple_priv_to_dev(priv); + int ret = -EINVAL; - if (li->link >= SNDRV_MAX_LINKS) { - dev_err(dev, "too many links\n"); - return -EINVAL; - } + if (li->link >= SNDRV_MAX_LINKS) + goto end; /* * DON'T REMOVE platforms @@ -470,8 +456,9 @@ static int graph_count_noml(struct simple_util_priv *priv, li->link += 1; /* 1xCPU-Codec */ dev_dbg(dev, "Count As Normal\n"); - - return 0; + ret = 0; +end: + return graph_ret(priv, ret); } static int graph_count_dpcm(struct simple_util_priv *priv, @@ -480,11 +467,10 @@ static int graph_count_dpcm(struct simple_util_priv *priv, struct link_info *li) { struct device *dev = simple_priv_to_dev(priv); + int ret = -EINVAL; - if (li->link >= SNDRV_MAX_LINKS) { - dev_err(dev, "too many links\n"); - return -EINVAL; - } + if (li->link >= SNDRV_MAX_LINKS) + goto end; if (li->cpu) { /* @@ -503,8 +489,9 @@ static int graph_count_dpcm(struct simple_util_priv *priv, } dev_dbg(dev, "Count As DPCM\n"); - - return 0; + ret = 0; +end: + return graph_ret(priv, ret); } static int graph_get_dais_count(struct simple_util_priv *priv, @@ -564,40 +551,41 @@ static int graph_get_dais_count(struct simple_util_priv *priv, int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) { struct snd_soc_card *card = simple_priv_to_card(priv); - int ret; + int ret = -ENOMEM; struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL); if (!li) - return -ENOMEM; + goto end; card->owner = THIS_MODULE; card->dev = dev; ret = graph_get_dais_count(priv, li); if (ret < 0) - return ret; + goto end; + ret = -EINVAL; if (!li->link) - return -EINVAL; + goto end; ret = simple_util_init_priv(priv, li); if (ret < 0) - return ret; + goto end; priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); if (IS_ERR(priv->pa_gpio)) { ret = PTR_ERR(priv->pa_gpio); dev_err(dev, "failed to get amplifier gpio: %d\n", ret); - return ret; + goto end; } ret = simple_util_parse_widgets(card, NULL); if (ret < 0) - return ret; + goto end; ret = simple_util_parse_routing(card, NULL); if (ret < 0) - return ret; + goto end; memset(li, 0, sizeof(*li)); ret = graph_for_each_link(priv, li, @@ -606,7 +594,7 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) if (ret < 0) goto err; - ret = simple_util_parse_card_name(card, NULL); + ret = simple_util_parse_card_name(priv, NULL); if (ret < 0) goto err; @@ -619,10 +607,9 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) goto err; return 0; - err: simple_util_clean_reference(card); - +end: return dev_err_probe(dev, ret, "parse error\n"); } EXPORT_SYMBOL_GPL(audio_graph_parse_of); diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi deleted file mode 100644 index 9efd31206c9b..000000000000 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ /dev/null @@ -1,702 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * audio-graph-card2-custom-sample.dtsi - * - * Copyright (C) 2020 Renesas Electronics Corp. - * Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This sample indicates how to use audio-graph-card2 and its - * custom driver. "audio-graph-card2-custom-sample" is the custome driver - * which is using audio-graph-card2. - * - * You can easily use this sample by adding below line on your DT file, - * and add new CONFIG to your .config. - * - * #include "../../../../../sound/soc/generic/audio-graph-card2-custom-sample.dtsi" - * - * CONFIG_SND_AUDIO_GRAPH_CARD2 - * CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE - * CONFIG_SND_TEST_COMPONENT - * - * - * You can indicate more detail each device behavior as debug if you modify - * "compatible" on each test-component. see below - * - * test_cpu { - * - compatible = "test-cpu"; - * + compatible = "test-cpu-verbose"; - * ... - * }; - * - * test_codec { - * - compatible = "test-codec"; - * + compatible = "test-codec-verbose"; - * ... - * }; - * - * - * Below sample doesn't use "format" property, - * because test-component driver (test-cpu/test-codec) is supporting - * snd_soc_dai_ops :: .auto_selectable_formats. - * see - * snd_soc_runtime_get_dai_fmt() - * linux/sound/soc/generic/test-component.c :: test_dai_formats - */ -/ { - /* - * @ : used at links - * - * [Normal] - * cpu0 <-@-----------------> codec0 - * - * [Semi-Multi] - * - * CPU:Codec = 1:N - * - * +-+ - * cpu7 <-@------->| |-> codec12 - * | |-> codec13 - * +-+ - * - * [Multi-CPU/Codec-0] - * +-+ +-+ - * cpu1 <--| |<-@--------->| |-> codec1 - * cpu2 <--| | | |-> codec2 - * +-+ +-+ - * - * [Multi-CPU/Codec-1] - * - * +-+ +-+ - * | |<-@--------->| | - * | | | | - * cpu8 <--| |<----------->| |-> codec14 - * cpu9 <--| |<---+------->| |-> codec15 - * +-+ \------>| |-> codec16 - * +-+ - * - * [Multi-CPU/Codec-2] - * - * +-+ +-+ - * | |<-@--------->| | - * | | | | - * cpu10 <-| |<----------->| |-> codec17 - * cpu11 <-| |<-----+----->| |-> codec18 - * cpu12 <-| |<----/ +-+ - * +-+ - * - * [DPCM] - * - * CPU3/CPU4 are converting rate to 44100 - * - * FE BE - * **** - * cpu3 <-@--* *--@-> codec3 - * cpu4 <-@--* * (44.1kHz) - * **** - * - * [DPCM-Multi] - * - * --NOTE-- - * Multi-FE is not supported by ASoC. - * - * FE BE - * **** +-+ - * cpu5 <-@--* *--@-> | | -> codec4 - * cpu6 <-@--* * | | -> codec5 - * **** +-+ - * - * [Codec2Codec] - * +-@-> codec6 - * | - * +---> codec7 - * - * [Codec2Codec-Multi] - * - * --NOTE-- - * Multi connect N:M is not supported by ASoC. - * - * +-+ - * +-@->| |-> codec8 - * | | |-> codec9 - * | +-+ - * | +-+ - * +--->| |-> codec10 - * | |-> codec11 - * +-+ - */ - audio-graph-card2-custom-sample { - /* - * You can use audio-graph-card2 directly by using - * - * compatible = "audio-graph-card2"; - */ - compatible = "audio-graph-card2-custom-sample"; - - /* for [DPCM] */ - /* BE FE */ - routing = "TC DAI3 Playback", "DAI3 Playback", - "TC DAI3 Playback", "DAI4 Playback", - "DAI3 Capture", "TC DAI3 Capture", - "DAI4 Capture", "TC DAI3 Capture", - /* for [DPCM-Multi] */ - /* BE FE */ - "TC DAI4 Playback", "DAI5 Playback", - "TC DAI5 Playback", "DAI5 Playback", - "TC DAI4 Playback", "DAI6 Playback", - "TC DAI5 Playback", "DAI6 Playback", - "DAI5 Capture", "TC DAI4 Capture", - "DAI5 Capture", "TC DAI5 Capture", - "DAI6 Capture", "TC DAI4 Capture", - "DAI6 Capture", "TC DAI5 Capture", - /* for [Codec2Codec] */ - "TC OUT", "TC DAI7 Playback", - "TC DAI6 Capture", "TC IN", - /* for [Codec2Codec-Multi] */ - "TC OUT", "TC DAI10 Playback", - "TC DAI8 Capture", "TC IN", - "TC OUT", "TC DAI11 Playback", - "TC DAI9 Capture", "TC IN"; - - links = < - /* - * [Normal]: cpu side only - * cpu0/codec0 - */ - &cpu0 - - /* - * [Semi-Multi] - * cpu7/codec12/codec13 - */ - &sm0 - - /* - * [Multi-CPU/Codec-0]: cpu side only - * cpu1/cpu2/codec1/codec2 - */ - &mcpu0 - - /* - * [Multi-CPU/Codec-1]: cpu side only - * cpu8/cpu9/codec14/codec15/codec16 - * - * Because it will reach to the maximum of sound minor number, - * disable it so far. - * If you want to try it, please disable some other one instead. - */ - //&mcpu1 - - /* - * [Multi-CPU/Codec-2]: cpu side only - * cpu10/cpu11/cpu12/codec17/codec18 - * - * Because it will reach to the maximum of sound minor number, - * disable it so far. - * If you want to try it, please disable some other one instead. - */ - //&mcpu2 - - /* - * [DPCM]: both FE / BE - * cpu3/cpu4/codec3 - */ - &fe00 &fe01 &be0 - - /* - * [DPCM-Multi]: both FE / BE - * cpu5/cpu6/codec4/codec5 - */ - &fe10 &fe11 &be1 - - /* - * [Codec2Codec]: cpu side only - * codec6/codec7 - */ - &c2c - - /* - * [Codec2Codec-Multi]: cpu side only - * codec8/codec9/codec10/codec11 - */ - &c2c_m - >; - - multi { - #address-cells = <1>; - #size-cells = <0>; - - /* - * [Multi-CPU-0] - * - * +---+ +---+ - * cpu1 <--|A X|<-@------->|x a|-> codec1 - * cpu2 <--|B | | b|-> codec2 - * +---+ +---+ - */ - ports@0 { - reg = <0>; - #address-cells = <1>; - #size-cells = <0>; - mcpu0: port@0 { reg = <0>; mcpu00_ep: endpoint { remote-endpoint = <&mcodec00_ep>; };};/* (X) to pair */ - port@1 { reg = <1>; mcpu01_ep: endpoint { remote-endpoint = <&cpu1_ep>; };};/* (A) Multi Element */ - port@2 { reg = <2>; mcpu02_ep: endpoint { remote-endpoint = <&cpu2_ep>; };};/* (B) Multi Element */ - }; - - /* - * [Multi-Codec-0] - * - * +---+ +---+ - * cpu1 <--|A X|<-@------->|x a|-> codec1 - * cpu2 <--|B | | b|-> codec2 - * +---+ +---+ - */ - ports@1 { - reg = <1>; - #address-cells = <1>; - #size-cells = <0>; - port@0 { reg = <0>; mcodec00_ep: endpoint { remote-endpoint = <&mcpu00_ep>; };};/* (x) to pair */ - port@1 { reg = <1>; mcodec01_ep: endpoint { remote-endpoint = <&codec1_ep>; };};/* (a) Multi Element */ - port@2 { reg = <2>; mcodec02_ep: endpoint { remote-endpoint = <&codec2_ep>; };};/* (b) Multi Element */ - }; - - /* - * [DPCM-Multi]::BE - * - * FE BE - * **** +---+ - * cpu5 <-@--* *-----@--->|x a|-> codec4 - * cpu6 <-@--* * | b|-> codec5 - * **** +---+ - */ - ports@2 { - reg = <2>; - #address-cells = <1>; - #size-cells = <0>; - port@0 { reg = <0>; mbe_ep: endpoint { remote-endpoint = <&be10_ep>; };};/* (x) to pair */ - port@1 { reg = <1>; mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; };};/* (a) Multi Element */ - port@2 { reg = <2>; mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; };};/* (b) Multi Element */ - }; - - /* - * [Codec2Codec-Multi]::CPU - * - * +---+ - * +-@->|X A|-> codec8 - * | | B|-> codec9 - * | +---+ - * | +---+ - * +--->|x a|-> codec10 - * | b|-> codec11 - * +---+ - */ - ports@3 { - reg = <3>; - #address-cells = <1>; - #size-cells = <0>; - port@0 { reg = <0>; mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; };};/* (X) to pair */ - port@1 { reg = <1>; mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; };};/* (A) Multi Element */ - port@2 { reg = <2>; mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; };};/* (B) Multi Element */ - }; - - /* - * [Codec2Codec-Multi]::Codec - * - * +---+ - * +-@->|X A|-> codec8 - * | | B|-> codec9 - * | +---+ - * | +---+ - * +--->|x a|-> codec10 - * | b|-> codec11 - * +---+ - */ - ports@4 { - reg = <4>; - #address-cells = <1>; - #size-cells = <0>; - port@0 { reg = <0>; mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; };};/* (x) to pair */ - port@1 { reg = <1>; mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; };};/* (a) Multi Element */ - port@2 { reg = <2>; mc2c11_ep: endpoint { remote-endpoint = <&codec11_ep>; };};/* (b) Multi Element */ - }; - - /* - * [Semi-Multi] - * - * +---+ - * cpu7 <-@------->|X A|-> codec12 - * | B|-> codec13 - * +---+ - */ - ports@5 { - reg = <5>; - #address-cells = <1>; - #size-cells = <0>; - port@0 { reg = <0>; smcodec0_ep: endpoint { remote-endpoint = <&cpu7_ep>; };};/* (X) to pair */ - port@1 { reg = <1>; smcodec1_ep: endpoint { remote-endpoint = <&codec12_ep>; };};/* (A) Multi Element */ - port@2 { reg = <2>; smcodec2_ep: endpoint { remote-endpoint = <&codec13_ep>; };};/* (B) Multi Element */ - }; - - /* - * [Multi-CPU-1] - * - * +---+ +---+ - * | X|<-@------->|x | - * | | | | - * cpu8 <--|A 1|<--------->|3 a|-> codec14 - * cpu9 <--|B 2|<---+----->|4 b|-> codec15 - * +---+ \---->|5 c|-> codec16 - * +---+ - */ - ports@6 { - reg = <6>; - #address-cells = <1>; - #size-cells = <0>; - mcpu1: port@0 { reg = <0>; mcpu10_ep: endpoint { remote-endpoint = <&mcodec10_ep>; };}; /* (X) to pair */ - port@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; - mcpu11_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu8_ep>; }; /* (A) Multi Element */ - mcpu11_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec11_ep_0>; }; /* (1) connected Codec */ - }; - port@2 { - #address-cells = <1>; - #size-cells = <0>; - reg = <2>; - mcpu12_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu9_ep>; }; /* (B) Multi Element */ - mcpu12_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec12_ep_0>; }; /* (2) connected Codec */ - mcpu12_ep_1: endpoint@2 { reg = <2>; remote-endpoint = <&mcodec13_ep_0>; }; /* (2) connected Codec */ - }; - }; - - /* - * [Multi-Codec-1] - * - * +---+ +---+ - * | X|<-@------->|x | - * | | | | - * cpu8 <--|A 1|<--------->|3 a|-> codec14 - * cpu9 <--|B 2|<---+----->|4 b|-> codec15 - * +---+ \---->|5 c|-> codec16 - * +---+ - */ - ports@7 { - reg = <7>; - #address-cells = <1>; - #size-cells = <0>; - port@0 { reg = <0>; mcodec10_ep: endpoint { remote-endpoint = <&mcpu10_ep>; };}; /* (x) to pair */ - port@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; - mcodec11_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec14_ep>; }; /* (a) Multi Element */ - mcodec11_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu11_ep_0>; }; /* (3) connected CPU */ - }; - port@2 { - #address-cells = <1>; - #size-cells = <0>; - reg = <2>; - mcodec12_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec15_ep>; }; /* (b) Multi Element */ - mcodec12_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu12_ep_0>; }; /* (4) connected CPU */ - }; - port@3 { - #address-cells = <1>; - #size-cells = <0>; - reg = <3>; - mcodec13_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec16_ep>; }; /* (c) Multi Element */ - mcodec13_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu12_ep_1>; }; /* (5) connected CPU */ - }; - }; - - /* - * [Multi-CPU-2] - * - * +---+ +---+ - * | X|<-@------->|x | - * | | | | - * cpu10 <-|A 1|<--------->|4 a|-> codec17 - * cpu11 <-|B 2|<-----+--->|5 b|-> codec18 - * cpu12 <-|C 3|<----/ +---+ - * +---+ - */ - ports@8 { - reg = <8>; - #address-cells = <1>; - #size-cells = <0>; - mcpu2: port@0 { reg = <0>; mcpu20_ep: endpoint { remote-endpoint = <&mcodec20_ep>; };}; /* (X) to pair */ - port@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; - mcpu21_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu10_ep>; }; /* (A) Multi Element */ - mcpu21_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec21_ep_0>; }; /* (1) connected Codec */ - }; - port@2 { - #address-cells = <1>; - #size-cells = <0>; - reg = <2>; - mcpu22_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu11_ep>; }; /* (B) Multi Element */ - mcpu22_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec22_ep_0>; }; /* (2) connected Codec */ - }; - port@3 { - #address-cells = <1>; - #size-cells = <0>; - reg = <3>; - mcpu23_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu12_ep>; }; /* (C) Multi Element */ - mcpu23_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec22_ep_1>; }; /* (3) connected Codec */ - }; - }; - - /* - * [Multi-Codec-2] - * - * +---+ +---+ - * | X|<-@------->|x | - * | | | | - * cpu10 <-|A 1|<--------->|4 a|-> codec17 - * cpu11 <-|B 2|<-----+--->|5 b|-> codec18 - * cpu12 <-|C 3|<----/ +---+ - * +---+ - */ - ports@9 { - reg = <9>; - #address-cells = <1>; - #size-cells = <0>; - port@0 { reg = <0>; mcodec20_ep: endpoint { remote-endpoint = <&mcpu20_ep>; };}; /* (x) to pair */ - port@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; - mcodec21_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec17_ep>; }; /* (a) Multi Element */ - mcodec21_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu21_ep_0>; }; /* (4) connected CPU */ - }; - port@2 { - #address-cells = <1>; - #size-cells = <0>; - reg = <2>; - mcodec22_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec18_ep>; }; /* (b) Multi Element */ - mcodec22_ep_0: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu22_ep_0>; }; /* (5) connected CPU */ - mcodec22_ep_1: endpoint@2 { reg = <2>; remote-endpoint = <&mcpu23_ep_0>; }; /* (5) connected CPU */ - }; - }; - }; - - dpcm { - #address-cells = <1>; - #size-cells = <0>; - - ports@0 { - reg = <0>; - - #address-cells = <1>; - #size-cells = <0>; - /* - * [DPCM]::FE - * - * FE BE - * **** - * cpu3 <-@(fe00)--* *--(be0)@--> codec3 - * cpu4 <-@(fe01)--* * (44.1kHz) - * **** - */ - fe00: port@0 { reg = <0>; fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; }; - fe01: port@1 { reg = <1>; fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; }; - - /* - * [DPCM-Multi]::FE - * - * FE BE - * **** +-+ - * cpu5 <-@(fe10)--* *---(be1)@-->| |-> codec4 - * cpu6 <-@(fe11)--* * | |-> codec5 - * **** +-+ - */ - fe10: port@2 { reg = <2>; fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; }; - fe11: port@3 { reg = <3>; fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; }; - }; - - ports@1 { - reg = <1>; - - #address-cells = <1>; - #size-cells = <0>; - /* - * [DPCM]::BE - * - * FE BE - * **** - * cpu3 <-@(fe00)--* *--(be0)@--> codec3 - * cpu4 <-@(fe01)--* * (44.1kHz) - * **** - */ - be0: port@0 { reg = <0>; be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; }; - - /* - * [DPCM-Multi]::BE - * - * FE BE - * **** +-+ - * cpu5 <-@(fe10)--* *---(be1)@-->| |-> codec4 - * cpu6 <-@(fe11)--* * | |-> codec5 - * **** +-+ - */ - be1: port@1 { reg = <1>; be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; }; - }; - }; - - codec2codec { - #address-cells = <1>; - #size-cells = <0>; - /* - * [Codec2Codec] - * - * +-@(c2c)-> codec6 - * | - * +--------> codec7 - */ - ports@0 { - reg = <0>; - - #address-cells = <1>; - #size-cells = <0>; - - /* use default settings */ - c2c: port@0 { reg = <0>; c2cf_ep: endpoint { remote-endpoint = <&codec6_ep>; }; }; - port@1 { reg = <1>; c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; }; - }; - - /* - * [Codec2Codec-Multi] - * - * +-+ - * +-@(c2c_m)-->| |-> codec8 - * | | |-> codec9 - * | +-+ - * | +-+ - * +----------->| |-> codec10 - * | |-> codec11 - * +-+ - */ - ports@1 { - reg = <1>; - - #address-cells = <1>; - #size-cells = <0>; - - /* use original settings */ - rate = <48000>; - c2c_m: port@0 { reg = <0>; c2cmf_ep: endpoint { remote-endpoint = <&mc2c0_ep>; }; }; - port@1 { reg = <1>; c2cmb_ep: endpoint { remote-endpoint = <&mc2c1_ep>; }; }; - }; - }; - }; - - test_cpu { - /* - * update compatible to indicate more detail behaviour - * if you want. see test-compatible for more detail. - * - * ex) - * - compatible = "test-cpu"; - * + compatible = "test-cpu-verbose"; - */ - compatible = "test-cpu"; - ports { - #address-cells = <1>; - #size-cells = <0>; - - bitclock-master; - frame-master; - /* [Normal] */ - cpu0: port@0 { reg = <0>; cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; }; - - /* [Multi-CPU-0] */ - port@1 { reg = <1>; cpu1_ep: endpoint { remote-endpoint = <&mcpu01_ep>; }; }; - port@2 { reg = <2>; cpu2_ep: endpoint { remote-endpoint = <&mcpu02_ep>; }; }; - - /* [DPCM]::FE */ - port@3 { reg = <3>; cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; }; - port@4 { reg = <4>; cpu4_ep: endpoint { remote-endpoint = <&fe01_ep>; }; }; - - /* [DPCM-Multi]::FE */ - port@5 { reg = <5>; cpu5_ep: endpoint { remote-endpoint = <&fe10_ep>; }; }; - port@6 { reg = <6>; cpu6_ep: endpoint { remote-endpoint = <&fe11_ep>; }; }; - - /* [Semi-Multi] */ - sm0: port@7 { reg = <7>; cpu7_ep: endpoint { remote-endpoint = <&smcodec0_ep>; }; }; - - /* [Multi-CPU-1] */ - port@8 { reg = <8>; cpu8_ep: endpoint { remote-endpoint = <&mcpu11_ep>; }; }; - port@9 { reg = <9>; cpu9_ep: endpoint { remote-endpoint = <&mcpu12_ep>; }; }; - /* [Multi-CPU-2] */ - port@a { reg = <10>; cpu10_ep: endpoint { remote-endpoint = <&mcpu21_ep>; }; }; - port@b { reg = <11>; cpu11_ep: endpoint { remote-endpoint = <&mcpu22_ep>; }; }; - port@c { reg = <12>; cpu12_ep: endpoint { remote-endpoint = <&mcpu23_ep>; }; }; - }; - }; - - test_codec { - /* - * update compatible to indicate more detail behaviour - * if you want. see test-compatible for more detail. - * - * ex) - * - compatible = "test-codec"; - * + compatible = "test-codec-verbose"; - */ - compatible = "test-codec"; - ports { - #address-cells = <1>; - #size-cells = <0>; - - /* - * prefix can be added to *component*, - * see audio-graph-card2::routing - */ - prefix = "TC"; - - /* [Normal] */ - port@0 { reg = <0>; codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; }; - - /* [Multi-Codec-0] */ - port@1 { reg = <1>; codec1_ep: endpoint { remote-endpoint = <&mcodec01_ep>; }; }; - port@2 { reg = <2>; codec2_ep: endpoint { remote-endpoint = <&mcodec02_ep>; }; }; - - /* [DPCM]::BE */ - port@3 { - convert-rate = <44100>; - reg = <3>; codec3_ep: endpoint { remote-endpoint = <&be00_ep>; }; - }; - - /* [DPCM-Multi]::BE */ - port@4 { reg = <4>; codec4_ep: endpoint { remote-endpoint = <&mbe1_ep>; }; }; - port@5 { reg = <5>; codec5_ep: endpoint { remote-endpoint = <&mbe2_ep>; }; }; - - /* [Codec2Codec] */ - port@6 { bitclock-master; - frame-master; - reg = <6>; codec6_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; }; - port@7 { reg = <7>; codec7_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; }; - - /* [Codec2Codec-Multi] */ - port@8 { bitclock-master; - frame-master; - reg = <8>; codec8_ep: endpoint { remote-endpoint = <&mc2c00_ep>; }; }; - port@9 { reg = <9>; codec9_ep: endpoint { remote-endpoint = <&mc2c01_ep>; }; }; - port@a { reg = <10>; codec10_ep: endpoint { remote-endpoint = <&mc2c10_ep>; }; }; - port@b { reg = <11>; codec11_ep: endpoint { remote-endpoint = <&mc2c11_ep>; }; }; - - /* [Semi-Multi] */ - port@c { reg = <12>; codec12_ep: endpoint { remote-endpoint = <&smcodec1_ep>; }; }; - port@d { reg = <13>; codec13_ep: endpoint { remote-endpoint = <&smcodec2_ep>; }; }; - - /* [Multi-Codec-1] */ - port@e { reg = <14>; codec14_ep: endpoint { remote-endpoint = <&mcodec11_ep>; }; }; - port@f { reg = <15>; codec15_ep: endpoint { remote-endpoint = <&mcodec12_ep>; }; }; - port@10 { reg = <16>; codec16_ep: endpoint { remote-endpoint = <&mcodec13_ep>; }; }; - /* [Multi-Codec-2] */ - port@11 { reg = <17>; codec17_ep: endpoint { remote-endpoint = <&mcodec21_ep>; }; }; - port@12 { reg = <18>; codec18_ep: endpoint { remote-endpoint = <&mcodec22_ep>; }; }; - }; - }; -}; diff --git a/sound/soc/generic/audio-graph-card2-custom-sample1.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample1.dtsi new file mode 100644 index 000000000000..12d40e05de46 --- /dev/null +++ b/sound/soc/generic/audio-graph-card2-custom-sample1.dtsi @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * audio-graph-card2-custom-sample1.dtsi + * + * Copyright (C) 2020 Renesas Electronics Corp. + * Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This sample indicates how to use audio-graph-card2 and its + * custom driver. "audio-graph-card2-custom-sample" is the custome driver + * which is using audio-graph-card2. + * + * You can easily use this sample by adding below line on your DT file, + * and add new CONFIG to your .config. + * + * #include "../../../../../sound/soc/generic/audio-graph-card2-custom-sample1.dtsi" + * + * CONFIG_SND_AUDIO_GRAPH_CARD2 + * CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE + * CONFIG_SND_TEST_COMPONENT + * + * + * You can indicate more detail each device behavior as debug if you modify + * "compatible" on each test-component. see below + * + * test_cpu { + * - compatible = "test-cpu"; + * + compatible = "test-cpu-verbose"; + * ... + * }; + * + * test_codec { + * - compatible = "test-codec"; + * + compatible = "test-codec-verbose"; + * ... + * }; + * + * + * Below sample doesn't use "format" property, + * because test-component driver (test-cpu/test-codec) is supporting + * snd_soc_dai_ops :: .auto_selectable_formats. + * see + * snd_soc_runtime_get_dai_fmt() + * linux/sound/soc/generic/test-component.c :: test_dai_formats + */ +/ { + audio-graph-card2-custom-sample-1 { + /* + * You can use audio-graph-card2 directly by using + * + * compatible = "audio-graph-card2"; + */ + compatible = "audio-graph-card2-custom-sample"; + label = "card2-custom-sample-1"; + + /* + * @ : used at links + */ + links = < + /* + * + * [Normal] + * + * <cpu1_0> + * cpu1_0 <-@-----> codec1_0 + */ + &cpu1_0 /* CPU side only */ + + /* + * [Semi-Multi] + * + * CPU:Codec = 1:N + * + * <sm> +-+ + * cpu1_1 <--@---->| |-> codec1_1 + * | |-> codec1_2 + * +-+ + */ + &sm /* CPU side only */ + + /* + * [Multi-CPU/Codec-A] + * + * +-+ <mcpuA> +-+ + * cpu1_2 <-| |<---@------>| |-> codec1_3 + * cpu1_3 <-| | | |-> codec1_4 + * +-+ +-+ + */ + &mcpuA /* CPU side only */ + + /* + * [Multi-CPU/Codec-B] + * + * +-+ <mcpuB> +-+ + * | |<---@------>| | + * | | | | + * cpu1_4 <-| |<---------->| |-> codec1_5 + * cpu1_5 <-| |<---+------>| |-> codec1_6 + * +-+ \----->| |-> codec1_7 + * +-+ + */ + &mcpuB /* CPU side only */ + + /* + * [Multi-CPU/Codec-C] + * + * +-+ <mcpuC> +-+ + * | |<---@------>| | + * | | | | + * cpu1_6 <-| |<---------->| |-> codec1_8 + * cpu1_7 <-| |<-----+---->| |-> codec1_9 + * cpu1_8 <-| |<----/ +-+ + * +-+ + */ + &mcpuC /* CPU side only */ + >; + + multi { + #address-cells = <1>; + #size-cells = <0>; + + /* + * [Semi-Multi] + * + * <sm> +---+ + * cpu1_1 <---@--->|X A|-> codec1_1 + * | B|-> codec1_2 + * +---+ + */ + ports@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; smcodec_ep: endpoint { remote-endpoint = <&cpu1_1_ep>; };};/* (X) to pair */ + port@1 { reg = <1>; smcodec_A_ep: endpoint { remote-endpoint = <&codec1_1_ep>; };};/* (A) Multi Element */ + port@2 { reg = <2>; smcodec_B_ep: endpoint { remote-endpoint = <&codec1_2_ep>; };};/* (B) Multi Element */ + }; + + /* + * [Multi-CPU-A] + * + * +---+ <mcpuA> +---+ + * cpu1_2 <-|A X|<---@---->|x a|-> codec1_3 + * cpu1_3 <-|B | | b|-> codec1_4 + * +---+ +---+ + */ + ports@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + mcpuA: port@0 { reg = <0>; mcpu_A_ep: endpoint { remote-endpoint = <&mcodec_A_ep>; };}; /* (X) to pair */ + port@1 { reg = <1>; mcpu_AA_ep: endpoint { remote-endpoint = <&cpu1_2_ep>; };}; /* (A) Multi Element */ + port@2 { reg = <2>; mcpu_AB_ep: endpoint { remote-endpoint = <&cpu1_3_ep>; };}; /* (B) Multi Element */ + }; + + /* + * [Multi-Codec-A] + * + * +---+ <mcpuA> +---+ + * cpu1_2 <-|A X|<-@------>|x a|-> codec1_3 + * cpu1_3 <-|B | | b|-> codec1_4 + * +---+ +---+ + */ + ports@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mcodec_A_ep: endpoint { remote-endpoint = <&mcpu_A_ep>; };}; /* (x) to pair */ + port@1 { reg = <1>; mcodec_Aa_ep: endpoint { remote-endpoint = <&codec1_3_ep>; };}; /* (a) Multi Element */ + port@2 { reg = <2>; mcodec_Ab_ep: endpoint { remote-endpoint = <&codec1_4_ep>; };}; /* (b) Multi Element */ + }; + + /* + * [Multi-CPU-B] + * + * +---+ <mcpuB> +---+ + * | X|<---@---->|x | + * | | | | + * cpu1_4 <-|A 1|<-------->|3 a|-> codec1_5 + * cpu1_5 <-|B 2|<---+---->|4 b|-> codec1_6 + * +---+ \--->|5 c|-> codec1_7 + * +---+ + */ + ports@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + mcpuB: port@0 { + reg = <0>; + mcpu_BX_ep: endpoint { remote-endpoint = <&mcodec_Bx_ep>; }; /* (X) to pair */ + }; + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + mcpu_BA_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu1_4_ep>; }; /* (A) Multi Element */ + mcpu_B1_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec_B3_ep>; }; /* (1) connected Codec */ + }; + port@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + mcpu_BB_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu1_5_ep>; }; /* (B) Multi Element */ + mcpu_B2_0_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec_B4_ep>; }; /* (2) connected Codec */ + mcpu_B2_1_ep: endpoint@2 { reg = <2>; remote-endpoint = <&mcodec_B5_ep>; }; /* (2) connected Codec */ + }; + }; + + /* + * [Multi-Codec-B] + * + * +---+ <mcpuB> +---+ + * | X|<-@------>|x | + * | | | | + * cpu1_4 <-|A 1|<-------->|3 a|-> codec1_5 + * cpu1_5 <-|B 2|<---+---->|4 b|-> codec1_6 + * +---+ \--->|5 c|-> codec1_7 + * +---+ + */ + ports@4 { + reg = <4>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + mcodec_Bx_ep: endpoint { remote-endpoint = <&mcpu_BX_ep>; }; /* (x) to pair */ + }; + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + mcodec_Ba_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec1_5_ep>;}; /* (a) Multi Element */ + mcodec_B3_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu_B1_ep>; }; /* (3) connected CPU */ + }; + port@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + mcodec_Bb_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec1_6_ep>; }; /* (b) Multi Element */ + mcodec_B4_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu_B2_0_ep>;}; /* (4) connected CPU */ + }; + port@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + mcodec_Bc_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec1_7_ep>; }; /* (c) Multi Element */ + mcodec_B5_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu_B2_1_ep>;}; /* (5) connected CPU */ + }; + }; + + /* + * [Multi-CPU-C] + * + * +---+ <mcpuC> +---+ + * | X|<-@------>|x | + * | | | | + * cpu1_6 <-|A 1|<-------->|4 a|-> codec1_8 + * cpu1_7 <-|B 2|<-----+-->|5 b|-> codec1_9 + * cpu1_8 <-|C 3|<----/ +---+ + * +---+ + */ + ports@5 { + reg = <5>; + #address-cells = <1>; + #size-cells = <0>; + mcpuC: port@0 { + reg = <0>; + mcpu_CX_ep: endpoint { remote-endpoint = <&mcodec_Cx_ep>; }; /* (X) to pair */ + }; + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + mcpu_CA_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu1_6_ep>; }; /* (A) Multi Element */ + mcpu_C1_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec_C4_ep>; }; /* (1) connected Codec */ + }; + port@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + mcpu_CB_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu1_7_ep>; }; /* (B) Multi Element */ + mcpu_C2_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec_C5_0_ep>; }; /* (2) connected Codec */ + }; + port@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + mcpu_CC_ep: endpoint@0 { reg = <0>; remote-endpoint = <&cpu1_8_ep>; }; /* (C) Multi Element */ + mcpu_C3_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcodec_C5_1_ep>; }; /* (3) connected Codec */ + }; + }; + + /* + * [Multi-Codec-C] + * + * +---+ <mcpuC> +---+ + * | X|<-@------>|x | + * | | | | + * cpu1_6 <-|A 1|<-------->|4 a|-> codec1_8 + * cpu1_7 <-|B 2|<-----+-->|5 b|-> codec1_9 + * cpu1_8 <-|C 3|<----/ +---+ + * +---+ + */ + ports@6 { + reg = <6>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + mcodec_Cx_ep: endpoint { remote-endpoint = <&mcpu_CX_ep>; }; /* (x) to pair */ + }; + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + mcodec_Ca_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec1_8_ep>;}; /* (a) Multi Element */ + mcodec_C4_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu_C1_ep>; }; /* (4) connected CPU */ + }; + port@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + mcodec_Cb_ep: endpoint@0 { reg = <0>; remote-endpoint = <&codec1_9_ep>;}; /* (b) Multi Element */ + mcodec_C5_0_ep: endpoint@1 { reg = <1>; remote-endpoint = <&mcpu_C2_ep>; }; /* (5) connected CPU */ + mcodec_C5_1_ep: endpoint@2 { reg = <2>; remote-endpoint = <&mcpu_C3_ep>; }; /* (5) connected CPU */ + }; + }; + }; + }; + + test_cpu_1 { + /* + * update compatible to indicate more detail behaviour + * if you want. see test-compatible for more detail. + * + * ex) + * - compatible = "test-cpu"; + * + compatible = "test-cpu-verbose"; + */ + compatible = "test-cpu"; + ports { + #address-cells = <1>; + #size-cells = <0>; + + bitclock-master; + frame-master; + + /* [Normal] */ + cpu1_0: port@0 { reg = <0>; cpu1_0_ep: endpoint { remote-endpoint = <&codec1_0_ep>;}; }; + /* [Semi-Multi] */ + sm: port@1 { reg = <1>; cpu1_1_ep: endpoint { remote-endpoint = <&smcodec_ep>; }; }; + /* [Multi-CPU-A] */ + port@2 { reg = <2>; cpu1_2_ep: endpoint { remote-endpoint = <&mcpu_AA_ep>; }; }; + port@3 { reg = <3>; cpu1_3_ep: endpoint { remote-endpoint = <&mcpu_AB_ep>; }; }; + /* [Multi-CPU-B] */ + port@4 { reg = <4>; cpu1_4_ep: endpoint { remote-endpoint = <&mcpu_BA_ep>; }; }; + port@5 { reg = <5>; cpu1_5_ep: endpoint { remote-endpoint = <&mcpu_BB_ep>; }; }; + /* [Multi-CPU-C] */ + port@6 { reg = <6>; cpu1_6_ep: endpoint { remote-endpoint = <&mcpu_CA_ep>; }; }; + port@7 { reg = <7>; cpu1_7_ep: endpoint { remote-endpoint = <&mcpu_CB_ep>; }; }; + port@8 { reg = <8>; cpu1_8_ep: endpoint { remote-endpoint = <&mcpu_CC_ep>; }; }; + }; + }; + + test_codec_1 { + /* + * update compatible to indicate more detail behaviour + * if you want. see test-compatible for more detail. + * + * ex) + * - compatible = "test-codec"; + * + compatible = "test-codec-verbose"; + */ + compatible = "test-codec"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + /* [Normal] */ + port@0 { reg = <0>; codec1_0_ep: endpoint { remote-endpoint = <&cpu1_0_ep>; }; }; + /* [Semi-Multi] */ + port@1 { reg = <1>; codec1_1_ep: endpoint { remote-endpoint = <&smcodec_A_ep>; }; }; + port@2 { reg = <2>; codec1_2_ep: endpoint { remote-endpoint = <&smcodec_B_ep>; }; }; + /* [Multi-Codec-0] */ + port@3 { reg = <3>; codec1_3_ep: endpoint { remote-endpoint = <&mcodec_Aa_ep>; }; }; + port@4 { reg = <4>; codec1_4_ep: endpoint { remote-endpoint = <&mcodec_Ab_ep>; }; }; + /* [Multi-Codec-1] */ + port@5 { reg = <5>; codec1_5_ep: endpoint { remote-endpoint = <&mcodec_Ba_ep>; }; }; + port@6 { reg = <6>; codec1_6_ep: endpoint { remote-endpoint = <&mcodec_Bb_ep>; }; }; + port@7 { reg = <7>; codec1_7_ep: endpoint { remote-endpoint = <&mcodec_Bc_ep>; }; }; + /* [Multi-Codec-2] */ + port@8 { reg = <8>; codec1_8_ep: endpoint { remote-endpoint = <&mcodec_Ca_ep>; }; }; + port@9 { reg = <9>; codec1_9_ep: endpoint { remote-endpoint = <&mcodec_Cb_ep>; }; }; + }; + }; +}; diff --git a/sound/soc/generic/audio-graph-card2-custom-sample2.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample2.dtsi new file mode 100644 index 000000000000..1fb061a10ab1 --- /dev/null +++ b/sound/soc/generic/audio-graph-card2-custom-sample2.dtsi @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * audio-graph-card2-custom-sample2.dtsi + * + * Copyright (C) 2020 Renesas Electronics Corp. + * Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This sample indicates how to use audio-graph-card2 and its + * custom driver. "audio-graph-card2-custom-sample" is the custome driver + * which is using audio-graph-card2. + * + * You can easily use this sample by adding below line on your DT file, + * and add new CONFIG to your .config. + * + * #include "../../../../../sound/soc/generic/audio-graph-card2-custom-sample2.dtsi" + * + * CONFIG_SND_AUDIO_GRAPH_CARD2 + * CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE + * CONFIG_SND_TEST_COMPONENT + * + * + * You can indicate more detail each device behavior as debug if you modify + * "compatible" on each test-component. see below + * + * test_cpu { + * - compatible = "test-cpu"; + * + compatible = "test-cpu-verbose"; + * ... + * }; + * + * test_codec { + * - compatible = "test-codec"; + * + compatible = "test-codec-verbose"; + * ... + * }; + * + * + * Below sample doesn't use "format" property, + * because test-component driver (test-cpu/test-codec) is supporting + * snd_soc_dai_ops :: .auto_selectable_formats. + * see + * snd_soc_runtime_get_dai_fmt() + * linux/sound/soc/generic/test-component.c :: test_dai_formats + */ +/ { + audio-graph-card2-custom-sample-2 { + /* + * You can use audio-graph-card2 directly by using + * + * compatible = "audio-graph-card2"; + */ + compatible = "audio-graph-card2-custom-sample"; + label = "card2-custom-sample-2"; + + /* for [DPCM] */ + /* BE FE */ + routing = "TC DAI0 Playback", "DAI0 Playback", + "TC DAI0 Playback", "DAI1 Playback", + "DAI0 Capture", "TC DAI0 Capture", + "DAI1 Capture", "TC DAI0 Capture", + /* for [DPCM-Multi] */ + /* BE FE */ + "TC DAI1 Playback", "DAI2 Playback", + "TC DAI2 Playback", "DAI2 Playback", + "TC DAI1 Playback", "DAI3 Playback", + "TC DAI2 Playback", "DAI3 Playback", + "DAI2 Capture", "TC DAI1 Capture", + "DAI2 Capture", "TC DAI2 Capture", + "DAI3 Capture", "TC DAI1 Capture", + "DAI3 Capture", "TC DAI2 Capture", + /* for [Codec2Codec] */ + "TC OUT", "TC DAI4 Playback", + "TC DAI3 Capture", "TC IN", + /* for [Codec2Codec-Multi] */ + "TC OUT", "TC DAI7 Playback", + "TC DAI5 Capture", "TC IN", + "TC OUT", "TC DAI8 Playback", + "TC DAI6 Capture", "TC IN"; + + /* + * @ : used at links + */ + links = < + /* + * [DPCM] + * + * cpu20/cpu21 are converting rate to 44.1kHz + * + * FE BE + * <feA> **** <beA> + * cpu2_0 <----@---* *------@---> codec2_0 (44.1kHz) + * cpu2_1 <----@---* * + * <feB> **** + */ + &feA &feB &beA /* both FE / BE */ + + /* + * [DPCM-Multi] + * + * FE BE + * <feC> **** <beB> +-+ + * cpu2_2 <----@---* *------@---> | | -> codec2_1 + * cpu2_3 <----@---* * | | -> codec2_2 + * <feD> **** +-+ + */ + &feC &feD &beB /* both FE / BE*/ + + /* + * [Codec2Codec] + * + * <c2c> + * +-@-> codec2_3 + * | + * +---> codec2_4 + */ + &c2c /* CPU side only */ + + /* + * [Codec2Codec-Multi] + * + * --NOTE-- + * Multi connect N:M is not supported by ASoC. + * + * <c2c_m> +-+ + * +---@-->| |-> codec2_5 + * | | |-> codec2_6 + * | +-+ + * | +-+ + * +------>| |-> codec2_7 + * | |-> codec2_8 + * +-+ + */ + &c2c_m /* CPU side only */ + >; + + multi { + #address-cells = <1>; + #size-cells = <0>; + + /* + * [DPCM-Multi]::BE + * + * FE BE + * <feC> **** <beB> +---+ + * cpu2_2 <----@---* *------@---> |x a| -> codec2_1 + * cpu2_3 <----@---* * | b| -> codec2_2 + * <feD> **** +---+ + */ + ports@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mbe_x_ep: endpoint { remote-endpoint = <&beB_ep>; };};/* (x) to pair */ + port@1 { reg = <1>; mbe_a_ep: endpoint { remote-endpoint = <&codec2_1_ep>; };};/* (a) Multi Element */ + port@2 { reg = <2>; mbe_b_ep: endpoint { remote-endpoint = <&codec2_2_ep>; };};/* (b) Multi Element */ + }; + + /* + * [Codec2Codec-Multi]::CPU + * + * <c2c_m> c2cmf +---+ + * +---@---------->|X A|-> codec2_5 + * | | B|-> codec2_6 + * | +---+ + * | c2cmb +---+ + * +-------------->|x a|-> codec2_7 + * | b|-> codec2_8 + * +---+ + */ + ports@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mc2c0X_ep: endpoint { remote-endpoint = <&c2cmf_ep>; };};/* (X) to pair */ + port@1 { reg = <1>; mc2c0A_ep: endpoint { remote-endpoint = <&codec2_5_ep>; };};/* (A) Multi Element */ + port@2 { reg = <2>; mc2c0B_ep: endpoint { remote-endpoint = <&codec2_6_ep>; };};/* (B) Multi Element */ + }; + + /* + * [Codec2Codec-Multi]::Codec + * + * <c2c_m> c2cmf +---+ + * +---@---------->|X A|-> codec2_5 + * | | B|-> codec2_6 + * | +---+ + * | c2cmb +---+ + * +-------------->|x a|-> codec2_7 + * | b|-> codec2_8 + * +---+ + */ + ports@4 { + reg = <4>; + #address-cells = <1>; + #size-cells = <0>; + port@0 { reg = <0>; mc2c1x_ep: endpoint { remote-endpoint = <&c2cmb_ep>; };};/* (x) to pair */ + port@1 { reg = <1>; mc2c1a_ep: endpoint { remote-endpoint = <&codec2_7_ep>; };};/* (a) Multi Element */ + port@2 { reg = <2>; mc2c1b_ep: endpoint { remote-endpoint = <&codec2_8_ep>; };};/* (b) Multi Element */ + }; + }; + + dpcm { + #address-cells = <1>; + #size-cells = <0>; + + /* FE part */ + ports@0 { + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + /* + * [DPCM]::FE + * + * FE BE + * <feA> **** <beA> + * cpu2_0 <----@---* *------@---> codec2_0 (44.1kHz) + * cpu2_1 <----@---* * + * <feB> **** + */ + feA: port@0 { reg = <0>; feA_ep: endpoint { remote-endpoint = <&cpu2_0_ep>; }; }; + feB: port@1 { reg = <1>; feB_ep: endpoint { remote-endpoint = <&cpu2_1_ep>; }; }; + + /* + * [DPCM-Multi]::FE + * + * FE BE + * <feC> **** <beB> +-+ + * cpu2_2 <----@---* *------@---> | | -> codec2_1 + * cpu2_3 <----@---* * | | -> codec2_2 + * <feD> **** +-+ + */ + feC: port@2 { reg = <2>; feC_ep: endpoint { remote-endpoint = <&cpu2_2_ep>; }; }; + feD: port@3 { reg = <3>; feD_ep: endpoint { remote-endpoint = <&cpu2_3_ep>; }; }; + }; + + /* BE part */ + ports@1 { + reg = <1>; + + #address-cells = <1>; + #size-cells = <0>; + /* + * [DPCM]::BE + * + * FE BE + * <feA> **** <beA> + * cpu2_0 <----@---* *------@---> codec2_0 (44.1kHz) + * cpu2_1 <----@---* * + * <feB> **** + */ + beA: port@0 { reg = <0>; beA_ep: endpoint { remote-endpoint = <&codec2_0_ep>; }; }; + + /* + * [DPCM-Multi]::BE + * + * FE BE + * <feC> **** <beB> +-------+ + * cpu2_2 <----@---* *------@---> |mbe_x | -> codec2_1 + * cpu2_3 <----@---* * | | -> codec2_2 + * <feD> **** +-------+ + */ + beB: port@1 { reg = <1>; beB_ep: endpoint { remote-endpoint = <&mbe_x_ep>; }; }; + }; + }; + + codec2codec { + #address-cells = <1>; + #size-cells = <0>; + /* + * [Codec2Codec] + * + * <c2c> + * +-@--> codec2_3 + * | + * +----> codec2_4 + */ + ports@0 { + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + /* use default settings */ + c2c: port@0 { reg = <0>; c2cf_ep: endpoint { remote-endpoint = <&codec2_3_ep>; }; }; + port@1 { reg = <1>; c2cb_ep: endpoint { remote-endpoint = <&codec2_4_ep>; }; }; + }; + + /* + * [Codec2Codec-Multi] + * + * <c2c_m> c2cmf +--------+ + * +---@---------->|mc2c0X |-> codec2_5 + * | | |-> codec2_6 + * | +--------+ + * | c2cmb +--------+ + * +-------------->|mc2c1x |-> codec2_7 + * | |-> codec2_8 + * +--------+ + */ + ports@1 { + reg = <1>; + + #address-cells = <1>; + #size-cells = <0>; + + /* use original settings */ + rate = <48000>; + c2c_m: port@0 { reg = <0>; c2cmf_ep: endpoint { remote-endpoint = <&mc2c0X_ep>; }; }; + port@1 { reg = <1>; c2cmb_ep: endpoint { remote-endpoint = <&mc2c1x_ep>; }; }; + }; + }; + }; + + test_cpu_2 { + /* + * update compatible to indicate more detail behaviour + * if you want. see test-compatible for more detail. + * + * ex) + * - compatible = "test-cpu"; + * + compatible = "test-cpu-verbose"; + */ + compatible = "test-cpu"; + ports { + #address-cells = <1>; + #size-cells = <0>; + + bitclock-master; + frame-master; + + /* [DPCM]::FE */ + port@0 { reg = <0>; cpu2_0_ep: endpoint { remote-endpoint = <&feA_ep>; };}; + port@1 { reg = <1>; cpu2_1_ep: endpoint { remote-endpoint = <&feB_ep>; };}; + /* [DPCM-Multi]::FE */ + port@2 { reg = <2>; cpu2_2_ep: endpoint { remote-endpoint = <&feC_ep>; };}; + port@3 { reg = <3>; cpu2_3_ep: endpoint { remote-endpoint = <&feD_ep>; };}; + }; + }; + + test_codec_2 { + /* + * update compatible to indicate more detail behaviour + * if you want. see test-compatible for more detail. + * + * ex) + * - compatible = "test-codec"; + * + compatible = "test-codec-verbose"; + */ + compatible = "test-codec"; + ports { + #address-cells = <1>; + #size-cells = <0>; + + /* + * prefix can be added to *component*, + * see audio-graph-card2::routing + */ + prefix = "TC"; + + /* [DPCM]::BE */ + port@0 { + convert-rate = <44100>; + reg = <0>; codec2_0_ep: endpoint { remote-endpoint = <&beA_ep>; }; + }; + /* [DPCM-Multi]::BE */ + port@1 { reg = <1>; codec2_1_ep: endpoint { remote-endpoint = <&mbe_a_ep>; };}; + port@2 { reg = <2>; codec2_2_ep: endpoint { remote-endpoint = <&mbe_b_ep>; };}; + /* [Codec2Codec] */ + port@3 { bitclock-master; + frame-master; + reg = <3>; codec2_3_ep: endpoint { remote-endpoint = <&c2cf_ep>; };}; + port@4 { reg = <4>; codec2_4_ep: endpoint { remote-endpoint = <&c2cb_ep>; };}; + /* [Codec2Codec-Multi] */ + port@5 { bitclock-master; + frame-master; + reg = <5>; codec2_5_ep: endpoint { remote-endpoint = <&mc2c0A_ep>; };}; + port@6 { reg = <6>; codec2_6_ep: endpoint { remote-endpoint = <&mc2c0B_ep>; };}; + port@7 { reg = <7>; codec2_7_ep: endpoint { remote-endpoint = <&mc2c1a_ep>; };}; + port@8 { reg = <8>; codec2_8_ep: endpoint { remote-endpoint = <&mc2c1b_ep>; };}; + }; + }; +}; diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 1f5c4e8ff1b9..5dcc78c551a2 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -234,6 +234,13 @@ enum graph_type { #define GRAPH_NODENAME_DPCM "dpcm" #define GRAPH_NODENAME_C2C "codec2codec" +#define graph_ret(priv, ret) _graph_ret(priv, __func__, ret) +static inline int _graph_ret(struct simple_util_priv *priv, + const char *func, int ret) +{ + return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func); +} + #define ep_to_port(ep) of_get_parent(ep) static struct device_node *port_to_ports(struct device_node *port) { @@ -331,10 +338,9 @@ static int graph_lnk_is_multi(struct device_node *lnk) return __graph_get_type(lnk) == GRAPH_MULTI; } -static struct device_node *graph_get_next_multi_ep(struct device_node **port) +static struct device_node *graph_get_next_multi_ep(struct device_node **port, int idx) { - struct device_node *ports = port_to_ports(*port); - struct device_node *ep = NULL; + struct device_node *ports __free(device_node) = port_to_ports(*port); struct device_node *rep = NULL; /* @@ -352,15 +358,22 @@ static struct device_node *graph_get_next_multi_ep(struct device_node **port) * port@1 { rep1 }; * }; */ - *port = of_graph_get_next_port(ports, *port); + + /* + * Don't use of_graph_get_next_port() here + * + * In overlay case, "port" are not necessarily in order. So we need to use + * of_graph_get_port_by_id() instead + */ + of_node_put(*port); + + *port = of_graph_get_port_by_id(ports, idx); if (*port) { - ep = of_graph_get_next_port_endpoint(*port, NULL); + struct device_node *ep __free(device_node) = of_graph_get_next_port_endpoint(*port, NULL); + rep = of_graph_get_remote_endpoint(ep); } - of_node_put(ep); - of_node_put(ports); - return rep; } @@ -373,16 +386,13 @@ static const struct snd_soc_ops graph_ops = { static void graph_parse_convert(struct device_node *ep, struct simple_dai_props *props) { - struct device_node *port = ep_to_port(ep); - struct device_node *ports = port_to_ports(port); + struct device_node *port __free(device_node) = ep_to_port(ep); + struct device_node *ports __free(device_node) = port_to_ports(port); struct simple_util_data *adata = &props->adata; simple_util_parse_convert(ports, NULL, adata); simple_util_parse_convert(port, NULL, adata); simple_util_parse_convert(ep, NULL, adata); - - of_node_put(port); - of_node_put(ports); } static int __graph_parse_node(struct simple_util_priv *priv, @@ -406,21 +416,21 @@ static int __graph_parse_node(struct simple_util_priv *priv, dai = simple_props_to_dai_codec(dai_props, idx); } - ret = graph_util_parse_dai(dev, ep, dlc, &is_single_links); + ret = graph_util_parse_dai(priv, ep, dlc, &is_single_links); if (ret < 0) - return ret; + goto end; ret = simple_util_parse_tdm(ep, dai); if (ret < 0) - return ret; + goto end; - ret = simple_util_parse_tdm_width_map(dev, ep, dai); + ret = simple_util_parse_tdm_width_map(priv, ep, dai); if (ret < 0) - return ret; + goto end; ret = simple_util_parse_clk(dev, ep, dai, dlc); if (ret < 0) - return ret; + goto end; /* * set DAI Name @@ -440,22 +450,22 @@ static int __graph_parse_node(struct simple_util_priv *priv, case GRAPH_NORMAL: /* run is_cpu only. see audio_graph2_link_normal() */ if (is_cpu) - simple_util_set_dailink_name(dev, dai_link, "%s%s-%s%s", + simple_util_set_dailink_name(priv, dai_link, "%s%s-%s%s", cpus->dai_name, cpu_multi, codecs->dai_name, codec_multi); break; case GRAPH_DPCM: if (is_cpu) - simple_util_set_dailink_name(dev, dai_link, "fe.%pOFP.%s%s", + simple_util_set_dailink_name(priv, dai_link, "fe.%pOFP.%s%s", cpus->of_node, cpus->dai_name, cpu_multi); else - simple_util_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s", + simple_util_set_dailink_name(priv, dai_link, "be.%pOFP.%s%s", codecs->of_node, codecs->dai_name, codec_multi); break; case GRAPH_C2C: /* run is_cpu only. see audio_graph2_link_c2c() */ if (is_cpu) - simple_util_set_dailink_name(dev, dai_link, "c2c.%s%s-%s%s", + simple_util_set_dailink_name(priv, dai_link, "c2c.%s%s-%s%s", cpus->dai_name, cpu_multi, codecs->dai_name, codec_multi); break; @@ -471,14 +481,11 @@ static int __graph_parse_node(struct simple_util_priv *priv, if (!is_cpu && gtype == GRAPH_DPCM) { struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, idx); struct snd_soc_codec_conf *cconf = simple_props_to_codec_conf(dai_props, idx); - struct device_node *rport = ep_to_port(ep); - struct device_node *rports = port_to_ports(rport); + struct device_node *rport __free(device_node) = ep_to_port(ep); + struct device_node *rports __free(device_node) = port_to_ports(rport); snd_soc_of_parse_node_prefix(rports, cconf, codecs->of_node, "prefix"); snd_soc_of_parse_node_prefix(rport, cconf, codecs->of_node, "prefix"); - - of_node_put(rport); - of_node_put(rports); } if (is_cpu) { @@ -488,11 +495,12 @@ static int __graph_parse_node(struct simple_util_priv *priv, simple_util_canonicalize_cpu(cpus, is_single_links); simple_util_canonicalize_platform(platforms, cpus); } - - return 0; +end: + return graph_ret(priv, ret); } -static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link, +static int graph_parse_node_multi_nm(struct simple_util_priv *priv, + struct snd_soc_dai_link *dai_link, int *nm_idx, int cpu_idx, struct device_node *mcpu_port) { @@ -526,25 +534,21 @@ static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link, * }; * }; */ - struct device_node *mcpu_ep = of_graph_get_next_port_endpoint(mcpu_port, NULL); - struct device_node *mcpu_ports = port_to_ports(mcpu_port); - struct device_node *mcpu_port_top = of_graph_get_next_port(mcpu_ports, NULL); - struct device_node *mcpu_ep_top = of_graph_get_next_port_endpoint(mcpu_port_top, NULL); - struct device_node *mcodec_ep_top = of_graph_get_remote_endpoint(mcpu_ep_top); - struct device_node *mcodec_port_top = ep_to_port(mcodec_ep_top); - struct device_node *mcodec_ports = port_to_ports(mcodec_port_top); + struct device_node *mcpu_ep __free(device_node) = of_graph_get_next_port_endpoint(mcpu_port, NULL); + struct device_node *mcpu_ports __free(device_node) = port_to_ports(mcpu_port); + struct device_node *mcpu_port_top __free(device_node) = of_graph_get_next_port(mcpu_ports, NULL); + struct device_node *mcpu_ep_top __free(device_node) = of_graph_get_next_port_endpoint(mcpu_port_top, NULL); + struct device_node *mcodec_ep_top __free(device_node) = of_graph_get_remote_endpoint(mcpu_ep_top); + struct device_node *mcodec_port_top __free(device_node) = ep_to_port(mcodec_ep_top); + struct device_node *mcodec_ports __free(device_node) = port_to_ports(mcodec_port_top); int nm_max = max(dai_link->num_cpus, dai_link->num_codecs); - int ret = 0; + int ret = -EINVAL; - if (cpu_idx > dai_link->num_cpus) { - ret = -EINVAL; - goto mcpu_err; - } + if (cpu_idx > dai_link->num_cpus) + goto end; for_each_of_graph_port_endpoint(mcpu_port, mcpu_ep_n) { - struct device_node *mcodec_ep_n; - struct device_node *mcodec_port; - int codec_idx; + int codec_idx = 0; /* ignore 1st ep which is for element */ if (mcpu_ep_n == mcpu_ep) @@ -553,16 +557,13 @@ static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link, if (*nm_idx > nm_max) break; - mcodec_ep_n = of_graph_get_remote_endpoint(mcpu_ep_n); - mcodec_port = ep_to_port(mcodec_ep_n); + struct device_node *mcodec_ep_n __free(device_node) = of_graph_get_remote_endpoint(mcpu_ep_n); + struct device_node *mcodec_port __free(device_node) = ep_to_port(mcodec_ep_n); - if (mcodec_ports != port_to_ports(mcodec_port)) { - ret = -EINVAL; - goto mcpu_err; - } - - codec_idx = 0; ret = -EINVAL; + if (mcodec_ports != port_to_ports(mcodec_port)) + break; + for_each_of_graph_port(mcodec_ports, mcodec_port_i) { /* ignore 1st port which is for pair connection */ @@ -582,20 +583,11 @@ static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link, } codec_idx++; } - of_node_put(mcodec_port); - of_node_put(mcodec_ep_n); if (ret < 0) break; } -mcpu_err: - of_node_put(mcpu_ep); - of_node_put(mcpu_port_top); - of_node_put(mcpu_ep_top); - of_node_put(mcodec_ep_top); - of_node_put(mcodec_port_top); - of_node_put(mcodec_ports); - - return ret; +end: + return graph_ret(priv, ret); } static int graph_parse_node_multi(struct simple_util_priv *priv, @@ -605,7 +597,6 @@ static int graph_parse_node_multi(struct simple_util_priv *priv, { struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct device *dev = simple_priv_to_dev(priv); - struct device_node *ep; int ret = -ENOMEM; int nm_idx = 0; int nm_max = max(dai_link->num_cpus, dai_link->num_codecs); @@ -640,18 +631,17 @@ static int graph_parse_node_multi(struct simple_util_priv *priv, * }; * }; */ - ep = graph_get_next_multi_ep(&port); + struct device_node *ep __free(device_node) = graph_get_next_multi_ep(&port, idx + 1); if (!ep) break; ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, idx); - of_node_put(ep); if (ret < 0) goto multi_err; /* CPU:Codec = N:M */ if (is_cpu && dai_link->ch_maps) { - ret = graph_parse_node_multi_nm(dai_link, &nm_idx, idx, port); + ret = graph_parse_node_multi_nm(priv, dai_link, &nm_idx, idx, port); if (ret < 0) goto multi_err; } @@ -661,35 +651,34 @@ static int graph_parse_node_multi(struct simple_util_priv *priv, ret = -EINVAL; multi_err: - return ret; + return graph_ret(priv, ret); } static int graph_parse_node_single(struct simple_util_priv *priv, enum graph_type gtype, - struct device_node *port, + struct device_node *ep, struct link_info *li, int is_cpu) { - struct device_node *ep = of_graph_get_next_port_endpoint(port, NULL); - int ret = __graph_parse_node(priv, gtype, ep, li, is_cpu, 0); - - of_node_put(ep); - - return ret; + return graph_ret(priv, __graph_parse_node(priv, gtype, ep, li, is_cpu, 0)); } static int graph_parse_node(struct simple_util_priv *priv, enum graph_type gtype, - struct device_node *port, + struct device_node *ep, struct link_info *li, int is_cpu) { + struct device_node *port __free(device_node) = ep_to_port(ep); + int ret; + if (graph_lnk_is_multi(port)) - return graph_parse_node_multi(priv, gtype, port, li, is_cpu); + ret = graph_parse_node_multi(priv, gtype, port, li, is_cpu); else - return graph_parse_node_single(priv, gtype, port, li, is_cpu); + ret = graph_parse_node_single(priv, gtype, ep, li, is_cpu); + + return graph_ret(priv, ret); } -static void graph_parse_daifmt(struct device_node *node, - unsigned int *daifmt, unsigned int *bit_frame) +static void graph_parse_daifmt(struct device_node *node, unsigned int *daifmt) { unsigned int fmt; @@ -714,16 +703,6 @@ static void graph_parse_daifmt(struct device_node *node, * }; */ - /* - * clock_provider: - * - * It can be judged it is provider - * if (A) or (B) or (C) has bitclock-master / frame-master flag. - * - * use "or" - */ - *bit_frame |= snd_soc_daifmt_parse_clock_provider_as_bitmap(node, NULL); - #define update_daifmt(name) \ if (!(*daifmt & SND_SOC_DAIFMT_##name##_MASK) && \ (fmt & SND_SOC_DAIFMT_##name##_MASK)) \ @@ -741,51 +720,64 @@ static void graph_parse_daifmt(struct device_node *node, update_daifmt(INV); } +static unsigned int graph_parse_bitframe(struct device_node *ep) +{ + struct device_node *port __free(device_node) = ep_to_port(ep); + struct device_node *ports __free(device_node) = port_to_ports(port); + + return snd_soc_daifmt_clock_provider_from_bitmap( + snd_soc_daifmt_parse_clock_provider_as_bitmap(ep, NULL) | + snd_soc_daifmt_parse_clock_provider_as_bitmap(port, NULL) | + snd_soc_daifmt_parse_clock_provider_as_bitmap(ports, NULL)); +} + static void graph_link_init(struct simple_util_priv *priv, struct device_node *lnk, - struct device_node *port_cpu, - struct device_node *port_codec, + struct device_node *ep_cpu, + struct device_node *ep_codec, struct link_info *li, int is_cpu_node) { struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); - struct device_node *ep_cpu, *ep_codec; - struct device_node *ports_cpu, *ports_codec; - unsigned int daifmt = 0, daiclk = 0; + struct device_node *port_cpu = ep_to_port(ep_cpu); + struct device_node *port_codec = ep_to_port(ep_codec); + struct device_node *multi_cpu_port = NULL, *multi_codec_port = NULL; + struct snd_soc_dai_link_component *dlc; + unsigned int daifmt = 0; bool playback_only = 0, capture_only = 0; enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT; enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT; - unsigned int bit_frame = 0; + int multi_cpu_port_idx = 1, multi_codec_port_idx = 1; + int i; - of_node_get(port_cpu); if (graph_lnk_is_multi(port_cpu)) { - ep_cpu = graph_get_next_multi_ep(&port_cpu); + multi_cpu_port = port_cpu; + ep_cpu = graph_get_next_multi_ep(&multi_cpu_port, multi_cpu_port_idx++); of_node_put(port_cpu); port_cpu = ep_to_port(ep_cpu); } else { - ep_cpu = of_graph_get_next_port_endpoint(port_cpu, NULL); + of_node_get(ep_cpu); } - ports_cpu = port_to_ports(port_cpu); + struct device_node *ports_cpu __free(device_node) = port_to_ports(port_cpu); - of_node_get(port_codec); if (graph_lnk_is_multi(port_codec)) { - ep_codec = graph_get_next_multi_ep(&port_codec); + multi_codec_port = port_codec; + ep_codec = graph_get_next_multi_ep(&multi_codec_port, multi_codec_port_idx++); of_node_put(port_codec); port_codec = ep_to_port(ep_codec); } else { - ep_codec = of_graph_get_next_port_endpoint(port_codec, NULL); + of_node_get(ep_codec); } - ports_codec = port_to_ports(port_codec); - + struct device_node *ports_codec __free(device_node) = port_to_ports(port_codec); - graph_parse_daifmt(ep_cpu, &daifmt, &bit_frame); - graph_parse_daifmt(ep_codec, &daifmt, &bit_frame); - graph_parse_daifmt(port_cpu, &daifmt, &bit_frame); - graph_parse_daifmt(port_codec, &daifmt, &bit_frame); - graph_parse_daifmt(ports_cpu, &daifmt, &bit_frame); - graph_parse_daifmt(ports_codec, &daifmt, &bit_frame); - graph_parse_daifmt(lnk, &daifmt, &bit_frame); + graph_parse_daifmt(ep_cpu, &daifmt); + graph_parse_daifmt(ep_codec, &daifmt); + graph_parse_daifmt(port_cpu, &daifmt); + graph_parse_daifmt(port_codec, &daifmt); + graph_parse_daifmt(ports_cpu, &daifmt); + graph_parse_daifmt(ports_codec, &daifmt); + graph_parse_daifmt(lnk, &daifmt); graph_util_parse_link_direction(lnk, &playback_only, &capture_only); graph_util_parse_link_direction(ports_cpu, &playback_only, &capture_only); @@ -811,14 +803,21 @@ static void graph_link_init(struct simple_util_priv *priv, graph_util_parse_trigger_order(priv, ep_cpu, &trigger_start, &trigger_stop); graph_util_parse_trigger_order(priv, ep_codec, &trigger_start, &trigger_stop); - /* - * convert bit_frame - * We need to flip clock_provider if it was CPU node, - * because it is Codec base. - */ - daiclk = snd_soc_daifmt_clock_provider_from_bitmap(bit_frame); - if (is_cpu_node) - daiclk = snd_soc_daifmt_clock_provider_flipped(daiclk); + for_each_link_cpus(dai_link, i, dlc) { + dlc->ext_fmt = graph_parse_bitframe(ep_cpu); + + if (multi_cpu_port) + ep_cpu = graph_get_next_multi_ep(&multi_cpu_port, multi_cpu_port_idx++); + } + + for_each_link_codecs(dai_link, i, dlc) { + dlc->ext_fmt = graph_parse_bitframe(ep_codec); + + if (multi_codec_port) + ep_codec = graph_get_next_multi_ep(&multi_codec_port, multi_codec_port_idx++); + } + + /*** Don't use port_cpu / port_codec after here ***/ dai_link->playback_only = playback_only; dai_link->capture_only = capture_only; @@ -826,14 +825,12 @@ static void graph_link_init(struct simple_util_priv *priv, dai_link->trigger_start = trigger_start; dai_link->trigger_stop = trigger_stop; - dai_link->dai_fmt = daifmt | daiclk; + dai_link->dai_fmt = daifmt; dai_link->init = simple_util_dai_init; dai_link->ops = &graph_ops; if (priv->ops) dai_link->ops = priv->ops; - of_node_put(ports_cpu); - of_node_put(ports_codec); of_node_put(port_cpu); of_node_put(port_codec); of_node_put(ep_cpu); @@ -845,8 +842,8 @@ int audio_graph2_link_normal(struct simple_util_priv *priv, struct link_info *li) { struct device_node *cpu_port = lnk; - struct device_node *cpu_ep = of_graph_get_next_port_endpoint(cpu_port, NULL); - struct device_node *codec_port = of_graph_get_remote_port(cpu_ep); + struct device_node *cpu_ep __free(device_node) = of_graph_get_next_port_endpoint(cpu_port, NULL); + struct device_node *codec_ep __free(device_node) = of_graph_get_remote_endpoint(cpu_ep); int ret; /* @@ -854,23 +851,21 @@ int audio_graph2_link_normal(struct simple_util_priv *priv, * see * __graph_parse_node() :: DAI Naming */ - ret = graph_parse_node(priv, GRAPH_NORMAL, codec_port, li, 0); + ret = graph_parse_node(priv, GRAPH_NORMAL, codec_ep, li, 0); if (ret < 0) - goto err; + goto end; /* * call CPU, and set DAI Name */ - ret = graph_parse_node(priv, GRAPH_NORMAL, cpu_port, li, 1); + ret = graph_parse_node(priv, GRAPH_NORMAL, cpu_ep, li, 1); if (ret < 0) - goto err; + goto end; - graph_link_init(priv, lnk, cpu_port, codec_port, li, 1); -err: - of_node_put(codec_port); - of_node_put(cpu_ep); + graph_link_init(priv, lnk, cpu_ep, codec_ep, li, 1); - return ret; +end: + return graph_ret(priv, ret); } EXPORT_SYMBOL_GPL(audio_graph2_link_normal); @@ -878,17 +873,17 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { - struct device_node *ep = of_graph_get_next_port_endpoint(lnk, NULL); - struct device_node *rep = of_graph_get_remote_endpoint(ep); - struct device_node *cpu_port = NULL; - struct device_node *codec_port = NULL; + struct device_node *ep __free(device_node) = of_graph_get_next_port_endpoint(lnk, NULL); + struct device_node *rep __free(device_node) = of_graph_get_remote_endpoint(ep); + struct device_node *cpu_ep = NULL; + struct device_node *codec_ep = NULL; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); int is_cpu = graph_util_is_ports0(lnk); int ret; if (is_cpu) { - cpu_port = of_graph_get_remote_port(ep); /* rport */ + cpu_ep = rep; /* * dpcm { @@ -917,12 +912,12 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, dai_link->dynamic = 1; dai_link->dpcm_merged_format = 1; - ret = graph_parse_node(priv, GRAPH_DPCM, cpu_port, li, 1); + ret = graph_parse_node(priv, GRAPH_DPCM, cpu_ep, li, 1); if (ret) - goto err; + return ret; } else { - codec_port = of_graph_get_remote_port(ep); /* rport */ + codec_ep = rep; /* * dpcm { @@ -953,22 +948,17 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv, dai_link->no_pcm = 1; dai_link->be_hw_params_fixup = simple_util_be_hw_params_fixup; - ret = graph_parse_node(priv, GRAPH_DPCM, codec_port, li, 0); + ret = graph_parse_node(priv, GRAPH_DPCM, codec_ep, li, 0); if (ret < 0) - goto err; + return ret; } graph_parse_convert(ep, dai_props); /* at node of <dpcm> */ graph_parse_convert(rep, dai_props); /* at node of <CPU/Codec> */ - graph_link_init(priv, lnk, cpu_port, codec_port, li, is_cpu); -err: - of_node_put(ep); - of_node_put(rep); - of_node_put(cpu_port); - of_node_put(codec_port); + graph_link_init(priv, lnk, cpu_ep, codec_ep, li, is_cpu); - return ret; + return graph_ret(priv, ret); } EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm); @@ -977,9 +967,9 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv, struct link_info *li) { struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); - struct device_node *port0, *port1, *ports; - struct device_node *codec0_port, *codec1_port; - struct device_node *ep0, *ep1; + struct device_node *port0 = lnk; + struct device_node *ports __free(device_node) = port_to_ports(port0); + struct device_node *port1 __free(device_node) = of_graph_get_next_port(ports, port0); u32 val = 0; int ret = -EINVAL; @@ -999,10 +989,6 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv, * }; * }; */ - of_node_get(lnk); - port0 = lnk; - ports = port_to_ports(port0); - port1 = of_graph_get_next_port(ports, port0); /* * Card2 can use original Codec2Codec settings if DT has. @@ -1018,8 +1004,13 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv, struct snd_soc_pcm_stream *c2c_conf; c2c_conf = devm_kzalloc(dev, sizeof(*c2c_conf), GFP_KERNEL); - if (!c2c_conf) - goto err1; + if (!c2c_conf) { + /* + * Clang doesn't allow to use "goto end" before calling __free(), + * because it bypasses the initialization. Use graph_ret() directly. + */ + return graph_ret(priv, -ENOMEM); + } c2c_conf->formats = SNDRV_PCM_FMTBIT_S32_LE; /* update ME */ c2c_conf->rates = SNDRV_PCM_RATE_8000_384000; @@ -1032,40 +1023,31 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv, dai_link->num_c2c_params = 1; } - ep0 = of_graph_get_next_port_endpoint(port0, NULL); - ep1 = of_graph_get_next_port_endpoint(port1, NULL); + struct device_node *ep0 __free(device_node) = of_graph_get_next_port_endpoint(port0, NULL); + struct device_node *ep1 __free(device_node) = of_graph_get_next_port_endpoint(port1, NULL); - codec0_port = of_graph_get_remote_port(ep0); - codec1_port = of_graph_get_remote_port(ep1); + struct device_node *codec0_ep __free(device_node) = of_graph_get_remote_endpoint(ep0); + struct device_node *codec1_ep __free(device_node) = of_graph_get_remote_endpoint(ep1); /* * call Codec first. * see * __graph_parse_node() :: DAI Naming */ - ret = graph_parse_node(priv, GRAPH_C2C, codec1_port, li, 0); + ret = graph_parse_node(priv, GRAPH_C2C, codec1_ep, li, 0); if (ret < 0) - goto err2; + goto end; /* * call CPU, and set DAI Name */ - ret = graph_parse_node(priv, GRAPH_C2C, codec0_port, li, 1); + ret = graph_parse_node(priv, GRAPH_C2C, codec0_ep, li, 1); if (ret < 0) - goto err2; - - graph_link_init(priv, lnk, codec0_port, codec1_port, li, 1); -err2: - of_node_put(ep0); - of_node_put(ep1); - of_node_put(codec0_port); - of_node_put(codec1_port); -err1: - of_node_put(ports); - of_node_put(port0); - of_node_put(port1); + goto end; - return ret; + graph_link_init(priv, lnk, codec0_ep, codec1_ep, li, 1); +end: + return graph_ret(priv, ret); } EXPORT_SYMBOL_GPL(audio_graph2_link_c2c); @@ -1113,7 +1095,7 @@ static int graph_link(struct simple_util_priv *priv, li->link++; err: - return ret; + return graph_ret(priv, ret); } static int graph_counter(struct device_node *lnk) @@ -1153,8 +1135,8 @@ static int graph_count_normal(struct simple_util_priv *priv, struct link_info *li) { struct device_node *cpu_port = lnk; - struct device_node *cpu_ep = of_graph_get_next_port_endpoint(cpu_port, NULL); - struct device_node *codec_port = of_graph_get_remote_port(cpu_ep); + struct device_node *cpu_ep __free(device_node) = of_graph_get_next_port_endpoint(cpu_port, NULL); + struct device_node *codec_port __free(device_node) = of_graph_get_remote_port(cpu_ep); /* * CPU { @@ -1171,9 +1153,6 @@ static int graph_count_normal(struct simple_util_priv *priv, li->num[li->link].codecs = graph_counter(codec_port); - of_node_put(cpu_ep); - of_node_put(codec_port); - return 0; } @@ -1181,8 +1160,8 @@ static int graph_count_dpcm(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { - struct device_node *ep = of_graph_get_next_port_endpoint(lnk, NULL); - struct device_node *rport = of_graph_get_remote_port(ep); + struct device_node *ep __free(device_node) = of_graph_get_next_port_endpoint(lnk, NULL); + struct device_node *rport __free(device_node) = of_graph_get_remote_port(ep); /* * dpcm { @@ -1211,9 +1190,6 @@ static int graph_count_dpcm(struct simple_util_priv *priv, li->num[li->link].codecs = graph_counter(rport); /* BE */ } - of_node_put(ep); - of_node_put(rport); - return 0; } @@ -1221,13 +1197,13 @@ static int graph_count_c2c(struct simple_util_priv *priv, struct device_node *lnk, struct link_info *li) { - struct device_node *ports = port_to_ports(lnk); - struct device_node *port0 = lnk; - struct device_node *port1 = of_graph_get_next_port(ports, of_node_get(port0)); - struct device_node *ep0 = of_graph_get_next_port_endpoint(port0, NULL); - struct device_node *ep1 = of_graph_get_next_port_endpoint(port1, NULL); - struct device_node *codec0 = of_graph_get_remote_port(ep0); - struct device_node *codec1 = of_graph_get_remote_port(ep1); + struct device_node *ports __free(device_node) = port_to_ports(lnk); + struct device_node *port0 = of_node_get(lnk); + struct device_node *port1 = of_node_get(of_graph_get_next_port(ports, of_node_get(port0))); + struct device_node *ep0 __free(device_node) = of_graph_get_next_port_endpoint(port0, NULL); + struct device_node *ep1 __free(device_node) = of_graph_get_next_port_endpoint(port1, NULL); + struct device_node *codec0 __free(device_node) = of_graph_get_remote_port(ep0); + struct device_node *codec1 __free(device_node) = of_graph_get_remote_port(ep1); /* * codec2codec { @@ -1247,13 +1223,6 @@ static int graph_count_c2c(struct simple_util_priv *priv, li->num[li->link].codecs = graph_counter(codec1); - of_node_put(ports); - of_node_put(port1); - of_node_put(ep0); - of_node_put(ep1); - of_node_put(codec0); - of_node_put(codec1); - return 0; } @@ -1297,7 +1266,7 @@ static int graph_count(struct simple_util_priv *priv, li->link++; err: - return ret; + return graph_ret(priv, ret); } static int graph_for_each_link(struct simple_util_priv *priv, @@ -1314,7 +1283,7 @@ static int graph_for_each_link(struct simple_util_priv *priv, struct device_node *node = dev->of_node; struct device_node *lnk; enum graph_type gtype; - int rc, ret; + int rc, ret = 0; /* loop for all listed CPU port */ of_for_each_phandle(&it, rc, node, "links", NULL, 0) { @@ -1324,10 +1293,10 @@ static int graph_for_each_link(struct simple_util_priv *priv, ret = func(priv, hooks, gtype, lnk, li); if (ret < 0) - return ret; + break; } - return 0; + return graph_ret(priv, ret); } int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, @@ -1380,7 +1349,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, if (ret < 0) goto err; - ret = simple_util_parse_card_name(card, NULL); + ret = simple_util_parse_card_name(priv, NULL); if (ret < 0) goto err; @@ -1403,7 +1372,7 @@ err: if (ret < 0) dev_err_probe(dev, ret, "parse error\n"); - return ret; + return graph_ret(priv, ret); } EXPORT_SYMBOL_GPL(audio_graph2_parse_of); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 24b371f32066..a1ccc300e68c 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -15,6 +15,13 @@ #include <sound/pcm_params.h> #include <sound/simple_card_utils.h> +#define simple_ret(priv, ret) _simple_ret(priv, __func__, ret) +static inline int _simple_ret(struct simple_util_priv *priv, + const char *func, int ret) +{ + return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func); +} + int simple_util_get_sample_fmt(struct simple_util_data *data) { int i; @@ -133,34 +140,42 @@ int simple_util_parse_daifmt(struct device *dev, } EXPORT_SYMBOL_GPL(simple_util_parse_daifmt); -int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, +int simple_util_parse_tdm_width_map(struct simple_util_priv *priv, struct device_node *np, struct simple_util_dai *dai) { + struct device *dev = simple_priv_to_dev(priv); int n, i, ret; u32 *p; - if (!of_property_read_bool(np, "dai-tdm-slot-width-map")) - return 0; + /* + * NOTE + * + * Clang doesn't allow to use "goto end" before calling __free(), + * because it bypasses the initialization. Use simple_ret() directly. + */ n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32)); + if (n <= 0) + return 0; + if (n % 3) { dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n"); - return -EINVAL; + return simple_ret(priv, -EINVAL); /* see NOTE */ } + ret = -ENOMEM; dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL); if (!dai->tdm_width_map) - return -ENOMEM; + return simple_ret(priv, ret); /* see NOTE */ - u32 *array_values __free(kfree) = kcalloc(n, sizeof(*array_values), - GFP_KERNEL); + u32 *array_values __free(kfree) = kcalloc(n, sizeof(*array_values), GFP_KERNEL); if (!array_values) - return -ENOMEM; + goto end; ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n); if (ret < 0) { dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret); - return ret; + goto end; } p = array_values; @@ -171,15 +186,17 @@ int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np, } dai->n_tdm_widths = i; - - return 0; + ret = 0; +end: + return simple_ret(priv, ret); } EXPORT_SYMBOL_GPL(simple_util_parse_tdm_width_map); -int simple_util_set_dailink_name(struct device *dev, +int simple_util_set_dailink_name(struct simple_util_priv *priv, struct snd_soc_dai_link *dai_link, const char *fmt, ...) { + struct device *dev = simple_priv_to_dev(priv); va_list ap; char *name = NULL; int ret = -ENOMEM; @@ -195,13 +212,14 @@ int simple_util_set_dailink_name(struct device *dev, dai_link->stream_name = name; } - return ret; + return simple_ret(priv, ret); } EXPORT_SYMBOL_GPL(simple_util_set_dailink_name); -int simple_util_parse_card_name(struct snd_soc_card *card, +int simple_util_parse_card_name(struct simple_util_priv *priv, char *prefix) { + struct snd_soc_card *card = simple_priv_to_card(priv); int ret; if (!prefix) @@ -215,13 +233,13 @@ int simple_util_parse_card_name(struct snd_soc_card *card, snprintf(prop, sizeof(prop), "%sname", prefix); ret = snd_soc_of_parse_card_name(card, prop); if (ret < 0) - return ret; + goto end; } if (!card->name && card->dai_link) card->name = card->dai_link->name; - - return 0; +end: + return simple_ret(priv, ret); } EXPORT_SYMBOL_GPL(simple_util_parse_card_name); @@ -296,7 +314,7 @@ int simple_util_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *props = simple_priv_to_props(priv, rtd->id); + struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd); struct simple_util_dai *dai; unsigned int fixed_sysclk = 0; int i1, i2, i; @@ -349,7 +367,8 @@ cpu_err: break; simple_clk_disable(dai); } - return ret; + + return simple_ret(priv, ret); } EXPORT_SYMBOL_GPL(simple_util_startup); @@ -357,7 +376,7 @@ void simple_util_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *props = simple_priv_to_props(priv, rtd->id); + struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd); struct simple_util_dai *dai; int i; @@ -365,8 +384,7 @@ void simple_util_shutdown(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, i); if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai)) - snd_soc_dai_set_sysclk(cpu_dai, - 0, 0, SND_SOC_CLOCK_OUT); + snd_soc_dai_set_sysclk(cpu_dai, 0, 0, dai->clk_direction); simple_clk_disable(dai); } @@ -374,24 +392,26 @@ void simple_util_shutdown(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, i); if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai)) - snd_soc_dai_set_sysclk(codec_dai, - 0, 0, SND_SOC_CLOCK_IN); + snd_soc_dai_set_sysclk(codec_dai, 0, 0, dai->clk_direction); simple_clk_disable(dai); } } EXPORT_SYMBOL_GPL(simple_util_shutdown); -static int simple_set_clk_rate(struct device *dev, - struct simple_util_dai *simple_dai, - unsigned long rate) +static int simple_set_clk_rate(struct simple_util_priv *priv, + struct simple_util_dai *simple_dai, + unsigned long rate) { + struct device *dev = simple_priv_to_dev(priv); + int ret = -EINVAL; + if (!simple_dai) return 0; if (simple_dai->clk_fixed && rate != simple_dai->sysclk) { dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate); - return -EINVAL; + goto end; } if (!simple_dai->clk) @@ -400,12 +420,15 @@ static int simple_set_clk_rate(struct device *dev, if (clk_get_rate(simple_dai->clk) == rate) return 0; - return clk_set_rate(simple_dai->clk, rate); + ret = clk_set_rate(simple_dai->clk, rate); +end: + return simple_ret(priv, ret); } -static int simple_set_tdm(struct snd_soc_dai *dai, - struct simple_util_dai *simple_dai, - struct snd_pcm_hw_params *params) +static int simple_set_tdm(struct simple_util_priv *priv, + struct snd_soc_dai *dai, + struct simple_util_dai *simple_dai, + struct snd_pcm_hw_params *params) { int sample_bits = params_width(params); int slot_width, slot_count; @@ -433,12 +456,8 @@ static int simple_set_tdm(struct snd_soc_dai *dai, simple_dai->rx_slot_mask, slot_count, slot_width); - if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret); - return ret; - } - return 0; + return simple_ret(priv, ret); } int simple_util_hw_params(struct snd_pcm_substream *substream, @@ -448,7 +467,7 @@ int simple_util_hw_params(struct snd_pcm_substream *substream, struct simple_util_dai *pdai; struct snd_soc_dai *sdai; struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *props = simple_priv_to_props(priv, rtd->id); + struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd); unsigned int mclk, mclk_fs = 0; int i, ret; @@ -460,15 +479,15 @@ int simple_util_hw_params(struct snd_pcm_substream *substream, mclk = params_rate(params) * mclk_fs; for_each_prop_dai_codec(props, i, pdai) { - ret = simple_set_clk_rate(rtd->dev, pdai, mclk); + ret = simple_set_clk_rate(priv, pdai, mclk); if (ret < 0) - return ret; + goto end; } for_each_prop_dai_cpu(props, i, pdai) { - ret = simple_set_clk_rate(rtd->dev, pdai, mclk); + ret = simple_set_clk_rate(priv, pdai, mclk); if (ret < 0) - return ret; + goto end; } /* Ensure sysclk is set on all components in case any @@ -479,37 +498,40 @@ int simple_util_hw_params(struct snd_pcm_substream *substream, ret = snd_soc_component_set_sysclk(component, 0, 0, mclk, SND_SOC_CLOCK_IN); if (ret && ret != -ENOTSUPP) - return ret; + goto end; } for_each_rtd_codec_dais(rtd, i, sdai) { - ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN); + pdai = simple_props_to_dai_codec(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); if (ret && ret != -ENOTSUPP) - return ret; + goto end; } for_each_rtd_cpu_dais(rtd, i, sdai) { - ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT); + pdai = simple_props_to_dai_cpu(props, i); + ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction); if (ret && ret != -ENOTSUPP) - return ret; + goto end; } } for_each_prop_dai_codec(props, i, pdai) { sdai = snd_soc_rtd_to_codec(rtd, i); - ret = simple_set_tdm(sdai, pdai, params); + ret = simple_set_tdm(priv, sdai, pdai, params); if (ret < 0) - return ret; + goto end; } for_each_prop_dai_cpu(props, i, pdai) { sdai = snd_soc_rtd_to_cpu(rtd, i); - ret = simple_set_tdm(sdai, pdai, params); + ret = simple_set_tdm(priv, sdai, pdai, params); if (ret < 0) - return ret; + goto end; } - - return 0; + ret = 0; +end: + return simple_ret(priv, ret); } EXPORT_SYMBOL_GPL(simple_util_hw_params); @@ -517,7 +539,7 @@ int simple_util_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->id); + struct simple_dai_props *dai_props = runtime_simple_priv_to_props(priv, rtd); struct simple_util_data *data = &dai_props->adata; 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); @@ -537,7 +559,8 @@ int simple_util_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, } EXPORT_SYMBOL_GPL(simple_util_be_hw_params_fixup); -static int simple_init_dai(struct snd_soc_dai *dai, struct simple_util_dai *simple_dai) +static int simple_init_dai(struct simple_util_priv *priv, + struct snd_soc_dai *dai, struct simple_util_dai *simple_dai) { int ret; @@ -549,7 +572,7 @@ static int simple_init_dai(struct snd_soc_dai *dai, struct simple_util_dai *simp simple_dai->clk_direction); if (ret && ret != -ENOTSUPP) { dev_err(dai->dev, "simple-card: set_sysclk error\n"); - return ret; + goto end; } } @@ -561,11 +584,12 @@ static int simple_init_dai(struct snd_soc_dai *dai, struct simple_util_dai *simp simple_dai->slot_width); if (ret && ret != -ENOTSUPP) { dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); - return ret; + goto end; } } - - return 0; + ret = 0; +end: + return simple_ret(priv, ret); } static inline int simple_component_is_codec(struct snd_soc_component *component) @@ -573,7 +597,8 @@ static inline int simple_component_is_codec(struct snd_soc_component *component) return component->driver->endianness; } -static int simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd, +static int simple_init_for_codec2codec(struct simple_util_priv *priv, + struct snd_soc_pcm_runtime *rtd, struct simple_dai_props *dai_props) { struct snd_soc_dai_link *dai_link = rtd->dai_link; @@ -605,12 +630,13 @@ static int simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd, if (ret < 0) { dev_err(rtd->dev, "simple-card: no valid dai_link params\n"); - return ret; + goto end; } + ret = -ENOMEM; c2c_params = devm_kzalloc(rtd->dev, sizeof(*c2c_params), GFP_KERNEL); if (!c2c_params) - return -ENOMEM; + goto end; c2c_params->formats = hw.formats; c2c_params->rates = hw.rates; @@ -622,32 +648,32 @@ static int simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd, dai_link->c2c_params = c2c_params; dai_link->num_c2c_params = 1; - return 0; + ret = 0; +end: + return simple_ret(priv, ret); } int simple_util_dai_init(struct snd_soc_pcm_runtime *rtd) { struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *props = simple_priv_to_props(priv, rtd->id); + struct simple_dai_props *props = runtime_simple_priv_to_props(priv, rtd); struct simple_util_dai *dai; int i, ret; for_each_prop_dai_codec(props, i, dai) { - ret = simple_init_dai(snd_soc_rtd_to_codec(rtd, i), dai); + ret = simple_init_dai(priv, snd_soc_rtd_to_codec(rtd, i), dai); if (ret < 0) - return ret; + goto end; } for_each_prop_dai_cpu(props, i, dai) { - ret = simple_init_dai(snd_soc_rtd_to_cpu(rtd, i), dai); + ret = simple_init_dai(priv, snd_soc_rtd_to_cpu(rtd, i), dai); if (ret < 0) - return ret; + goto end; } - ret = simple_init_for_codec2codec(rtd, props); - if (ret < 0) - return ret; - - return 0; + ret = simple_init_for_codec2codec(priv, rtd, props); +end: + return simple_ret(priv, ret); } EXPORT_SYMBOL_GPL(simple_util_dai_init); @@ -713,7 +739,7 @@ int simple_util_parse_routing(struct snd_soc_card *card, snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); - if (!of_property_read_bool(node, prop)) + if (!of_property_present(node, prop)) return 0; return snd_soc_of_parse_audio_routing(card, prop); @@ -731,7 +757,7 @@ int simple_util_parse_widgets(struct snd_soc_card *card, snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); - if (of_property_read_bool(node, prop)) + if (of_property_present(node, prop)) return snd_soc_of_parse_audio_simple_widgets(card, prop); /* no widgets is not error */ @@ -832,7 +858,7 @@ int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) priv->aux_jacks = devm_kcalloc(card->dev, num, sizeof(struct snd_soc_jack), GFP_KERNEL); if (!priv->aux_jacks) - return -ENOMEM; + return simple_ret(priv, -ENOMEM); for_each_card_auxs(card, component) { char id[128]; @@ -993,48 +1019,37 @@ int graph_util_card_probe(struct snd_soc_card *card) ret = simple_util_init_hp(card, &priv->hp_jack, NULL); if (ret < 0) - return ret; + goto end; ret = simple_util_init_mic(card, &priv->mic_jack, NULL); - if (ret < 0) - return ret; - - return 0; +end: + return simple_ret(priv, ret); } EXPORT_SYMBOL_GPL(graph_util_card_probe); int graph_util_is_ports0(struct device_node *np) { - struct device_node *port, *ports, *ports0, *top; - int ret; + struct device_node *parent __free(device_node) = of_get_parent(np); + struct device_node *port; /* np is "endpoint" or "port" */ - if (of_node_name_eq(np, "endpoint")) { - port = of_get_parent(np); - } else { + if (of_node_name_eq(np, "endpoint")) + port = parent; + else port = np; - of_node_get(port); - } - - ports = of_get_parent(port); - top = of_get_parent(ports); - ports0 = of_get_child_by_name(top, "ports"); - - ret = ports0 == ports; - of_node_put(port); - of_node_put(ports); - of_node_put(ports0); - of_node_put(top); + struct device_node *ports __free(device_node) = of_get_parent(port); + struct device_node *top __free(device_node) = of_get_parent(ports); + struct device_node *ports0 __free(device_node) = of_get_child_by_name(top, "ports"); - return ret; + return ports0 == ports; } EXPORT_SYMBOL_GPL(graph_util_is_ports0); static int graph_get_dai_id(struct device_node *ep) { - struct device_node *node; - struct device_node *endpoint; + struct device_node *node __free(device_node) = of_graph_get_port_parent(ep); + struct device_node *port __free(device_node) = of_get_parent(ep); struct of_endpoint info; int i, id; int ret; @@ -1053,16 +1068,16 @@ static int graph_get_dai_id(struct device_node *ep) * only of_graph_parse_endpoint(). * We need to check "reg" property */ - if (of_property_present(ep, "reg")) - return info.id; - node = of_get_parent(ep); - ret = of_property_present(node, "reg"); - of_node_put(node); + /* check port first */ + ret = of_property_present(port, "reg"); if (ret) return info.port; + + /* check endpoint 2nd as backup */ + if (of_property_present(ep, "reg")) + return info.id; } - node = of_graph_get_port_parent(ep); /* * Non HDMI sound case, counting port/endpoint on its DT @@ -1070,23 +1085,24 @@ static int graph_get_dai_id(struct device_node *ep) */ i = 0; id = -1; - for_each_endpoint_of_node(node, endpoint) { - if (endpoint == ep) + for_each_of_graph_port(node, p) { + if (port == p) { id = i; + break; + } i++; } - of_node_put(node); - if (id < 0) return -ENODEV; return id; } -int graph_util_parse_dai(struct device *dev, struct device_node *ep, +int graph_util_parse_dai(struct simple_util_priv *priv, struct device_node *ep, struct snd_soc_dai_link_component *dlc, int *is_single_link) { + struct device *dev = simple_priv_to_dev(priv); struct device_node *node; struct of_phandle_args args = {}; struct snd_soc_dai *dai; @@ -1103,10 +1119,12 @@ int graph_util_parse_dai(struct device *dev, struct device_node *ep, args.np = ep; dai = snd_soc_get_dai_via_args(&args); if (dai) { + ret = -ENOMEM; + dlc->of_node = node; dlc->dai_name = snd_soc_dai_name_get(dai); dlc->dai_args = snd_soc_copy_dai_args(dev, &args); if (!dlc->dai_args) - return -ENOMEM; + goto end; goto parse_dai_end; } @@ -1138,14 +1156,15 @@ int graph_util_parse_dai(struct device *dev, struct device_node *ep, ret = snd_soc_get_dlc(&args, dlc); if (ret < 0) { of_node_put(node); - return ret; + goto end; } parse_dai_end: if (is_single_link) *is_single_link = of_graph_get_endpoint_count(node) == 1; - - return 0; + ret = 0; +end: + return simple_ret(priv, ret); } EXPORT_SYMBOL_GPL(graph_util_parse_dai); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 76a1d05e2ebe..5af6d1b308f2 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -29,7 +29,16 @@ static const struct snd_soc_ops simple_ops = { .hw_params = simple_util_hw_params, }; -static int simple_parse_platform(struct device_node *node, struct snd_soc_dai_link_component *dlc) +#define simple_ret(priv, ret) _simple_ret(priv, __func__, ret) +static inline int _simple_ret(struct simple_util_priv *priv, + const char *func, int ret) +{ + return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func); +} + +static int simple_parse_platform(struct simple_util_priv *priv, + struct device_node *node, + struct snd_soc_dai_link_component *dlc) { struct of_phandle_args args; int ret; @@ -43,7 +52,7 @@ static int simple_parse_platform(struct device_node *node, struct snd_soc_dai_li */ ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args); if (ret) - return ret; + return simple_ret(priv, ret); /* dai_name is not required and may not exist for plat component */ @@ -52,11 +61,12 @@ static int simple_parse_platform(struct device_node *node, struct snd_soc_dai_li return 0; } -static int simple_parse_dai(struct device *dev, +static int simple_parse_dai(struct simple_util_priv *priv, struct device_node *node, struct snd_soc_dai_link_component *dlc, int *is_single_link) { + struct device *dev = simple_priv_to_dev(priv); struct of_phandle_args args; struct snd_soc_dai *dai; int ret; @@ -70,17 +80,18 @@ static int simple_parse_dai(struct device *dev, */ ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args); if (ret) - return ret; + goto end; /* * Try to find from DAI args */ dai = snd_soc_get_dai_via_args(&args); if (dai) { + ret = -ENOMEM; dlc->dai_name = snd_soc_dai_name_get(dai); dlc->dai_args = snd_soc_copy_dai_args(dev, &args); if (!dlc->dai_args) - return -ENOMEM; + goto end; goto parse_dai_end; } @@ -106,13 +117,14 @@ static int simple_parse_dai(struct device *dev, */ ret = snd_soc_get_dlc(&args, dlc); if (ret < 0) - return ret; + goto end; parse_dai_end: if (is_single_link) *is_single_link = !args.args_count; - - return 0; + ret = 0; +end: + return simple_ret(priv, ret); } static void simple_parse_convert(struct device *dev, @@ -120,14 +132,12 @@ static void simple_parse_convert(struct device *dev, struct simple_util_data *adata) { struct device_node *top = dev->of_node; - struct device_node *node = of_get_parent(np); + struct device_node *node __free(device_node) = of_get_parent(np); simple_util_parse_convert(top, PREFIX, adata); simple_util_parse_convert(node, PREFIX, adata); simple_util_parse_convert(node, NULL, adata); simple_util_parse_convert(np, NULL, adata); - - of_node_put(node); } static int simple_parse_node(struct simple_util_priv *priv, @@ -151,19 +161,17 @@ static int simple_parse_node(struct simple_util_priv *priv, dai = simple_props_to_dai_codec(dai_props, 0); } - ret = simple_parse_dai(dev, np, dlc, cpu); + ret = simple_parse_dai(priv, np, dlc, cpu); if (ret) - return ret; + goto end; ret = simple_util_parse_clk(dev, np, dai, dlc); if (ret) - return ret; + goto end; ret = simple_util_parse_tdm(np, dai); - if (ret) - return ret; - - return 0; +end: + return simple_ret(priv, ret); } static int simple_link_init(struct simple_util_priv *priv, @@ -176,7 +184,7 @@ static int simple_link_init(struct simple_util_priv *priv, struct device_node *top = dev->of_node; struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); - struct device_node *node = of_get_parent(cpu); + struct device_node *node __free(device_node) = of_get_parent(cpu); enum snd_soc_trigger_order trigger_start = SND_SOC_TRIGGER_ORDER_DEFAULT; enum snd_soc_trigger_order trigger_stop = SND_SOC_TRIGGER_ORDER_DEFAULT; bool playback_only = 0, capture_only = 0; @@ -185,7 +193,7 @@ static int simple_link_init(struct simple_util_priv *priv, ret = simple_util_parse_daifmt(dev, node, codec, prefix, &dai_link->dai_fmt); if (ret < 0) - goto init_end; + goto end; graph_util_parse_link_direction(top, &playback_only, &capture_only); graph_util_parse_link_direction(node, &playback_only, &capture_only); @@ -215,11 +223,9 @@ static int simple_link_init(struct simple_util_priv *priv, dai_link->init = simple_util_dai_init; dai_link->ops = &simple_ops; - ret = simple_util_set_dailink_name(dev, dai_link, name); -init_end: - of_node_put(node); - - return ret; + ret = simple_util_set_dailink_name(priv, dai_link, name); +end: + return simple_ret(priv, ret); } static int simple_dai_link_of_dpcm(struct simple_util_priv *priv, @@ -232,7 +238,7 @@ static int simple_dai_link_of_dpcm(struct simple_util_priv *priv, struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); struct device_node *top = dev->of_node; - struct device_node *node = of_get_parent(np); + struct device_node *node __free(device_node) = of_get_parent(np); char *prefix = ""; char dai_name[64]; int ret; @@ -296,8 +302,7 @@ static int simple_dai_link_of_dpcm(struct simple_util_priv *priv, out_put_node: li->link++; - of_node_put(node); - return ret; + return simple_ret(priv, ret); } static int simple_dai_link_of(struct simple_util_priv *priv, @@ -312,15 +317,13 @@ static int simple_dai_link_of(struct simple_util_priv *priv, struct snd_soc_dai_link_component *codecs = snd_soc_link_to_codec(dai_link, 0); struct snd_soc_dai_link_component *platforms = snd_soc_link_to_platform(dai_link, 0); struct device_node *cpu = NULL; - struct device_node *node = NULL; - struct device_node *plat = NULL; char dai_name[64]; char prop[128]; char *prefix = ""; int ret, single_cpu = 0; cpu = np; - node = of_get_parent(np); + struct device_node *node __free(device_node) = of_get_parent(np); dev_dbg(dev, "link_of (%pOF)\n", node); @@ -329,7 +332,7 @@ static int simple_dai_link_of(struct simple_util_priv *priv, prefix = PREFIX; snprintf(prop, sizeof(prop), "%splat", prefix); - plat = of_get_child_by_name(node, prop); + struct device_node *plat __free(device_node) = of_get_child_by_name(node, prop); ret = simple_parse_node(priv, cpu, li, prefix, &single_cpu); if (ret < 0) @@ -339,7 +342,7 @@ static int simple_dai_link_of(struct simple_util_priv *priv, if (ret < 0) goto dai_link_of_err; - ret = simple_parse_platform(plat, platforms); + ret = simple_parse_platform(priv, plat, platforms); if (ret < 0) goto dai_link_of_err; @@ -352,12 +355,9 @@ static int simple_dai_link_of(struct simple_util_priv *priv, ret = simple_link_init(priv, cpu, codec, li, prefix, dai_name); dai_link_of_err: - of_node_put(plat); - of_node_put(node); - li->link++; - return ret; + return simple_ret(priv, ret); } static int __simple_for_each_link(struct simple_util_priv *priv, @@ -374,7 +374,6 @@ static int __simple_for_each_link(struct simple_util_priv *priv, struct device *dev = simple_priv_to_dev(priv); struct device_node *top = dev->of_node; struct device_node *node; - struct device_node *add_devs; uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev); bool is_top = 0; int ret = 0; @@ -386,14 +385,11 @@ static int __simple_for_each_link(struct simple_util_priv *priv, is_top = 1; } - add_devs = of_get_child_by_name(top, PREFIX "additional-devs"); + struct device_node *add_devs __free(device_node) = of_get_child_by_name(top, PREFIX "additional-devs"); /* loop for all dai-link */ do { struct simple_util_data adata; - struct device_node *codec; - struct device_node *plat; - struct device_node *np; int num = of_get_child_count(node); /* Skip additional-devs node */ @@ -403,26 +399,26 @@ static int __simple_for_each_link(struct simple_util_priv *priv, } /* get codec */ - codec = of_get_child_by_name(node, is_top ? - PREFIX "codec" : "codec"); + struct device_node *codec __free(device_node) = + of_get_child_by_name(node, is_top ? PREFIX "codec" : "codec"); if (!codec) { ret = -ENODEV; goto error; } /* get platform */ - plat = of_get_child_by_name(node, is_top ? - PREFIX "plat" : "plat"); + struct device_node *plat __free(device_node) = + of_get_child_by_name(node, is_top ? PREFIX "plat" : "plat"); /* get convert-xxx property */ memset(&adata, 0, sizeof(adata)); - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { if (np == add_devs) continue; simple_parse_convert(dev, np, &adata); } /* loop for all CPU/Codec node */ - for_each_child_of_node(node, np) { + for_each_child_of_node_scoped(node, np) { if (plat == np || add_devs == np) continue; /* @@ -452,23 +448,17 @@ static int __simple_for_each_link(struct simple_util_priv *priv, ret = func_noml(priv, np, codec, li, is_top); } - if (ret < 0) { - of_node_put(codec); - of_node_put(plat); - of_node_put(np); + if (ret < 0) goto error; - } } - of_node_put(codec); - of_node_put(plat); node = of_get_next_child(top, node); } while (!is_top && node); - error: - of_node_put(add_devs); +error: of_node_put(node); - return ret; + + return simple_ret(priv, ret); } static int simple_for_each_link(struct simple_util_priv *priv, @@ -501,7 +491,7 @@ static int simple_for_each_link(struct simple_util_priv *priv, break; } - return ret; + return simple_ret(priv, ret); } static void simple_depopulate_aux(void *data) @@ -514,19 +504,19 @@ static void simple_depopulate_aux(void *data) static int simple_populate_aux(struct simple_util_priv *priv) { struct device *dev = simple_priv_to_dev(priv); - struct device_node *node; + struct device_node *node __free(device_node) = of_get_child_by_name(dev->of_node, PREFIX "additional-devs"); int ret; - node = of_get_child_by_name(dev->of_node, PREFIX "additional-devs"); if (!node) return 0; ret = of_platform_populate(node, NULL, NULL, dev); - of_node_put(node); if (ret) - return ret; + goto end; - return devm_add_action_or_reset(dev, simple_depopulate_aux, priv); + ret = devm_add_action_or_reset(dev, simple_depopulate_aux, priv); +end: + return simple_ret(priv, ret); } static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) @@ -536,15 +526,15 @@ static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) ret = simple_util_parse_widgets(card, PREFIX); if (ret < 0) - return ret; + goto end; ret = simple_util_parse_routing(card, PREFIX); if (ret < 0) - return ret; + goto end; ret = simple_util_parse_pin_switches(card, PREFIX); if (ret < 0) - return ret; + goto end; /* Single/Muti DAI link(s) & New style of DT node */ memset(li, 0, sizeof(*li)); @@ -552,19 +542,19 @@ static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li) simple_dai_link_of, simple_dai_link_of_dpcm); if (ret < 0) - return ret; + goto end; - ret = simple_util_parse_card_name(card, PREFIX); + ret = simple_util_parse_card_name(priv, PREFIX); if (ret < 0) - return ret; + goto end; ret = simple_populate_aux(priv); if (ret < 0) - return ret; + goto end; ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); - - return ret; +end: + return simple_ret(priv, ret); } static int simple_count_noml(struct simple_util_priv *priv, @@ -572,12 +562,10 @@ static int simple_count_noml(struct simple_util_priv *priv, struct device_node *codec, struct link_info *li, bool is_top) { - if (li->link >= SNDRV_MAX_LINKS) { - struct device *dev = simple_priv_to_dev(priv); + int ret = -EINVAL; - dev_err(dev, "too many links\n"); - return -EINVAL; - } + if (li->link >= SNDRV_MAX_LINKS) + goto end; /* * DON'T REMOVE platforms @@ -599,8 +587,9 @@ static int simple_count_noml(struct simple_util_priv *priv, li->num[li->link].codecs = 1; li->link += 1; - - return 0; + ret = 0; +end: + return simple_ret(priv, ret); } static int simple_count_dpcm(struct simple_util_priv *priv, @@ -608,12 +597,10 @@ static int simple_count_dpcm(struct simple_util_priv *priv, struct device_node *codec, struct link_info *li, bool is_top) { - if (li->link >= SNDRV_MAX_LINKS) { - struct device *dev = simple_priv_to_dev(priv); + int ret = -EINVAL; - dev_err(dev, "too many links\n"); - return -EINVAL; - } + if (li->link >= SNDRV_MAX_LINKS) + goto end; if (li->cpu) { /* @@ -630,8 +617,9 @@ static int simple_count_dpcm(struct simple_util_priv *priv, li->link++; /* dummy-Codec */ } - - return 0; + ret = 0; +end: + return simple_ret(priv, ret); } static int simple_get_dais_count(struct simple_util_priv *priv, @@ -707,17 +695,15 @@ static int simple_soc_probe(struct snd_soc_card *card) ret = simple_util_init_hp(card, &priv->hp_jack, PREFIX); if (ret < 0) - return ret; + goto end; ret = simple_util_init_mic(card, &priv->mic_jack, PREFIX); if (ret < 0) - return ret; + goto end; ret = simple_util_init_aux_jacks(priv, PREFIX); - if (ret < 0) - return ret; - - return 0; +end: + return simple_ret(priv, ret); } static int simple_probe(struct platform_device *pdev) @@ -739,20 +725,22 @@ static int simple_probe(struct platform_device *pdev) card->probe = simple_soc_probe; card->driver_name = "simple-card"; + ret = -ENOMEM; struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL); if (!li) - return -ENOMEM; + goto end; ret = simple_get_dais_count(priv, li); if (ret < 0) - return ret; + goto end; + ret = -EINVAL; if (!li->link) - return -EINVAL; + goto end; ret = simple_util_init_priv(priv, li); if (ret < 0) - return ret; + goto end; if (np && of_device_is_available(np)) { @@ -819,8 +807,8 @@ static int simple_probe(struct platform_device *pdev) return 0; err: simple_util_clean_reference(card); - - return ret; +end: + return dev_err_probe(dev, ret, "parse error\n"); } static const struct of_device_id simple_of_match[] = { diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index 6a988976fb0d..98d2ab68883a 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -539,7 +539,6 @@ static void img_i2s_in_dev_remove(struct platform_device *pdev) img_i2s_in_runtime_suspend(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static int img_i2s_in_suspend(struct device *dev) { struct img_i2s_in *i2s = dev_get_drvdata(dev); @@ -586,7 +585,6 @@ static int img_i2s_in_resume(struct device *dev) return 0; } -#endif static const struct of_device_id img_i2s_in_of_match[] = { { .compatible = "img,i2s-in" }, @@ -595,16 +593,15 @@ static const struct of_device_id img_i2s_in_of_match[] = { MODULE_DEVICE_TABLE(of, img_i2s_in_of_match); static const struct dev_pm_ops img_i2s_in_pm_ops = { - SET_RUNTIME_PM_OPS(img_i2s_in_runtime_suspend, - img_i2s_in_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(img_i2s_in_suspend, img_i2s_in_resume) + RUNTIME_PM_OPS(img_i2s_in_runtime_suspend, img_i2s_in_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(img_i2s_in_suspend, img_i2s_in_resume) }; static struct platform_driver img_i2s_in_driver = { .driver = { .name = "img-i2s-in", .of_match_table = img_i2s_in_of_match, - .pm = &img_i2s_in_pm_ops + .pm = pm_ptr(&img_i2s_in_pm_ops) }, .probe = img_i2s_in_probe, .remove = img_i2s_in_dev_remove diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index 1211e6184d97..aedcf56a5359 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -539,7 +539,6 @@ static void img_i2s_out_dev_remove(struct platform_device *pdev) img_i2s_out_runtime_suspend(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static int img_i2s_out_suspend(struct device *dev) { struct img_i2s_out *i2s = dev_get_drvdata(dev); @@ -586,7 +585,6 @@ static int img_i2s_out_resume(struct device *dev) return 0; } -#endif static const struct of_device_id img_i2s_out_of_match[] = { { .compatible = "img,i2s-out" }, @@ -595,16 +593,15 @@ static const struct of_device_id img_i2s_out_of_match[] = { MODULE_DEVICE_TABLE(of, img_i2s_out_of_match); static const struct dev_pm_ops img_i2s_out_pm_ops = { - SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend, - img_i2s_out_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume) + RUNTIME_PM_OPS(img_i2s_out_runtime_suspend, img_i2s_out_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume) }; static struct platform_driver img_i2s_out_driver = { .driver = { .name = "img-i2s-out", .of_match_table = img_i2s_out_of_match, - .pm = &img_i2s_out_pm_ops + .pm = pm_ptr(&img_i2s_out_pm_ops) }, .probe = img_i2s_out_probe, .remove = img_i2s_out_dev_remove diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index 4ec63119d67c..86b60e6dab38 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -300,15 +300,14 @@ static const struct of_device_id img_prl_out_of_match[] = { MODULE_DEVICE_TABLE(of, img_prl_out_of_match); static const struct dev_pm_ops img_prl_out_pm_ops = { - SET_RUNTIME_PM_OPS(img_prl_out_suspend, - img_prl_out_resume, NULL) + RUNTIME_PM_OPS(img_prl_out_suspend, img_prl_out_resume, NULL) }; static struct platform_driver img_prl_out_driver = { .driver = { .name = "img-parallel-out", .of_match_table = img_prl_out_of_match, - .pm = &img_prl_out_pm_ops + .pm = pm_ptr(&img_prl_out_pm_ops) }, .probe = img_prl_out_probe, .remove = img_prl_out_dev_remove diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c index 3c513f5b8c54..82295e2508fa 100644 --- a/sound/soc/img/img-spdif-in.c +++ b/sound/soc/img/img-spdif-in.c @@ -817,7 +817,6 @@ static void img_spdif_in_dev_remove(struct platform_device *pdev) img_spdif_in_runtime_suspend(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static int img_spdif_in_suspend(struct device *dev) { struct img_spdif_in *spdif = dev_get_drvdata(dev); @@ -857,7 +856,6 @@ static int img_spdif_in_resume(struct device *dev) return 0; } -#endif static const struct of_device_id img_spdif_in_of_match[] = { { .compatible = "img,spdif-in" }, @@ -866,16 +864,15 @@ static const struct of_device_id img_spdif_in_of_match[] = { MODULE_DEVICE_TABLE(of, img_spdif_in_of_match); static const struct dev_pm_ops img_spdif_in_pm_ops = { - SET_RUNTIME_PM_OPS(img_spdif_in_runtime_suspend, - img_spdif_in_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(img_spdif_in_suspend, img_spdif_in_resume) + RUNTIME_PM_OPS(img_spdif_in_runtime_suspend, img_spdif_in_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(img_spdif_in_suspend, img_spdif_in_resume) }; static struct platform_driver img_spdif_in_driver = { .driver = { .name = "img-spdif-in", .of_match_table = img_spdif_in_of_match, - .pm = &img_spdif_in_pm_ops + .pm = pm_ptr(&img_spdif_in_pm_ops) }, .probe = img_spdif_in_probe, .remove = img_spdif_in_dev_remove diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c index 402695b5fc41..52f696219ef4 100644 --- a/sound/soc/img/img-spdif-out.c +++ b/sound/soc/img/img-spdif-out.c @@ -409,7 +409,6 @@ static void img_spdif_out_dev_remove(struct platform_device *pdev) img_spdif_out_runtime_suspend(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static int img_spdif_out_suspend(struct device *dev) { struct img_spdif_out *spdif = dev_get_drvdata(dev); @@ -448,7 +447,7 @@ static int img_spdif_out_resume(struct device *dev) return 0; } -#endif + static const struct of_device_id img_spdif_out_of_match[] = { { .compatible = "img,spdif-out" }, {} @@ -456,16 +455,15 @@ static const struct of_device_id img_spdif_out_of_match[] = { MODULE_DEVICE_TABLE(of, img_spdif_out_of_match); static const struct dev_pm_ops img_spdif_out_pm_ops = { - SET_RUNTIME_PM_OPS(img_spdif_out_runtime_suspend, - img_spdif_out_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(img_spdif_out_suspend, img_spdif_out_resume) + RUNTIME_PM_OPS(img_spdif_out_runtime_suspend, img_spdif_out_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(img_spdif_out_suspend, img_spdif_out_resume) }; static struct platform_driver img_spdif_out_driver = { .driver = { .name = "img-spdif-out", .of_match_table = img_spdif_out_of_match, - .pm = &img_spdif_out_pm_ops + .pm = pm_ptr(&img_spdif_out_pm_ops) }, .probe = img_spdif_out_probe, .remove = img_spdif_out_dev_remove diff --git a/sound/soc/img/pistachio-internal-dac.c b/sound/soc/img/pistachio-internal-dac.c index fdeceb271e7f..ec4f891295c9 100644 --- a/sound/soc/img/pistachio-internal-dac.c +++ b/sound/soc/img/pistachio-internal-dac.c @@ -224,7 +224,6 @@ static void pistachio_internal_dac_remove(struct platform_device *pdev) regulator_disable(dac->supply); } -#ifdef CONFIG_PM static int pistachio_internal_dac_rt_resume(struct device *dev) { struct pistachio_internal_dac *dac = dev_get_drvdata(dev); @@ -251,11 +250,10 @@ static int pistachio_internal_dac_rt_suspend(struct device *dev) return 0; } -#endif static const struct dev_pm_ops pistachio_internal_dac_pm_ops = { - SET_RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend, - pistachio_internal_dac_rt_resume, NULL) + RUNTIME_PM_OPS(pistachio_internal_dac_rt_suspend, + pistachio_internal_dac_rt_resume, NULL) }; static const struct of_device_id pistachio_internal_dac_of_match[] = { @@ -268,7 +266,7 @@ static struct platform_driver pistachio_internal_dac_plat_driver = { .driver = { .name = "img-pistachio-internal-dac", .of_match_table = pistachio_internal_dac_of_match, - .pm = &pistachio_internal_dac_pm_ops + .pm = pm_ptr(&pistachio_internal_dac_pm_ops) }, .probe = pistachio_internal_dac_probe, .remove = pistachio_internal_dac_remove diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index 27516ef57185..3dccf0a57a3a 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -12,6 +12,7 @@ #include "avs.h" #include "messages.h" #include "path.h" +#include "registers.h" #include "topology.h" static irqreturn_t avs_apl_dsp_interrupt(struct avs_dev *adev) @@ -125,7 +126,7 @@ int avs_apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) struct avs_apl_log_buffer_layout layout; void __iomem *addr, *buf; size_t dump_size; - u16 offset = 0; + u32 offset = 0; u8 *dump, *pos; dump_size = AVS_FW_REGS_SIZE + msg->ext.coredump.stack_dump_size; diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index eca6ec0428bb..585543f872fc 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -51,6 +51,7 @@ struct avs_dsp_ops { int (* const load_basefw)(struct avs_dev *, struct firmware *); int (* const load_lib)(struct avs_dev *, struct firmware *, u32); int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32); + int (* const config_basefw)(struct avs_dev *); int (* const enable_logs)(struct avs_dev *, enum avs_log_enable, u32, u32, unsigned long, u32 *); int (* const log_buffer_offset)(struct avs_dev *, u32); diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 0266edeafc19..2d706edcbf92 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -312,6 +312,18 @@ static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = { {}, }; +static struct snd_soc_acpi_mach avs_mbl_i2s_machines[] = { + { + .id = "PCM3168A", + .drv_name = "avs_pcm3168a", + .mach_params = { + .i2s_link_mask = AVS_SSP(0) | AVS_SSP(2), + }, + .tplg_filename = "pcm3168a-tplg.bin", + }, + {} +}; + static struct snd_soc_acpi_mach avs_test_i2s_machines[] = { { .drv_name = "avs_i2s_test", @@ -378,10 +390,11 @@ static const struct avs_acpi_boards i2s_boards[] = { AVS_MACH_ENTRY(HDA_ICL_LP, avs_icl_i2s_machines), AVS_MACH_ENTRY(HDA_TGL_LP, avs_tgl_i2s_machines), AVS_MACH_ENTRY(HDA_EHL_0, avs_tgl_i2s_machines), + AVS_MACH_ENTRY(HDA_ADL_N, avs_mbl_i2s_machines), AVS_MACH_ENTRY(HDA_ADL_P, avs_tgl_i2s_machines), AVS_MACH_ENTRY(HDA_RPL_P_0, avs_tgl_i2s_machines), - AVS_MACH_ENTRY(HDA_RPL_M, avs_tgl_i2s_machines), - {}, + AVS_MACH_ENTRY(HDA_RPL_M, avs_mbl_i2s_machines), + {} }; static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev) diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig index 00b0f6c176d6..ba4bee42124c 100644 --- a/sound/soc/intel/avs/boards/Kconfig +++ b/sound/soc/intel/avs/boards/Kconfig @@ -87,6 +87,16 @@ config SND_SOC_INTEL_AVS_MACH_NAU8825 Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_AVS_MACH_PCM3168A + tristate "pcm3168a I2S board" + depends on I2C + depends on MFD_INTEL_LPSS || COMPILE_TEST + select SND_SOC_PCM3168A_I2C + help + This adds support for AVS with PCM3168A I2C codec configuration. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + config SND_SOC_INTEL_AVS_MACH_PROBE tristate "Probing (data) board" depends on DEBUG_FS diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile index 4fbd936ffb3e..a95256b94dc8 100644 --- a/sound/soc/intel/avs/boards/Makefile +++ b/sound/soc/intel/avs/boards/Makefile @@ -9,6 +9,7 @@ snd-soc-avs-max98927-y := max98927.o snd-soc-avs-max98357a-y := max98357a.o snd-soc-avs-max98373-y := max98373.o snd-soc-avs-nau8825-y := nau8825.o +snd-soc-avs-pcm3168a-y := pcm3168a.o snd-soc-avs-probe-y := probe.o snd-soc-avs-rt274-y := rt274.o snd-soc-avs-rt286-y := rt286.o @@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o +obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PCM3168A) += snd-soc-avs-pcm3168a.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index 76078a7005b0..7217ae51e89c 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -113,7 +113,8 @@ static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) } num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; @@ -197,7 +198,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->be_hw_params_fixup = avs_da7219_be_fixup; dl->init = avs_da7219_codec_init; dl->exit = avs_da7219_codec_exit; diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c index 426ce37105ae..6f3c4f6c9302 100644 --- a/sound/soc/intel/avs/boards/es8336.c +++ b/sound/soc/intel/avs/boards/es8336.c @@ -109,7 +109,8 @@ static int avs_es8336_codec_init(struct snd_soc_pcm_runtime *runtime) data = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c index 6570209c1a63..c55c1d60b777 100644 --- a/sound/soc/intel/avs/boards/max98357a.c +++ b/sound/soc/intel/avs/boards/max98357a.c @@ -78,7 +78,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->be_hw_params_fixup = avs_max98357a_be_fixup; dl->nonatomic = 1; dl->no_pcm = 1; diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c index 6f25e66344b7..970f82bf4ce8 100644 --- a/sound/soc/intel/avs/boards/max98373.c +++ b/sound/soc/intel/avs/boards/max98373.c @@ -111,7 +111,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->name = devm_kasprintf(dev, GFP_KERNEL, AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); - dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs) * 2, GFP_KERNEL); + dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL); if (!dl->name || !dl->cpus || !dl->codecs) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c index ad18c4e9a670..e4ce553bf1d6 100644 --- a/sound/soc/intel/avs/boards/max98927.c +++ b/sound/soc/intel/avs/boards/max98927.c @@ -108,7 +108,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->name = devm_kasprintf(dev, GFP_KERNEL, AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); - dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs) * 2, GFP_KERNEL); + dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL); if (!dl->name || !dl->cpus || !dl->codecs) return -ENOMEM; @@ -127,7 +127,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->be_hw_params_fixup = avs_max98927_be_fixup; dl->nonatomic = 1; dl->no_pcm = 1; diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c index bf902540744c..e203ceb83b87 100644 --- a/sound/soc/intel/avs/boards/nau8825.c +++ b/sound/soc/intel/avs/boards/nau8825.c @@ -88,7 +88,8 @@ static int avs_nau8825_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; @@ -203,7 +204,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_nau8825_codec_init; dl->exit = avs_nau8825_codec_exit; dl->be_hw_params_fixup = avs_nau8825_be_fixup; diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c new file mode 100644 index 000000000000..5d0e7a5bdc74 --- /dev/null +++ b/sound/soc/intel/avs/boards/pcm3168a.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2024-2025 Intel Corporation +// +// Author: Cezary Rojewski <cezary.rojewski@intel.com> +// + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +static const struct snd_soc_dapm_widget card_widgets[] = { + SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL), + SND_SOC_DAPM_HP("CPB Stereo HP 2", NULL), + SND_SOC_DAPM_HP("CPB Stereo HP 3", NULL), + SND_SOC_DAPM_LINE("CPB Line Out", NULL), + SND_SOC_DAPM_MIC("CPB Stereo Mic 1", NULL), + SND_SOC_DAPM_MIC("CPB Stereo Mic 2", NULL), + SND_SOC_DAPM_LINE("CPB Line In", NULL), +}; + +static const struct snd_soc_dapm_route card_routes[] = { + { "CPB Stereo HP 1", NULL, "AOUT1L" }, + { "CPB Stereo HP 1", NULL, "AOUT1R" }, + { "CPB Stereo HP 2", NULL, "AOUT2L" }, + { "CPB Stereo HP 2", NULL, "AOUT2R" }, + { "CPB Stereo HP 3", NULL, "AOUT3L" }, + { "CPB Stereo HP 3", NULL, "AOUT3R" }, + { "CPB Line Out", NULL, "AOUT4L" }, + { "CPB Line Out", NULL, "AOUT4R" }, + + { "AIN1L", NULL, "CPB Stereo Mic 1" }, + { "AIN1R", NULL, "CPB Stereo Mic 1" }, + { "AIN2L", NULL, "CPB Stereo Mic 2" }, + { "AIN2R", NULL, "CPB Stereo Mic 2" }, + { "AIN3L", NULL, "CPB Line In" }, + { "AIN3R", NULL, "CPB Line In" }, +}; + +static int avs_pcm3168a_be_fixup(struct snd_soc_pcm_runtime *runtime, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* Set SSP to 24 bit. */ + snd_mask_none(fmt); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +SND_SOC_DAILINK_DEF(pcm3168a_dac, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-dac"))); +SND_SOC_DAILINK_DEF(pcm3168a_adc, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-adc"))); +SND_SOC_DAILINK_DEF(cpu_ssp0, DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin"))); +SND_SOC_DAILINK_DEF(cpu_ssp2, DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin"))); + +static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links) +{ + struct snd_soc_dai_link_component *platform; + struct snd_soc_dai_link *dl; + const int num_dl = 2; + + dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL); + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); + if (!dl || !platform) + return -ENOMEM; + + platform->name = dev_name(dev); + dl[0].num_cpus = 1; + dl[0].num_codecs = 1; + dl[0].platforms = platform; + dl[0].num_platforms = 1; + dl[0].dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; + dl[0].be_hw_params_fixup = avs_pcm3168a_be_fixup; + dl[0].nonatomic = 1; + dl[0].no_pcm = 1; + memcpy(&dl[1], &dl[0], sizeof(*dl)); + + dl[0].name = "SSP0-Codec-dac"; + dl[0].cpus = cpu_ssp0; + dl[0].codecs = pcm3168a_dac; + dl[1].name = "SSP2-Codec-adc"; + dl[1].cpus = cpu_ssp2; + dl[1].codecs = pcm3168a_adc; + + *links = dl; + *num_links = num_dl; + return 0; +} + +static int avs_pcm3168a_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_soc_card *card; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links); + if (ret) + return ret; + + card->name = "avs_pcm3168a"; + card->dev = dev; + card->owner = THIS_MODULE; + card->dapm_widgets = card_widgets; + card->num_dapm_widgets = ARRAY_SIZE(card_widgets); + card->dapm_routes = card_routes; + card->num_dapm_routes = ARRAY_SIZE(card_routes); + card->fully_routed = true; + + return devm_snd_soc_register_card(dev, card); +} + +static const struct platform_device_id avs_pcm3168a_driver_ids[] = { + { + .name = "avs_pcm3168a", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids); + +static struct platform_driver avs_pcm3168a_driver = { + .probe = avs_pcm3168a_probe, + .driver = { + .name = "avs_pcm3168a", + .pm = &snd_soc_pm_ops, + }, + .id_table = avs_pcm3168a_driver_ids, +}; + +module_platform_driver(avs_pcm3168a_driver); + +MODULE_DESCRIPTION("Intel pcm3168a machine driver"); +MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c index 4b6c02a40204..29977aee2153 100644 --- a/sound/soc/intel/avs/boards/rt274.c +++ b/sound/soc/intel/avs/boards/rt274.c @@ -98,7 +98,8 @@ static int avs_rt274_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; @@ -178,7 +179,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_rt274_codec_init; dl->exit = avs_rt274_codec_exit; dl->be_hw_params_fixup = avs_rt274_be_fixup; diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c index e40563ca99fd..6a27c6a9f394 100644 --- a/sound/soc/intel/avs/boards/rt286.c +++ b/sound/soc/intel/avs/boards/rt286.c @@ -59,7 +59,8 @@ static int avs_rt286_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; @@ -146,7 +147,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_rt286_codec_init; dl->exit = avs_rt286_codec_exit; dl->be_hw_params_fixup = avs_rt286_be_fixup; diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c index 94fce07c83f9..a7a463d6f852 100644 --- a/sound/soc/intel/avs/boards/rt298.c +++ b/sound/soc/intel/avs/boards/rt298.c @@ -70,7 +70,8 @@ static int avs_rt298_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; @@ -164,9 +165,9 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->num_platforms = 1; dl->id = 0; if (dmi_first_match(kblr_dmi_table)) - dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; else - dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_rt298_codec_init; dl->exit = avs_rt298_codec_exit; dl->be_hw_params_fixup = avs_rt298_be_fixup; diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c index 30588d9e9ba3..d1c3e29a28ff 100644 --- a/sound/soc/intel/avs/boards/rt5514.c +++ b/sound/soc/intel/avs/boards/rt5514.c @@ -116,7 +116,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_rt5514_codec_init; dl->be_hw_params_fixup = avs_rt5514_be_fixup; dl->nonatomic = 1; diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c index b456b9d14665..e3310b3268ba 100644 --- a/sound/soc/intel/avs/boards/rt5663.c +++ b/sound/soc/intel/avs/boards/rt5663.c @@ -65,7 +65,8 @@ static int avs_rt5663_codec_init(struct snd_soc_pcm_runtime *runtime) jack = &priv->jack; num_pins = ARRAY_SIZE(card_headset_pins); - pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_headset_pins, num_pins, + sizeof(card_headset_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c index 335960cfd7ba..339df0b944c1 100644 --- a/sound/soc/intel/avs/boards/rt5682.c +++ b/sound/soc/intel/avs/boards/rt5682.c @@ -102,7 +102,8 @@ static int avs_rt5682_codec_init(struct snd_soc_pcm_runtime *runtime) jack = snd_soc_card_get_drvdata(card); num_pins = ARRAY_SIZE(card_jack_pins); - pins = devm_kmemdup(card->dev, card_jack_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + pins = devm_kmemdup_array(card->dev, card_jack_pins, num_pins, + sizeof(card_jack_pins[0]), GFP_KERNEL); if (!pins) return -ENOMEM; diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c index cfef00462f66..7667790d5273 100644 --- a/sound/soc/intel/avs/boards/ssm4567.c +++ b/sound/soc/intel/avs/boards/ssm4567.c @@ -97,7 +97,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->name = devm_kasprintf(dev, GFP_KERNEL, AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot)); dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL); - dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs) * 2, GFP_KERNEL); + dl->codecs = devm_kcalloc(dev, 2, sizeof(*dl->codecs), GFP_KERNEL); if (!dl->name || !dl->cpus || !dl->codecs) return -ENOMEM; @@ -116,7 +116,7 @@ static int avs_create_dai_link(struct device *dev, const char *platform_name, in dl->platforms = platform; dl->num_platforms = 1; dl->id = 0; - dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS; + dl->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBC_CFC; dl->init = avs_ssm4567_codec_init; dl->be_hw_params_fixup = avs_ssm4567_be_fixup; dl->nonatomic = 1; diff --git a/sound/soc/intel/avs/cnl.c b/sound/soc/intel/avs/cnl.c index bd3c4bb8bf5a..03f8fb0dc187 100644 --- a/sound/soc/intel/avs/cnl.c +++ b/sound/soc/intel/avs/cnl.c @@ -9,6 +9,7 @@ #include <sound/hdaudio_ext.h> #include "avs.h" #include "messages.h" +#include "registers.h" static void avs_cnl_ipc_interrupt(struct avs_dev *adev) { diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c index dc7dc45e0a0a..2e01dc75a15a 100644 --- a/sound/soc/intel/avs/control.c +++ b/sound/soc/intel/avs/control.c @@ -6,6 +6,7 @@ // Cezary Rojewski <cezary.rojewski@intel.com> // +#include <linux/cleanup.h> #include <sound/soc.h> #include "avs.h" #include "control.h" @@ -31,8 +32,11 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i list_for_each_entry(path, &adev->path_list, node) { list_for_each_entry(ppl, &path->ppl_list, node) { list_for_each_entry(mod, &ppl->mod_list, node) { - if (guid_equal(&mod->template->cfg_ext->type, &AVS_PEAKVOL_MOD_UUID) - && mod->template->ctl_id == id) { + guid_t *type = &mod->template->cfg_ext->type; + + if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) || + guid_equal(type, &AVS_GAIN_MOD_UUID)) && + mod->template->ctl_id == id) { spin_unlock(&adev->path_list_lock); return mod; } @@ -44,70 +48,168 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i return NULL; } -int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +int avs_control_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { - struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private; - struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol); - struct avs_volume_cfg *dspvols = NULL; + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; + struct avs_control_data *ctl_data = mc->dobj.private; struct avs_path_module *active_module; + struct avs_volume_cfg *dspvols; + struct avs_dev *adev; size_t num_dspvols; - int ret = 0; + int ret, i; + + adev = avs_get_kcontrol_adev(kctl); - /* prevent access to modules while path is being constructed */ - mutex_lock(&adev->path_mutex); + /* Prevent access to modules while path is being constructed. */ + guard(mutex)(&adev->path_mutex); active_module = avs_get_volume_module(adev, ctl_data->id); if (active_module) { ret = avs_ipc_peakvol_get_volume(adev, active_module->module_id, active_module->instance_id, &dspvols, &num_dspvols); - if (!ret) - ucontrol->value.integer.value[0] = dspvols[0].target_volume; + if (ret) + return AVS_IPC_RET(ret); - ret = AVS_IPC_RET(ret); + /* Do not copy more than the control can store. */ + num_dspvols = min_t(u32, num_dspvols, SND_SOC_TPLG_MAX_CHAN); + for (i = 0; i < num_dspvols; i++) + ctl_data->values[i] = dspvols[i].target_volume; kfree(dspvols); - } else { - ucontrol->value.integer.value[0] = ctl_data->volume; } - mutex_unlock(&adev->path_mutex); - return ret; + memcpy(uctl->value.integer.value, ctl_data->values, sizeof(ctl_data->values)); + return 0; } -int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +int avs_control_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { - struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private; - struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol); - long *volume = &ctl_data->volume; struct avs_path_module *active_module; - struct avs_volume_cfg dspvol = {0}; - long ctlvol = ucontrol->value.integer.value[0]; - int ret = 0, changed = 0; + struct avs_control_data *ctl_data; + struct soc_mixer_control *mc; + struct avs_dev *adev; + long *input; + int ret, i; + + mc = (struct soc_mixer_control *)kctl->private_value; + ctl_data = mc->dobj.private; + adev = avs_get_kcontrol_adev(kctl); + input = uctl->value.integer.value; + i = 0; + + /* mc->num_channels can be 0. */ + do { + if (input[i] < mc->min || input[i] > mc->max) + return -EINVAL; + } while (++i < mc->num_channels); + + if (!memcmp(ctl_data->values, input, sizeof(ctl_data->values))) + return 0; + + /* Prevent access to modules while path is being constructed. */ + guard(mutex)(&adev->path_mutex); - if (ctlvol < 0 || ctlvol > mc->max) - return -EINVAL; + active_module = avs_get_volume_module(adev, ctl_data->id); + if (active_module) { + ret = avs_peakvol_set_volume(adev, active_module, mc, input); + if (ret) + return ret; + } - /* prevent access to modules while path is being constructed */ - mutex_lock(&adev->path_mutex); + memcpy(ctl_data->values, input, sizeof(ctl_data->values)); + return 1; +} - if (*volume != ctlvol) { - *volume = ctlvol; - changed = 1; - } +int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = max_t(u32, 1, mc->num_channels); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max; + return 0; +} + +int avs_control_mute_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; + struct avs_control_data *ctl_data = mc->dobj.private; + struct avs_path_module *active_module; + struct avs_mute_cfg *dspmutes; + struct avs_dev *adev; + size_t num_dspmutes; + int ret, i; + + adev = avs_get_kcontrol_adev(kctl); + + /* Prevent access to modules while path is being constructed. */ + guard(mutex)(&adev->path_mutex); active_module = avs_get_volume_module(adev, ctl_data->id); if (active_module) { - dspvol.channel_id = AVS_ALL_CHANNELS_MASK; - dspvol.target_volume = *volume; + ret = avs_ipc_peakvol_get_mute(adev, active_module->module_id, + active_module->instance_id, &dspmutes, + &num_dspmutes); + if (ret) + return AVS_IPC_RET(ret); + + /* Do not copy more than the control can store. */ + num_dspmutes = min_t(u32, num_dspmutes, SND_SOC_TPLG_MAX_CHAN); + for (i = 0; i < num_dspmutes; i++) + ctl_data->values[i] = !dspmutes[i].mute; + kfree(dspmutes); + } - ret = avs_ipc_peakvol_set_volume(adev, active_module->module_id, - active_module->instance_id, &dspvol); - ret = AVS_IPC_RET(ret); + memcpy(uctl->value.integer.value, ctl_data->values, sizeof(ctl_data->values)); + return 0; +} + +int avs_control_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) +{ + struct avs_path_module *active_module; + struct avs_control_data *ctl_data; + struct soc_mixer_control *mc; + struct avs_dev *adev; + long *input; + int ret, i; + + mc = (struct soc_mixer_control *)kctl->private_value; + ctl_data = mc->dobj.private; + adev = avs_get_kcontrol_adev(kctl); + input = uctl->value.integer.value; + i = 0; + + /* mc->num_channels can be 0. */ + do { + if (input[i] < mc->min || input[i] > mc->max) + return -EINVAL; + } while (++i < mc->num_channels); + + if (!memcmp(ctl_data->values, input, sizeof(ctl_data->values))) + return 0; + + /* Prevent access to modules while path is being constructed. */ + guard(mutex)(&adev->path_mutex); + + active_module = avs_get_volume_module(adev, ctl_data->id); + if (active_module) { + ret = avs_peakvol_set_mute(adev, active_module, mc, input); + if (ret) + return ret; } - mutex_unlock(&adev->path_mutex); + memcpy(ctl_data->values, input, sizeof(ctl_data->values)); + return 1; +} + +int avs_control_mute_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; - return ret ? ret : changed; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = max_t(u32, 1, mc->num_channels); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max; + return 0; } diff --git a/sound/soc/intel/avs/control.h b/sound/soc/intel/avs/control.h index d9fac3569e8d..08b2919e4629 100644 --- a/sound/soc/intel/avs/control.h +++ b/sound/soc/intel/avs/control.h @@ -10,14 +10,18 @@ #define __SOUND_SOC_INTEL_AVS_CTRL_H #include <sound/control.h> +#include <uapi/sound/asoc.h> struct avs_control_data { u32 id; - - long volume; + long values[SND_SOC_TPLG_MAX_CHAN]; }; -int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int avs_control_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl); +int avs_control_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl); +int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo); +int avs_control_mute_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl); +int avs_control_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl); +int avs_control_mute_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo); #endif diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index 73d4bde9b2f7..8fbf33e30dfc 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -612,7 +612,7 @@ static int avs_suspend_standby(struct avs_dev *adev) return 0; } -static int __maybe_unused avs_suspend_common(struct avs_dev *adev, bool low_power) +static int avs_suspend_common(struct avs_dev *adev, bool low_power) { struct hdac_bus *bus = &adev->base.core; int ret; @@ -673,7 +673,7 @@ static int avs_resume_standby(struct avs_dev *adev) return 0; } -static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool low_power, bool purge) +static int avs_resume_common(struct avs_dev *adev, bool low_power, bool purge) { struct hdac_bus *bus = &adev->base.core; int ret; @@ -696,41 +696,41 @@ static int __maybe_unused avs_resume_common(struct avs_dev *adev, bool low_power return 0; } -static int __maybe_unused avs_suspend(struct device *dev) +static int avs_suspend(struct device *dev) { return avs_suspend_common(to_avs_dev(dev), true); } -static int __maybe_unused avs_resume(struct device *dev) +static int avs_resume(struct device *dev) { return avs_resume_common(to_avs_dev(dev), true, true); } -static int __maybe_unused avs_runtime_suspend(struct device *dev) +static int avs_runtime_suspend(struct device *dev) { return avs_suspend_common(to_avs_dev(dev), true); } -static int __maybe_unused avs_runtime_resume(struct device *dev) +static int avs_runtime_resume(struct device *dev) { return avs_resume_common(to_avs_dev(dev), true, false); } -static int __maybe_unused avs_freeze(struct device *dev) +static int avs_freeze(struct device *dev) { return avs_suspend_common(to_avs_dev(dev), false); } -static int __maybe_unused avs_thaw(struct device *dev) +static int avs_thaw(struct device *dev) { return avs_resume_common(to_avs_dev(dev), false, true); } -static int __maybe_unused avs_poweroff(struct device *dev) +static int avs_poweroff(struct device *dev) { return avs_suspend_common(to_avs_dev(dev), false); } -static int __maybe_unused avs_restore(struct device *dev) +static int avs_restore(struct device *dev) { return avs_resume_common(to_avs_dev(dev), false, true); } @@ -742,7 +742,7 @@ static const struct dev_pm_ops avs_dev_pm = { .thaw = avs_thaw, .poweroff = avs_poweroff, .restore = avs_restore, - SET_RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL) + RUNTIME_PM_OPS(avs_runtime_suspend, avs_runtime_resume, NULL) }; static const struct avs_sram_spec skl_sram_spec = { @@ -829,10 +829,10 @@ static const struct avs_spec jsl_desc = { .hipc = &cnl_hipc_spec, }; -#define AVS_TGL_BASED_SPEC(sname) \ +#define AVS_TGL_BASED_SPEC(sname, min) \ static const struct avs_spec sname##_desc = { \ .name = #sname, \ - .min_fw_version = { 10, 29, 0, 5646 }, \ + .min_fw_version = { 10, min, 0, 5646 }, \ .dsp_ops = &avs_tgl_dsp_ops, \ .core_init_mask = 1, \ .attributes = AVS_PLATATTR_IMR, \ @@ -840,11 +840,11 @@ static const struct avs_spec sname##_desc = { \ .hipc = &cnl_hipc_spec, \ } -AVS_TGL_BASED_SPEC(lkf); -AVS_TGL_BASED_SPEC(tgl); -AVS_TGL_BASED_SPEC(ehl); -AVS_TGL_BASED_SPEC(adl); -AVS_TGL_BASED_SPEC(adl_n); +AVS_TGL_BASED_SPEC(lkf, 28); +AVS_TGL_BASED_SPEC(tgl, 29); +AVS_TGL_BASED_SPEC(ehl, 30); +AVS_TGL_BASED_SPEC(adl, 35); +AVS_TGL_BASED_SPEC(adl_n, 35); static const struct pci_device_id avs_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) }, @@ -893,7 +893,7 @@ static struct pci_driver avs_pci_driver = { .shutdown = avs_pci_shutdown, .dev_groups = avs_attr_groups, .driver = { - .pm = &avs_dev_pm, + .pm = pm_ptr(&avs_dev_pm), }, }; module_pci_driver(avs_pci_driver); @@ -902,3 +902,13 @@ MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); MODULE_AUTHOR("Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>"); MODULE_DESCRIPTION("Intel cAVS sound driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("intel/skl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/apl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/cnl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/icl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/jsl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/lkf/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/tgl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/ehl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/adl/dsp_basefw.bin"); +MODULE_FIRMWARE("intel/adl_n/dsp_basefw.bin"); diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c index 1767ded4d983..8c4edda97f75 100644 --- a/sound/soc/intel/avs/debugfs.c +++ b/sound/soc/intel/avs/debugfs.c @@ -10,6 +10,7 @@ #include <linux/kfifo.h> #include <linux/wait.h> #include <linux/sched/signal.h> +#include <linux/string_helpers.h> #include <sound/soc.h> #include "avs.h" #include "messages.h" diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c index 7b47e52c2b39..b9de691e9b9b 100644 --- a/sound/soc/intel/avs/dsp.c +++ b/sound/soc/intel/avs/dsp.c @@ -6,6 +6,7 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/string_choices.h> #include <sound/hdaudio_ext.h> #include "avs.h" #include "registers.h" @@ -39,7 +40,7 @@ int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) AVS_ADSPCS_TIMEOUT_US); if (ret) dev_err(adev->dev, "core_mask %d power %s failed: %d\n", - core_mask, power ? "on" : "off", ret); + core_mask, str_on_off(power), ret); return ret; } diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index 4fba46e77c47..08ed9d96738a 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -184,10 +184,11 @@ static void avs_dsp_receive_rx(struct avs_dev *adev, u64 header) { struct avs_ipc *ipc = adev->ipc; union avs_reply_msg msg = AVS_MSG(header); - u64 reg; + u32 sts, lec; - reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); - trace_avs_ipc_reply_msg(header, reg); + sts = snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)); + lec = snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)); + trace_avs_ipc_reply_msg(header, sts, lec); ipc->rx.header = header; /* Abort copying payload if request processing was unsuccessful. */ @@ -209,10 +210,11 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) union avs_notify_msg msg = AVS_MSG(header); size_t data_size = 0; void *data = NULL; - u64 reg; + u32 sts, lec; - reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); - trace_avs_ipc_notify_msg(header, reg); + sts = snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)); + lec = snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)); + trace_avs_ipc_notify_msg(header, sts, lec); /* Ignore spurious notifications until handshake is established. */ if (!adev->ipc->ready && msg.notify_msg_type != AVS_NOTIFY_FW_READY) { @@ -367,13 +369,16 @@ static void avs_ipc_msg_init(struct avs_ipc *ipc, struct avs_ipc_msg *reply) static void avs_dsp_send_tx(struct avs_dev *adev, struct avs_ipc_msg *tx, bool read_fwregs) { const struct avs_spec *const spec = adev->spec; - u64 reg = ULONG_MAX; + u32 sts = UINT_MAX; + u32 lec = UINT_MAX; tx->header |= spec->hipc->req_busy_mask; - if (read_fwregs) - reg = readq(avs_sram_addr(adev, AVS_FW_REGS_WINDOW)); + if (read_fwregs) { + sts = snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)); + lec = snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev)); + } - trace_avs_request(tx, reg); + trace_avs_request(tx, sts, lec); if (tx->size) memcpy_toio(avs_downlink_addr(adev), tx->data, tx->size); diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c index 890efd2f1fea..0b29941feb0e 100644 --- a/sound/soc/intel/avs/loader.c +++ b/sound/soc/intel/avs/loader.c @@ -167,7 +167,8 @@ int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw) (reg & AVS_ROM_INIT_DONE) == AVS_ROM_INIT_DONE, AVS_ROM_INIT_POLLING_US, SKL_ROM_INIT_TIMEOUT_US); if (ret < 0) { - dev_err(adev->dev, "rom init timeout: %d\n", ret); + dev_err(adev->dev, "rom init failed: %d, status: 0x%08x, lec: 0x%08x\n", + ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); return ret; } @@ -180,7 +181,8 @@ int avs_cldma_load_basefw(struct avs_dev *adev, struct firmware *fw) AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US); hda_cldma_stop(cl); if (ret < 0) { - dev_err(adev->dev, "transfer fw failed: %d\n", ret); + dev_err(adev->dev, "transfer fw failed: %d, status: 0x%08x, lec: 0x%08x\n", + ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); return ret; } @@ -308,12 +310,13 @@ avs_hda_init_rom(struct avs_dev *adev, unsigned int dma_id, bool purge) } /* await ROM init */ - ret = snd_hdac_adsp_readq_poll(adev, spec->sram->rom_status_offset, reg, + ret = snd_hdac_adsp_readl_poll(adev, spec->sram->rom_status_offset, reg, (reg & 0xF) == AVS_ROM_INIT_DONE || (reg & 0xF) == APL_ROM_FW_ENTERED, AVS_ROM_INIT_POLLING_US, APL_ROM_INIT_TIMEOUT_US); if (ret < 0) { - dev_err(adev->dev, "rom init timeout: %d\n", ret); + dev_err(adev->dev, "rom init failed: %d, status: 0x%08x, lec: 0x%08x\n", + ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); goto err; } @@ -337,15 +340,15 @@ static int avs_imr_load_basefw(struct avs_dev *adev) /* DMA id ignored when flashing from IMR as no transfer occurs. */ ret = avs_hda_init_rom(adev, 0, false); - if (ret < 0) { - dev_err(adev->dev, "rom init failed: %d\n", ret); + if (ret < 0) return ret; - } ret = wait_for_completion_timeout(&adev->fw_ready, msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS)); if (!ret) { - dev_err(adev->dev, "firmware ready timeout\n"); + dev_err(adev->dev, "firmware ready timeout, status: 0x%08x, lec: 0x%08x\n", + snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)), + snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); return -ETIMEDOUT; } @@ -392,7 +395,7 @@ int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw) ret = avs_hda_init_rom(adev, dma_id, true); if (!ret) break; - dev_info(adev->dev, "#%d rom init fail: %d\n", i + 1, ret); + dev_info(adev->dev, "#%d rom init failed: %d\n", i + 1, ret); } if (ret < 0) goto cleanup_resources; @@ -404,7 +407,8 @@ int avs_hda_load_basefw(struct avs_dev *adev, struct firmware *fw) AVS_FW_INIT_POLLING_US, AVS_FW_INIT_TIMEOUT_US); snd_hdac_dsp_trigger(hstream, false); if (ret < 0) { - dev_err(adev->dev, "transfer fw failed: %d\n", ret); + dev_err(adev->dev, "transfer fw failed: %d, status: 0x%08x, lec: 0x%08x\n", + ret, reg, snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); } @@ -584,7 +588,9 @@ static int avs_dsp_load_basefw(struct avs_dev *adev) ret = wait_for_completion_timeout(&adev->fw_ready, msecs_to_jiffies(AVS_FW_INIT_TIMEOUT_MS)); if (!ret) { - dev_err(adev->dev, "firmware ready timeout\n"); + dev_err(adev->dev, "firmware ready timeout, status: 0x%08x, lec: 0x%08x\n", + snd_hdac_adsp_readl(adev, AVS_FW_REG_STATUS(adev)), + snd_hdac_adsp_readl(adev, AVS_FW_REG_ERROR(adev))); avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); ret = -ETIMEDOUT; goto release_fw; @@ -597,7 +603,7 @@ release_fw: return ret; } -int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) +static int avs_load_firmware(struct avs_dev *adev, bool purge) { struct avs_soc_component *acomp; int ret, i; @@ -651,40 +657,41 @@ reenable_gating: return 0; } -int avs_dsp_first_boot_firmware(struct avs_dev *adev) +static int avs_config_basefw(struct avs_dev *adev) { - int ret, i; + int ret; - if (avs_platattr_test(adev, CLDMA)) { - ret = hda_cldma_init(&code_loader, &adev->base.core, - adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE); - if (ret < 0) { - dev_err(adev->dev, "cldma init failed: %d\n", ret); + if (adev->spec->dsp_ops->config_basefw) { + ret = avs_dsp_op(adev, config_basefw); + if (ret) return ret; - } } - ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); - if (ret < 0) - return ret; + return 0; +} - ret = avs_dsp_boot_firmware(adev, true); - if (ret < 0) { - dev_err(adev->dev, "firmware boot failed: %d\n", ret); +int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) +{ + int ret; + + ret = avs_load_firmware(adev, purge); + if (ret) return ret; - } + + return avs_config_basefw(adev); +} + +static int avs_dsp_alloc_resources(struct avs_dev *adev) +{ + int ret, i; ret = avs_ipc_get_hw_config(adev, &adev->hw_cfg); - if (ret) { - dev_err(adev->dev, "get hw cfg failed: %d\n", ret); + if (ret) return AVS_IPC_RET(ret); - } ret = avs_ipc_get_fw_config(adev, &adev->fw_cfg); - if (ret) { - dev_err(adev->dev, "get fw cfg failed: %d\n", ret); + if (ret) return AVS_IPC_RET(ret); - } adev->core_refs = devm_kcalloc(adev->dev, adev->hw_cfg.dsp_cores, sizeof(*adev->core_refs), GFP_KERNEL); @@ -703,6 +710,31 @@ int avs_dsp_first_boot_firmware(struct avs_dev *adev) strscpy(adev->lib_names[0], "BASEFW", AVS_LIB_NAME_SIZE); ida_init(&adev->ppl_ida); - return 0; } + +int avs_dsp_first_boot_firmware(struct avs_dev *adev) +{ + int ret; + + if (avs_platattr_test(adev, CLDMA)) { + ret = hda_cldma_init(&code_loader, &adev->base.core, + adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE); + if (ret < 0) { + dev_err(adev->dev, "cldma init failed: %d\n", ret); + return ret; + } + } + + ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); + if (ret < 0) + return ret; + + ret = avs_dsp_boot_firmware(adev, true); + if (ret < 0) { + dev_err(adev->dev, "firmware boot failed: %d\n", ret); + return ret; + } + + return avs_dsp_alloc_resources(adev); +} diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index ec458bd51b10..a5ba27983091 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -400,10 +400,12 @@ int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg) AVS_BASEFW_FIRMWARE_CONFIG, NULL, 0, &payload, &payload_size); if (ret) - return ret; + goto err; /* Non-zero payload expected for FIRMWARE_CONFIG. */ - if (!payload_size) - return -EREMOTEIO; + if (!payload_size) { + ret = -EREMOTEIO; + goto err; + } while (offset < payload_size) { tlv = (struct avs_tlv *)(payload + offset); @@ -502,6 +504,47 @@ int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg) /* No longer needed, free it as it's owned by the get_large_config() caller. */ kfree(payload); +err: + if (ret) + dev_err(adev->dev, "get fw cfg failed: %d\n", ret); + return ret; +} + +int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...) +{ + struct avs_tlv *tlv; + void *payload; + size_t offset; + va_list args; + int ret, i; + + payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + va_start(args, num_tlvs); + for (offset = i = 0; i < num_tlvs && offset < AVS_MAILBOX_SIZE - sizeof(*tlv); i++) { + tlv = (struct avs_tlv *)(payload + offset); + tlv->type = va_arg(args, u32); + tlv->length = va_arg(args, u32); + + offset += sizeof(*tlv) + tlv->length; + if (offset > AVS_MAILBOX_SIZE) + break; + + memcpy(tlv->value, va_arg(args, u8*), tlv->length); + } + + if (i == num_tlvs) + ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, + AVS_BASEFW_FIRMWARE_CONFIG, payload, offset); + else + ret = -ERANGE; + + va_end(args); + kfree(payload); + if (ret) + dev_err(adev->dev, "set fw cfg failed: %d\n", ret); return ret; } @@ -517,10 +560,12 @@ int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg) AVS_BASEFW_HARDWARE_CONFIG, NULL, 0, &payload, &payload_size); if (ret) - return ret; + goto err; /* Non-zero payload expected for HARDWARE_CONFIG. */ - if (!payload_size) - return -EREMOTEIO; + if (!payload_size) { + ret = -EREMOTEIO; + goto err; + } while (offset < payload_size) { tlv = (struct avs_tlv *)(payload + offset); @@ -590,6 +635,9 @@ int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg) exit: /* No longer needed, free it as it's owned by the get_large_config() caller. */ kfree(payload); +err: + if (ret) + dev_err(adev->dev, "get hw cfg failed: %d\n", ret); return ret; } @@ -629,13 +677,6 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, (u8 *)&cpr_fmt, sizeof(cpr_fmt)); } -int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, - struct avs_volume_cfg *vol) -{ - return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, (u8 *)vol, - sizeof(*vol)); -} - int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, struct avs_volume_cfg **vols, size_t *num_vols) { @@ -658,6 +699,110 @@ int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_ return 0; } +int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vol) +{ + return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, + (u8 *)vol, sizeof(*vol)); +} + +int avs_ipc_peakvol_set_volumes(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vols, size_t num_vols) +{ + struct avs_tlv *tlv; + size_t offset; + size_t size; + u8 *payload; + int ret, i; + + size = num_vols * sizeof(*vols); + size += num_vols * sizeof(*tlv); + if (size > AVS_MAILBOX_SIZE) + return -EINVAL; + + payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + for (offset = i = 0; i < num_vols; i++) { + tlv = (struct avs_tlv *)(payload + offset); + + tlv->type = AVS_PEAKVOL_VOLUME; + tlv->length = sizeof(*vols); + memcpy(tlv->value, &vols[i], tlv->length); + + offset += sizeof(*tlv) + tlv->length; + } + + ret = avs_ipc_set_large_config(adev, module_id, instance_id, AVS_VENDOR_CONFIG, payload, + size); + kfree(payload); + return ret; +} + +int avs_ipc_peakvol_get_mute(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg **mutes, size_t *num_mutes) +{ + size_t payload_size; + u8 *payload; + int ret; + + ret = avs_ipc_get_large_config(adev, module_id, instance_id, AVS_PEAKVOL_MUTE, NULL, 0, + &payload, &payload_size); + if (ret) + return ret; + + /* Non-zero payload expected for PEAKVOL_MUTE. */ + if (!payload_size) + return -EREMOTEIO; + + *mutes = (struct avs_mute_cfg *)payload; + *num_mutes = payload_size / sizeof(**mutes); + + return 0; +} + +int avs_ipc_peakvol_set_mute(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg *mute) +{ + return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_MUTE, + (u8 *)mute, sizeof(*mute)); +} + +int avs_ipc_peakvol_set_mutes(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg *mutes, size_t num_mutes) +{ + struct avs_tlv *tlv; + size_t offset; + size_t size; + u8 *payload; + int ret, i; + + size = num_mutes * sizeof(*mutes); + size += num_mutes * sizeof(*tlv); + if (size > AVS_MAILBOX_SIZE) + return -EINVAL; + + payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + for (offset = i = 0; i < num_mutes; i++) { + tlv = (struct avs_tlv *)(payload + offset); + + tlv->type = AVS_PEAKVOL_MUTE; + tlv->length = sizeof(*mutes); + memcpy(tlv->value, &mutes[i], tlv->length); + + offset += sizeof(*tlv) + tlv->length; + } + + ret = avs_ipc_set_large_config(adev, module_id, instance_id, AVS_VENDOR_CONFIG, payload, + size); + kfree(payload); + return ret; +} + #ifdef CONFIG_DEBUG_FS int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size) { diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index d0bdb7d9266c..2f243802ccc2 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -451,6 +451,8 @@ enum avs_fw_cfg_params { AVS_FW_CFG_RESERVED, AVS_FW_CFG_POWER_GATING_POLICY, AVS_FW_CFG_ASSERT_MODE, + AVS_FW_CFG_RESERVED2, + AVS_FW_CFG_BUS_HARDWARE_ID, }; struct avs_fw_cfg { @@ -475,7 +477,14 @@ struct avs_fw_cfg { u32 power_gating_policy; }; +struct avs_bus_hwid { + u32 device; + u32 subsystem; + u8 revision; +}; + int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg); +int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...); enum avs_hw_cfg_params { AVS_HW_CFG_AVS_VER, @@ -643,6 +652,9 @@ int avs_ipc_set_system_time(struct avs_dev *adev); #define AVS_INTELWOV_MOD_UUID \ GUID_INIT(0xEC774FA9, 0x28D3, 0x424A, 0x90, 0xE4, 0x69, 0xF9, 0x84, 0xF1, 0xEE, 0xB7) +#define AVS_WOVHOSTM_MOD_UUID \ + GUID_INIT(0xF9ED62B7, 0x092E, 0x4A90, 0x8F, 0x4D, 0x82, 0xDA, 0xA8, 0xB3, 0x8F, 0x3B) + /* channel map */ enum avs_channel_index { AVS_CHANNEL_LEFT = 0, @@ -802,6 +814,15 @@ struct avs_volume_cfg { } __packed; static_assert(sizeof(struct avs_volume_cfg) == 24); +struct avs_mute_cfg { + u32 channel_id; + u32 mute; + u32 curve_type; + u32 reserved; /* alignment */ + u64 curve_duration; +} __packed; +static_assert(sizeof(struct avs_mute_cfg) == 24); + struct avs_peakvol_cfg { struct avs_modcfg_base base; struct avs_volume_cfg vols[]; @@ -859,8 +880,7 @@ static_assert(sizeof(struct avs_aec_cfg) == 92); struct avs_asrc_cfg { struct avs_modcfg_base base; u32 out_freq; - u32 rsvd0:1; - u32 mode:1; + u32 mode:2; u32 rsvd2:2; u32 disable_jitter_buffer:1; u32 rsvd3:27; @@ -873,8 +893,20 @@ struct avs_wov_cfg { } __packed; static_assert(sizeof(struct avs_wov_cfg) == 44); +struct avs_whm_cfg { + struct avs_modcfg_base base; + /* Audio format for output pin 0 */ + struct avs_audio_format ref_fmt; + struct avs_audio_format out_fmt; + u32 wake_tick_period; + struct avs_copier_gtw_cfg gtw_cfg; +} __packed; +static_assert(sizeof(struct avs_whm_cfg) == 108); + /* Module runtime parameters */ +#define AVS_VENDOR_CONFIG 0xFF + enum avs_copier_runtime_param { AVS_COPIER_SET_SINK_FORMAT = 2, }; @@ -893,6 +925,7 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, enum avs_peakvol_runtime_param { AVS_PEAKVOL_VOLUME = 0, + AVS_PEAKVOL_MUTE = 3, }; enum avs_audio_curve_type { @@ -900,10 +933,18 @@ enum avs_audio_curve_type { AVS_AUDIO_CURVE_WINDOWS_FADE = 1, }; -int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, - struct avs_volume_cfg *vol); int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, struct avs_volume_cfg **vols, size_t *num_vols); +int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vol); +int avs_ipc_peakvol_set_volumes(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vols, size_t num_vols); +int avs_ipc_peakvol_get_mute(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg **mutes, size_t *num_mutes); +int avs_ipc_peakvol_set_mute(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg *mute); +int avs_ipc_peakvol_set_mutes(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_mute_cfg *mutes, size_t num_mutes); #define AVS_PROBE_INST_ID 0 diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index f31d5e2caa7b..ef0c1d125d66 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -115,154 +115,193 @@ avs_path_find_variant(struct avs_dev *adev, return NULL; } -__maybe_unused -static bool avs_dma_type_is_host(u32 dma_type) +static void avs_init_node_id(union avs_connector_node_id *node_id, + struct avs_tplg_modcfg_ext *te, u32 dma_id) { - return dma_type == AVS_DMA_HDA_HOST_OUTPUT || - dma_type == AVS_DMA_HDA_HOST_INPUT; -} + node_id->val = 0; + node_id->dma_type = te->copier.dma_type; -__maybe_unused -static bool avs_dma_type_is_link(u32 dma_type) -{ - return !avs_dma_type_is_host(dma_type); -} + switch (node_id->dma_type) { + case AVS_DMA_DMIC_LINK_INPUT: + case AVS_DMA_I2S_LINK_OUTPUT: + case AVS_DMA_I2S_LINK_INPUT: + /* Gateway's virtual index is statically assigned in the topology. */ + node_id->vindex = te->copier.vindex.val; + break; -__maybe_unused -static bool avs_dma_type_is_output(u32 dma_type) -{ - return dma_type == AVS_DMA_HDA_HOST_OUTPUT || - dma_type == AVS_DMA_HDA_LINK_OUTPUT || - dma_type == AVS_DMA_I2S_LINK_OUTPUT; -} + case AVS_DMA_HDA_HOST_OUTPUT: + case AVS_DMA_HDA_HOST_INPUT: + /* Gateway's virtual index is dynamically assigned with DMA ID */ + node_id->vindex = dma_id; + break; -__maybe_unused -static bool avs_dma_type_is_input(u32 dma_type) -{ - return !avs_dma_type_is_output(dma_type); + case AVS_DMA_HDA_LINK_OUTPUT: + case AVS_DMA_HDA_LINK_INPUT: + node_id->vindex = te->copier.vindex.val | dma_id; + break; + + default: + *node_id = INVALID_NODE_ID; + break; + } } -static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod) -{ - struct avs_tplg_module *t = mod->template; - struct avs_copier_cfg *cfg; - struct acpi_nhlt_format_config *ep_blob; - struct acpi_nhlt_endpoint *ep; - union avs_connector_node_id node_id = {0}; - size_t cfg_size, data_size; - void *data = NULL; - u32 dma_type; - int ret; +/* Every BLOB contains at least gateway attributes. */ +static struct acpi_nhlt_config *default_blob = (struct acpi_nhlt_config *)&(u32[2]) {4}; - data_size = sizeof(cfg->gtw_cfg.config); - dma_type = t->cfg_ext->copier.dma_type; - node_id.dma_type = dma_type; +static struct acpi_nhlt_config * +avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t) +{ + struct acpi_nhlt_format_config *fmtcfg; + struct avs_tplg_modcfg_ext *te; + struct avs_audio_format *fmt; + int link_type, dev_type; + int bus_id, dir; - switch (dma_type) { - struct avs_audio_format *fmt; - int direction; + te = t->cfg_ext; + switch (te->copier.dma_type) { case AVS_DMA_I2S_LINK_OUTPUT: - case AVS_DMA_I2S_LINK_INPUT: - if (avs_dma_type_is_input(dma_type)) - direction = SNDRV_PCM_STREAM_CAPTURE; - else - direction = SNDRV_PCM_STREAM_PLAYBACK; - - if (t->cfg_ext->copier.blob_fmt) - fmt = t->cfg_ext->copier.blob_fmt; - else if (direction == SNDRV_PCM_STREAM_CAPTURE) - fmt = t->in_fmt; - else - fmt = t->cfg_ext->copier.out_fmt; - - ep = acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP, - ACPI_NHLT_DEVICETYPE_CODEC, direction, - t->cfg_ext->copier.vindex.i2s.instance); - ep_blob = acpi_nhlt_endpoint_find_fmtcfg(ep, fmt->num_channels, fmt->sampling_freq, - fmt->valid_bit_depth, fmt->bit_depth); - if (!ep_blob) { - dev_err(adev->dev, "no I2S ep_blob found\n"); - return -ENOENT; - } - - data = ep_blob->config.capabilities; - data_size = ep_blob->config.capabilities_size; - /* I2S gateway's vindex is statically assigned in topology */ - node_id.vindex = t->cfg_ext->copier.vindex.val; + link_type = ACPI_NHLT_LINKTYPE_SSP; + dev_type = ACPI_NHLT_DEVICETYPE_CODEC; + bus_id = te->copier.vindex.i2s.instance; + dir = SNDRV_PCM_STREAM_PLAYBACK; + fmt = te->copier.out_fmt; + break; + case AVS_DMA_I2S_LINK_INPUT: + link_type = ACPI_NHLT_LINKTYPE_SSP; + dev_type = ACPI_NHLT_DEVICETYPE_CODEC; + bus_id = te->copier.vindex.i2s.instance; + dir = SNDRV_PCM_STREAM_CAPTURE; + fmt = t->in_fmt; break; case AVS_DMA_DMIC_LINK_INPUT: - direction = SNDRV_PCM_STREAM_CAPTURE; - - if (t->cfg_ext->copier.blob_fmt) - fmt = t->cfg_ext->copier.blob_fmt; - else - fmt = t->in_fmt; - - ep = acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, direction, 0); - ep_blob = acpi_nhlt_endpoint_find_fmtcfg(ep, fmt->num_channels, fmt->sampling_freq, - fmt->valid_bit_depth, fmt->bit_depth); - if (!ep_blob) { - dev_err(adev->dev, "no DMIC ep_blob found\n"); - return -ENOENT; - } - - data = ep_blob->config.capabilities; - data_size = ep_blob->config.capabilities_size; - /* DMIC gateway's vindex is statically assigned in topology */ - node_id.vindex = t->cfg_ext->copier.vindex.val; - + link_type = ACPI_NHLT_LINKTYPE_PDM; + dev_type = -1; /* ignored */ + bus_id = 0; + dir = SNDRV_PCM_STREAM_CAPTURE; + fmt = t->in_fmt; break; - case AVS_DMA_HDA_HOST_OUTPUT: - case AVS_DMA_HDA_HOST_INPUT: - /* HOST gateway's vindex is dynamically assigned with DMA id */ - node_id.vindex = mod->owner->owner->dma_id; - break; + default: + return default_blob; + } - case AVS_DMA_HDA_LINK_OUTPUT: - case AVS_DMA_HDA_LINK_INPUT: - node_id.vindex = t->cfg_ext->copier.vindex.val | - mod->owner->owner->dma_id; - break; + /* Override format selection if necessary. */ + if (te->copier.blob_fmt) + fmt = te->copier.blob_fmt; - case INVALID_OBJECT_ID: - default: - node_id = INVALID_NODE_ID; - break; + fmtcfg = acpi_nhlt_find_fmtcfg(link_type, dev_type, dir, bus_id, + fmt->num_channels, fmt->sampling_freq, fmt->valid_bit_depth, + fmt->bit_depth); + if (!fmtcfg) { + dev_warn(adev->dev, "Endpoint format configuration not found.\n"); + return ERR_PTR(-ENOENT); } - cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config) + data_size; - if (cfg_size > AVS_MAILBOX_SIZE) - return -EINVAL; + if (fmtcfg->config.capabilities_size < default_blob->capabilities_size) + return ERR_PTR(-ETOOSMALL); + /* The firmware expects the payload to be DWORD-aligned. */ + if (fmtcfg->config.capabilities_size % sizeof(u32)) + return ERR_PTR(-EINVAL); + return &fmtcfg->config; +} + +static int avs_fill_gtw_config(struct avs_dev *adev, struct avs_copier_gtw_cfg *gtw, + struct avs_tplg_module *t, size_t *cfg_size) +{ + struct acpi_nhlt_config *blob; + size_t gtw_size; + + blob = avs_nhlt_config_or_default(adev, t); + if (IS_ERR(blob)) + return PTR_ERR(blob); + + gtw_size = blob->capabilities_size; + if (*cfg_size + gtw_size > AVS_MAILBOX_SIZE) + return -E2BIG; + + gtw->config_length = gtw_size / sizeof(u32); + memcpy(gtw->config.blob, blob->capabilities, blob->capabilities_size); + *cfg_size += gtw_size; + + return 0; +} + +static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_tplg_modcfg_ext *te; + struct avs_copier_cfg *cfg; + size_t cfg_size; + u32 dma_id; + int ret; + + te = t->cfg_ext; cfg = adev->modcfg_buf; - memset(cfg, 0, cfg_size); + dma_id = mod->owner->owner->dma_id; + cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config); + + ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, &cfg_size); + if (ret) + return ret; + cfg->base.cpc = t->cfg_base->cpc; cfg->base.ibs = t->cfg_base->ibs; cfg->base.obs = t->cfg_base->obs; cfg->base.is_pages = t->cfg_base->is_pages; cfg->base.audio_fmt = *t->in_fmt; - cfg->out_fmt = *t->cfg_ext->copier.out_fmt; - cfg->feature_mask = t->cfg_ext->copier.feature_mask; - cfg->gtw_cfg.node_id = node_id; - cfg->gtw_cfg.dma_buffer_size = t->cfg_ext->copier.dma_buffer_size; - /* config_length in DWORDs */ - cfg->gtw_cfg.config_length = DIV_ROUND_UP(data_size, 4); - if (data) - memcpy(&cfg->gtw_cfg.config.blob, data, data_size); + cfg->out_fmt = *te->copier.out_fmt; + cfg->feature_mask = te->copier.feature_mask; + avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id); + cfg->gtw_cfg.dma_buffer_size = te->copier.dma_buffer_size; + mod->gtw_attrs = cfg->gtw_cfg.config.attrs; + + ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id, + t->domain, cfg, cfg_size, &mod->instance_id); + return ret; +} + +static int avs_whm_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_tplg_modcfg_ext *te; + struct avs_whm_cfg *cfg; + size_t cfg_size; + u32 dma_id; + int ret; + te = t->cfg_ext; + cfg = adev->modcfg_buf; + dma_id = mod->owner->owner->dma_id; + cfg_size = offsetof(struct avs_whm_cfg, gtw_cfg.config); + + ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, &cfg_size); + if (ret) + return ret; + + cfg->base.cpc = t->cfg_base->cpc; + cfg->base.ibs = t->cfg_base->ibs; + cfg->base.obs = t->cfg_base->obs; + cfg->base.is_pages = t->cfg_base->is_pages; + cfg->base.audio_fmt = *t->in_fmt; + cfg->ref_fmt = *te->whm.ref_fmt; + cfg->out_fmt = *te->whm.out_fmt; + cfg->wake_tick_period = te->whm.wake_tick_period; + avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id); + cfg->gtw_cfg.dma_buffer_size = te->whm.dma_buffer_size; mod->gtw_attrs = cfg->gtw_cfg.config.attrs; - ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, - t->core_id, t->domain, cfg, cfg_size, - &mod->instance_id); + ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id, + t->domain, cfg, cfg_size, &mod->instance_id); return ret; } -static struct avs_control_data *avs_get_module_control(struct avs_path_module *mod) +static struct soc_mixer_control *avs_get_module_control(struct avs_path_module *mod, + const char *name) { struct avs_tplg_module *t = mod->template; struct avs_tplg_path_template *path_tmpl; @@ -278,27 +317,93 @@ static struct avs_control_data *avs_get_module_control(struct avs_path_module *m mc = (struct soc_mixer_control *)w->kcontrols[i]->private_value; ctl_data = (struct avs_control_data *)mc->dobj.private; - if (ctl_data->id == t->ctl_id) - return ctl_data; + if (ctl_data->id == t->ctl_id && strstr(w->kcontrols[i]->id.name, name)) + return mc; } return NULL; } +int avs_peakvol_set_volume(struct avs_dev *adev, struct avs_path_module *mod, + struct soc_mixer_control *mc, long *input) +{ + struct avs_volume_cfg vols[SND_SOC_TPLG_MAX_CHAN] = {{0}}; + struct avs_control_data *ctl_data; + struct avs_tplg_module *t; + int ret, i; + + ctl_data = mc->dobj.private; + t = mod->template; + if (!input) + input = ctl_data->values; + + if (mc->num_channels) { + for (i = 0; i < mc->num_channels; i++) { + vols[i].channel_id = i; + vols[i].target_volume = input[i]; + vols[i].curve_type = t->cfg_ext->peakvol.curve_type; + vols[i].curve_duration = t->cfg_ext->peakvol.curve_duration; + } + + ret = avs_ipc_peakvol_set_volumes(adev, mod->module_id, mod->instance_id, vols, + mc->num_channels); + return AVS_IPC_RET(ret); + } + + /* Target all channels if no individual selected. */ + vols[0].channel_id = AVS_ALL_CHANNELS_MASK; + vols[0].target_volume = input[0]; + vols[0].curve_type = t->cfg_ext->peakvol.curve_type; + vols[0].curve_duration = t->cfg_ext->peakvol.curve_duration; + + ret = avs_ipc_peakvol_set_volume(adev, mod->module_id, mod->instance_id, &vols[0]); + return AVS_IPC_RET(ret); +} + +int avs_peakvol_set_mute(struct avs_dev *adev, struct avs_path_module *mod, + struct soc_mixer_control *mc, long *input) +{ + struct avs_mute_cfg mutes[SND_SOC_TPLG_MAX_CHAN] = {{0}}; + struct avs_control_data *ctl_data; + struct avs_tplg_module *t; + int ret, i; + + ctl_data = mc->dobj.private; + t = mod->template; + if (!input) + input = ctl_data->values; + + if (mc->num_channels) { + for (i = 0; i < mc->num_channels; i++) { + mutes[i].channel_id = i; + mutes[i].mute = !input[i]; + mutes[i].curve_type = t->cfg_ext->peakvol.curve_type; + mutes[i].curve_duration = t->cfg_ext->peakvol.curve_duration; + } + + ret = avs_ipc_peakvol_set_mutes(adev, mod->module_id, mod->instance_id, mutes, + mc->num_channels); + return AVS_IPC_RET(ret); + } + + /* Target all channels if no individual selected. */ + mutes[0].channel_id = AVS_ALL_CHANNELS_MASK; + mutes[0].mute = !input[0]; + mutes[0].curve_type = t->cfg_ext->peakvol.curve_type; + mutes[0].curve_duration = t->cfg_ext->peakvol.curve_duration; + + ret = avs_ipc_peakvol_set_mute(adev, mod->module_id, mod->instance_id, &mutes[0]); + return AVS_IPC_RET(ret); +} + static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod) { struct avs_tplg_module *t = mod->template; - struct avs_control_data *ctl_data; + struct soc_mixer_control *mc; struct avs_peakvol_cfg *cfg; - int volume = S32_MAX; size_t cfg_size; int ret; - ctl_data = avs_get_module_control(mod); - if (ctl_data) - volume = ctl_data->volume; - - /* As 2+ channels controls are unsupported, have a single block for all channels. */ cfg_size = struct_size(cfg, vols, 1); if (cfg_size > AVS_MAILBOX_SIZE) return -EINVAL; @@ -310,15 +415,28 @@ static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod) cfg->base.obs = t->cfg_base->obs; cfg->base.is_pages = t->cfg_base->is_pages; cfg->base.audio_fmt = *t->in_fmt; - cfg->vols[0].target_volume = volume; cfg->vols[0].channel_id = AVS_ALL_CHANNELS_MASK; - cfg->vols[0].curve_type = AVS_AUDIO_CURVE_NONE; - cfg->vols[0].curve_duration = 0; + cfg->vols[0].target_volume = S32_MAX; + cfg->vols[0].curve_type = t->cfg_ext->peakvol.curve_type; + cfg->vols[0].curve_duration = t->cfg_ext->peakvol.curve_duration; ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id, t->domain, cfg, cfg_size, &mod->instance_id); + if (ret) + return ret; - return ret; + /* Now configure both VOLUME and MUTE parameters. */ + mc = avs_get_module_control(mod, "Volume"); + if (mc) { + ret = avs_peakvol_set_volume(adev, mod, mc, NULL); + if (ret) + return ret; + } + + mc = avs_get_module_control(mod, "Switch"); + if (mc) + return avs_peakvol_set_mute(adev, mod, mc, NULL); + return 0; } static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *mod) @@ -533,6 +651,7 @@ static struct avs_module_create avs_module_create[] = { { &AVS_ASRC_MOD_UUID, avs_asrc_create }, { &AVS_INTELWOV_MOD_UUID, avs_wov_create }, { &AVS_PROBE_MOD_UUID, avs_probe_create }, + { &AVS_WOVHOSTM_MOD_UUID, avs_whm_create }, }; static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_module *mod) diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h index bfd253c9fa95..7ed7e94e0a56 100644 --- a/sound/soc/intel/avs/path.h +++ b/sound/soc/intel/avs/path.h @@ -69,4 +69,9 @@ int avs_path_reset(struct avs_path *path); int avs_path_pause(struct avs_path *path); int avs_path_run(struct avs_path *path, int trigger); +int avs_peakvol_set_volume(struct avs_dev *adev, struct avs_path_module *mod, + struct soc_mixer_control *mc, long *input); +int avs_peakvol_set_mute(struct avs_dev *adev, struct avs_path_module *mod, + struct soc_mixer_control *mc, long *input); + #endif diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 945f9c0a6a54..dac463390da1 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -161,6 +161,7 @@ static int avs_dai_be_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dpcm *dpcm; be = snd_soc_substream_to_rtd(substream); + /* dpcm_fe_dai_open() guarantees the list is not empty at this point. */ for_each_dpcm_fe(be, substream->stream, dpcm) { fe = dpcm->fe; fe_hw_params = &fe->dpcm[substream->stream].hw_params; @@ -576,6 +577,7 @@ static int avs_dai_fe_hw_params(struct snd_pcm_substream *substream, hdac_stream(host_stream)->format_val = 0; fe = snd_soc_substream_to_rtd(substream); + /* dpcm_fe_dai_open() guarantees the list is not empty at this point. */ for_each_dpcm_be(fe, substream->stream, dpcm) { be = dpcm->be; be_hw_params = &be->dpcm[substream->stream].hw_params; @@ -1378,7 +1380,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l for_each_set_bit(i, &port_mask, ssp_count) cpu_count += hweight_long(tdms[i]); - cpus = devm_kzalloc(adev->dev, sizeof(*cpus) * cpu_count, GFP_KERNEL); + cpus = devm_kcalloc(adev->dev, cpu_count, sizeof(*cpus), GFP_KERNEL); if (!cpus) return -ENOMEM; @@ -1564,6 +1566,7 @@ static int avs_component_hda_probe(struct snd_soc_component *component) if (ret < 0) { dev_err(component->dev, "create widgets failed: %d\n", ret); + snd_soc_unregister_dai(dai); goto exit; } } @@ -1578,8 +1581,8 @@ exit: static void avs_component_hda_remove(struct snd_soc_component *component) { - avs_component_hda_unregister_dais(component); avs_component_remove(component); + avs_component_hda_unregister_dais(component); } static int avs_component_hda_open(struct snd_soc_component *component, diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index f76e91cff2a9..368ede05f2cd 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -9,6 +9,8 @@ #ifndef __SOUND_SOC_INTEL_AVS_REGS_H #define __SOUND_SOC_INTEL_AVS_REGS_H +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/iopoll.h> #include <linux/sizes.h> #define AZX_PCIREG_PGCTL 0x44 @@ -74,7 +76,7 @@ /* Constants used when accessing SRAM, space shared with firmware */ #define AVS_FW_REG_BASE(adev) ((adev)->spec->sram->base_offset) #define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0) -#define AVS_FW_REG_ERROR_CODE(adev) (AVS_FW_REG_BASE(adev) + 0x4) +#define AVS_FW_REG_ERROR(adev) (AVS_FW_REG_BASE(adev) + 0x4) #define AVS_WINDOW_CHUNK_SIZE SZ_4K #define AVS_FW_REGS_SIZE AVS_WINDOW_CHUNK_SIZE @@ -98,4 +100,47 @@ #define avs_downlink_addr(adev) \ avs_sram_addr(adev, AVS_DOWNLINK_WINDOW) +#define snd_hdac_adsp_writeb(adev, reg, value) \ + snd_hdac_reg_writeb(&(adev)->base.core, (adev)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readb(adev, reg) \ + snd_hdac_reg_readb(&(adev)->base.core, (adev)->dsp_ba + (reg)) +#define snd_hdac_adsp_writew(adev, reg, value) \ + snd_hdac_reg_writew(&(adev)->base.core, (adev)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readw(adev, reg) \ + snd_hdac_reg_readw(&(adev)->base.core, (adev)->dsp_ba + (reg)) +#define snd_hdac_adsp_writel(adev, reg, value) \ + snd_hdac_reg_writel(&(adev)->base.core, (adev)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readl(adev, reg) \ + snd_hdac_reg_readl(&(adev)->base.core, (adev)->dsp_ba + (reg)) +#define snd_hdac_adsp_writeq(adev, reg, value) \ + snd_hdac_reg_writeq(&(adev)->base.core, (adev)->dsp_ba + (reg), value) +#define snd_hdac_adsp_readq(adev, reg) \ + snd_hdac_reg_readq(&(adev)->base.core, (adev)->dsp_ba + (reg)) + +#define snd_hdac_adsp_updateb(adev, reg, mask, val) \ + snd_hdac_adsp_writeb(adev, reg, \ + (snd_hdac_adsp_readb(adev, reg) & ~(mask)) | (val)) +#define snd_hdac_adsp_updatew(adev, reg, mask, val) \ + snd_hdac_adsp_writew(adev, reg, \ + (snd_hdac_adsp_readw(adev, reg) & ~(mask)) | (val)) +#define snd_hdac_adsp_updatel(adev, reg, mask, val) \ + snd_hdac_adsp_writel(adev, reg, \ + (snd_hdac_adsp_readl(adev, reg) & ~(mask)) | (val)) +#define snd_hdac_adsp_updateq(adev, reg, mask, val) \ + snd_hdac_adsp_writeq(adev, reg, \ + (snd_hdac_adsp_readq(adev, reg) & ~(mask)) | (val)) + +#define snd_hdac_adsp_readb_poll(adev, reg, val, cond, delay_us, timeout_us) \ + readb_poll_timeout((adev)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) +#define snd_hdac_adsp_readw_poll(adev, reg, val, cond, delay_us, timeout_us) \ + readw_poll_timeout((adev)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) +#define snd_hdac_adsp_readl_poll(adev, reg, val, cond, delay_us, timeout_us) \ + readl_poll_timeout((adev)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) +#define snd_hdac_adsp_readq_poll(adev, reg, val, cond, delay_us, timeout_us) \ + readq_poll_timeout((adev)->dsp_ba + (reg), val, cond, \ + delay_us, timeout_us) + #endif /* __SOUND_SOC_INTEL_AVS_REGS_H */ diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index 34f859d6e5a4..d66ef000de9e 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -12,6 +12,7 @@ #include "avs.h" #include "cldma.h" #include "messages.h" +#include "registers.h" void avs_skl_ipc_interrupt(struct avs_dev *adev) { diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c index a9019ff5e3af..56905f2b9eb2 100644 --- a/sound/soc/intel/avs/tgl.c +++ b/sound/soc/intel/avs/tgl.c @@ -6,7 +6,11 @@ // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> // +#include <linux/pci.h> #include "avs.h" +#include "messages.h" + +#define CPUID_TSC_LEAF 0x15 static int avs_tgl_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) { @@ -35,6 +39,34 @@ static int avs_tgl_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stal return avs_dsp_core_stall(adev, core_mask, stall); } +static int avs_tgl_config_basefw(struct avs_dev *adev) +{ + struct pci_dev *pci = adev->base.pci; + struct avs_bus_hwid hwid; + int ret; +#ifdef CONFIG_X86 + unsigned int ecx; + +#include <asm/cpuid.h> + ecx = cpuid_ecx(CPUID_TSC_LEAF); + if (ecx) { + ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_XTAL_FREQ_HZ, sizeof(ecx), &ecx); + if (ret) + return AVS_IPC_RET(ret); + } +#endif + + hwid.device = pci->device; + hwid.subsystem = pci->subsystem_vendor | (pci->subsystem_device << 16); + hwid.revision = pci->revision; + + ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_BUS_HARDWARE_ID, sizeof(hwid), &hwid); + if (ret) + return AVS_IPC_RET(ret); + + return 0; +} + const struct avs_dsp_ops avs_tgl_dsp_ops = { .power = avs_tgl_dsp_core_power, .reset = avs_tgl_dsp_core_reset, @@ -44,6 +76,7 @@ const struct avs_dsp_ops avs_tgl_dsp_ops = { .load_basefw = avs_icl_load_basefw, .load_lib = avs_hda_load_library, .transfer_mods = avs_hda_transfer_modules, + .config_basefw = avs_tgl_config_basefw, .log_buffer_offset = avs_icl_log_buffer_offset, .log_buffer_status = avs_apl_log_buffer_status, .coredump = avs_apl_coredump, diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index 5cda527020c7..3c222c352701 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -815,6 +815,66 @@ static const struct avs_tplg_token_parser modcfg_ext_parsers[] = { .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_output_pins), .parse = avs_parse_short_token, }, + { + .token = AVS_TKN_MODCFG_WHM_REF_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.ref_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_WHM_OUT_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.out_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_WHM_WAKE_TICK_PERIOD_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.wake_tick_period), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_WHM_VINDEX_U8, + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.vindex), + .parse = avs_parse_byte_token, + }, + { + .token = AVS_TKN_MODCFG_WHM_DMA_TYPE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_type), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_WHM_DMABUFF_SIZE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_buffer_size), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_WHM_BLOB_AFMT_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.blob_fmt), + .parse = avs_parse_audio_format_ptr, + }, + { + .token = AVS_TKN_MODCFG_PEAKVOL_VOLUME_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.target_volume), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_PEAKVOL_CURVE_TYPE_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.curve_type), + .parse = avs_parse_word_token, + }, + { + .token = AVS_TKN_MODCFG_PEAKVOL_CURVE_DURATION_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.curve_duration), + .parse = avs_parse_word_token, + }, }; static const struct avs_tplg_token_parser pin_format_parsers[] = { @@ -1466,7 +1526,7 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o static const struct avs_tplg_token_parser mod_init_config_parsers[] = { { - .token = AVS_TKN_MOD_INIT_CONFIG_ID_U32, + .token = AVS_TKN_INIT_CONFIG_ID_U32, .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, .offset = offsetof(struct avs_tplg_init_config, id), .parse = avs_parse_word_token, @@ -1519,7 +1579,7 @@ static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp, esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size); ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config), - AVS_TKN_MOD_INIT_CONFIG_ID_U32, + AVS_TKN_INIT_CONFIG_ID_U32, mod_init_config_parsers, ARRAY_SIZE(mod_init_config_parsers)); @@ -1845,13 +1905,23 @@ static int avs_manifest(struct snd_soc_component *comp, int index, return 0; } -#define AVS_CONTROL_OPS_VOLUME 257 +enum { + AVS_CONTROL_OPS_VOLUME = 257, + AVS_CONTROL_OPS_MUTE, +}; static const struct snd_soc_tplg_kcontrol_ops avs_control_ops[] = { { .id = AVS_CONTROL_OPS_VOLUME, .get = avs_control_volume_get, .put = avs_control_volume_put, + .info = avs_control_volume_info, + }, + { + .id = AVS_CONTROL_OPS_MUTE, + .get = avs_control_mute_get, + .put = avs_control_mute_put, + .info = avs_control_mute_info, }, }; @@ -1873,18 +1943,20 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_ struct avs_control_data *ctl_data; struct soc_mixer_control *mc; size_t block_size; - int ret; + int ret, i; switch (le32_to_cpu(hdr->type)) { case SND_SOC_TPLG_TYPE_MIXER: - tmc = container_of(hdr, typeof(*tmc), hdr); - tuples = tmc->priv.array; - block_size = le32_to_cpu(tmc->priv.size); break; default: return -EINVAL; } + mc = (struct soc_mixer_control *)ctmpl->private_value; + tmc = container_of(hdr, typeof(*tmc), hdr); + tuples = tmc->priv.array; + block_size = le32_to_cpu(tmc->priv.size); + ctl_data = devm_kzalloc(comp->card->dev, sizeof(*ctl_data), GFP_KERNEL); if (!ctl_data) return -ENOMEM; @@ -1895,8 +1967,13 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_ if (ret) return ret; - mc = (struct soc_mixer_control *)ctmpl->private_value; mc->dobj.private = ctl_data; + if (tmc->invert) { + ctl_data->values[0] = mc->max; + for (i = 1; i < mc->num_channels; i++) + ctl_data->values[i] = mc->max; + } + return 0; } diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h index 7892e3797f63..304880997717 100644 --- a/sound/soc/intel/avs/topology.h +++ b/sound/soc/intel/avs/topology.h @@ -74,10 +74,17 @@ struct avs_tplg_modcfg_ext { union avs_virtual_index vindex; u32 dma_type; u32 dma_buffer_size; - u32 config_length; - /* config_data part of priv data */ } copier; struct { + struct avs_audio_format *ref_fmt; + struct avs_audio_format *out_fmt; + u32 wake_tick_period; + union avs_virtual_index vindex; + u32 dma_type; + u32 dma_buffer_size; + struct avs_audio_format *blob_fmt; /* optional override */ + } whm; + struct { u32 out_channel_config; u32 coefficients_select; s32 coefficients[AVS_CHANNELS_MAX]; @@ -106,6 +113,11 @@ struct avs_tplg_modcfg_ext { struct { struct avs_audio_format *out_fmt; } micsel; + struct { + u32 target_volume; + u32 curve_type; + u32 curve_duration; + } peakvol; }; }; diff --git a/sound/soc/intel/avs/trace.h b/sound/soc/intel/avs/trace.h index c9eaa5a60ed3..f4288d0ad5ef 100644 --- a/sound/soc/intel/avs/trace.h +++ b/sound/soc/intel/avs/trace.h @@ -37,60 +37,62 @@ TRACE_EVENT(avs_dsp_core_op, void trace_avs_msg_payload(const void *data, size_t size); -#define trace_avs_request(msg, fwregs) \ +#define trace_avs_request(msg, sts, lec) \ ({ \ - trace_avs_ipc_request_msg((msg)->header, fwregs); \ + trace_avs_ipc_request_msg((msg)->header, sts, lec); \ trace_avs_msg_payload((msg)->data, (msg)->size); \ }) -#define trace_avs_reply(msg, fwregs) \ +#define trace_avs_reply(msg, sts, lec) \ ({ \ - trace_avs_ipc_reply_msg((msg)->header, fwregs); \ + trace_avs_ipc_reply_msg((msg)->header, sts, lec); \ trace_avs_msg_payload((msg)->data, (msg)->size); \ }) -#define trace_avs_notify(msg, fwregs) \ +#define trace_avs_notify(msg, sts, lec) \ ({ \ - trace_avs_ipc_notify_msg((msg)->header, fwregs); \ + trace_avs_ipc_notify_msg((msg)->header, sts, lec); \ trace_avs_msg_payload((msg)->data, (msg)->size); \ }) #endif DECLARE_EVENT_CLASS(avs_ipc_msg_hdr, - TP_PROTO(u64 header, u64 fwregs), + TP_PROTO(u64 header, u32 sts, u32 lec), - TP_ARGS(header, fwregs), + TP_ARGS(header, sts, lec), TP_STRUCT__entry( __field(u64, header) - __field(u64, fwregs) + __field(u32, sts) + __field(u32, lec) ), TP_fast_assign( __entry->header = header; - __entry->fwregs = fwregs; + __entry->sts = sts; + __entry->lec = lec; ), TP_printk("primary: 0x%08X, extension: 0x%08X,\n" - "fwstatus: 0x%08X, fwerror: 0x%08X", + "status: 0x%08X, error: 0x%08X", lower_32_bits(__entry->header), upper_32_bits(__entry->header), - lower_32_bits(__entry->fwregs), upper_32_bits(__entry->fwregs)) + __entry->sts, __entry->lec) ); DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_request_msg, - TP_PROTO(u64 header, u64 fwregs), - TP_ARGS(header, fwregs) + TP_PROTO(u64 header, u32 sts, u32 lec), + TP_ARGS(header, sts, lec) ); DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_reply_msg, - TP_PROTO(u64 header, u64 fwregs), - TP_ARGS(header, fwregs) + TP_PROTO(u64 header, u32 sts, u32 lec), + TP_ARGS(header, sts, lec) ); DEFINE_EVENT(avs_ipc_msg_hdr, avs_ipc_notify_msg, - TP_PROTO(u64 header, u64 fwregs), - TP_ARGS(header, fwregs) + TP_PROTO(u64 header, u32 sts, u32 lec), + TP_ARGS(header, sts, lec) ); TRACE_EVENT_CONDITION(avs_ipc_msg_payload, diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 9caa4407c1ca..6446cda0f857 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -1132,7 +1132,22 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, - { /* Vexia Edu Atla 10 tablet */ + { + /* Vexia Edu Atla 10 tablet 5V version */ + .matches = { + /* Having all 3 of these not set is somewhat unique */ + DMI_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."), + DMI_MATCH(DMI_PRODUCT_NAME, "To be filled by O.E.M."), + DMI_MATCH(DMI_BOARD_NAME, "To be filled by O.E.M."), + /* Above strings are too generic, also match on BIOS date */ + DMI_MATCH(DMI_BIOS_DATE, "05/14/2015"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_JD_NOT_INV | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { /* Vexia Edu Atla 10 tablet 9V version */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 22668bac74a1..0554c7e2cb34 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -124,8 +124,6 @@ static int skl_hda_audio_probe(struct platform_device *pdev) return ret; card->dev = &pdev->dev; - if (!snd_soc_acpi_sof_parent(&pdev->dev)) - card->disable_route_checks = true; if (mach->mach_params.dmic_num > 0) { card->components = devm_kasprintf(card->dev, GFP_KERNEL, diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index c9f9c9b0de9b..90dafa810b2e 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -13,6 +13,7 @@ #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> #include <linux/soundwire/sdw_intel.h> +#include <sound/core.h> #include <sound/soc-acpi.h> #include "sof_sdw_common.h" #include "../../codecs/rt711.h" @@ -22,6 +23,8 @@ static int quirk_override = -1; module_param_named(quirk, quirk_override, int, 0444); MODULE_PARM_DESC(quirk, "Board-specific quirk override"); +#define DMIC_DEFAULT_CHANNELS 2 + static void log_quirks(struct device *dev) { if (SOC_SDW_JACK_JDSRC(sof_sdw_quirk)) @@ -42,6 +45,8 @@ static void log_quirks(struct device *dev) dev_dbg(dev, "quirk SOC_SDW_CODEC_SPKR enabled\n"); if (sof_sdw_quirk & SOC_SDW_SIDECAR_AMPS) dev_dbg(dev, "quirk SOC_SDW_SIDECAR_AMPS enabled\n"); + if (sof_sdw_quirk & SOC_SDW_CODEC_MIC) + dev_dbg(dev, "quirk SOC_SDW_CODEC_MIC enabled\n"); } static int sof_sdw_quirk_cb(const struct dmi_system_id *id) @@ -608,25 +613,41 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { .callback = sof_sdw_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "3838") + DMI_MATCH(DMI_PRODUCT_NAME, "83JX") }, - .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS), + .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | SOC_SDW_CODEC_MIC), }, { .callback = sof_sdw_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "3832") + DMI_MATCH(DMI_PRODUCT_NAME, "83LC") }, - .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS), + .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | SOC_SDW_CODEC_MIC), }, { .callback = sof_sdw_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "380E") + DMI_MATCH(DMI_PRODUCT_NAME, "83MC") }, - .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS), + .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | SOC_SDW_CODEC_MIC), + }, { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83NM") + }, + .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | SOC_SDW_CODEC_MIC), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83HM") + }, + .driver_data = (void *)(SOC_SDW_SIDECAR_AMPS | + SOC_SDW_CODEC_MIC), }, { .callback = sof_sdw_quirk_cb, @@ -728,9 +749,36 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOC_SDW_PCH_DMIC), }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_PRODUCT_NAME, "Fatcat"), + }, + .driver_data = (void *)(SOC_SDW_PCH_DMIC | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, {} }; +static const struct snd_pci_quirk sof_sdw_ssid_quirk_table[] = { + SND_PCI_QUIRK(0x1043, 0x1e13, "ASUS Zenbook S14", SOC_SDW_CODEC_MIC), + {} +}; + +static void sof_sdw_check_ssid_quirk(const struct snd_soc_acpi_mach *mach) +{ + const struct snd_pci_quirk *quirk_entry; + + quirk_entry = snd_pci_quirk_lookup_id(mach->mach_params.subsystem_vendor, + mach->mach_params.subsystem_device, + sof_sdw_ssid_quirk_table); + + if (quirk_entry) + sof_sdw_quirk = quirk_entry->value; +} + static struct snd_soc_dai_link_component platform_component[] = { { /* name might be overridden during probe */ @@ -755,7 +803,9 @@ static int create_sdw_dailink(struct snd_soc_card *card, int *be_id, struct snd_soc_codec_conf **codec_conf) { struct device *dev = card->dev; + struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private; struct asoc_sdw_endpoint *sof_end; int stream; @@ -852,6 +902,11 @@ static int create_sdw_dailink(struct snd_soc_card *card, codecs[j].name = sof_end->codec_name; codecs[j].dai_name = sof_end->dai_info->dai_name; + if (sof_end->dai_info->dai_type == SOC_SDW_DAI_TYPE_MIC && + mach_params->dmic_num > 0) { + dev_warn(dev, + "Both SDW DMIC and PCH DMIC are present, if incorrect, please set kernel params snd_sof_intel_hda_generic dmic_num=0 to disable PCH DMIC\n"); + } j++; } @@ -899,7 +954,7 @@ static int create_sdw_dailinks(struct snd_soc_card *card, /* generate DAI links by each sdw link */ while (sof_dais->initialised) { - int current_be_id; + int current_be_id = 0; ret = create_sdw_dailink(card, sof_dais, dai_links, ¤t_be_id, codec_conf); @@ -1127,22 +1182,24 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) hdmi_num = SOF_PRE_TGL_HDMI_COUNT; /* enable dmic01 & dmic16k */ - if (sof_sdw_quirk & SOC_SDW_PCH_DMIC || mach_params->dmic_num) { - if (ctx->ignore_internal_dmic) - dev_warn(dev, "Ignoring PCH DMIC\n"); - else - dmic_num = 2; + if (ctx->ignore_internal_dmic) { + dev_dbg(dev, "SoundWire DMIC is used, ignoring internal DMIC\n"); + mach_params->dmic_num = 0; + } else if (mach_params->dmic_num) { + dmic_num = 2; + } else if (sof_sdw_quirk & SOC_SDW_PCH_DMIC) { + dmic_num = 2; + /* + * mach_params->dmic_num will be used to set the cfg-mics value of + * card->components string. Set it to the default value. + */ + mach_params->dmic_num = DMIC_DEFAULT_CHANNELS; } - /* - * mach_params->dmic_num will be used to set the cfg-mics value of card->components - * string. Overwrite it to the actual number of PCH DMICs used in the device. - */ - mach_params->dmic_num = dmic_num; if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) bt_num = 1; - dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n", + dev_dbg(dev, "DAI link numbers: sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n", sdw_be_num, ssp_num, dmic_num, intel_ctx->hdmi.idisp_codec ? hdmi_num : 0, bt_num); @@ -1256,6 +1313,13 @@ static int mc_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, ctx); + if (mach->mach_params.subsystem_id_set) { + snd_soc_card_set_pci_ssid(card, + mach->mach_params.subsystem_vendor, + mach->mach_params.subsystem_device); + sof_sdw_check_ssid_quirk(mach); + } + dmi_check_system(sof_sdw_quirk_table); if (quirk_override != -1) { @@ -1271,12 +1335,6 @@ static int mc_probe(struct platform_device *pdev) for (i = 0; i < ctx->codec_info_list_count; i++) codec_info_list[i].amp_num = 0; - if (mach->mach_params.subsystem_id_set) { - snd_soc_card_set_pci_ssid(card, - mach->mach_params.subsystem_vendor, - mach->mach_params.subsystem_device); - } - ret = sof_card_dai_links_create(card); if (ret < 0) return ret; diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c index 2aa637124bec..faa916f40069 100644 --- a/sound/soc/intel/catpt/device.c +++ b/sound/soc/intel/catpt/device.c @@ -28,7 +28,7 @@ #define CREATE_TRACE_POINTS #include "trace.h" -static int __maybe_unused catpt_suspend(struct device *dev) +static int catpt_suspend(struct device *dev) { struct catpt_dev *cdev = dev_get_drvdata(dev); struct dma_chan *chan; @@ -72,7 +72,7 @@ release_dma_chan: return catpt_dsp_power_down(cdev); } -static int __maybe_unused catpt_resume(struct device *dev) +static int catpt_resume(struct device *dev) { struct catpt_dev *cdev = dev_get_drvdata(dev); int ret, i; @@ -106,7 +106,7 @@ static int __maybe_unused catpt_resume(struct device *dev) return 0; } -static int __maybe_unused catpt_runtime_suspend(struct device *dev) +static int catpt_runtime_suspend(struct device *dev) { if (!try_module_get(dev->driver->owner)) { dev_info(dev, "module unloading, skipping suspend\n"); @@ -117,14 +117,14 @@ static int __maybe_unused catpt_runtime_suspend(struct device *dev) return catpt_suspend(dev); } -static int __maybe_unused catpt_runtime_resume(struct device *dev) +static int catpt_runtime_resume(struct device *dev) { return catpt_resume(dev); } static const struct dev_pm_ops catpt_dev_pm = { - SET_SYSTEM_SLEEP_PM_OPS(catpt_suspend, catpt_resume) - SET_RUNTIME_PM_OPS(catpt_runtime_suspend, catpt_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(catpt_suspend, catpt_resume) + RUNTIME_PM_OPS(catpt_runtime_suspend, catpt_runtime_resume, NULL) }; /* machine board owned by CATPT is removed with this hook */ @@ -378,7 +378,7 @@ static struct platform_driver catpt_acpi_driver = { .driver = { .name = "intel_catpt", .acpi_match_table = catpt_ids, - .pm = &catpt_dev_pm, + .pm = pm_ptr(&catpt_dev_pm), .dev_groups = catpt_attr_groups, }, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index bb1324fb588e..a68efbe98948 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -214,6 +214,15 @@ static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1316_2_group2_adr[] = { + { + .adr = 0x000232025D131601ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1316-2" + } +}; + static const struct snd_soc_acpi_adr_device rt1316_1_single_adr[] = { { .adr = 0x000130025D131601ull, @@ -547,6 +556,20 @@ static const struct snd_soc_acpi_link_adr adl_chromebook_base[] = { {} }; +static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link02[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt1316_0_group2_adr), + .adr_d = rt1316_0_group2_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1316_2_group2_adr), + .adr_d = rt1316_2_group2_adr, + }, + {} +}; + static const struct snd_soc_acpi_codecs adl_max98357a_amp = { .num_codecs = 1, .codecs = {"MX98357A"} @@ -749,6 +772,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-adl-sdw-max98373-rt5682.tplg", }, + { + .link_mask = BIT(0) | BIT(2), + .links = adl_sdw_rt1316_link02, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-adl-rt1316-l02.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_sdw_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c index 24d850df77ca..32147dc9d2d6 100644 --- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c @@ -138,7 +138,7 @@ static const struct snd_soc_acpi_adr_device cs35l56_2_r1_adr[] = { }, }; -static const struct snd_soc_acpi_adr_device cs35l56_3_l1_adr[] = { +static const struct snd_soc_acpi_adr_device cs35l56_3_l3_adr[] = { { .adr = 0x00033301fa355601ull, .num_endpoints = 1, @@ -147,6 +147,24 @@ static const struct snd_soc_acpi_adr_device cs35l56_3_l1_adr[] = { }, }; +static const struct snd_soc_acpi_adr_device cs35l56_2_r3_adr[] = { + { + .adr = 0x00023301fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "AMP2" + }, +}; + +static const struct snd_soc_acpi_adr_device cs35l56_3_l1_adr[] = { + { + .adr = 0x00033101fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "AMP1" + }, +}; + static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { { /* Jack Playback Endpoint */ .num = 0, @@ -306,6 +324,25 @@ static const struct snd_soc_acpi_link_adr arl_cs42l43_l0_cs35l56_2_l23[] = { }, { .mask = BIT(3), + .num_adr = ARRAY_SIZE(cs35l56_3_l3_adr), + .adr_d = cs35l56_3_l3_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr arl_cs42l43_l0_cs35l56_3_l23[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs42l43_0_adr), + .adr_d = cs42l43_0_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(cs35l56_2_r3_adr), + .adr_d = cs35l56_2_r3_adr, + }, + { + .mask = BIT(3), .num_adr = ARRAY_SIZE(cs35l56_3_l1_adr), .adr_d = cs35l56_3_l1_adr, }, @@ -407,6 +444,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = { .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", }, { + .link_mask = BIT(0) | BIT(2) | BIT(3), + .links = arl_cs42l43_l0_cs35l56_3_l23, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg", + }, + { .link_mask = BIT(0) | BIT(2), .links = arl_cs42l43_l0_cs35l56_l2, .drv_name = "sof_sdw", diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c index 98a9c36d7a4c..e04f6de746eb 100644 --- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c @@ -37,6 +37,13 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = { .group_id = 1, }; +static const struct snd_soc_acpi_endpoint spk_1_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, +}; + static const struct snd_soc_acpi_endpoint spk_2_endpoint = { .num = 0, .aggregated = 1, @@ -51,6 +58,27 @@ static const struct snd_soc_acpi_endpoint spk_3_endpoint = { .group_id = 1, }; +static const struct snd_soc_acpi_endpoint spk_4_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 4, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_5_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 5, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_6_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 6, + .group_id = 1, +}; + static const struct snd_soc_acpi_endpoint rt712_endpoints[] = { { .num = 0, @@ -91,6 +119,23 @@ static const struct snd_soc_acpi_endpoint rt722_endpoints[] = { }, }; +static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = { + /* Jack Endpoint */ + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + /* DMIC Endpoint */ + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints_endpoints[] = { /* Jack Endpoint */ { @@ -142,6 +187,33 @@ static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { }, }; +static const struct snd_soc_acpi_endpoint cs42l43_amp_spkagg_endpoints[] = { + { /* Jack Playback Endpoint */ + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* DMIC Capture Endpoint */ + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Jack Capture Endpoint */ + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Speaker Playback Endpoint */ + .num = 3, + .aggregated = 1, + .group_position = 0, + .group_id = 1, + }, +}; + static const struct snd_soc_acpi_adr_device cs35l56_2_l_adr[] = { { .adr = 0x00023001FA355601ull, @@ -187,6 +259,48 @@ static const struct snd_soc_acpi_adr_device cs35l56_3_lr_adr[] = { } }; +static const struct snd_soc_acpi_adr_device cs35l56_1_3amp_adr[] = { + { + .adr = 0x00013001fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_1_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00013101fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_2_endpoint, + .name_prefix = "AMP2" + }, + { + .adr = 0x00013201fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_3_endpoint, + .name_prefix = "AMP3" + } +}; + +static const struct snd_soc_acpi_adr_device cs35l56_3_3amp_adr[] = { + { + .adr = 0x00033301fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_4_endpoint, + .name_prefix = "AMP4" + }, + { + .adr = 0x00033401fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_5_endpoint, + .name_prefix = "AMP5" + }, + { + .adr = 0x00033501fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_6_endpoint, + .name_prefix = "AMP6" + } +}; + static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = { { .adr = 0x00003001FA424301ull, @@ -196,6 +310,15 @@ static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = { } }; +static const struct snd_soc_acpi_adr_device cs42l43_2_adr[] = { + { + .adr = 0x00023001fa424301ull, + .num_endpoints = ARRAY_SIZE(cs42l43_amp_spkagg_endpoints), + .endpoints = cs42l43_amp_spkagg_endpoints, + .name_prefix = "cs42l43" + } +}; + static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { { .adr = 0x000030025D071101ull, @@ -295,6 +418,24 @@ static const struct snd_soc_acpi_adr_device rt1320_1_group1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1320_1_group2_adr[] = { + { + .adr = 0x000130025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "rt1320-1" + } +}; + +static const struct snd_soc_acpi_adr_device rt1320_3_group2_adr[] = { + { + .adr = 0x000330025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1320-2" + } +}; + static const struct snd_soc_acpi_adr_device rt713_0_adr[] = { { .adr = 0x000031025D071301ull, @@ -304,6 +445,15 @@ static const struct snd_soc_acpi_adr_device rt713_0_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt713_vb_2_adr[] = { + { + .adr = 0x000230025d071301ull, + .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), + .endpoints = jack_dmic_endpoints, + .name_prefix = "rt713" + } +}; + static const struct snd_soc_acpi_adr_device rt714_0_adr[] = { { .adr = 0x000030025D071401ull, @@ -364,6 +514,25 @@ static const struct snd_soc_acpi_link_adr lnl_cs42l43_l0_cs35l56_l23[] = { {} }; +static const struct snd_soc_acpi_link_adr lnl_cs42l43_l2_cs35l56x6_l13[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(cs42l43_2_adr), + .adr_d = cs42l43_2_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs35l56_1_3amp_adr), + .adr_d = cs35l56_1_3amp_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(cs35l56_3_3amp_adr), + .adr_d = cs35l56_3_3amp_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr lnl_rvp[] = { { .mask = BIT(0), @@ -453,6 +622,25 @@ static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_l0_rt1318_l1[] = { {} }; +static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_vb_l2_rt1320_l13[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt713_vb_2_adr), + .adr_d = rt713_vb_2_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1320_1_group2_adr), + .adr_d = rt1320_1_group2_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt1320_3_group2_adr), + .adr_d = rt1320_3_group2_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr lnl_sdw_rt712_vb_l2_rt1320_l1[] = { { .mask = BIT(2), @@ -490,6 +678,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .sof_tplg_filename = "sof-lnl-rt715-rt711-rt1308-mono.tplg", }, { + .link_mask = BIT(0), + .links = sdw_mockup_multi_func, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-rt722-l0.tplg", /* Reuse the existing tplg file */ + }, + { .link_mask = GENMASK(3, 0), .links = lnl_3_in_1_sdca, .drv_name = "sof_sdw", @@ -502,6 +696,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .sof_tplg_filename = "sof-lnl-cs42l43-l0-cs35l56-l23.tplg", }, { + .link_mask = BIT(1) | BIT(2) | BIT(3), + .links = lnl_cs42l43_l2_cs35l56x6_l13, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-lnl-cs42l43-l2-cs35l56x6-l13.tplg", + }, + { .link_mask = BIT(0) | BIT(3), .links = lnl_cs42l43_l0_cs35l56_l3, .drv_name = "sof_sdw", @@ -550,6 +750,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = { .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, .sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg" }, + { + .link_mask = BIT(1) | BIT(2) | BIT(3), + .links = lnl_sdw_rt713_vb_l2_rt1320_l13, + .drv_name = "sof_sdw", + .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, + .sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg" + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_sdw_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index 03fc5a187012..9e611e3667ad 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -330,7 +330,7 @@ static const struct snd_soc_acpi_adr_device rt1316_3_single_adr[] = { static const struct snd_soc_acpi_adr_device rt1318_1_single_adr[] = { { - .adr = 0x000130025D131801, + .adr = 0x000130025D131801ull, .num_endpoints = 1, .endpoints = &single_endpoint, .name_prefix = "rt1318-1" @@ -441,6 +441,179 @@ static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = { } }; +/* CS42L43 - speaker DAI aggregated with 4 amps */ +static const struct snd_soc_acpi_endpoint cs42l43_4amp_spkagg_endpoints[] = { + { /* Jack Playback Endpoint */ + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* DMIC Capture Endpoint */ + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Jack Capture Endpoint */ + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Speaker Playback Endpoint */ + .num = 3, + .aggregated = 1, + .group_position = 4, + .group_id = 1, + }, +}; + +/* CS42L43 on link3 aggregated with 4 amps */ +static const struct snd_soc_acpi_adr_device cs42l43_l3_4amp_spkagg_adr[] = { + { + .adr = 0x00033001FA424301ull, + .num_endpoints = ARRAY_SIZE(cs42l43_4amp_spkagg_endpoints), + .endpoints = cs42l43_4amp_spkagg_endpoints, + .name_prefix = "cs42l43" + } +}; + +static const struct snd_soc_acpi_endpoint cs35l56_l_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 0, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 0, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_r_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 1, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_2_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 2, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 2, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_3_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 3, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 3, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_4_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 4, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 4, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_5_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 5, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 5, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_6_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 6, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 6, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_7_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 7, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 7, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_adr_device cs35l56_0_adr[] = { + { + .adr = 0x00003301FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00003201FA355601ull, + .num_endpoints = 1, + .endpoints = &spk_2_endpoint, + .name_prefix = "AMP2" + } +}; + static const struct snd_soc_acpi_adr_device cs35l56_1_adr[] = { { .adr = 0x00013701FA355601ull, @@ -471,17 +644,71 @@ static const struct snd_soc_acpi_adr_device cs35l56_2_adr[] = { } }; +static const struct snd_soc_acpi_adr_device cs35l56_0_fb_adr[] = { + { + .adr = 0x00003301FA355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints), + .endpoints = cs35l56_l_fb_endpoints, + .name_prefix = "AMP1" + }, + { + .adr = 0x00003201FA355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_2_fb_endpoints), + .endpoints = cs35l56_2_fb_endpoints, + .name_prefix = "AMP2" + }, + { + .adr = 0x00003101FA355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_4_fb_endpoints), + .endpoints = cs35l56_4_fb_endpoints, + .name_prefix = "AMP3" + }, + { + .adr = 0x00003001FA355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_6_fb_endpoints), + .endpoints = cs35l56_6_fb_endpoints, + .name_prefix = "AMP4" + }, +}; + +static const struct snd_soc_acpi_adr_device cs35l56_1_fb_adr[] = { + { + .adr = 0x00013701FA355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints), + .endpoints = cs35l56_r_fb_endpoints, + .name_prefix = "AMP8" + }, + { + .adr = 0x00013601FA355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_3_fb_endpoints), + .endpoints = cs35l56_3_fb_endpoints, + .name_prefix = "AMP7" + }, + { + .adr = 0x00013501FA355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_5_fb_endpoints), + .endpoints = cs35l56_5_fb_endpoints, + .name_prefix = "AMP6" + }, + { + .adr = 0x00013401FA355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_7_fb_endpoints), + .endpoints = cs35l56_7_fb_endpoints, + .name_prefix = "AMP5" + }, +}; + static const struct snd_soc_acpi_adr_device cs35l56_2_r_adr[] = { { .adr = 0x00023201FA355601ull, - .num_endpoints = 1, - .endpoints = &spk_r_endpoint, + .num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints), + .endpoints = cs35l56_r_fb_endpoints, .name_prefix = "AMP3" }, { .adr = 0x00023301FA355601ull, - .num_endpoints = 1, - .endpoints = &spk_3_endpoint, + .num_endpoints = ARRAY_SIZE(cs35l56_3_fb_endpoints), + .endpoints = cs35l56_3_fb_endpoints, .name_prefix = "AMP4" } @@ -490,14 +717,14 @@ static const struct snd_soc_acpi_adr_device cs35l56_2_r_adr[] = { static const struct snd_soc_acpi_adr_device cs35l56_3_l_adr[] = { { .adr = 0x00033001fa355601ull, - .num_endpoints = 1, - .endpoints = &spk_l_endpoint, + .num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints), + .endpoints = cs35l56_l_fb_endpoints, .name_prefix = "AMP1" }, { .adr = 0x00033101fa355601ull, - .num_endpoints = 1, - .endpoints = &spk_2_endpoint, + .num_endpoints = ARRAY_SIZE(cs35l56_2_fb_endpoints), + .endpoints = cs35l56_2_fb_endpoints, .name_prefix = "AMP2" } }; @@ -765,6 +992,40 @@ static const struct snd_soc_acpi_link_adr cs42l43_link0_cs35l56_link2_link3[] = {} }; +static const struct snd_soc_acpi_link_adr cs42l43_link3_cs35l56_x4_link0_link1_spkagg[] = { + /* Expected order: jack -> amp */ + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(cs42l43_l3_4amp_spkagg_adr), + .adr_d = cs42l43_l3_4amp_spkagg_adr, + }, + { + .mask = BIT(1), + .num_adr = 2, + .adr_d = cs35l56_1_adr, + }, + { + .mask = BIT(0), + .num_adr = 2, + .adr_d = cs35l56_0_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr mtl_cs35l56_x8_link0_link1_fb[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs35l56_1_fb_adr), + .adr_d = cs35l56_1_fb_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs35l56_0_fb_adr), + .adr_d = cs35l56_0_fb_adr, + }, + {} +}; + /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { /* mockup tests need to be first */ @@ -842,12 +1103,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l23.tplg", }, { + .link_mask = BIT(0) | BIT(1) | BIT(3), + .links = cs42l43_link3_cs35l56_x4_link0_link1_spkagg, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-cs42l43-l3-cs35l56-l01-spkagg.tplg", + }, + { .link_mask = GENMASK(2, 0), .links = mtl_cs42l43_cs35l56, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l12.tplg", }, { + .link_mask = BIT(0) | BIT(1), + .links = mtl_cs35l56_x8_link0_link1_fb, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg" + }, + { .link_mask = BIT(0), .links = mtl_cs42l43_l0, .drv_name = "sof_sdw", diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c index f1c0d7a02cda..6603d8de501c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c @@ -8,6 +8,7 @@ #include <sound/soc-acpi.h> #include <sound/soc-acpi-intel-match.h> +#include "soc-acpi-intel-sdca-quirks.h" #include "soc-acpi-intel-sdw-mockup-match.h" #include <sound/soc-acpi-intel-ssp-common.h> @@ -35,6 +36,62 @@ static const struct snd_soc_acpi_endpoint single_endpoint = { .group_id = 0, }; +static const struct snd_soc_acpi_endpoint spk_l_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 0, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_r_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_1_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_2_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 2, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_3_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 3, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_4_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 4, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_5_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 5, + .group_id = 1, +}; + +static const struct snd_soc_acpi_endpoint spk_6_endpoint = { + .num = 0, + .aggregated = 1, + .group_position = 6, + .group_id = 1, +}; + /* * Multi-function codecs with three endpoints created for * headset, amp and dmic functions. @@ -60,6 +117,161 @@ static const struct snd_soc_acpi_endpoint rt_mf_endpoints[] = { }, }; +static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = { + /* Jack Endpoint */ + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + /* DMIC Endpoint */ + { + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints_endpoints[] = { + /* Jack Endpoint */ + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + /* Amp Endpoint, work as spk_l_endpoint */ + { + .num = 1, + .aggregated = 1, + .group_position = 0, + .group_id = 1, + }, + /* DMIC Endpoint */ + { + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_endpoint cs42l43_amp_spkagg_endpoints[] = { + { /* Jack Playback Endpoint */ + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* DMIC Capture Endpoint */ + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Jack Capture Endpoint */ + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Speaker Playback Endpoint */ + .num = 3, + .aggregated = 1, + .group_position = 0, + .group_id = 1, + }, +}; + +static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = { + { /* Jack Playback Endpoint */ + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* DMIC Capture Endpoint */ + .num = 1, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Jack Capture Endpoint */ + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + { /* Speaker Playback Endpoint */ + .num = 3, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + +static const struct snd_soc_acpi_adr_device cs42l43_2_adr[] = { + { + .adr = 0x00023001fa424301ull, + .num_endpoints = ARRAY_SIZE(cs42l43_amp_spkagg_endpoints), + .endpoints = cs42l43_amp_spkagg_endpoints, + .name_prefix = "cs42l43" + } +}; + +static const struct snd_soc_acpi_adr_device cs35l56_1_3amp_adr[] = { + { + .adr = 0x00013001fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_1_endpoint, + .name_prefix = "AMP1" + }, + { + .adr = 0x00013101fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_2_endpoint, + .name_prefix = "AMP2" + }, + { + .adr = 0x00013201fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_3_endpoint, + .name_prefix = "AMP3" + } +}; + +static const struct snd_soc_acpi_adr_device cs35l56_3_3amp_adr[] = { + { + .adr = 0x00033301fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_4_endpoint, + .name_prefix = "AMP4" + }, + { + .adr = 0x00033401fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_5_endpoint, + .name_prefix = "AMP5" + }, + { + .adr = 0x00033501fa355601ull, + .num_endpoints = 1, + .endpoints = &spk_6_endpoint, + .name_prefix = "AMP6" + } +}; + +static const struct snd_soc_acpi_adr_device cs42l43_3_adr[] = { + { + .adr = 0x00033001FA424301ull, + .num_endpoints = ARRAY_SIZE(cs42l43_endpoints), + .endpoints = cs42l43_endpoints, + .name_prefix = "cs42l43" + } +}; + static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { { .adr = 0x000030025D071101ull, @@ -69,6 +281,42 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt712_vb_2_group1_adr[] = { + { + .adr = 0x000230025D071201ull, + .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints_endpoints), + .endpoints = jack_amp_g1_dmic_endpoints_endpoints, + .name_prefix = "rt712" + } +}; + +static const struct snd_soc_acpi_adr_device rt712_vb_3_group1_adr[] = { + { + .adr = 0x000330025D071201ull, + .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints_endpoints), + .endpoints = jack_amp_g1_dmic_endpoints_endpoints, + .name_prefix = "rt712" + } +}; + +static const struct snd_soc_acpi_adr_device rt713_vb_2_adr[] = { + { + .adr = 0x000230025d071301ull, + .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), + .endpoints = jack_dmic_endpoints, + .name_prefix = "rt713" + } +}; + +static const struct snd_soc_acpi_adr_device rt713_vb_3_adr[] = { + { + .adr = 0x000330025D071301ull, + .num_endpoints = ARRAY_SIZE(jack_dmic_endpoints), + .endpoints = jack_dmic_endpoints, + .name_prefix = "rt713" + } +}; + static const struct snd_soc_acpi_adr_device rt721_3_single_adr[] = { { .adr = 0x000330025d072101ull, @@ -114,6 +362,78 @@ static const struct snd_soc_acpi_adr_device rt722_3_single_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1320_1_group1_adr[] = { + { + .adr = 0x000130025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1320-1" + } +}; + +static const struct snd_soc_acpi_adr_device rt1320_1_group2_adr[] = { + { + .adr = 0x000130025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "rt1320-1" + } +}; + +static const struct snd_soc_acpi_adr_device rt1320_2_group1_adr[] = { + { + .adr = 0x000230025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1320-1" + } +}; + +static const struct snd_soc_acpi_adr_device rt1320_2_group2_adr[] = { + { + .adr = 0x000230025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1320-1" + } +}; + +static const struct snd_soc_acpi_adr_device rt1320_3_group2_adr[] = { + { + .adr = 0x000330025D132001ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1320-2" + } +}; + +static const struct snd_soc_acpi_link_adr ptl_cs42l43_l2_cs35l56x6_l13[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(cs42l43_2_adr), + .adr_d = cs42l43_2_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs35l56_1_3amp_adr), + .adr_d = cs35l56_1_3amp_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(cs35l56_3_3amp_adr), + .adr_d = cs35l56_3_3amp_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr ptl_cs42l43_l3[] = { + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(cs42l43_3_adr), + .adr_d = cs42l43_3_adr, + } +}; + static const struct snd_soc_acpi_link_adr ptl_rt722_only[] = { { .mask = BIT(0), @@ -150,6 +470,72 @@ static const struct snd_soc_acpi_link_adr ptl_rvp[] = { {} }; +static const struct snd_soc_acpi_link_adr ptl_sdw_rt713_vb_l2_rt1320_l13[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt713_vb_2_adr), + .adr_d = rt713_vb_2_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1320_1_group2_adr), + .adr_d = rt1320_1_group2_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt1320_3_group2_adr), + .adr_d = rt1320_3_group2_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr ptl_sdw_rt713_vb_l3_rt1320_l12[] = { + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt713_vb_3_adr), + .adr_d = rt713_vb_3_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1320_1_group2_adr), + .adr_d = rt1320_1_group2_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1320_2_group2_adr), + .adr_d = rt1320_2_group2_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr ptl_sdw_rt712_vb_l2_rt1320_l1[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt712_vb_2_group1_adr), + .adr_d = rt712_vb_2_group1_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1320_1_group1_adr), + .adr_d = rt1320_1_group1_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr ptl_sdw_rt712_vb_l3_rt1320_l2[] = { + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt712_vb_3_group1_adr), + .adr_d = rt712_vb_3_group1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1320_2_group1_adr), + .adr_d = rt1320_2_group1_adr, + }, + {} +}; + /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { /* mockup tests need to be first */ @@ -172,6 +558,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .sof_tplg_filename = "sof-ptl-rt715-rt711-rt1308-mono.tplg", }, { + .link_mask = BIT(1) | BIT(2) | BIT(3), + .links = ptl_cs42l43_l2_cs35l56x6_l13, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-ptl-cs42l43-l2-cs35l56x6-l13.tplg", + }, + { + .link_mask = BIT(0), + .links = sdw_mockup_multi_func, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-ptl-rt722.tplg", /* Reuse the existing tplg file */ + }, + { .link_mask = BIT(0), .links = ptl_rvp, .drv_name = "sof_sdw", @@ -179,6 +577,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { }, { .link_mask = BIT(3), + .links = ptl_cs42l43_l3, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-ptl-cs42l43-l3.tplg", + }, + { + .link_mask = BIT(3), .links = ptl_rt721_l3, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt721.tplg", @@ -201,6 +605,34 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-ptl-rt722.tplg", }, + { + .link_mask = BIT(1) | BIT(2), + .links = ptl_sdw_rt712_vb_l2_rt1320_l1, + .drv_name = "sof_sdw", + .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, + .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg" + }, + { + .link_mask = BIT(2) | BIT(3), + .links = ptl_sdw_rt712_vb_l3_rt1320_l2, + .drv_name = "sof_sdw", + .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, + .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l2.tplg" + }, + { + .link_mask = BIT(1) | BIT(2) | BIT(3), + .links = ptl_sdw_rt713_vb_l2_rt1320_l13, + .drv_name = "sof_sdw", + .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, + .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg" + }, + { + .link_mask = BIT(1) | BIT(2) | BIT(3), + .links = ptl_sdw_rt713_vb_l3_rt1320_l12, + .drv_name = "sof_sdw", + .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb, + .sof_tplg_filename = "sof-ptl-rt713-l3-rt1320-l12.tplg" + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_ptl_sdw_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c b/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c index a3d33997736a..d122ce69fa4f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c @@ -31,6 +31,30 @@ static const struct snd_soc_acpi_endpoint sdw_mockup_r_endpoint = { .group_id = 1, }; +static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = { + /* Jack Endpoint */ + { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, + /* Amp Endpoint, work as spk_l_endpoint */ + { + .num = 1, + .aggregated = 1, + .group_position = 0, + .group_id = 1, + }, + /* DMIC Endpoint */ + { + .num = 2, + .aggregated = 0, + .group_position = 0, + .group_id = 0, + }, +}; + static const struct snd_soc_acpi_adr_device sdw_mockup_headset_0_adr[] = { { .adr = 0x0000000105AA5500ull, @@ -103,6 +127,15 @@ static const struct snd_soc_acpi_adr_device sdw_mockup_amp_2_group1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device sdw_mockup_multi_function_adr[] = { + { + .adr = 0x0000000105AAAA01ull, + .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints), + .endpoints = jack_amp_g1_dmic_endpoints, + .name_prefix = "sdw_mockup_mmulti-function" + } +}; + const struct snd_soc_acpi_link_adr sdw_mockup_headset_1amp_mic[] = { { .mask = BIT(0), @@ -164,3 +197,12 @@ const struct snd_soc_acpi_link_adr sdw_mockup_mic_headset_1amp[] = { }, {} }; + +const struct snd_soc_acpi_link_adr sdw_mockup_multi_func[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(sdw_mockup_multi_function_adr), + .adr_d = sdw_mockup_multi_function_adr, + }, + {} +}; diff --git a/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h b/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h index c99eecd19e03..f7ed5beaca96 100644 --- a/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h +++ b/sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h @@ -13,5 +13,6 @@ extern const struct snd_soc_acpi_link_adr sdw_mockup_headset_1amp_mic[]; extern const struct snd_soc_acpi_link_adr sdw_mockup_headset_2amps_mic[]; extern const struct snd_soc_acpi_link_adr sdw_mockup_mic_headset_1amp[]; +extern const struct snd_soc_acpi_link_adr sdw_mockup_multi_func[]; #endif diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index 161ba532d270..b77aafb0bfb6 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -536,6 +536,194 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines); +static const struct snd_soc_acpi_endpoint cs35l56_l_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 0, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 0, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_r_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 1, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 1, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_2_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 2, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 2, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_3_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 3, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 3, + .group_id = 2, + }, +}; + +static const struct snd_soc_acpi_endpoint cs35l56_4_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 4, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 4, + .group_id = 2, + } +}; + +static const struct snd_soc_acpi_endpoint cs35l56_5_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 5, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 5, + .group_id = 2, + } +}; + +static const struct snd_soc_acpi_endpoint cs35l56_6_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 6, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 6, + .group_id = 2, + } +}; + +static const struct snd_soc_acpi_endpoint cs35l56_7_fb_endpoints[] = { + { /* Speaker Playback Endpoint */ + .num = 0, + .aggregated = 1, + .group_position = 7, + .group_id = 1, + }, + { /* Feedback Capture Endpoint */ + .num = 1, + .aggregated = 1, + .group_position = 7, + .group_id = 2, + } +}; + +static const struct snd_soc_acpi_adr_device cs35l56_sdw_eight_1_4_fb_adr[] = { + { + .adr = 0x00003301fa355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints), + .endpoints = cs35l56_l_fb_endpoints, + .name_prefix = "AMP1" + }, + { + .adr = 0x00003201fa355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_2_fb_endpoints), + .endpoints = cs35l56_2_fb_endpoints, + .name_prefix = "AMP2" + }, + { + .adr = 0x00003101fa355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_4_fb_endpoints), + .endpoints = cs35l56_4_fb_endpoints, + .name_prefix = "AMP3" + }, + { + .adr = 0x00003001fa355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_6_fb_endpoints), + .endpoints = cs35l56_6_fb_endpoints, + .name_prefix = "AMP4" + }, +}; + +static const struct snd_soc_acpi_adr_device cs35l56_sdw_eight_5_8_fb_adr[] = { + { + .adr = 0x00013701fa355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints), + .endpoints = cs35l56_r_fb_endpoints, + .name_prefix = "AMP8" + }, + { + .adr = 0x00013601fa355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_3_fb_endpoints), + .endpoints = cs35l56_3_fb_endpoints, + .name_prefix = "AMP7" + }, + { + .adr = 0x00013501fa355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_5_fb_endpoints), + .endpoints = cs35l56_5_fb_endpoints, + .name_prefix = "AMP6" + }, + { + .adr = 0x00013401fa355601ull, + .num_endpoints = ARRAY_SIZE(cs35l56_7_fb_endpoints), + .endpoints = cs35l56_7_fb_endpoints, + .name_prefix = "AMP5" + }, +}; + +static const struct snd_soc_acpi_link_adr up_extreme_cs35l56_sdw_eight[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(cs35l56_sdw_eight_5_8_fb_adr), + .adr_d = cs35l56_sdw_eight_5_8_fb_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(cs35l56_sdw_eight_1_4_fb_adr), + .adr_d = cs35l56_sdw_eight_1_4_fb_adr, + }, + {} +}; + /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { /* mockup tests need to be first */ @@ -635,6 +823,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-tgl-rt711.tplg", }, + { + .link_mask = BIT(0) | BIT(1), + .links = up_extreme_cs35l56_sdw_eight, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-tgl-cs35l56-l01-fb8.tplg" + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_sdw_machines); diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c index aa5de167e790..4ed71d11ad77 100644 --- a/sound/soc/intel/keembay/kmb_platform.c +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -869,7 +869,7 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2; - kmb_i2s->use_pio = !(of_property_read_bool(np, "dmas")); + kmb_i2s->use_pio = !of_property_present(np, "dmas"); if (kmb_i2s->use_pio) { irq = platform_get_irq_optional(pdev, 0); diff --git a/sound/soc/kirkwood/armada-370-db.c b/sound/soc/kirkwood/armada-370-db.c index 79ee7599f06a..77a5f440364e 100644 --- a/sound/soc/kirkwood/armada-370-db.c +++ b/sound/soc/kirkwood/armada-370-db.c @@ -73,20 +73,20 @@ static struct snd_soc_dai_link a370db_dai[] = { { .name = "CS42L51", .stream_name = "analog", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC, .ops = &a370db_ops, SND_SOC_DAILINK_REG(analog), }, { .name = "S/PDIF out", .stream_name = "spdif-out", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(spdif_out), }, { .name = "S/PDIF in", .stream_name = "spdif-in", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(spdif_in), }, }; diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c index 3044d9ab3d4d..3809068f5620 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c @@ -500,26 +500,6 @@ static int mtk_memif_set_rate_fs(struct mtk_base_afe *afe, return 0; } -int mtk_memif_set_rate(struct mtk_base_afe *afe, - int id, unsigned int rate) -{ - int fs = 0; - - if (!afe->get_dai_fs) { - dev_err(afe->dev, "%s(), error, afe->get_dai_fs == NULL\n", - __func__); - return -EINVAL; - } - - fs = afe->get_dai_fs(afe, id, rate); - - if (fs < 0) - return -EINVAL; - - return mtk_memif_set_rate_fs(afe, id, fs); -} -EXPORT_SYMBOL_GPL(mtk_memif_set_rate); - int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream, int id, unsigned int rate) { diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h index 8cec90671827..b6d0f2b27e86 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.h +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h @@ -42,8 +42,6 @@ int mtk_memif_set_addr(struct mtk_base_afe *afe, int id, size_t dma_bytes); int mtk_memif_set_channel(struct mtk_base_afe *afe, int id, unsigned int channel); -int mtk_memif_set_rate(struct mtk_base_afe *afe, - int id, unsigned int rate); int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream, int id, unsigned int rate); int mtk_memif_set_format(struct mtk_base_afe *afe, diff --git a/sound/soc/mediatek/common/mtk-soc-card.h b/sound/soc/mediatek/common/mtk-soc-card.h index 3f6e24dd22df..a1d2794ac1f7 100644 --- a/sound/soc/mediatek/common/mtk-soc-card.h +++ b/sound/soc/mediatek/common/mtk-soc-card.h @@ -16,6 +16,7 @@ struct mtk_soc_card_data { const struct mtk_sof_priv *sof_priv; struct list_head sof_dai_link_list; struct mtk_platform_card_data *card_data; + struct snd_soc_component *accdet; void *mach_priv; }; diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c index 3bbf42c42805..713a368f79cf 100644 --- a/sound/soc/mediatek/common/mtk-soundcard-driver.c +++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <sound/soc.h> #include "mtk-dsp-sof-common.h" @@ -192,7 +193,9 @@ EXPORT_SYMBOL_GPL(mtk_soundcard_common_capture_ops); int mtk_soundcard_common_probe(struct platform_device *pdev) { - struct device_node *platform_node, *adsp_node; + struct device_node *platform_node, *adsp_node, *accdet_node; + struct snd_soc_component *accdet_comp; + struct platform_device *accdet_pdev; const struct mtk_soundcard_pdata *pdata; struct mtk_soc_card_data *soc_card_data; struct snd_soc_dai_link *orig_dai_link, *dai_link; @@ -221,7 +224,7 @@ int mtk_soundcard_common_probe(struct platform_device *pdev) card->name = pdata->card_name; } - needs_legacy_probe = !of_property_read_bool(pdev->dev.of_node, "audio-routing"); + needs_legacy_probe = !of_property_present(pdev->dev.of_node, "audio-routing"); if (needs_legacy_probe) { /* * If we have no .soc_probe() callback there's no way of using @@ -250,6 +253,20 @@ int mtk_soundcard_common_probe(struct platform_device *pdev) soc_card_data->card_data->jacks = jacks; + accdet_node = of_parse_phandle(pdev->dev.of_node, "mediatek,accdet", 0); + if (accdet_node) { + accdet_pdev = of_find_device_by_node(accdet_node); + if (accdet_pdev) { + accdet_comp = snd_soc_lookup_component(&accdet_pdev->dev, NULL); + if (accdet_comp) + soc_card_data->accdet = accdet_comp; + else + dev_err(&pdev->dev, "No sound component found from mediatek,accdet property\n"); + } else { + dev_err(&pdev->dev, "No device found from mediatek,accdet property\n"); + } + } + platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0); if (!platform_node) return dev_err_probe(&pdev->dev, -EINVAL, @@ -262,7 +279,7 @@ int mtk_soundcard_common_probe(struct platform_device *pdev) adsp_node = NULL; if (adsp_node) { - if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) { + if (of_property_present(pdev->dev.of_node, "mediatek,dai-link")) { ret = mtk_sof_dailink_parse_of(card, pdev->dev.of_node, "mediatek,dai-link", card->dai_link, card->num_links); diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c index 5f11bc5438bd..fcae38135d93 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c @@ -1462,15 +1462,15 @@ static const struct of_device_id mt2701_afe_pcm_dt_match[] = { MODULE_DEVICE_TABLE(of, mt2701_afe_pcm_dt_match); static const struct dev_pm_ops mt2701_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mt2701_afe_runtime_suspend, - mt2701_afe_runtime_resume, NULL) + RUNTIME_PM_OPS(mt2701_afe_runtime_suspend, + mt2701_afe_runtime_resume, NULL) }; static struct platform_driver mt2701_afe_pcm_driver = { .driver = { .name = "mt2701-audio", .of_match_table = mt2701_afe_pcm_dt_match, - .pm = &mt2701_afe_pm_ops, + .pm = pm_ptr(&mt2701_afe_pm_ops), }, .probe = mt2701_afe_pcm_dev_probe, .remove = mt2701_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c index 00a79867235d..778a9dccfcaa 100644 --- a/sound/soc/mediatek/mt2701/mt2701-cs42448.c +++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c @@ -266,7 +266,7 @@ static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = { [DAI_LINK_BE_I2S0] = { .name = "mt2701-cs42448-I2S0", .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_GATED, .ops = &mt2701_cs42448_be_ops, SND_SOC_DAILINK_REG(be_i2s0), @@ -274,7 +274,7 @@ static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = { [DAI_LINK_BE_I2S1] = { .name = "mt2701-cs42448-I2S1", .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_GATED, .ops = &mt2701_cs42448_be_ops, SND_SOC_DAILINK_REG(be_i2s1), @@ -282,7 +282,7 @@ static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = { [DAI_LINK_BE_I2S2] = { .name = "mt2701-cs42448-I2S2", .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_GATED, .ops = &mt2701_cs42448_be_ops, SND_SOC_DAILINK_REG(be_i2s2), @@ -290,7 +290,7 @@ static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = { [DAI_LINK_BE_I2S3] = { .name = "mt2701-cs42448-I2S3", .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_GATED, .ops = &mt2701_cs42448_be_ops, SND_SOC_DAILINK_REG(be_i2s3), diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c index 2814f0570928..84b3d6cd77a5 100644 --- a/sound/soc/mediatek/mt2701/mt2701-wm8960.c +++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c @@ -83,7 +83,7 @@ static struct snd_soc_dai_link mt2701_wm8960_dai_links[] = { { .name = "wm8960-codec", .no_pcm = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_GATED, .ops = &mt2701_wm8960_be_ops, SND_SOC_DAILINK_REG(codec), diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index 9159b42adf6a..f62a32f2f2b6 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -879,15 +879,15 @@ static const struct of_device_id mt6797_afe_pcm_dt_match[] = { MODULE_DEVICE_TABLE(of, mt6797_afe_pcm_dt_match); static const struct dev_pm_ops mt6797_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mt6797_afe_runtime_suspend, - mt6797_afe_runtime_resume, NULL) + RUNTIME_PM_OPS(mt6797_afe_runtime_suspend, + mt6797_afe_runtime_resume, NULL) }; static struct platform_driver mt6797_afe_pcm_driver = { .driver = { .name = "mt6797-audio", .of_match_table = mt6797_afe_pcm_dt_match, - .pm = &mt6797_afe_pm_ops, + .pm = pm_ptr(&mt6797_afe_pm_ops), }, .probe = mt6797_afe_pcm_dev_probe, .remove = mt6797_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c index 7db090414d59..7a6ad9116e55 100644 --- a/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c +++ b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c @@ -589,15 +589,15 @@ static const struct of_device_id mt7986_afe_pcm_dt_match[] = { MODULE_DEVICE_TABLE(of, mt7986_afe_pcm_dt_match); static const struct dev_pm_ops mt7986_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mt7986_afe_runtime_suspend, - mt7986_afe_runtime_resume, NULL) + RUNTIME_PM_OPS(mt7986_afe_runtime_suspend, + mt7986_afe_runtime_resume, NULL) }; static struct platform_driver mt7986_afe_pcm_driver = { .driver = { .name = "mt7986-audio", .of_match_table = mt7986_afe_pcm_dt_match, - .pm = &mt7986_afe_pm_ops, + .pm = pm_ptr(&mt7986_afe_pm_ops), }, .probe = mt7986_afe_pcm_dev_probe, .remove = mt7986_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c b/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c index d57971413a04..fc55ff47b7bc 100644 --- a/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c +++ b/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c @@ -348,10 +348,10 @@ static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: etdm_data->slave_mode = true; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: etdm_data->slave_mode = false; break; default: diff --git a/sound/soc/mediatek/mt7986/mt7986-wm8960.c b/sound/soc/mediatek/mt7986/mt7986-wm8960.c index c3d1e2eeb0e5..f1dc18222be7 100644 --- a/sound/soc/mediatek/mt7986/mt7986-wm8960.c +++ b/sound/soc/mediatek/mt7986/mt7986-wm8960.c @@ -63,7 +63,7 @@ static struct snd_soc_dai_link mt7986_wm8960_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_GATED, SND_SOC_DAILINK_REG(codec), }, diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index 03250273ea9c..04ed0cfec174 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -1212,15 +1212,15 @@ static const struct of_device_id mt8173_afe_pcm_dt_match[] = { MODULE_DEVICE_TABLE(of, mt8173_afe_pcm_dt_match); static const struct dev_pm_ops mt8173_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mt8173_afe_runtime_suspend, - mt8173_afe_runtime_resume, NULL) + RUNTIME_PM_OPS(mt8173_afe_runtime_suspend, + mt8173_afe_runtime_resume, NULL) }; static struct platform_driver mt8173_afe_pcm_driver = { .driver = { .name = "mt8173-afe-pcm", .of_match_table = mt8173_afe_pcm_dt_match, - .pm = &mt8173_afe_pm_ops, + .pm = pm_ptr(&mt8173_afe_pm_ops), }, .probe = mt8173_afe_pcm_dev_probe, .remove = mt8173_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index 0724564cee6a..49ebb67c818a 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -122,7 +122,7 @@ static struct snd_soc_dai_link mt8173_max98090_dais[] = { .init = mt8173_max98090_init, .ops = &mt8173_max98090_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(hifi), }, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index d8e4e70d834c..dc063d85e62f 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -156,7 +156,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5514_dais[] = { .no_pcm = 1, .init = mt8173_rt5650_rt5514_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ops = &mt8173_rt5650_rt5514_ops, .ignore_pmdown_time = 1, SND_SOC_DAILINK_REG(codec), diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 488f2314dbf7..a1ba5df87e1e 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -197,7 +197,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .no_pcm = 1, .init = mt8173_rt5650_rt5676_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ops = &mt8173_rt5650_rt5676_ops, .ignore_pmdown_time = 1, SND_SOC_DAILINK_REG(codec), @@ -214,7 +214,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = { .stream_name = "rt5650_rt5676 intercodec", .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, SND_SOC_DAILINK_REG(intercodec), }, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 59c19fdd8675..7d6a3586cdd5 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -235,7 +235,7 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = { .no_pcm = 1, .init = mt8173_rt5650_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ops = &mt8173_rt5650_ops, .ignore_pmdown_time = 1, SND_SOC_DAILINK_REG(codec), diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 3f377ba4ad53..d083b4bf0f95 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -1257,15 +1257,15 @@ static const struct of_device_id mt8183_afe_pcm_dt_match[] = { MODULE_DEVICE_TABLE(of, mt8183_afe_pcm_dt_match); static const struct dev_pm_ops mt8183_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mt8183_afe_runtime_suspend, - mt8183_afe_runtime_resume, NULL) + RUNTIME_PM_OPS(mt8183_afe_runtime_suspend, + mt8183_afe_runtime_resume, NULL) }; static struct platform_driver mt8183_afe_pcm_driver = { .driver = { .name = "mt8183-audio", .of_match_table = mt8183_afe_pcm_dt_match, - .pm = &mt8183_afe_pm_ops, + .pm = pm_ptr(&mt8183_afe_pm_ops), }, .probe = mt8183_afe_pcm_dev_probe, .remove = mt8183_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 1d8881e0a361..3388e076ccc9 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -563,7 +563,7 @@ static struct snd_soc_dai_link mt8183_da7219_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_IF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .playback_only = 1, .ignore_suspend = 1, .be_hw_params_fixup = mt8183_i2s_hw_params_fixup, diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index 6267c8554c15..497a9043be7b 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -575,7 +575,7 @@ static struct snd_soc_dai_link mt8183_mt6358_ts3a227_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_IF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .playback_only = 1, .ignore_suspend = 1, .be_hw_params_fixup = mt8183_i2s_hw_params_fixup, @@ -783,7 +783,7 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) strcmp(dai_link->name, "I2S3") == 0) dai_link->dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; + SND_SOC_DAIFMT_CBP_CFP; } if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0) { diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c index 70ec101890d3..daaca36a2d08 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c @@ -329,61 +329,6 @@ void mt8186_afe_disable_clock(struct mtk_base_afe *afe) clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]); } -int mt8186_afe_suspend_clock(struct mtk_base_afe *afe) -{ - struct mt8186_afe_private *afe_priv = afe->platform_priv; - int ret; - - /* set audio int bus to 26M */ - ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]); - if (ret) { - dev_info(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[CLK_MUX_AUDIOINTBUS], ret); - goto clk_mux_audio_intbus_err; - } - ret = mt8186_set_audio_int_bus_parent(afe, CLK_CLK26M); - if (ret) - goto clk_mux_audio_intbus_parent_err; - - clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]); - - return 0; - -clk_mux_audio_intbus_parent_err: - mt8186_set_audio_int_bus_parent(afe, CLK_TOP_MAINPLL_D2_D4); -clk_mux_audio_intbus_err: - clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]); - return ret; -} - -int mt8186_afe_resume_clock(struct mtk_base_afe *afe) -{ - struct mt8186_afe_private *afe_priv = afe->platform_priv; - int ret; - - /* set audio int bus to normal working clock */ - ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]); - if (ret) { - dev_info(afe->dev, "%s clk_prepare_enable %s fail %d\n", - __func__, aud_clks[CLK_MUX_AUDIOINTBUS], ret); - goto clk_mux_audio_intbus_err; - } - ret = mt8186_set_audio_int_bus_parent(afe, - CLK_TOP_MAINPLL_D2_D4); - if (ret) - goto clk_mux_audio_intbus_parent_err; - - clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]); - - return 0; - -clk_mux_audio_intbus_parent_err: - mt8186_set_audio_int_bus_parent(afe, CLK_CLK26M); -clk_mux_audio_intbus_err: - clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]); - return ret; -} - int mt8186_apll1_enable(struct mtk_base_afe *afe) { struct mt8186_afe_private *afe_priv = afe->platform_priv; diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.h b/sound/soc/mediatek/mt8186/mt8186-afe-clk.h index a9d59e506d9a..e524833ce780 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-clk.h +++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.h @@ -85,8 +85,6 @@ int mt8186_afe_enable_cgs(struct mtk_base_afe *afe); void mt8186_afe_disable_cgs(struct mtk_base_afe *afe); int mt8186_afe_enable_clock(struct mtk_base_afe *afe); void mt8186_afe_disable_clock(struct mtk_base_afe *afe); -int mt8186_afe_suspend_clock(struct mtk_base_afe *afe); -int mt8186_afe_resume_clock(struct mtk_base_afe *afe); int mt8186_apll1_enable(struct mtk_base_afe *afe); void mt8186_apll1_disable(struct mtk_base_afe *afe); diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c index bafbef96a42d..db7c93401bee 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c @@ -2978,15 +2978,15 @@ static const struct of_device_id mt8186_afe_pcm_dt_match[] = { MODULE_DEVICE_TABLE(of, mt8186_afe_pcm_dt_match); static const struct dev_pm_ops mt8186_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mt8186_afe_runtime_suspend, - mt8186_afe_runtime_resume, NULL) + RUNTIME_PM_OPS(mt8186_afe_runtime_suspend, + mt8186_afe_runtime_resume, NULL) }; static struct platform_driver mt8186_afe_pcm_driver = { .driver = { .name = "mt8186-audio", .of_match_table = mt8186_afe_pcm_dt_match, - .pm = &mt8186_afe_pm_ops, + .pm = pm_ptr(&mt8186_afe_pm_ops), }, .probe = mt8186_afe_pcm_dev_probe, }; diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366.c b/sound/soc/mediatek/mt8186/mt8186-mt6366.c index a5ef913743d4..43546012cf61 100644 --- a/sound/soc/mediatek/mt8186/mt8186-mt6366.c +++ b/sound/soc/mediatek/mt8186/mt8186-mt6366.c @@ -875,7 +875,7 @@ static struct snd_soc_dai_link mt8186_mt6366_rt1019_rt5682s_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_IB_IF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .playback_only = 1, .ignore_suspend = 1, .init = mt8186_mt6366_rt1019_rt5682s_hdmi_init, diff --git a/sound/soc/mediatek/mt8188/Makefile b/sound/soc/mediatek/mt8188/Makefile index 1178bce45c50..b9f3e4ad7b07 100644 --- a/sound/soc/mediatek/mt8188/Makefile +++ b/sound/soc/mediatek/mt8188/Makefile @@ -6,6 +6,7 @@ snd-soc-mt8188-afe-y := \ mt8188-afe-pcm.o \ mt8188-audsys-clk.o \ mt8188-dai-adda.o \ + mt8188-dai-dmic.o \ mt8188-dai-etdm.o \ mt8188-dai-pcm.o diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c index e69c1bb2cb23..7f411b857782 100644 --- a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c +++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c @@ -58,7 +58,15 @@ static const char *aud_clks[MT8188_CLK_NUM] = { [MT8188_CLK_AUD_ADC] = "aud_adc", [MT8188_CLK_AUD_DAC_HIRES] = "aud_dac_hires", [MT8188_CLK_AUD_A1SYS_HP] = "aud_a1sys_hp", + [MT8188_CLK_AUD_AFE_DMIC1] = "aud_afe_dmic1", + [MT8188_CLK_AUD_AFE_DMIC2] = "aud_afe_dmic2", + [MT8188_CLK_AUD_AFE_DMIC3] = "aud_afe_dmic3", + [MT8188_CLK_AUD_AFE_DMIC4] = "aud_afe_dmic4", [MT8188_CLK_AUD_ADC_HIRES] = "aud_adc_hires", + [MT8188_CLK_AUD_DMIC_HIRES1] = "aud_dmic_hires1", + [MT8188_CLK_AUD_DMIC_HIRES2] = "aud_dmic_hires2", + [MT8188_CLK_AUD_DMIC_HIRES3] = "aud_dmic_hires3", + [MT8188_CLK_AUD_DMIC_HIRES4] = "aud_dmic_hires4", [MT8188_CLK_AUD_I2SIN] = "aud_i2sin", [MT8188_CLK_AUD_TDM_IN] = "aud_tdm_in", [MT8188_CLK_AUD_I2S_OUT] = "aud_i2s_out", diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.h b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h index ec53c171c170..c6c78d684f3e 100644 --- a/sound/soc/mediatek/mt8188/mt8188-afe-clk.h +++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h @@ -54,7 +54,15 @@ enum { MT8188_CLK_AUD_ADC, MT8188_CLK_AUD_DAC_HIRES, MT8188_CLK_AUD_A1SYS_HP, + MT8188_CLK_AUD_AFE_DMIC1, + MT8188_CLK_AUD_AFE_DMIC2, + MT8188_CLK_AUD_AFE_DMIC3, + MT8188_CLK_AUD_AFE_DMIC4, MT8188_CLK_AUD_ADC_HIRES, + MT8188_CLK_AUD_DMIC_HIRES1, + MT8188_CLK_AUD_DMIC_HIRES2, + MT8188_CLK_AUD_DMIC_HIRES3, + MT8188_CLK_AUD_DMIC_HIRES4, MT8188_CLK_AUD_I2SIN, MT8188_CLK_AUD_TDM_IN, MT8188_CLK_AUD_I2S_OUT, diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-common.h b/sound/soc/mediatek/mt8188/mt8188-afe-common.h index 1304d685a306..01aa11242e29 100644 --- a/sound/soc/mediatek/mt8188/mt8188-afe-common.h +++ b/sound/soc/mediatek/mt8188/mt8188-afe-common.h @@ -137,6 +137,7 @@ struct mt8188_afe_private { int mt8188_afe_fs_timing(unsigned int rate); /* dai register */ int mt8188_dai_adda_register(struct mtk_base_afe *afe); +int mt8188_dai_dmic_register(struct mtk_base_afe *afe); int mt8188_dai_etdm_register(struct mtk_base_afe *afe); int mt8188_dai_pcm_register(struct mtk_base_afe *afe); diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c index 73e5c63aeec8..ac4fdf8ba78f 100644 --- a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c +++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c @@ -652,6 +652,7 @@ static struct snd_soc_dai_driver mt8188_memif_dai_driver[] = { static const struct snd_kcontrol_new o002_mix[] = { SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN2, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I004 Switch", AFE_CONN2, 4, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN2, 12, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN2, 20, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN2, 22, 1, 0), @@ -662,6 +663,8 @@ static const struct snd_kcontrol_new o002_mix[] = { static const struct snd_kcontrol_new o003_mix[] = { SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN3, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I005 Switch", AFE_CONN3, 5, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I006 Switch", AFE_CONN3, 6, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN3, 13, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN3, 21, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN3, 23, 1, 0), @@ -672,6 +675,8 @@ static const struct snd_kcontrol_new o003_mix[] = { static const struct snd_kcontrol_new o004_mix[] = { SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN4, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I006 Switch", AFE_CONN4, 6, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I008 Switch", AFE_CONN4, 8, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I014 Switch", AFE_CONN4, 14, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN4, 24, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I074 Switch", AFE_CONN4_2, 10, 1, 0), @@ -679,6 +684,8 @@ static const struct snd_kcontrol_new o004_mix[] = { static const struct snd_kcontrol_new o005_mix[] = { SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN5, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I007 Switch", AFE_CONN5, 7, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I010 Switch", AFE_CONN5, 10, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I015 Switch", AFE_CONN5, 15, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN5, 25, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I075 Switch", AFE_CONN5_2, 11, 1, 0), @@ -686,6 +693,7 @@ static const struct snd_kcontrol_new o005_mix[] = { static const struct snd_kcontrol_new o006_mix[] = { SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN6, 0, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I008 Switch", AFE_CONN6, 8, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I016 Switch", AFE_CONN6, 16, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN6, 26, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I076 Switch", AFE_CONN6_2, 12, 1, 0), @@ -693,18 +701,21 @@ static const struct snd_kcontrol_new o006_mix[] = { static const struct snd_kcontrol_new o007_mix[] = { SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN7, 1, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("I009 Switch", AFE_CONN7, 9, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I017 Switch", AFE_CONN7, 17, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN7, 27, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I077 Switch", AFE_CONN7_2, 13, 1, 0), }; static const struct snd_kcontrol_new o008_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I010 Switch", AFE_CONN8, 10, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I018 Switch", AFE_CONN8, 18, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN8, 28, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I078 Switch", AFE_CONN8_2, 14, 1, 0), }; static const struct snd_kcontrol_new o009_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I011 Switch", AFE_CONN9, 11, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I019 Switch", AFE_CONN9, 19, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN9, 29, 1, 0), SOC_DAPM_SINGLE_AUTODISABLE("I079 Switch", AFE_CONN9_2, 15, 1, 0), @@ -1275,6 +1286,18 @@ static const struct snd_soc_dapm_route mt8188_memif_routes[] = { {"O002", "I070 Switch", "I070"}, {"O003", "I071 Switch", "I071"}, + {"O002", "I004 Switch", "I004"}, + {"O003", "I005 Switch", "I005"}, + {"O003", "I006 Switch", "I006"}, + {"O004", "I006 Switch", "I006"}, + {"O004", "I008 Switch", "I008"}, + {"O005", "I007 Switch", "I007"}, + {"O005", "I010 Switch", "I010"}, + {"O006", "I008 Switch", "I008"}, + {"O007", "I009 Switch", "I009"}, + {"O008", "I010 Switch", "I010"}, + {"O009", "I011 Switch", "I011"}, + {"O034", "I000 Switch", "I000"}, {"O035", "I001 Switch", "I001"}, {"O034", "I002 Switch", "I002"}, @@ -2855,10 +2878,6 @@ static bool mt8188_is_volatile_reg(struct device *dev, unsigned int reg) case AFE_DMIC3_SRC_DEBUG_MON0: case AFE_DMIC3_UL_SRC_MON0: case AFE_DMIC3_UL_SRC_MON1: - case DMIC_GAIN1_CUR: - case DMIC_GAIN2_CUR: - case DMIC_GAIN3_CUR: - case DMIC_GAIN4_CUR: case ETDM_IN1_MONITOR: case ETDM_IN2_MONITOR: case ETDM_OUT1_MONITOR: @@ -3076,6 +3095,7 @@ static int mt8188_dai_memif_register(struct mtk_base_afe *afe) typedef int (*dai_register_cb)(struct mtk_base_afe *); static const dai_register_cb dai_register_cbs[] = { mt8188_dai_adda_register, + mt8188_dai_dmic_register, mt8188_dai_etdm_register, mt8188_dai_pcm_register, mt8188_dai_memif_register, @@ -3361,15 +3381,15 @@ static const struct of_device_id mt8188_afe_pcm_dt_match[] = { MODULE_DEVICE_TABLE(of, mt8188_afe_pcm_dt_match); static const struct dev_pm_ops mt8188_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mt8188_afe_runtime_suspend, - mt8188_afe_runtime_resume, NULL) + RUNTIME_PM_OPS(mt8188_afe_runtime_suspend, + mt8188_afe_runtime_resume, NULL) }; static struct platform_driver mt8188_afe_pcm_driver = { .driver = { .name = "mt8188-audio", .of_match_table = mt8188_afe_pcm_dt_match, - .pm = &mt8188_afe_pm_ops, + .pm = pm_ptr(&mt8188_afe_pm_ops), }, .probe = mt8188_afe_pcm_dev_probe, }; diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c index c796ad8b62ee..40d2ab0a7677 100644 --- a/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c +++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c @@ -84,6 +84,10 @@ static const struct afe_gate aud_clks[CLK_AUD_NR_CLK] = { GATE_AUD1(CLK_AUD_AFE_26M_DMIC_TM, "aud_afe_26m_dmic_tm", "top_a1sys_hp", 14), GATE_AUD1(CLK_AUD_UL_TML_HIRES, "aud_ul_tml_hires", "top_audio_h", 16), GATE_AUD1(CLK_AUD_ADC_HIRES, "aud_adc_hires", "top_audio_h", 17), + GATE_AUD1(CLK_AUD_DMIC_HIRES1, "aud_dmic_hires1", "top_audio_h", 20), + GATE_AUD1(CLK_AUD_DMIC_HIRES2, "aud_dmic_hires2", "top_audio_h", 21), + GATE_AUD1(CLK_AUD_DMIC_HIRES3, "aud_dmic_hires3", "top_audio_h", 22), + GATE_AUD1(CLK_AUD_DMIC_HIRES4, "aud_dmic_hires4", "top_audio_h", 23), /* AUD3 */ GATE_AUD3(CLK_AUD_LINEIN_TUNER, "aud_linein_tuner", "top_apll5", 5), diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h b/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h index 6f34ffc760e0..9cb732863c10 100644 --- a/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h +++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h @@ -33,6 +33,10 @@ enum{ CLK_AUD_AFE_26M_DMIC_TM, CLK_AUD_UL_TML_HIRES, CLK_AUD_ADC_HIRES, + CLK_AUD_DMIC_HIRES1, + CLK_AUD_DMIC_HIRES2, + CLK_AUD_DMIC_HIRES3, + CLK_AUD_DMIC_HIRES4, CLK_AUD_LINEIN_TUNER, CLK_AUD_EARC_TUNER, CLK_AUD_I2SIN, diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c b/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c new file mode 100644 index 000000000000..adcea7818be2 --- /dev/null +++ b/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC Audio DAI DMIC I/F Control + * + * Copyright (c) 2020 MediaTek Inc. + * Author: Bicycle Tsai <bicycle.tsai@mediatek.com> + * Trevor Wu <trevor.wu@mediatek.com> + * Parker Yang <parker.yang@mediatek.com> + */ + +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include "mt8188-afe-clk.h" +#include "mt8188-afe-common.h" +#include "mt8188-reg.h" + +/* DMIC HW Gain configuration maximum value. */ +#define DMIC_GAIN_MAX_STEP GENMASK(19, 0) +#define DMIC_GAIN_MAX_PER_STEP GENMASK(7, 0) +#define DMIC_GAIN_MAX_TARGET GENMASK(27, 0) +#define DMIC_GAIN_MAX_CURRENT GENMASK(27, 0) + +#define CLK_PHASE_SEL_CH1 0 +#define CLK_PHASE_SEL_CH2 ((CLK_PHASE_SEL_CH1) + 4) + +#define DMIC1_SRC_SEL 0 +#define DMIC2_SRC_SEL 0 +#define DMIC3_SRC_SEL 2 +#define DMIC4_SRC_SEL 0 +#define DMIC5_SRC_SEL 4 +#define DMIC6_SRC_SEL 0 +#define DMIC7_SRC_SEL 6 +#define DMIC8_SRC_SEL 0 + +enum { + SUPPLY_SEQ_DMIC_GAIN, + SUPPLY_SEQ_DMIC_CK, +}; + +enum { + DMIC0, + DMIC1, + DMIC2, + DMIC3, + DMIC_NUM, +}; + +struct mtk_dai_dmic_ctrl_reg { + unsigned int con0; +}; + +struct mtk_dai_dmic_hw_gain_ctrl_reg { + unsigned int bypass; + unsigned int con0; +}; + +struct mtk_dai_dmic_priv { + unsigned int gain_on[DMIC_NUM]; + unsigned int channels; + bool hires_required; +}; + +static const struct mtk_dai_dmic_ctrl_reg dmic_ctrl_regs[DMIC_NUM] = { + [DMIC0] = { + .con0 = AFE_DMIC0_UL_SRC_CON0, + }, + [DMIC1] = { + .con0 = AFE_DMIC1_UL_SRC_CON0, + }, + [DMIC2] = { + .con0 = AFE_DMIC2_UL_SRC_CON0, + }, + [DMIC3] = { + .con0 = AFE_DMIC3_UL_SRC_CON0, + }, +}; + +static const struct mtk_dai_dmic_ctrl_reg *get_dmic_ctrl_reg(int id) +{ + if (id < 0 || id >= DMIC_NUM) + return NULL; + + return &dmic_ctrl_regs[id]; +} + +static const struct mtk_dai_dmic_hw_gain_ctrl_reg + dmic_hw_gain_ctrl_regs[DMIC_NUM] = { + [DMIC0] = { + .bypass = DMIC_BYPASS_HW_GAIN, + .con0 = DMIC_GAIN1_CON0, + }, + [DMIC1] = { + .bypass = DMIC_BYPASS_HW_GAIN, + .con0 = DMIC_GAIN2_CON0, + }, + [DMIC2] = { + .bypass = DMIC_BYPASS_HW_GAIN, + .con0 = DMIC_GAIN3_CON0, + }, + [DMIC3] = { + .bypass = DMIC_BYPASS_HW_GAIN, + .con0 = DMIC_GAIN4_CON0, + }, +}; + +static const struct mtk_dai_dmic_hw_gain_ctrl_reg + *get_dmic_hw_gain_ctrl_reg(struct mtk_base_afe *afe, int id) +{ + if ((id < 0) || (id >= DMIC_NUM)) { + dev_dbg(afe->dev, "%s invalid id\n", __func__); + return NULL; + } + + return &dmic_hw_gain_ctrl_regs[id]; +} + +static void mtk_dai_dmic_hw_gain_bypass(struct mtk_base_afe *afe, + unsigned int id, bool bypass) +{ + const struct mtk_dai_dmic_hw_gain_ctrl_reg *reg; + unsigned int msk; + + reg = get_dmic_hw_gain_ctrl_reg(afe, id); + if (!reg) + return; + + switch (id) { + case DMIC0: + msk = DMIC_BYPASS_HW_GAIN_DMIC1_BYPASS; + break; + case DMIC1: + msk = DMIC_BYPASS_HW_GAIN_DMIC2_BYPASS; + break; + case DMIC2: + msk = DMIC_BYPASS_HW_GAIN_DMIC3_BYPASS; + break; + case DMIC3: + msk = DMIC_BYPASS_HW_GAIN_DMIC4_BYPASS; + break; + default: + return; + } + + if (bypass) + regmap_set_bits(afe->regmap, reg->bypass, msk); + else + regmap_clear_bits(afe->regmap, reg->bypass, msk); +} + +static void mtk_dai_dmic_hw_gain_on(struct mtk_base_afe *afe, unsigned int id, + bool on) +{ + const struct mtk_dai_dmic_hw_gain_ctrl_reg *reg = get_dmic_hw_gain_ctrl_reg(afe, id); + + if (!reg) + return; + + if (on) + regmap_set_bits(afe->regmap, reg->con0, DMIC_GAIN_CON0_GAIN_ON); + else + regmap_clear_bits(afe->regmap, reg->con0, DMIC_GAIN_CON0_GAIN_ON); +} + +static const struct reg_sequence mtk_dai_dmic_iir_coeff_reg_defaults[] = { + { AFE_DMIC0_IIR_COEF_02_01, 0x00000000 }, + { AFE_DMIC0_IIR_COEF_04_03, 0x00003FB8 }, + { AFE_DMIC0_IIR_COEF_06_05, 0x3FB80000 }, + { AFE_DMIC0_IIR_COEF_08_07, 0x3FB80000 }, + { AFE_DMIC0_IIR_COEF_10_09, 0x0000C048 }, + { AFE_DMIC1_IIR_COEF_02_01, 0x00000000 }, + { AFE_DMIC1_IIR_COEF_04_03, 0x00003FB8 }, + { AFE_DMIC1_IIR_COEF_06_05, 0x3FB80000 }, + { AFE_DMIC1_IIR_COEF_08_07, 0x3FB80000 }, + { AFE_DMIC1_IIR_COEF_10_09, 0x0000C048 }, + { AFE_DMIC2_IIR_COEF_02_01, 0x00000000 }, + { AFE_DMIC2_IIR_COEF_04_03, 0x00003FB8 }, + { AFE_DMIC2_IIR_COEF_06_05, 0x3FB80000 }, + { AFE_DMIC2_IIR_COEF_08_07, 0x3FB80000 }, + { AFE_DMIC2_IIR_COEF_10_09, 0x0000C048 }, + { AFE_DMIC3_IIR_COEF_02_01, 0x00000000 }, + { AFE_DMIC3_IIR_COEF_04_03, 0x00003FB8 }, + { AFE_DMIC3_IIR_COEF_06_05, 0x3FB80000 }, + { AFE_DMIC3_IIR_COEF_08_07, 0x3FB80000 }, + { AFE_DMIC3_IIR_COEF_10_09, 0x0000C048 }, +}; + +static int mtk_dai_dmic_load_iir_coeff_table(struct mtk_base_afe *afe) +{ + return regmap_multi_reg_write(afe->regmap, + mtk_dai_dmic_iir_coeff_reg_defaults, + ARRAY_SIZE(mtk_dai_dmic_iir_coeff_reg_defaults)); +} + +static int mtk_dai_dmic_configure_array(struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + const u32 mask = PWR2_TOP_CON_DMIC8_SRC_SEL_MASK | + PWR2_TOP_CON_DMIC7_SRC_SEL_MASK | + PWR2_TOP_CON_DMIC6_SRC_SEL_MASK | + PWR2_TOP_CON_DMIC5_SRC_SEL_MASK | + PWR2_TOP_CON_DMIC4_SRC_SEL_MASK | + PWR2_TOP_CON_DMIC3_SRC_SEL_MASK | + PWR2_TOP_CON_DMIC2_SRC_SEL_MASK | + PWR2_TOP_CON_DMIC1_SRC_SEL_MASK; + const u32 val = PWR2_TOP_CON_DMIC8_SRC_SEL_VAL(DMIC8_SRC_SEL) | + PWR2_TOP_CON_DMIC7_SRC_SEL_VAL(DMIC7_SRC_SEL) | + PWR2_TOP_CON_DMIC6_SRC_SEL_VAL(DMIC6_SRC_SEL) | + PWR2_TOP_CON_DMIC5_SRC_SEL_VAL(DMIC5_SRC_SEL) | + PWR2_TOP_CON_DMIC4_SRC_SEL_VAL(DMIC4_SRC_SEL) | + PWR2_TOP_CON_DMIC3_SRC_SEL_VAL(DMIC3_SRC_SEL) | + PWR2_TOP_CON_DMIC2_SRC_SEL_VAL(DMIC2_SRC_SEL) | + PWR2_TOP_CON_DMIC1_SRC_SEL_VAL(DMIC1_SRC_SEL); + + return regmap_update_bits(afe->regmap, PWR2_TOP_CON0, mask, val); +} + +/* This function assumes that the caller checked that channels is valid */ +static u8 mtk_dmic_channels_to_dmic_number(unsigned int channels) +{ + switch (channels) { + case 1: + return DMIC0; + case 2: + return DMIC1; + case 3: + return DMIC2; + case 4: + default: + return DMIC3; + } +} + +static void mtk_dai_dmic_hw_gain_enable(struct mtk_base_afe *afe, + unsigned int channels, bool enable) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN]; + u8 dmic_num; + int i; + + dmic_num = mtk_dmic_channels_to_dmic_number(channels); + for (i = dmic_num; i >= DMIC0; i--) { + if (enable && dmic_priv->gain_on[i]) { + mtk_dai_dmic_hw_gain_bypass(afe, i, false); + mtk_dai_dmic_hw_gain_on(afe, i, true); + } else { + mtk_dai_dmic_hw_gain_on(afe, i, false); + mtk_dai_dmic_hw_gain_bypass(afe, i, true); + } + } +} + +static int mtk_dmic_gain_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN]; + unsigned int channels = dmic_priv->channels; + + dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + if (!channels) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mtk_dai_dmic_hw_gain_enable(afe, channels, true); + break; + case SND_SOC_DAPM_POST_PMD: + mtk_dai_dmic_hw_gain_enable(afe, channels, false); + break; + default: + break; + } + + return 0; +} + +static int mtk_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN]; + const struct mtk_dai_dmic_ctrl_reg *reg = NULL; + unsigned int channels = dmic_priv->channels; + unsigned int msk; + u8 dmic_num; + int i; + + dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n", + __func__, w->name, event); + + if (!channels) + return -EINVAL; + + dmic_num = mtk_dmic_channels_to_dmic_number(channels); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* request fifo soft rst */ + msk = 0; + for (i = dmic_num; i >= DMIC0; i--) + msk |= PWR2_TOP_CON1_DMIC_FIFO_SOFT_RST_EN(i); + + regmap_set_bits(afe->regmap, PWR2_TOP_CON1, msk); + + msk = AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH1_CTL | + AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH2_CTL | + AFE_DMIC_UL_SRC_CON0_UL_SDM_3_LEVEL_CTL | + AFE_DMIC_UL_SRC_CON0_UL_IIR_ON_TMP_CTL; + + for (i = dmic_num; i >= DMIC0; i--) { + reg = get_dmic_ctrl_reg(i); + if (reg) + regmap_set_bits(afe->regmap, reg->con0, msk); + } + break; + case SND_SOC_DAPM_POST_PMU: + msk = AFE_DMIC_UL_SRC_CON0_UL_SRC_ON_TMP_CTL; + + for (i = dmic_num; i >= DMIC0; i--) { + reg = get_dmic_ctrl_reg(i); + if (reg) + regmap_set_bits(afe->regmap, reg->con0, msk); + } + + if (dmic_priv->hires_required) { + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES1]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES2]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES3]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES4]); + } + + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC1]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC2]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC3]); + mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC4]); + + /* release fifo soft rst */ + msk = 0; + for (i = dmic_num; i >= DMIC0; i--) + msk |= PWR2_TOP_CON1_DMIC_FIFO_SOFT_RST_EN(i); + + regmap_clear_bits(afe->regmap, PWR2_TOP_CON1, msk); + break; + case SND_SOC_DAPM_PRE_PMD: + msk = AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH1_CTL | + AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH2_CTL | + AFE_DMIC_UL_SRC_CON0_UL_SRC_ON_TMP_CTL | + AFE_DMIC_UL_SRC_CON0_UL_IIR_ON_TMP_CTL | + AFE_DMIC_UL_SRC_CON0_UL_SDM_3_LEVEL_CTL; + + for (i = dmic_num; i >= DMIC0; i--) { + reg = get_dmic_ctrl_reg(i); + if (reg) + regmap_set_bits(afe->regmap, reg->con0, msk); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* should delayed 1/fs(smallest is 8k) = 125us before afe off */ + usleep_range(125, 126); + + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC1]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC2]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC3]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE_DMIC4]); + + if (dmic_priv->hires_required) { + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES1]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES2]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES3]); + mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_DMIC_HIRES4]); + } + break; + default: + break; + } + + return 0; +} + +static int mtk_dai_dmic_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN]; + unsigned int rate = params_rate(params); + unsigned int channels = params_channels(params); + const struct mtk_dai_dmic_ctrl_reg *reg = NULL; + u32 val = AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH1(CLK_PHASE_SEL_CH1) | + AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH2(CLK_PHASE_SEL_CH2) | + AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL(0); + const u32 msk = AFE_DMIC_UL_SRC_CON0_UL_TWO_WIRE_MODE_CTL | + AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_MASK | + AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL_MASK | + AFE_DMIC_UL_VOICE_MODE_MASK; + u8 dmic_num; + int ret; + int i; + + if (!channels || channels > 8) + return -EINVAL; + + ret = mtk_dai_dmic_configure_array(dai); + if (ret < 0) + return ret; + + ret = mtk_dai_dmic_load_iir_coeff_table(afe); + if (ret < 0) + return ret; + + switch (rate) { + case 96000: + val |= AFE_DMIC_UL_CON0_VOCIE_MODE_96K; + dmic_priv->hires_required = 1; + break; + case 48000: + val |= AFE_DMIC_UL_CON0_VOCIE_MODE_48K; + dmic_priv->hires_required = 0; + break; + case 32000: + val |= AFE_DMIC_UL_CON0_VOCIE_MODE_32K; + dmic_priv->hires_required = 0; + break; + case 16000: + val |= AFE_DMIC_UL_CON0_VOCIE_MODE_16K; + dmic_priv->hires_required = 0; + break; + case 8000: + val |= AFE_DMIC_UL_CON0_VOCIE_MODE_8K; + dmic_priv->hires_required = 0; + break; + default: + dev_dbg(afe->dev, "%s invalid rate %u, use 48000Hz\n", __func__, rate); + val |= AFE_DMIC_UL_CON0_VOCIE_MODE_48K; + dmic_priv->hires_required = 0; + break; + } + + dmic_num = mtk_dmic_channels_to_dmic_number(channels); + for (i = dmic_num; i >= DMIC0; i--) { + reg = get_dmic_ctrl_reg(i); + if (reg) { + ret = regmap_update_bits(afe->regmap, reg->con0, msk, val); + if (ret < 0) + return ret; + } + } + + dmic_priv->channels = channels; + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_dmic_ops = { + .hw_params = mtk_dai_dmic_hw_params, +}; + +#define MTK_DMIC_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000) + +#define MTK_DMIC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_dmic_driver[] = { + { + .name = "DMIC", + .id = MT8188_AFE_IO_DMIC_IN, + .capture = { + .stream_name = "DMIC Capture", + .channels_min = 1, + .channels_max = 8, + .rates = MTK_DMIC_RATES, + .formats = MTK_DMIC_FORMATS, + }, + .ops = &mtk_dai_dmic_ops, + }, +}; + +static const struct snd_soc_dapm_widget mtk_dai_dmic_widgets[] = { + SND_SOC_DAPM_MIXER("I004", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I005", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I006", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I007", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I008", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I009", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I010", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I011", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("DMIC_GAIN_ON", SUPPLY_SEQ_DMIC_GAIN, + SND_SOC_NOPM, 0, 0, + mtk_dmic_gain_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("DMIC_CK_ON", SUPPLY_SEQ_DMIC_CK, + PWR2_TOP_CON1, + PWR2_TOP_CON1_DMIC_CKDIV_ON_SHIFT, 0, + mtk_dmic_event, + SND_SOC_DAPM_PRE_POST_PMU | + SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_INPUT("DMIC_INPUT"), +}; + +static const struct snd_soc_dapm_route mtk_dai_dmic_routes[] = { + {"I004", NULL, "DMIC Capture"}, + {"I005", NULL, "DMIC Capture"}, + {"I006", NULL, "DMIC Capture"}, + {"I007", NULL, "DMIC Capture"}, + {"I008", NULL, "DMIC Capture"}, + {"I009", NULL, "DMIC Capture"}, + {"I010", NULL, "DMIC Capture"}, + {"I011", NULL, "DMIC Capture"}, + {"DMIC Capture", NULL, "DMIC_CK_ON"}, + {"DMIC Capture", NULL, "DMIC_GAIN_ON"}, + {"DMIC Capture", NULL, "DMIC_INPUT"}, +}; + +static const char * const mt8188_dmic_gain_enable_text[] = { + "Bypass", "Connect", +}; + +static SOC_ENUM_SINGLE_EXT_DECL(dmic_gain_on_enum, + mt8188_dmic_gain_enable_text); + +static int mtk_dai_dmic_hw_gain_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN]; + unsigned int source = ucontrol->value.enumerated.item[0]; + unsigned int *cached; + + if (source >= e->items) + return -EINVAL; + + if (!strcmp(kcontrol->id.name, "DMIC1_HW_GAIN_EN")) + cached = &dmic_priv->gain_on[0]; + else if (!strcmp(kcontrol->id.name, "DMIC2_HW_GAIN_EN")) + cached = &dmic_priv->gain_on[1]; + else if (!strcmp(kcontrol->id.name, "DMIC3_HW_GAIN_EN")) + cached = &dmic_priv->gain_on[2]; + else if (!strcmp(kcontrol->id.name, "DMIC4_HW_GAIN_EN")) + cached = &dmic_priv->gain_on[3]; + else + return -EINVAL; + + if (source == *cached) + return 0; + + *cached = source; + return 1; +} + +static int mtk_dai_dmic_hw_gain_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_dmic_priv *dmic_priv = afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN]; + unsigned int val; + + if (!strcmp(kcontrol->id.name, "DMIC1_HW_GAIN_EN")) + val = dmic_priv->gain_on[0]; + else if (!strcmp(kcontrol->id.name, "DMIC2_HW_GAIN_EN")) + val = dmic_priv->gain_on[1]; + else if (!strcmp(kcontrol->id.name, "DMIC3_HW_GAIN_EN")) + val = dmic_priv->gain_on[2]; + else if (!strcmp(kcontrol->id.name, "DMIC4_HW_GAIN_EN")) + val = dmic_priv->gain_on[3]; + else + return -EINVAL; + + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static const struct snd_kcontrol_new mtk_dai_dmic_controls[] = { + SOC_ENUM_EXT("DMIC1_HW_GAIN_EN", dmic_gain_on_enum, + mtk_dai_dmic_hw_gain_ctrl_get, + mtk_dai_dmic_hw_gain_ctrl_put), + SOC_ENUM_EXT("DMIC2_HW_GAIN_EN", dmic_gain_on_enum, + mtk_dai_dmic_hw_gain_ctrl_get, + mtk_dai_dmic_hw_gain_ctrl_put), + SOC_ENUM_EXT("DMIC3_HW_GAIN_EN", dmic_gain_on_enum, + mtk_dai_dmic_hw_gain_ctrl_get, + mtk_dai_dmic_hw_gain_ctrl_put), + SOC_ENUM_EXT("DMIC4_HW_GAIN_EN", dmic_gain_on_enum, + mtk_dai_dmic_hw_gain_ctrl_get, + mtk_dai_dmic_hw_gain_ctrl_put), + SOC_SINGLE("DMIC1_HW_GAIN_TARGET", DMIC_GAIN1_CON1, + 0, DMIC_GAIN_MAX_TARGET, 0), + SOC_SINGLE("DMIC2_HW_GAIN_TARGET", DMIC_GAIN2_CON1, + 0, DMIC_GAIN_MAX_TARGET, 0), + SOC_SINGLE("DMIC3_HW_GAIN_TARGET", DMIC_GAIN3_CON1, + 0, DMIC_GAIN_MAX_TARGET, 0), + SOC_SINGLE("DMIC4_HW_GAIN_TARGET", DMIC_GAIN4_CON1, + 0, DMIC_GAIN_MAX_TARGET, 0), + SOC_SINGLE("DMIC1_HW_GAIN_CURRENT", DMIC_GAIN1_CUR, + 0, DMIC_GAIN_MAX_CURRENT, 0), + SOC_SINGLE("DMIC2_HW_GAIN_CURRENT", DMIC_GAIN2_CUR, + 0, DMIC_GAIN_MAX_CURRENT, 0), + SOC_SINGLE("DMIC3_HW_GAIN_CURRENT", DMIC_GAIN3_CUR, + 0, DMIC_GAIN_MAX_CURRENT, 0), + SOC_SINGLE("DMIC4_HW_GAIN_CURRENT", DMIC_GAIN4_CUR, + 0, DMIC_GAIN_MAX_CURRENT, 0), + SOC_SINGLE("DMIC1_HW_GAIN_UP_STEP", DMIC_GAIN1_CON3, + 0, DMIC_GAIN_MAX_STEP, 0), + SOC_SINGLE("DMIC2_HW_GAIN_UP_STEP", DMIC_GAIN2_CON3, + 0, DMIC_GAIN_MAX_STEP, 0), + SOC_SINGLE("DMIC3_HW_GAIN_UP_STEP", DMIC_GAIN3_CON3, + 0, DMIC_GAIN_MAX_STEP, 0), + SOC_SINGLE("DMIC4_HW_GAIN_UP_STEP", DMIC_GAIN4_CON3, + 0, DMIC_GAIN_MAX_STEP, 0), + SOC_SINGLE("DMIC1_HW_GAIN_DOWN_STEP", DMIC_GAIN1_CON2, + 0, DMIC_GAIN_MAX_STEP, 0), + SOC_SINGLE("DMIC2_HW_GAIN_DOWN_STEP", DMIC_GAIN2_CON2, + 0, DMIC_GAIN_MAX_STEP, 0), + SOC_SINGLE("DMIC3_HW_GAIN_DOWN_STEP", DMIC_GAIN3_CON2, + 0, DMIC_GAIN_MAX_STEP, 0), + SOC_SINGLE("DMIC4_HW_GAIN_DOWN_STEP", DMIC_GAIN4_CON2, + 0, DMIC_GAIN_MAX_STEP, 0), + SOC_SINGLE("DMIC1_HW_GAIN_SAMPLE_PER_STEP", DMIC_GAIN1_CON0, + DMIC_GAIN_CON0_SAMPLE_PER_STEP_SHIFT, DMIC_GAIN_MAX_PER_STEP, 0), + SOC_SINGLE("DMIC2_HW_GAIN_SAMPLE_PER_STEP", DMIC_GAIN2_CON0, + DMIC_GAIN_CON0_SAMPLE_PER_STEP_SHIFT, DMIC_GAIN_MAX_PER_STEP, 0), + SOC_SINGLE("DMIC3_HW_GAIN_SAMPLE_PER_STEP", DMIC_GAIN3_CON0, + DMIC_GAIN_CON0_SAMPLE_PER_STEP_SHIFT, DMIC_GAIN_MAX_PER_STEP, 0), + SOC_SINGLE("DMIC4_HW_GAIN_SAMPLE_PER_STEP", DMIC_GAIN4_CON0, + DMIC_GAIN_CON0_SAMPLE_PER_STEP_SHIFT, DMIC_GAIN_MAX_PER_STEP, 0), +}; + +static int init_dmic_priv_data(struct mtk_base_afe *afe) +{ + struct mt8188_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_dmic_priv *dmic_priv; + + dmic_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_dmic_priv), + GFP_KERNEL); + if (!dmic_priv) + return -ENOMEM; + + afe_priv->dai_priv[MT8188_AFE_IO_DMIC_IN] = dmic_priv; + return 0; +} + +int mt8188_dai_dmic_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_dmic_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_dmic_driver); + dai->dapm_widgets = mtk_dai_dmic_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_dmic_widgets); + dai->dapm_routes = mtk_dai_dmic_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_dmic_routes); + dai->controls = mtk_dai_dmic_controls; + dai->num_controls = ARRAY_SIZE(mtk_dai_dmic_controls); + + return init_dmic_priv_data(afe); +} diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index 2d0d04e0232d..20dc9470ba76 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -17,6 +17,7 @@ #include "mt8188-afe-common.h" #include "../../codecs/nau8825.h" #include "../../codecs/mt6359.h" +#include "../../codecs/mt6359-accdet.h" #include "../../codecs/rt5682.h" #include "../common/mtk-afe-platform-driver.h" #include "../common/mtk-soundcard-driver.h" @@ -150,6 +151,11 @@ SND_SOC_DAILINK_DEFS(dl_src, "mt6359-snd-codec-aif1")), DAILINK_COMP_ARRAY(COMP_EMPTY())); +SND_SOC_DAILINK_DEFS(DMIC_BE, + DAILINK_COMP_ARRAY(COMP_CPU("DMIC")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + SND_SOC_DAILINK_DEFS(dptx, DAILINK_COMP_ARRAY(COMP_CPU("DPTX")), DAILINK_COMP_ARRAY(COMP_DUMMY()), @@ -266,6 +272,17 @@ static struct snd_soc_jack_pin nau8825_jack_pins[] = { }, }; +static struct snd_soc_jack_pin mt8188_headset_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static const struct snd_kcontrol_new mt8188_dumb_spk_controls[] = { SOC_DAPM_PIN_SWITCH("Ext Spk"), }; @@ -297,6 +314,7 @@ static const struct snd_soc_dapm_widget mt8188_rear_spk_widgets[] = { static const struct snd_soc_dapm_widget mt8188_mt6359_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("AP DMIC", NULL), SND_SOC_DAPM_SINK("HDMI"), SND_SOC_DAPM_SINK("DP"), SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0), @@ -500,6 +518,35 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd) return 0; } +static int mt8188_mt6359_accdet_init(struct snd_soc_pcm_runtime *rtd) +{ + struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_jack *jack = &soc_card_data->card_data->jacks[MT8188_JACK_HEADSET]; + int ret; + + if (!soc_card_data->accdet) + return 0; + + ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + jack, mt8188_headset_jack_pins, + ARRAY_SIZE(mt8188_headset_jack_pins)); + if (ret) { + dev_err(rtd->dev, "Headset Jack create failed: %d\n", ret); + return ret; + } + + ret = mt6359_accdet_enable_jack_detect(soc_card_data->accdet, jack); + if (ret) { + dev_err(rtd->dev, "Headset Jack enable failed: %d\n", ret); + return ret; + } + + return 0; +} + static int mt8188_mt6359_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_component *cmpnt_codec = @@ -512,6 +559,8 @@ static int mt8188_mt6359_init(struct snd_soc_pcm_runtime *rtd) /* mtkaif calibration */ mt8188_mt6359_mtkaif_calibration(rtd); + mt8188_mt6359_accdet_init(rtd); + return 0; } @@ -533,6 +582,7 @@ enum { DAI_LINK_UL9_FE, DAI_LINK_UL10_FE, DAI_LINK_DL_SRC_BE, + DAI_LINK_DMIC_BE, DAI_LINK_DPTX_BE, DAI_LINK_ETDM1_IN_BE, DAI_LINK_ETDM2_IN_BE, @@ -1120,6 +1170,13 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { .playback_only = 1, SND_SOC_DAILINK_REG(dl_src), }, + [DAI_LINK_DMIC_BE] = { + .name = "DMIC_BE", + .no_pcm = 1, + .capture_only = 1, + .ignore_suspend = 1, + SND_SOC_DAILINK_REG(DMIC_BE), + }, [DAI_LINK_DPTX_BE] = { .name = "DPTX_BE", .ops = &mt8188_dptx_ops, diff --git a/sound/soc/mediatek/mt8188/mt8188-reg.h b/sound/soc/mediatek/mt8188/mt8188-reg.h index bdd885419ff3..2e9c65de249d 100644 --- a/sound/soc/mediatek/mt8188/mt8188-reg.h +++ b/sound/soc/mediatek/mt8188/mt8188-reg.h @@ -2837,9 +2837,20 @@ #define PWR2_TOP_CON_DMIC3_SRC_SEL_MASK GENMASK(16, 14) #define PWR2_TOP_CON_DMIC2_SRC_SEL_MASK GENMASK(13, 11) #define PWR2_TOP_CON_DMIC1_SRC_SEL_MASK GENMASK(10, 8) +#define PWR2_TOP_CON_DMIC8_SRC_SEL_VAL(x) ((x) << 29) +#define PWR2_TOP_CON_DMIC7_SRC_SEL_VAL(x) ((x) << 26) +#define PWR2_TOP_CON_DMIC6_SRC_SEL_VAL(x) ((x) << 23) +#define PWR2_TOP_CON_DMIC5_SRC_SEL_VAL(x) ((x) << 20) +#define PWR2_TOP_CON_DMIC4_SRC_SEL_VAL(x) ((x) << 17) +#define PWR2_TOP_CON_DMIC3_SRC_SEL_VAL(x) ((x) << 14) +#define PWR2_TOP_CON_DMIC2_SRC_SEL_VAL(x) ((x) << 11) +#define PWR2_TOP_CON_DMIC1_SRC_SEL_VAL(x) ((x) << 8) /* PWR2_TOP_CON1 */ -#define PWR2_TOP_CON1_DMIC_CKDIV_ON BIT(1) +#define PWR2_TOP_CON1_DMIC_FIFO_SOFT_RST_EN(x) BIT(5 + 6 * (x)) +#define PWR2_TOP_CON1_DMIC_CKDIV_ON BIT(1) +#define PWR2_TOP_CON1_DMIC_CKDIV_ON_SHIFT 1 + /* PCM_INTF_CON1 */ #define PCM_INTF_CON1_SYNC_OUT_INV BIT(23) @@ -2921,13 +2932,14 @@ #define AFE_DMIC_UL_SRC_CON0_UL_TWO_WIRE_MODE_CTL BIT(23) #define AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH2_CTL BIT(22) #define AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH1_CTL BIT(21) - +#define AFE_DMIC_UL_VOICE_MODE(x) (((x) & GENMASK(2, 0)) << 17) #define AFE_DMIC_UL_VOICE_MODE_MASK GENMASK(19, 17) #define AFE_DMIC_UL_CON0_VOCIE_MODE_8K AFE_DMIC_UL_VOICE_MODE(0) #define AFE_DMIC_UL_CON0_VOCIE_MODE_16K AFE_DMIC_UL_VOICE_MODE(1) #define AFE_DMIC_UL_CON0_VOCIE_MODE_32K AFE_DMIC_UL_VOICE_MODE(2) #define AFE_DMIC_UL_CON0_VOCIE_MODE_48K AFE_DMIC_UL_VOICE_MODE(3) #define AFE_DMIC_UL_CON0_VOCIE_MODE_96K AFE_DMIC_UL_VOICE_MODE(4) +#define AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL(x) (((x) & GENMASK(2, 0)) << 7) #define AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL_MASK GENMASK(9, 7) #define AFE_DMIC_UL_SRC_CON0_UL_IIR_ON_TMP_CTL BIT(10) #define AFE_DMIC_UL_SRC_CON0_UL_SDM_3_LEVEL_CTL BIT(1) @@ -2944,6 +2956,7 @@ /* DMIC_GAINx_CON0 */ #define DMIC_GAIN_CON0_GAIN_ON BIT(0) +#define DMIC_GAIN_CON0_SAMPLE_PER_STEP_SHIFT 8 #define DMIC_GAIN_CON0_SAMPLE_PER_STEP_MASK GENMASK(15, 8) /* DMIC_GAINx_CON1 */ diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index 9b502f4cd6ea..fd6af74d7995 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -2158,27 +2158,26 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; struct mt8192_afe_private *afe_priv; - struct device *dev; + struct device *dev = &pdev->dev; struct reset_control *rstc; int i, ret, irq_id; - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34)); if (ret) return ret; - afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL); if (!afe) return -ENOMEM; platform_set_drvdata(pdev, afe); - afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), + afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), GFP_KERNEL); if (!afe->platform_priv) return -ENOMEM; afe_priv = afe->platform_priv; - afe->dev = &pdev->dev; - dev = afe->dev; + afe->dev = dev; /* init audio related clock */ ret = mt8192_init_clock(afe); @@ -2196,7 +2195,7 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "failed to trigger audio reset\n"); - ret = devm_pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(dev); if (ret) return ret; @@ -2212,13 +2211,13 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) /* enable clock for regcache get default value from hw */ afe_priv->pm_runtime_bypass_reg_ctl = true; - pm_runtime_get_sync(&pdev->dev); + pm_runtime_get_sync(dev); ret = regmap_reinit_cache(afe->regmap, &mt8192_afe_regmap_config); if (ret) return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n"); - pm_runtime_put_sync(&pdev->dev); + pm_runtime_put_sync(dev); afe_priv->pm_runtime_bypass_reg_ctl = false; regcache_cache_only(afe->regmap, true); @@ -2285,7 +2284,7 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev) afe->runtime_suspend = mt8192_afe_runtime_suspend; /* register platform */ - ret = devm_snd_soc_register_component(&pdev->dev, + ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform, afe->dai_drivers, afe->num_dai_drivers); @@ -2314,15 +2313,15 @@ static const struct of_device_id mt8192_afe_pcm_dt_match[] = { MODULE_DEVICE_TABLE(of, mt8192_afe_pcm_dt_match); static const struct dev_pm_ops mt8192_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mt8192_afe_runtime_suspend, - mt8192_afe_runtime_resume, NULL) + RUNTIME_PM_OPS(mt8192_afe_runtime_suspend, + mt8192_afe_runtime_resume, NULL) }; static struct platform_driver mt8192_afe_pcm_driver = { .driver = { .name = "mt8192-audio", .of_match_table = mt8192_afe_pcm_dt_match, - .pm = &mt8192_afe_pm_ops, + .pm = pm_ptr(&mt8192_afe_pm_ops), }, .probe = mt8192_afe_pcm_dev_probe, .remove = mt8192_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index b1598cc5587e..bf483a8fb34a 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -920,7 +920,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .playback_only = 1, .ignore_suspend = 1, .be_hw_params_fixup = mt8192_i2s_hw_params_fixup, diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index 8016bfb35015..5d025ad72263 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -3188,15 +3188,15 @@ static const struct of_device_id mt8195_afe_pcm_dt_match[] = { MODULE_DEVICE_TABLE(of, mt8195_afe_pcm_dt_match); static const struct dev_pm_ops mt8195_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mt8195_afe_runtime_suspend, - mt8195_afe_runtime_resume, NULL) + RUNTIME_PM_OPS(mt8195_afe_runtime_suspend, + mt8195_afe_runtime_resume, NULL) }; static struct platform_driver mt8195_afe_pcm_driver = { .driver = { .name = "mt8195-audio", .of_match_table = mt8195_afe_pcm_dt_match, - .pm = &mt8195_afe_pm_ops, + .pm = pm_ptr(&mt8195_afe_pm_ops), }, .probe = mt8195_afe_pcm_dev_probe, .remove = mt8195_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c index 2b9cb3248795..df29a9fa5aee 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c @@ -1114,7 +1114,7 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .capture_only = 1, SND_SOC_DAILINK_REG(ETDM1_IN_BE), }, @@ -1123,7 +1123,7 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .capture_only = 1, .be_hw_params_fixup = mt8195_etdm_hw_params_fixup, SND_SOC_DAILINK_REG(ETDM2_IN_BE), @@ -1133,7 +1133,7 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .playback_only = 1, .be_hw_params_fixup = mt8195_etdm_hw_params_fixup, SND_SOC_DAILINK_REG(ETDM1_OUT_BE), @@ -1143,7 +1143,7 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .playback_only = 1, SND_SOC_DAILINK_REG(ETDM2_OUT_BE), }, @@ -1152,7 +1152,7 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .playback_only = 1, SND_SOC_DAILINK_REG(ETDM3_OUT_BE), }, @@ -1161,7 +1161,7 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(PCM1_BE), }, [DAI_LINK_UL_SRC1_BE] = { diff --git a/sound/soc/mediatek/mt8365/Makefile b/sound/soc/mediatek/mt8365/Makefile index 52ba45a8498a..b197025e34bb 100644 --- a/sound/soc/mediatek/mt8365/Makefile +++ b/sound/soc/mediatek/mt8365/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # MTK Platform driver -snd-soc-mt8365-pcm-objs := \ +snd-soc-mt8365-pcm-y := \ mt8365-afe-clk.o \ mt8365-afe-pcm.o \ mt8365-dai-adda.o \ diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-clk.c b/sound/soc/mediatek/mt8365/mt8365-afe-clk.c index 8a0af2ea8546..7078c01ba19b 100644 --- a/sound/soc/mediatek/mt8365/mt8365-afe-clk.c +++ b/sound/soc/mediatek/mt8365/mt8365-afe-clk.c @@ -49,8 +49,7 @@ int mt8365_afe_init_audio_clk(struct mtk_base_afe *afe) void mt8365_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk) { - if (clk) - clk_disable_unprepare(clk); + clk_disable_unprepare(clk); } int mt8365_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk, diff --git a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c index 743b46572144..10793bbe9275 100644 --- a/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c +++ b/sound/soc/mediatek/mt8365/mt8365-afe-pcm.c @@ -1957,7 +1957,7 @@ err_irq: return IRQ_HANDLED; } -static int __maybe_unused mt8365_afe_runtime_suspend(struct device *dev) +static int mt8365_afe_runtime_suspend(struct device *dev) { return 0; } @@ -1967,7 +1967,7 @@ static int mt8365_afe_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused mt8365_afe_suspend(struct device *dev) +static int mt8365_afe_suspend(struct device *dev) { struct mtk_base_afe *afe = dev_get_drvdata(dev); struct regmap *regmap = afe->regmap; @@ -1989,7 +1989,7 @@ static int __maybe_unused mt8365_afe_suspend(struct device *dev) return 0; } -static int __maybe_unused mt8365_afe_resume(struct device *dev) +static int mt8365_afe_resume(struct device *dev) { struct mtk_base_afe *afe = dev_get_drvdata(dev); struct regmap *regmap = afe->regmap; @@ -2009,7 +2009,7 @@ static int __maybe_unused mt8365_afe_resume(struct device *dev) return 0; } -static int __maybe_unused mt8365_afe_dev_runtime_suspend(struct device *dev) +static int mt8365_afe_dev_runtime_suspend(struct device *dev) { struct mtk_base_afe *afe = dev_get_drvdata(dev); @@ -2021,7 +2021,7 @@ static int __maybe_unused mt8365_afe_dev_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused mt8365_afe_dev_runtime_resume(struct device *dev) +static int mt8365_afe_dev_runtime_resume(struct device *dev) { struct mtk_base_afe *afe = dev_get_drvdata(dev); @@ -2250,17 +2250,16 @@ static const struct of_device_id mt8365_afe_pcm_dt_match[] = { MODULE_DEVICE_TABLE(of, mt8365_afe_pcm_dt_match); static const struct dev_pm_ops mt8365_afe_pm_ops = { - SET_RUNTIME_PM_OPS(mt8365_afe_dev_runtime_suspend, - mt8365_afe_dev_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(mt8365_afe_suspend, - mt8365_afe_resume) + RUNTIME_PM_OPS(mt8365_afe_dev_runtime_suspend, + mt8365_afe_dev_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(mt8365_afe_suspend, mt8365_afe_resume) }; static struct platform_driver mt8365_afe_pcm_driver = { .driver = { .name = "mt8365-afe-pcm", .of_match_table = mt8365_afe_pcm_dt_match, - .pm = &mt8365_afe_pm_ops, + .pm = pm_ptr(&mt8365_afe_pm_ops), }, .probe = mt8365_afe_pcm_dev_probe, .remove = mt8365_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c b/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c index 11b9a5bc7163..cae51756cead 100644 --- a/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c +++ b/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c @@ -313,7 +313,7 @@ static int mt8365_dai_set_config(struct mtk_base_afe *afe, } if ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) == - SND_SOC_DAIFMT_CBM_CFM) { + SND_SOC_DAIFMT_CBP_CFP) { val |= AFE_I2S_CON_SRC_SLAVE; val &= ~(u32)AFE_I2S_CON_FROM_IO_MUX;//from consys } @@ -523,7 +523,7 @@ static int mt8365_dai_i2s_startup(struct snd_pcm_substream *substream, bool i2s_in_slave = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) && ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) == - SND_SOC_DAIFMT_CBM_CFM); + SND_SOC_DAIFMT_CBP_CFP); mt8365_afe_enable_main_clk(afe); @@ -551,7 +551,7 @@ static void mt8365_dai_i2s_shutdown(struct snd_pcm_substream *substream, bool i2s_in_slave = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) && ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) == - SND_SOC_DAIFMT_CBM_CFM); + SND_SOC_DAIFMT_CBP_CFP); if (be->prepared[substream->stream]) { if (reset_i2s_out_change) @@ -613,7 +613,7 @@ static int mt8365_dai_i2s_prepare(struct snd_pcm_substream *substream, if (apply_i2s_in_change) { if ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) - == SND_SOC_DAIFMT_CBM_CFM) { + == SND_SOC_DAIFMT_CBP_CFP) { ret = mt8365_afe_set_2nd_i2s_asrc(afe, 32000, rate, (unsigned int)bit_width, 0, 0, 1); @@ -659,7 +659,7 @@ static int mt8365_dai_i2s_prepare(struct snd_pcm_substream *substream, mt8365_dai_set_enable(afe, i2s_data, true, true); if ((be->fmt_mode & SND_SOC_DAIFMT_MASTER_MASK) - == SND_SOC_DAIFMT_CBM_CFM) + == SND_SOC_DAIFMT_CBP_CFP) mt8365_afe_set_2nd_i2s_asrc_enable(afe, true); be->prepared[SNDRV_PCM_STREAM_CAPTURE] = true; @@ -712,7 +712,7 @@ static int mt8365_afe_2nd_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) be->fmt_mode |= (fmt & SND_SOC_DAIFMT_INV_MASK); - if (((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)) + if (((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBP_CFP)) be->fmt_mode |= (fmt & SND_SOC_DAIFMT_MASTER_MASK); return 0; diff --git a/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c b/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c index 3373b88da28e..0ec114a566ad 100644 --- a/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c +++ b/sound/soc/mediatek/mt8365/mt8365-dai-pcm.c @@ -189,10 +189,10 @@ static int mt8365_dai_pcm1_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: pcm_priv->slave_mode = true; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: pcm_priv->slave_mode = false; break; default: diff --git a/sound/soc/mediatek/mt8365/mt8365-mt6357.c b/sound/soc/mediatek/mt8365/mt8365-mt6357.c index d398e83ea052..a998fba82bfe 100644 --- a/sound/soc/mediatek/mt8365/mt8365-mt6357.c +++ b/sound/soc/mediatek/mt8365/mt8365-mt6357.c @@ -6,12 +6,19 @@ * Authors: Nicolas Belin <nbelin@baylibre.com> */ +#include <linux/array_size.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_gpio.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/types.h> + #include <sound/soc.h> #include <sound/pcm_params.h> + #include "mt8365-afe-common.h" -#include <linux/pinctrl/consumer.h> #include "../common/mtk-soc-card.h" #include "../common/mtk-soundcard-driver.h" @@ -218,7 +225,7 @@ static struct snd_soc_dai_link mt8365_mt6357_dais[] = { .id = DAI_LINK_2ND_I2S_INTF, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(i2s3), }, [DAI_LINK_DMIC] = { diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index 09103eef2a97..421b5d719fb3 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -529,7 +529,6 @@ static int axg_tdm_iface_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct snd_soc_dai_driver *dai_drv; struct axg_tdm_iface *iface; - int i; iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL); if (!iface) @@ -541,15 +540,11 @@ static int axg_tdm_iface_probe(struct platform_device *pdev) * We'll change the number of channel provided by DAI stream, so dpcm * channel merge can be done properly */ - dai_drv = devm_kcalloc(dev, ARRAY_SIZE(axg_tdm_iface_dai_drv), - sizeof(*dai_drv), GFP_KERNEL); + dai_drv = devm_kmemdup_array(dev, axg_tdm_iface_dai_drv, ARRAY_SIZE(axg_tdm_iface_dai_drv), + sizeof(axg_tdm_iface_dai_drv[0]), GFP_KERNEL); if (!dai_drv) return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(axg_tdm_iface_dai_drv); i++) - memcpy(&dai_drv[i], &axg_tdm_iface_dai_drv[i], - sizeof(*dai_drv)); - /* Bit clock provided on the pad */ iface->sclk = devm_clk_get(dev, "sclk"); if (IS_ERR(iface->sclk)) diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index 1a4ef124e4e2..cfc7f6e41ab5 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -119,10 +119,10 @@ unsigned int meson_card_parse_daifmt(struct device_node *node, /* If no master is provided, default to cpu master */ if (!bitclkmaster || bitclkmaster == cpu_node) { daifmt |= (!framemaster || framemaster == cpu_node) ? - SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; + SND_SOC_DAIFMT_CBC_CFC : SND_SOC_DAIFMT_CBC_CFP; } else { daifmt |= (!framemaster || framemaster == cpu_node) ? - SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; + SND_SOC_DAIFMT_CBP_CFC : SND_SOC_DAIFMT_CBP_CFP; } of_node_put(bitclkmaster); diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c index 571f65788c59..a80e9c9d6288 100644 --- a/sound/soc/meson/t9015.c +++ b/sound/soc/meson/t9015.c @@ -57,11 +57,11 @@ static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int val; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: val = I2S_MODE; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: val = 0; break; diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index a41a13ae38a5..245f17411638 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -59,7 +59,7 @@ static const struct snd_soc_ops mxs_sgtl5000_hifi_ops = { }; #define MXS_SGTL5000_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBS_CFS) + SND_SOC_DAIFMT_CBC_CFC) SND_SOC_DAILINK_DEFS(hifi_tx, diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 8caa1aa99bdc..6ca0a6bcd34c 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -260,7 +260,7 @@ static struct snd_soc_dai_link spitz_dai = { .name = "wm8750", .stream_name = "WM8750", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ops = &spitz_ops, SND_SOC_DAILINK_REG(wm8750), }; diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 928cf5cb5999..7ee60a58a336 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -44,20 +44,20 @@ int qcom_snd_parse_of(struct snd_soc_card *card) return ret; } - if (of_property_read_bool(dev->of_node, "widgets")) { + if (of_property_present(dev->of_node, "widgets")) { ret = snd_soc_of_parse_audio_simple_widgets(card, "widgets"); if (ret) return ret; } /* DAPM routes */ - if (of_property_read_bool(dev->of_node, "audio-routing")) { + if (of_property_present(dev->of_node, "audio-routing")) { ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); if (ret) return ret; } /* Deprecated, only for compatibility with old device trees */ - if (of_property_read_bool(dev->of_node, "qcom,audio-routing")) { + if (of_property_present(dev->of_node, "qcom,audio-routing")) { ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); if (ret) return ret; diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index addd2c4bdd3e..9946f12254b3 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -1232,14 +1232,16 @@ static int lpass_platform_copy(struct snd_soc_component *component, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (is_cdc_dma_port(dai_id)) { - ret = copy_from_iter_toio(dma_buf, buf, bytes); + if (copy_from_iter_toio(dma_buf, bytes, buf) != bytes) + ret = -EFAULT; } else { if (copy_from_iter((void __force *)dma_buf, bytes, buf) != bytes) ret = -EFAULT; } } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { if (is_cdc_dma_port(dai_id)) { - ret = copy_to_iter_fromio(buf, dma_buf, bytes); + if (copy_to_iter_fromio(dma_buf, bytes, buf) != bytes) + ret = -EFAULT; } else { if (copy_to_iter((void __force *)dma_buf, bytes, buf) != bytes) ret = -EFAULT; diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c index fbead6af3d95..7c6a9b0fda89 100644 --- a/sound/soc/qcom/lpass-sc7180.c +++ b/sound/soc/qcom/lpass-sc7180.c @@ -160,14 +160,14 @@ static int sc7180_lpass_exit(struct platform_device *pdev) return 0; } -static int __maybe_unused sc7180_lpass_dev_resume(struct device *dev) +static int sc7180_lpass_dev_resume(struct device *dev) { struct lpass_data *drvdata = dev_get_drvdata(dev); return clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); } -static int __maybe_unused sc7180_lpass_dev_suspend(struct device *dev) +static int sc7180_lpass_dev_suspend(struct device *dev) { struct lpass_data *drvdata = dev_get_drvdata(dev); @@ -176,7 +176,7 @@ static int __maybe_unused sc7180_lpass_dev_suspend(struct device *dev) } static const struct dev_pm_ops sc7180_lpass_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(sc7180_lpass_dev_suspend, sc7180_lpass_dev_resume) + SYSTEM_SLEEP_PM_OPS(sc7180_lpass_dev_suspend, sc7180_lpass_dev_resume) }; static const struct lpass_variant sc7180_data = { @@ -312,7 +312,7 @@ static struct platform_driver sc7180_lpass_cpu_platform_driver = { .driver = { .name = "sc7180-lpass-cpu", .of_match_table = of_match_ptr(sc7180_lpass_cpu_device_id), - .pm = &sc7180_lpass_pm_ops, + .pm = pm_ptr(&sc7180_lpass_pm_ops), }, .probe = asoc_qcom_lpass_cpu_platform_probe, .remove = asoc_qcom_lpass_cpu_platform_remove, diff --git a/sound/soc/qcom/lpass-sc7280.c b/sound/soc/qcom/lpass-sc7280.c index 7cd3e291382a..817c824f9179 100644 --- a/sound/soc/qcom/lpass-sc7280.c +++ b/sound/soc/qcom/lpass-sc7280.c @@ -233,14 +233,14 @@ static int sc7280_lpass_exit(struct platform_device *pdev) return 0; } -static int __maybe_unused sc7280_lpass_dev_resume(struct device *dev) +static int sc7280_lpass_dev_resume(struct device *dev) { struct lpass_data *drvdata = dev_get_drvdata(dev); return clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); } -static int __maybe_unused sc7280_lpass_dev_suspend(struct device *dev) +static int sc7280_lpass_dev_suspend(struct device *dev) { struct lpass_data *drvdata = dev_get_drvdata(dev); @@ -249,7 +249,7 @@ static int __maybe_unused sc7280_lpass_dev_suspend(struct device *dev) } static const struct dev_pm_ops sc7280_lpass_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(sc7280_lpass_dev_suspend, sc7280_lpass_dev_resume) + SYSTEM_SLEEP_PM_OPS(sc7280_lpass_dev_suspend, sc7280_lpass_dev_resume) }; static const struct lpass_variant sc7280_data = { @@ -442,7 +442,7 @@ static struct platform_driver sc7280_lpass_cpu_platform_driver = { .driver = { .name = "sc7280-lpass-cpu", .of_match_table = of_match_ptr(sc7280_lpass_cpu_device_id), - .pm = &sc7280_lpass_pm_ops, + .pm = pm_ptr(&sc7280_lpass_pm_ops), }, .probe = asoc_qcom_lpass_cpu_platform_probe, .remove = asoc_qcom_lpass_cpu_platform_remove, diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 2a2a5bd98110..11e252a70f69 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -230,7 +230,7 @@ int q6apm_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, phys_a return 0; } - buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_KERNEL); + buf = kcalloc(periods, sizeof(struct audio_buffer), GFP_KERNEL); if (!buf) { mutex_unlock(&graph->lock); return -ENOMEM; diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c index bc030ce29680..84b6ac5d2a20 100644 --- a/sound/soc/qcom/sc7180.c +++ b/sound/soc/qcom/sc7180.c @@ -397,7 +397,7 @@ static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream) switch (cpu_dai->id) { case MI2S_PRIMARY: snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S); runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; @@ -513,7 +513,7 @@ static int sc7180_snd_platform_probe(struct platform_device *pdev) card->controls = sc7180_snd_controls; card->num_controls = ARRAY_SIZE(sc7180_snd_controls); - if (of_property_read_bool(dev->of_node, "dmic-gpios")) { + if (of_property_present(dev->of_node, "dmic-gpios")) { card->dapm_widgets = sc7180_snd_dual_mic_widgets, card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets), card->controls = sc7180_snd_dual_mic_controls, diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c index 230af8d7b205..af412bd0c89f 100644 --- a/sound/soc/qcom/sc7280.c +++ b/sound/soc/qcom/sc7280.c @@ -342,8 +342,8 @@ static void sc7280_snd_shutdown(struct snd_pcm_substream *substream) static int sc7280_snd_startup(struct snd_pcm_substream *substream) { - unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; - unsigned int codec_dai_fmt = SND_SOC_DAIFMT_CBS_CFS; + unsigned int fmt = SND_SOC_DAIFMT_CBC_CFC; + unsigned int codec_dai_fmt = SND_SOC_DAIFMT_CBC_CFC; struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index a479d7e5b7fb..fcc7df75346f 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -215,6 +215,7 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, ret = sdm845_slim_snd_hw_params(substream, params); break; case QUATERNARY_MI2S_RX: + case SECONDARY_MI2S_RX: break; default: pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); @@ -356,6 +357,7 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); break; + case SECONDARY_MI2S_RX: case SECONDARY_MI2S_TX: codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S; if (++(data->sec_mi2s_clk_count) == 1) { @@ -371,8 +373,6 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT, MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); snd_soc_dai_set_fmt(cpu_dai, fmt); - - break; case QUATERNARY_TDM_RX_0: @@ -441,6 +441,7 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) } break; + case SECONDARY_MI2S_RX: case SECONDARY_MI2S_TX: if (--(data->sec_mi2s_clk_count) == 0) { snd_soc_dai_set_sysclk(cpu_dai, diff --git a/sound/soc/qcom/sdw.c b/sound/soc/qcom/sdw.c index f2eda2ff46c0..1d01b9329e08 100644 --- a/sound/soc/qcom/sdw.c +++ b/sound/soc/qcom/sdw.c @@ -23,9 +23,11 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + u32 rx_ch[SDW_MAX_PORTS], tx_ch[SDW_MAX_PORTS]; struct sdw_stream_runtime *sruntime; struct snd_soc_dai *codec_dai; - int ret, i; + u32 rx_ch_cnt = 0, tx_ch_cnt = 0; + int ret, i, j; sruntime = sdw_alloc_stream(cpu_dai->name); if (!sruntime) @@ -35,9 +37,35 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream) ret = snd_soc_dai_set_stream(codec_dai, sruntime, substream->stream); if (ret < 0 && ret != -ENOTSUPP) { - dev_err(rtd->dev, "Failed to set sdw stream on %s\n", - codec_dai->name); + dev_err(rtd->dev, "Failed to set sdw stream on %s\n", codec_dai->name); goto err_set_stream; + } else if (ret == -ENOTSUPP) { + /* Ignore unsupported */ + continue; + } + + ret = snd_soc_dai_get_channel_map(codec_dai, &tx_ch_cnt, tx_ch, + &rx_ch_cnt, rx_ch); + if (ret != 0 && ret != -ENOTSUPP) { + dev_err(rtd->dev, "Failed to get codec chan map %s\n", codec_dai->name); + goto err_set_stream; + } else if (ret == -ENOTSUPP) { + /* Ignore unsupported */ + continue; + } + } + + switch (cpu_dai->id) { + case RX_CODEC_DMA_RX_0: + case TX_CODEC_DMA_TX_3: + if (tx_ch_cnt || rx_ch_cnt) { + for_each_rtd_codec_dais(rtd, j, codec_dai) { + ret = snd_soc_dai_set_channel_map(codec_dai, + tx_ch_cnt, tx_ch, + rx_ch_cnt, rx_ch); + if (ret != 0 && ret != -ENOTSUPP) + goto err_set_stream; + } } } diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index 45e0c33fc3f3..9039107972e2 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -7,6 +7,7 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/pcm.h> +#include <sound/pcm_params.h> #include <linux/soundwire/sdw.h> #include <sound/jack.h> #include <linux/input-event-codes.h> @@ -39,9 +40,11 @@ static int sm8250_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); rate->min = rate->max = 48000; channels->min = channels->max = 2; + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); return 0; } diff --git a/sound/soc/renesas/Kconfig b/sound/soc/renesas/Kconfig index 426632996a0a..cb01fb36355f 100644 --- a/sound/soc/renesas/Kconfig +++ b/sound/soc/renesas/Kconfig @@ -67,7 +67,7 @@ config SND_SH7760_AC97 config SND_SIU_MIGOR tristate "SIU sound support on Migo-R" - depends on SH_MIGOR && I2C + depends on SH_MIGOR && I2C && DMADEVICES select SND_SOC_SH4_SIU select SND_SOC_WM8978 help diff --git a/sound/soc/renesas/migor.c b/sound/soc/renesas/migor.c index 5a0bc6edac0a..45fc594d331b 100644 --- a/sound/soc/renesas/migor.c +++ b/sound/soc/renesas/migor.c @@ -132,7 +132,7 @@ static struct snd_soc_dai_link migor_dai = { .name = "wm8978", .stream_name = "WM8978", .dai_fmt = SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ops = &migor_dai_ops, SND_SOC_DAILINK_REG(wm8978), }; diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c index d3709fd0409e..30afc942d381 100644 --- a/sound/soc/renesas/rcar/core.c +++ b/sound/soc/renesas/rcar/core.c @@ -1770,20 +1770,6 @@ int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io) return 1; } -int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io) -{ - 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); - - if (!runtime) { - dev_warn(dev, "Can't update kctrl when idle\n"); - return 0; - } - - return 1; -} - struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg) { cfg->cfg.val = cfg->val; @@ -2073,7 +2059,7 @@ static void rsnd_remove(struct platform_device *pdev) remove_func[i](priv); } -static int __maybe_unused rsnd_suspend(struct device *dev) +static int rsnd_suspend(struct device *dev) { struct rsnd_priv *priv = dev_get_drvdata(dev); @@ -2082,7 +2068,7 @@ static int __maybe_unused rsnd_suspend(struct device *dev) return 0; } -static int __maybe_unused rsnd_resume(struct device *dev) +static int rsnd_resume(struct device *dev) { struct rsnd_priv *priv = dev_get_drvdata(dev); @@ -2090,13 +2076,13 @@ static int __maybe_unused rsnd_resume(struct device *dev) } static const struct dev_pm_ops rsnd_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(rsnd_suspend, rsnd_resume) + SYSTEM_SLEEP_PM_OPS(rsnd_suspend, rsnd_resume) }; static struct platform_driver rsnd_driver = { .driver = { .name = "rcar_sound", - .pm = &rsnd_pm_ops, + .pm = pm_ptr(&rsnd_pm_ops), .of_match_table = rsnd_of_match, }, .probe = rsnd_probe, diff --git a/sound/soc/renesas/rcar/rsnd.h b/sound/soc/renesas/rcar/rsnd.h index a5f54b65313c..04c70690f7a2 100644 --- a/sound/soc/renesas/rcar/rsnd.h +++ b/sound/soc/renesas/rcar/rsnd.h @@ -742,7 +742,6 @@ struct rsnd_kctrl_cfg_s { #define rsnd_kctrl_vals(x) ((x).val) /* = (x).cfg.val[0] */ int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io); -int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io); struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg); struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg); int rsnd_kctrl_new(struct rsnd_mod *mod, diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c index e7f86db0d94c..7d73b183bda6 100644 --- a/sound/soc/renesas/rcar/src.c +++ b/sound/soc/renesas/rcar/src.c @@ -35,6 +35,7 @@ struct rsnd_src { struct rsnd_mod *dma; struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ + u32 current_sync_rate; int irq; }; @@ -100,7 +101,7 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, if (!rsnd_src_sync_is_enabled(mod)) return rsnd_io_converted_rate(io); - convert_rate = src->sync.val; + convert_rate = src->current_sync_rate; if (!convert_rate) convert_rate = rsnd_io_converted_rate(io); @@ -201,13 +202,73 @@ static const u32 chan222222[] = { static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 fin, fout, new_rate; + int inc, cnt, rate; + u64 base, val; + + if (!runtime) + return; + + if (!rsnd_src_sync_is_enabled(mod)) + return; + + fin = rsnd_src_get_in_rate(priv, io); + fout = rsnd_src_get_out_rate(priv, io); + + new_rate = src->sync.val; + + if (!new_rate) + new_rate = fout; + + /* Do nothing if no diff */ + if (new_rate == src->current_sync_rate) + return; + + /* + * SRCm_IFSVR::INTIFS can change within 1% + * see + * SRCm_IFSVR::INTIFS Note + */ + inc = fout / 100; + cnt = abs(new_rate - fout) / inc; + if (fout > new_rate) + inc *= -1; + + /* + * After start running SRC, we can update only SRC_IFSVR + * for Synchronous Mode + */ + base = (u64)0x0400000 * fin; + rate = fout; + for (int i = 0; i < cnt; i++) { + val = base; + rate += inc; + do_div(val, rate); + + rsnd_mod_write(mod, SRC_IFSVR, val); + } + val = base; + do_div(val, new_rate); + + rsnd_mod_write(mod, SRC_IFSVR, val); + + /* update current_sync_rate */ + src->current_sync_rate = new_rate; +} + +static void rsnd_src_init_convert_rate(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); int is_play = rsnd_io_is_play(io); int use_src = 0; u32 fin, fout; - u32 ifscr, fsrate, adinr; + u32 ifscr, adinr; u32 cr, route; u32 i_busif, o_busif, tmp; const u32 *bsdsr_table; @@ -245,26 +306,15 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, adinr = rsnd_get_adinr_bit(mod, io) | chan; /* - * SRC_IFSCR / SRC_IFSVR - */ - ifscr = 0; - fsrate = 0; - if (use_src) { - u64 n; - - ifscr = 1; - n = (u64)0x0400000 * fin; - do_div(n, fout); - fsrate = n; - } - - /* + * SRC_IFSCR * SRC_SRCCR / SRC_ROUTE_MODE0 */ + ifscr = 0; cr = 0x00011110; route = 0x0; if (use_src) { route = 0x1; + ifscr = 0x1; if (rsnd_src_sync_is_enabled(mod)) { cr |= 0x1; @@ -335,7 +385,6 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ rsnd_mod_write(mod, SRC_ADINR, adinr); rsnd_mod_write(mod, SRC_IFSCR, ifscr); - rsnd_mod_write(mod, SRC_IFSVR, fsrate); rsnd_mod_write(mod, SRC_SRCCR, cr); rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]); rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]); @@ -348,6 +397,9 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); + /* update SRC_IFSVR */ + rsnd_src_set_convert_rate(io, mod); + return; convert_rate_err: @@ -467,7 +519,8 @@ static int rsnd_src_init(struct rsnd_mod *mod, int ret; /* reset sync convert_rate */ - src->sync.val = 0; + src->sync.val = + src->current_sync_rate = 0; ret = rsnd_mod_power_on(mod); if (ret < 0) @@ -475,7 +528,7 @@ static int rsnd_src_init(struct rsnd_mod *mod, rsnd_src_activation(mod); - rsnd_src_set_convert_rate(io, mod); + rsnd_src_init_convert_rate(io, mod); rsnd_src_status_clear(mod); @@ -493,7 +546,8 @@ static int rsnd_src_quit(struct rsnd_mod *mod, rsnd_mod_power_off(mod); /* reset sync convert_rate */ - src->sync.val = 0; + src->sync.val = + src->current_sync_rate = 0; return 0; } @@ -531,6 +585,22 @@ static irqreturn_t rsnd_src_interrupt(int irq, void *data) return IRQ_HANDLED; } +static int rsnd_src_kctrl_accept_runtime(struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + if (!runtime) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_warn(dev, "\"SRC Out Rate\" can use during running\n"); + + return 0; + } + + return 1; +} + static int rsnd_src_probe_(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -585,7 +655,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, "SRC Out Rate Switch" : "SRC In Rate Switch", rsnd_kctrl_accept_anytime, - rsnd_src_set_convert_rate, + rsnd_src_init_convert_rate, &src->sen, 1); if (ret < 0) return ret; @@ -594,7 +664,7 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, rsnd_io_is_play(io) ? "SRC Out Rate" : "SRC In Rate", - rsnd_kctrl_accept_runtime, + rsnd_src_kctrl_accept_runtime, rsnd_src_set_convert_rate, &src->sync, 192000); diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c index b3d4e8ae07ef..0c6424a1fcac 100644 --- a/sound/soc/renesas/rcar/ssi.c +++ b/sound/soc/renesas/rcar/ssi.c @@ -336,7 +336,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, return 0; rate_err: - dev_err(dev, "unsupported clock rate\n"); + dev_err(dev, "unsupported clock rate (%d)\n", rate); + return ret; } diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c index 6efd017aaa7f..3a0af4ca7ab6 100644 --- a/sound/soc/renesas/rz-ssi.c +++ b/sound/soc/renesas/rz-ssi.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <linux/dmaengine.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/reset.h> @@ -71,7 +72,7 @@ #define PREALLOC_BUFFER (SZ_32K) #define PREALLOC_BUFFER_MAX (SZ_32K) -#define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-44.1kHz */ +#define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-48kHz */ #define SSI_FMTS SNDRV_PCM_FMTBIT_S16_LE #define SSI_CHAN_MIN 2 #define SSI_CHAN_MAX 2 @@ -99,7 +100,6 @@ struct rz_ssi_stream { struct rz_ssi_priv { void __iomem *base; - struct platform_device *pdev; struct reset_control *rstc; struct device *dev; struct clk *sfr_clk; @@ -163,16 +163,7 @@ static void rz_ssi_reg_mask_setl(struct rz_ssi_priv *priv, uint reg, writel(val, (priv->base + reg)); } -static inline struct snd_soc_dai * -rz_ssi_get_dai(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - - return snd_soc_rtd_to_cpu(rtd, 0); -} - -static inline bool rz_ssi_stream_is_play(struct rz_ssi_priv *ssi, - struct snd_pcm_substream *substream) +static inline bool rz_ssi_stream_is_play(struct snd_pcm_substream *substream) { return substream->stream == SNDRV_PCM_STREAM_PLAYBACK; } @@ -244,22 +235,21 @@ static void rz_ssi_stream_init(struct rz_ssi_stream *strm, static void rz_ssi_stream_quit(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { - struct snd_soc_dai *dai = rz_ssi_get_dai(strm->substream); + struct device *dev = ssi->dev; rz_ssi_set_substream(strm, NULL); if (strm->oerr_num > 0) - dev_info(dai->dev, "overrun = %d\n", strm->oerr_num); + dev_info(dev, "overrun = %d\n", strm->oerr_num); if (strm->uerr_num > 0) - dev_info(dai->dev, "underrun = %d\n", strm->uerr_num); + dev_info(dev, "underrun = %d\n", strm->uerr_num); } static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, unsigned int channels) { - static s8 ckdv[16] = { 1, 2, 4, 8, 16, 32, 64, 128, - 6, 12, 24, 48, 96, -1, -1, -1 }; + static u8 ckdv[] = { 1, 2, 4, 8, 16, 32, 64, 128, 6, 12, 24, 48, 96 }; unsigned int channel_bits = 32; /* System Word Length */ unsigned long bclk_rate = rate * channels * channel_bits; unsigned int div; @@ -318,7 +308,8 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, static void rz_ssi_set_idle(struct rz_ssi_priv *ssi) { - int timeout; + u32 tmp; + int ret; /* Disable irqs */ rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TUIEN | SSICR_TOIEN | @@ -331,15 +322,9 @@ static void rz_ssi_set_idle(struct rz_ssi_priv *ssi) SSISR_RUIRQ), 0); /* Wait for idle */ - timeout = 100; - while (--timeout) { - if (rz_ssi_reg_readl(ssi, SSISR) & SSISR_IIRQ) - break; - udelay(1); - } - - if (!timeout) - dev_info(ssi->dev, "timeout waiting for SSI idle\n"); + ret = readl_poll_timeout_atomic(ssi->base + SSISR, tmp, (tmp & SSISR_IIRQ), 1, 100); + if (ret) + dev_warn_ratelimited(ssi->dev, "timeout waiting for SSI idle\n"); /* Hold FIFOs in reset */ rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_FIFO_RST); @@ -347,7 +332,7 @@ static void rz_ssi_set_idle(struct rz_ssi_priv *ssi) static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { - bool is_play = rz_ssi_stream_is_play(ssi, strm->substream); + bool is_play = rz_ssi_stream_is_play(strm->substream); bool is_full_duplex; u32 ssicr, ssifcr; @@ -403,6 +388,15 @@ static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) return 0; } +static int rz_ssi_swreset(struct rz_ssi_priv *ssi) +{ + u32 tmp; + + rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_SSIRST); + rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_SSIRST, 0); + return readl_poll_timeout_atomic(ssi->base + SSIFCR, tmp, !(tmp & SSIFCR_SSIRST), 1, 5); +} + static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { strm->running = 0; @@ -415,8 +409,12 @@ static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0); /* Cancel all remaining DMA transactions */ - if (rz_ssi_is_dma_enabled(ssi)) - dmaengine_terminate_async(strm->dma_ch); + if (rz_ssi_is_dma_enabled(ssi)) { + if (ssi->playback.dma_ch) + dmaengine_terminate_async(ssi->playback.dma_ch); + if (ssi->capture.dma_ch) + dmaengine_terminate_async(ssi->capture.dma_ch); + } rz_ssi_set_idle(ssi); @@ -523,6 +521,8 @@ static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) sample_space = strm->fifo_sample_size; ssifsr = rz_ssi_reg_readl(ssi, SSIFSR); sample_space -= (ssifsr >> SSIFSR_TDC_SHIFT) & SSIFSR_TDC_MASK; + if (sample_space < 0) + return -EINVAL; /* Only add full frames at a time */ while (frames_left && (sample_space >= runtime->channels)) { @@ -680,7 +680,7 @@ static int rz_ssi_dma_transfer(struct rz_ssi_priv *ssi, */ return 0; - dir = rz_ssi_stream_is_play(ssi, substream) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + dir = rz_ssi_stream_is_play(substream) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; /* Always transfer 1 period */ amount = runtime->period_size; @@ -784,6 +784,32 @@ no_dma: return -ENODEV; } +static int rz_ssi_trigger_resume(struct rz_ssi_priv *ssi) +{ + int ret; + + if (rz_ssi_is_stream_running(&ssi->playback) || + rz_ssi_is_stream_running(&ssi->capture)) + return 0; + + ret = rz_ssi_swreset(ssi); + if (ret) + return ret; + + return rz_ssi_clk_setup(ssi, ssi->hw_params_cache.rate, + ssi->hw_params_cache.channels); +} + +static void rz_ssi_streams_suspend(struct rz_ssi_priv *ssi) +{ + if (rz_ssi_is_stream_running(&ssi->playback) || + rz_ssi_is_stream_running(&ssi->capture)) + return; + + ssi->playback.dma_buffer_pos = 0; + ssi->capture.dma_buffer_pos = 0; +} + static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -792,21 +818,21 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, int ret = 0, i, num_transfer = 1; switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - /* Soft Reset */ - if (!rz_ssi_is_stream_running(&ssi->playback) && - !rz_ssi_is_stream_running(&ssi->capture)) { - rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_SSIRST); - rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_SSIRST, 0); - udelay(5); - } + case SNDRV_PCM_TRIGGER_RESUME: + ret = rz_ssi_trigger_resume(ssi); + if (ret) + return ret; - rz_ssi_stream_init(strm, substream); + fallthrough; + + case SNDRV_PCM_TRIGGER_START: + if (cmd == SNDRV_PCM_TRIGGER_START) + rz_ssi_stream_init(strm, substream); if (ssi->dma_rt) { bool is_playback; - is_playback = rz_ssi_stream_is_play(ssi, substream); + is_playback = rz_ssi_stream_is_play(substream); ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch, is_playback); /* Fallback to pio */ @@ -829,6 +855,12 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, ret = rz_ssi_start(ssi, strm); break; + + case SNDRV_PCM_TRIGGER_SUSPEND: + rz_ssi_stop(ssi, strm); + rz_ssi_streams_suspend(ssi); + break; + case SNDRV_PCM_TRIGGER_STOP: rz_ssi_stop(ssi, strm); rz_ssi_stream_quit(ssi, strm); @@ -925,6 +957,7 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; unsigned int channels = params_channels(params); unsigned int rate = params_rate(params); + int ret; if (sample_bits != 16) { dev_err(ssi->dev, "Unsupported sample width: %d\n", @@ -951,6 +984,10 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream, rz_ssi_cache_hw_params(ssi, rate, channels, strm->sample_width, sample_bits); + ret = rz_ssi_swreset(ssi); + if (ret) + return ret; + return rz_ssi_clk_setup(ssi, rate, channels); } @@ -963,7 +1000,8 @@ static const struct snd_soc_dai_ops rz_ssi_dai_ops = { static const struct snd_pcm_hardware rz_ssi_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME, .buffer_bytes_max = PREALLOC_BUFFER, .period_bytes_min = 32, .period_bytes_max = 8192, @@ -986,7 +1024,8 @@ static int rz_ssi_pcm_open(struct snd_soc_component *component, static snd_pcm_uframes_t rz_ssi_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_dai *dai = rz_ssi_get_dai(substream); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0); struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream); @@ -1031,37 +1070,37 @@ static const struct snd_soc_component_driver rz_ssi_soc_component = { static int rz_ssi_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct rz_ssi_priv *ssi; struct clk *audio_clk; struct resource *res; int ret; - ssi = devm_kzalloc(&pdev->dev, sizeof(*ssi), GFP_KERNEL); + ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL); if (!ssi) return -ENOMEM; - ssi->pdev = pdev; - ssi->dev = &pdev->dev; + ssi->dev = dev; ssi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(ssi->base)) return PTR_ERR(ssi->base); ssi->phys = res->start; - ssi->clk = devm_clk_get(&pdev->dev, "ssi"); + ssi->clk = devm_clk_get(dev, "ssi"); if (IS_ERR(ssi->clk)) return PTR_ERR(ssi->clk); - ssi->sfr_clk = devm_clk_get(&pdev->dev, "ssi_sfr"); + ssi->sfr_clk = devm_clk_get(dev, "ssi_sfr"); if (IS_ERR(ssi->sfr_clk)) return PTR_ERR(ssi->sfr_clk); - audio_clk = devm_clk_get(&pdev->dev, "audio_clk1"); + audio_clk = devm_clk_get(dev, "audio_clk1"); if (IS_ERR(audio_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk), "no audio clk1"); ssi->audio_clk_1 = clk_get_rate(audio_clk); - audio_clk = devm_clk_get(&pdev->dev, "audio_clk2"); + audio_clk = devm_clk_get(dev, "audio_clk2"); if (IS_ERR(audio_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk), "no audio clk2"); @@ -1074,13 +1113,13 @@ static int rz_ssi_probe(struct platform_device *pdev) ssi->audio_mck = ssi->audio_clk_1 ? ssi->audio_clk_1 : ssi->audio_clk_2; /* Detect DMA support */ - ret = rz_ssi_dma_request(ssi, &pdev->dev); + ret = rz_ssi_dma_request(ssi, dev); if (ret < 0) { - dev_warn(&pdev->dev, "DMA not available, using PIO\n"); + dev_warn(dev, "DMA not available, using PIO\n"); ssi->playback.transfer = rz_ssi_pio_send; ssi->capture.transfer = rz_ssi_pio_recv; } else { - dev_info(&pdev->dev, "DMA enabled"); + dev_info(dev, "DMA enabled"); ssi->playback.transfer = rz_ssi_dma_transfer; ssi->capture.transfer = rz_ssi_dma_transfer; } @@ -1089,21 +1128,20 @@ static int rz_ssi_probe(struct platform_device *pdev) ssi->capture.priv = ssi; spin_lock_init(&ssi->lock); - dev_set_drvdata(&pdev->dev, ssi); + dev_set_drvdata(dev, ssi); /* Error Interrupt */ ssi->irq_int = platform_get_irq_byname(pdev, "int_req"); if (ssi->irq_int < 0) { - rz_ssi_release_dma_channels(ssi); - return ssi->irq_int; + ret = ssi->irq_int; + goto err_release_dma_chs; } - ret = devm_request_irq(&pdev->dev, ssi->irq_int, &rz_ssi_interrupt, - 0, dev_name(&pdev->dev), ssi); + ret = devm_request_irq(dev, ssi->irq_int, &rz_ssi_interrupt, + 0, dev_name(dev), ssi); if (ret < 0) { - rz_ssi_release_dma_channels(ssi); - return dev_err_probe(&pdev->dev, ret, - "irq request error (int_req)\n"); + dev_err_probe(dev, ret, "irq request error (int_req)\n"); + goto err_release_dma_chs; } if (!rz_ssi_is_dma_enabled(ssi)) { @@ -1115,11 +1153,11 @@ static int rz_ssi_probe(struct platform_device *pdev) if (ssi->irq_rt < 0) return ssi->irq_rt; - ret = devm_request_irq(&pdev->dev, ssi->irq_rt, + ret = devm_request_irq(dev, ssi->irq_rt, &rz_ssi_interrupt, 0, - dev_name(&pdev->dev), ssi); + dev_name(dev), ssi); if (ret < 0) - return dev_err_probe(&pdev->dev, ret, + return dev_err_probe(dev, ret, "irq request error (dma_rt)\n"); } else { if (ssi->irq_tx < 0) @@ -1128,52 +1166,48 @@ static int rz_ssi_probe(struct platform_device *pdev) if (ssi->irq_rx < 0) return ssi->irq_rx; - ret = devm_request_irq(&pdev->dev, ssi->irq_tx, + ret = devm_request_irq(dev, ssi->irq_tx, &rz_ssi_interrupt, 0, - dev_name(&pdev->dev), ssi); + dev_name(dev), ssi); if (ret < 0) - return dev_err_probe(&pdev->dev, ret, + return dev_err_probe(dev, ret, "irq request error (dma_tx)\n"); - ret = devm_request_irq(&pdev->dev, ssi->irq_rx, + ret = devm_request_irq(dev, ssi->irq_rx, &rz_ssi_interrupt, 0, - dev_name(&pdev->dev), ssi); + dev_name(dev), ssi); if (ret < 0) - return dev_err_probe(&pdev->dev, ret, + return dev_err_probe(dev, ret, "irq request error (dma_rx)\n"); } } - ssi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + ssi->rstc = devm_reset_control_get_exclusive(dev, NULL); if (IS_ERR(ssi->rstc)) { ret = PTR_ERR(ssi->rstc); - goto err_reset; + goto err_release_dma_chs; } - reset_control_deassert(ssi->rstc); - pm_runtime_enable(&pdev->dev); - ret = pm_runtime_resume_and_get(&pdev->dev); + /* Default 0 for power saving. Can be overridden via sysfs. */ + pm_runtime_set_autosuspend_delay(dev, 0); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); if (ret < 0) { - dev_err(&pdev->dev, "pm_runtime_resume_and_get failed\n"); - goto err_pm; + dev_err(dev, "Failed to enable runtime PM!\n"); + goto err_release_dma_chs; } - ret = devm_snd_soc_register_component(&pdev->dev, &rz_ssi_soc_component, + ret = devm_snd_soc_register_component(dev, &rz_ssi_soc_component, rz_ssi_soc_dai, ARRAY_SIZE(rz_ssi_soc_dai)); if (ret < 0) { - dev_err(&pdev->dev, "failed to register snd component\n"); - goto err_snd_soc; + dev_err(dev, "failed to register snd component\n"); + goto err_release_dma_chs; } return 0; -err_snd_soc: - pm_runtime_put(ssi->dev); -err_pm: - pm_runtime_disable(ssi->dev); - reset_control_assert(ssi->rstc); -err_reset: +err_release_dma_chs: rz_ssi_release_dma_channels(ssi); return ret; @@ -1185,8 +1219,6 @@ static void rz_ssi_remove(struct platform_device *pdev) rz_ssi_release_dma_channels(ssi); - pm_runtime_put(ssi->dev); - pm_runtime_disable(ssi->dev); reset_control_assert(ssi->rstc); } @@ -1196,10 +1228,30 @@ static const struct of_device_id rz_ssi_of_match[] = { }; MODULE_DEVICE_TABLE(of, rz_ssi_of_match); +static int rz_ssi_runtime_suspend(struct device *dev) +{ + struct rz_ssi_priv *ssi = dev_get_drvdata(dev); + + return reset_control_assert(ssi->rstc); +} + +static int rz_ssi_runtime_resume(struct device *dev) +{ + struct rz_ssi_priv *ssi = dev_get_drvdata(dev); + + return reset_control_deassert(ssi->rstc); +} + +static const struct dev_pm_ops rz_ssi_pm_ops = { + RUNTIME_PM_OPS(rz_ssi_runtime_suspend, rz_ssi_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + static struct platform_driver rz_ssi_driver = { .driver = { .name = "rz-ssi-pcm-audio", .of_match_table = rz_ssi_of_match, + .pm = pm_ptr(&rz_ssi_pm_ops), }, .probe = rz_ssi_probe, .remove = rz_ssi_remove, diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c index a65d923d94dc..ffbc5a39b493 100644 --- a/sound/soc/rockchip/rk3288_hdmi_analog.c +++ b/sound/soc/rockchip/rk3288_hdmi_analog.c @@ -148,7 +148,7 @@ static struct snd_soc_dai_link rk_dailink = { .ops = &rk_ops, /* Set codecs as slave */ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(audio), }; diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index 6c89c7331229..c8137e8883c4 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -338,7 +338,7 @@ static const struct snd_soc_dai_link rockchip_dais[] = { .stream_name = "DP PCM", .init = rockchip_sound_cdndp_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(cdndp), }, [DAILINK_DA7219] = { @@ -348,7 +348,7 @@ static const struct snd_soc_dai_link rockchip_dais[] = { .ops = &rockchip_sound_da7219_ops, /* set da7219 as slave */ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(da7219), }, [DAILINK_DMIC] = { @@ -356,7 +356,7 @@ static const struct snd_soc_dai_link rockchip_dais[] = { .stream_name = "DMIC PCM", .ops = &rockchip_sound_dmic_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(dmic), }, [DAILINK_MAX98357A] = { @@ -365,7 +365,7 @@ static const struct snd_soc_dai_link rockchip_dais[] = { .ops = &rockchip_sound_max98357a_ops, /* set max98357a as slave */ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(max98357a), }, [DAILINK_RT5514] = { @@ -374,7 +374,7 @@ static const struct snd_soc_dai_link rockchip_dais[] = { .ops = &rockchip_sound_rt5514_ops, /* set rt5514 as slave */ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(rt5514), }, /* RT5514 DSP for voice wakeup via spi bus */ diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 4315da4a47c1..0a0a95b4f520 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -860,8 +860,7 @@ static void rockchip_i2s_remove(struct platform_device *pdev) } static const struct dev_pm_ops rockchip_i2s_pm_ops = { - SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, - NULL) + RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, NULL) }; static struct platform_driver rockchip_i2s_driver = { @@ -870,7 +869,7 @@ static struct platform_driver rockchip_i2s_driver = { .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(rockchip_i2s_match), - .pm = &rockchip_i2s_pm_ops, + .pm = pm_ptr(&rockchip_i2s_pm_ops), }, }; module_platform_driver(rockchip_i2s_driver); diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index d1f28699652f..d9a1fab7f403 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -22,7 +22,6 @@ #define DRV_NAME "rockchip-i2s-tdm" -#define DEFAULT_MCLK_FS 256 #define CH_GRP_MAX 4 /* The max channel 8 / 2 */ #define MULTIPLEX_CH_MAX 10 @@ -70,6 +69,8 @@ struct rk_i2s_tdm_dev { bool has_playback; bool has_capture; struct snd_soc_dai_driver *dai; + unsigned int mclk_rx_freq; + unsigned int mclk_tx_freq; }; static int to_ch_num(unsigned int val) @@ -121,7 +122,7 @@ err_mclk_tx: return ret; } -static int __maybe_unused i2s_tdm_runtime_suspend(struct device *dev) +static int i2s_tdm_runtime_suspend(struct device *dev) { struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); @@ -133,7 +134,7 @@ static int __maybe_unused i2s_tdm_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused i2s_tdm_runtime_resume(struct device *dev) +static int i2s_tdm_runtime_resume(struct device *dev) { struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); int ret; @@ -450,11 +451,11 @@ static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai, break; case SND_SOC_DAIFMT_DSP_A: val = I2S_TXCR_TFS_TDM_PCM; - tdm_val = TDM_SHIFT_CTRL(0); + tdm_val = TDM_SHIFT_CTRL(2); break; case SND_SOC_DAIFMT_DSP_B: val = I2S_TXCR_TFS_TDM_PCM; - tdm_val = TDM_SHIFT_CTRL(2); + tdm_val = TDM_SHIFT_CTRL(4); break; default: ret = -EINVAL; @@ -514,33 +515,6 @@ static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream, I2S_XFER_RXS_START); } -static int rockchip_i2s_ch_to_io(unsigned int ch, bool substream_capture) -{ - if (substream_capture) { - switch (ch) { - case I2S_CHN_4: - return I2S_IO_6CH_OUT_4CH_IN; - case I2S_CHN_6: - return I2S_IO_4CH_OUT_6CH_IN; - case I2S_CHN_8: - return I2S_IO_2CH_OUT_8CH_IN; - default: - return I2S_IO_8CH_OUT_2CH_IN; - } - } else { - switch (ch) { - case I2S_CHN_4: - return I2S_IO_4CH_OUT_6CH_IN; - case I2S_CHN_6: - return I2S_IO_6CH_OUT_4CH_IN; - case I2S_CHN_8: - return I2S_IO_8CH_OUT_2CH_IN; - default: - return I2S_IO_2CH_OUT_8CH_IN; - } - } -} - static int rockchip_i2s_io_multiplex(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -577,7 +551,6 @@ static int rockchip_i2s_io_multiplex(struct snd_pcm_substream *substream, return -EINVAL; } - rockchip_i2s_ch_to_io(val, true); } else { struct snd_pcm_str *capture_str = &substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; @@ -645,6 +618,27 @@ static int rockchip_i2s_trcm_mode(struct snd_pcm_substream *substream, return 0; } +static int rockchip_i2s_tdm_set_sysclk(struct snd_soc_dai *cpu_dai, int stream, + unsigned int freq, int dir) +{ + struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai); + + if (i2s_tdm->clk_trcm) { + i2s_tdm->mclk_tx_freq = freq; + i2s_tdm->mclk_rx_freq = freq; + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + i2s_tdm->mclk_tx_freq = freq; + else + i2s_tdm->mclk_rx_freq = freq; + } + + dev_dbg(i2s_tdm->dev, "The target mclk_%s freq is: %d\n", + stream ? "rx" : "tx", freq); + + return 0; +} + static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -659,15 +653,19 @@ static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, if (i2s_tdm->clk_trcm == TRCM_TX) { mclk = i2s_tdm->mclk_tx; + mclk_rate = i2s_tdm->mclk_tx_freq; } else if (i2s_tdm->clk_trcm == TRCM_RX) { mclk = i2s_tdm->mclk_rx; + mclk_rate = i2s_tdm->mclk_rx_freq; } else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { mclk = i2s_tdm->mclk_tx; + mclk_rate = i2s_tdm->mclk_tx_freq; } else { mclk = i2s_tdm->mclk_rx; + mclk_rate = i2s_tdm->mclk_rx_freq; } - err = clk_set_rate(mclk, DEFAULT_MCLK_FS * params_rate(params)); + err = clk_set_rate(mclk, mclk_rate); if (err) return err; @@ -827,6 +825,7 @@ static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = { .hw_params = rockchip_i2s_tdm_hw_params, .set_bclk_ratio = rockchip_i2s_tdm_set_bclk_ratio, .set_fmt = rockchip_i2s_tdm_set_fmt, + .set_sysclk = rockchip_i2s_tdm_set_sysclk, .set_tdm_slot = rockchip_dai_tdm_slot, .trigger = rockchip_i2s_tdm_trigger, }; @@ -1391,7 +1390,7 @@ static void rockchip_i2s_tdm_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused rockchip_i2s_tdm_suspend(struct device *dev) +static int rockchip_i2s_tdm_suspend(struct device *dev) { struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); @@ -1400,7 +1399,7 @@ static int __maybe_unused rockchip_i2s_tdm_suspend(struct device *dev) return 0; } -static int __maybe_unused rockchip_i2s_tdm_resume(struct device *dev) +static int rockchip_i2s_tdm_resume(struct device *dev) { struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); int ret; @@ -1415,10 +1414,8 @@ static int __maybe_unused rockchip_i2s_tdm_resume(struct device *dev) } static const struct dev_pm_ops rockchip_i2s_tdm_pm_ops = { - SET_RUNTIME_PM_OPS(i2s_tdm_runtime_suspend, i2s_tdm_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(rockchip_i2s_tdm_suspend, - rockchip_i2s_tdm_resume) + RUNTIME_PM_OPS(i2s_tdm_runtime_suspend, i2s_tdm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rockchip_i2s_tdm_suspend, rockchip_i2s_tdm_resume) }; static struct platform_driver rockchip_i2s_tdm_driver = { @@ -1427,7 +1424,7 @@ static struct platform_driver rockchip_i2s_tdm_driver = { .driver = { .name = DRV_NAME, .of_match_table = rockchip_i2s_tdm_match, - .pm = &rockchip_i2s_tdm_pm_ops, + .pm = pm_ptr(&rockchip_i2s_tdm_pm_ops), }, }; module_platform_driver(rockchip_i2s_tdm_driver); diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index 783956dc83b5..b8326e9143b6 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -247,7 +247,7 @@ static struct snd_soc_dai_link rk_max98090_dailinks[] = { .ops = &rk_aif1_ops, /* set max98090 as slave */ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(analog), }, }; @@ -260,7 +260,7 @@ static struct snd_soc_dai_link rk_hdmi_dailinks[] = { .init = rk_hdmi_init, .ops = &rk_aif1_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(hdmi), } }; @@ -274,7 +274,7 @@ static struct snd_soc_dai_link rk_max98090_hdmi_dailinks[] = { .ops = &rk_aif1_ops, /* set max98090 as slave */ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(analog), }, [DAILINK_HDMI] = { @@ -283,7 +283,7 @@ static struct snd_soc_dai_link rk_max98090_hdmi_dailinks[] = { .init = rk_hdmi_init, .ops = &rk_aif1_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(hdmi), } }; diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index cae91108f7a8..c1ee470ec607 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -668,7 +668,6 @@ static void rockchip_pdm_remove(struct platform_device *pdev) clk_disable_unprepare(pdm->hclk); } -#ifdef CONFIG_PM_SLEEP static int rockchip_pdm_suspend(struct device *dev) { struct rk_pdm_dev *pdm = dev_get_drvdata(dev); @@ -693,12 +692,11 @@ static int rockchip_pdm_resume(struct device *dev) return ret; } -#endif static const struct dev_pm_ops rockchip_pdm_pm_ops = { - SET_RUNTIME_PM_OPS(rockchip_pdm_runtime_suspend, - rockchip_pdm_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(rockchip_pdm_suspend, rockchip_pdm_resume) + RUNTIME_PM_OPS(rockchip_pdm_runtime_suspend, + rockchip_pdm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rockchip_pdm_suspend, rockchip_pdm_resume) }; static struct platform_driver rockchip_pdm_driver = { @@ -707,7 +705,7 @@ static struct platform_driver rockchip_pdm_driver = { .driver = { .name = "rockchip-pdm", .of_match_table = of_match_ptr(rockchip_pdm_match), - .pm = &rockchip_pdm_pm_ops, + .pm = pm_ptr(&rockchip_pdm_pm_ops), }, }; diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index b085d80ea2e4..590b64b362f6 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -145,7 +145,7 @@ static struct snd_soc_dai_link rk_dailink = { .ops = &rk_aif1_ops, /* set rt5645 as slave */ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(pcm), }; diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index d87c0e4f6f91..d365168934dc 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -63,7 +63,7 @@ static const struct of_device_id rk_spdif_match[] __maybe_unused = { }; MODULE_DEVICE_TABLE(of, rk_spdif_match); -static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev) +static int rk_spdif_runtime_suspend(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); @@ -74,7 +74,7 @@ static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused rk_spdif_runtime_resume(struct device *dev) +static int rk_spdif_runtime_resume(struct device *dev) { struct rk_spdif_dev *spdif = dev_get_drvdata(dev); int ret; @@ -374,8 +374,7 @@ static void rk_spdif_remove(struct platform_device *pdev) } static const struct dev_pm_ops rk_spdif_pm_ops = { - SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, - NULL) + RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, NULL) }; static struct platform_driver rk_spdif_driver = { @@ -384,7 +383,7 @@ static struct platform_driver rk_spdif_driver = { .driver = { .name = "rockchip-spdif", .of_match_table = of_match_ptr(rk_spdif_match), - .pm = &rk_spdif_pm_ops, + .pm = pm_ptr(&rk_spdif_pm_ops), }, }; module_platform_driver(rk_spdif_driver); diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 01716df0c842..3723329b266d 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -474,7 +474,7 @@ static struct snd_soc_dai_link aries_dai[] = { .name = "WM8994 AIF1", .stream_name = "HiFi", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .ops = &aries_ops, SND_SOC_DAILINK_REG(aif1), }, @@ -510,13 +510,13 @@ static struct snd_soc_card aries_card = { }; static const struct aries_wm8994_variant fascinate4g_variant = { - .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS + .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_IB_NF, .has_fm_radio = false, }; static const struct aries_wm8994_variant aries_variant = { - .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM + .modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBP_CFP | SND_SOC_DAIFMT_IB_NF, .has_fm_radio = true, }; diff --git a/sound/soc/samsung/arndale.c b/sound/soc/samsung/arndale.c index 9619f550608c..172943bb3b24 100644 --- a/sound/soc/samsung/arndale.c +++ b/sound/soc/samsung/arndale.c @@ -95,7 +95,7 @@ static struct snd_soc_dai_link arndale_rt5631_dai[] = { .stream_name = "Primary", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .ops = &arndale_rt5631_ops, SND_SOC_DAILINK_REG(rt5631_hifi), }, @@ -112,7 +112,7 @@ static struct snd_soc_dai_link arndale_wm1811_dai[] = { .stream_name = "Primary", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .ops = &arndale_wm1811_ops, SND_SOC_DAILINK_REG(wm1811_hifi), }, diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c index 365b1aca4855..8dc3b2da4c8f 100644 --- a/sound/soc/samsung/bells.c +++ b/sound/soc/samsung/bells.c @@ -7,7 +7,6 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/jack.h> -#include <linux/gpio.h> #include <linux/module.h> #include "../codecs/wm5102.h" @@ -256,14 +255,14 @@ static struct snd_soc_dai_link bells_dai_wm2200[] = { .name = "CPU-DSP", .stream_name = "CPU-DSP", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, SND_SOC_DAILINK_REG(wm2200_cpu_dsp), }, { .name = "DSP-CODEC", .stream_name = "DSP-CODEC", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .c2c_params = &sub_params, .num_c2c_params = 1, .ignore_suspend = 1, @@ -293,14 +292,14 @@ static struct snd_soc_dai_link bells_dai_wm5102[] = { .name = "CPU-DSP", .stream_name = "CPU-DSP", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, SND_SOC_DAILINK_REG(wm5102_cpu_dsp), }, { .name = "DSP-CODEC", .stream_name = "DSP-CODEC", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .c2c_params = &sub_params, .num_c2c_params = 1, .ignore_suspend = 1, @@ -310,7 +309,7 @@ static struct snd_soc_dai_link bells_dai_wm5102[] = { .name = "Baseband", .stream_name = "Baseband", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .ignore_suspend = 1, .c2c_params = &baseband_params, .num_c2c_params = 1, @@ -320,7 +319,7 @@ static struct snd_soc_dai_link bells_dai_wm5102[] = { .name = "Sub", .stream_name = "Sub", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .ignore_suspend = 1, .c2c_params = &sub_params, .num_c2c_params = 1, @@ -351,14 +350,14 @@ static struct snd_soc_dai_link bells_dai_wm5110[] = { .name = "CPU-DSP", .stream_name = "CPU-DSP", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, SND_SOC_DAILINK_REG(wm5110_cpu_dsp), }, { .name = "DSP-CODEC", .stream_name = "DSP-CODEC", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .c2c_params = &sub_params, .num_c2c_params = 1, .ignore_suspend = 1, @@ -368,7 +367,7 @@ static struct snd_soc_dai_link bells_dai_wm5110[] = { .name = "Baseband", .stream_name = "Baseband", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .ignore_suspend = 1, .c2c_params = &baseband_params, .num_c2c_params = 1, @@ -378,7 +377,7 @@ static struct snd_soc_dai_link bells_dai_wm5110[] = { .name = "Sub", .stream_name = "Sub", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .ignore_suspend = 1, .c2c_params = &sub_params, .num_c2c_params = 1, diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 8f6deb06e234..e9964f0e010a 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1216,7 +1216,6 @@ static int i2s_alloc_dais(struct samsung_i2s_priv *priv, return 0; } -#ifdef CONFIG_PM static int i2s_runtime_suspend(struct device *dev) { struct samsung_i2s_priv *priv = dev_get_drvdata(dev); @@ -1254,7 +1253,6 @@ static int i2s_runtime_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM */ static void i2s_unregister_clocks(struct samsung_i2s_priv *priv) { @@ -1733,10 +1731,8 @@ MODULE_DEVICE_TABLE(of, exynos_i2s_match); #endif static const struct dev_pm_ops samsung_i2s_pm = { - SET_RUNTIME_PM_OPS(i2s_runtime_suspend, - i2s_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver samsung_i2s_driver = { @@ -1746,7 +1742,7 @@ static struct platform_driver samsung_i2s_driver = { .driver = { .name = "samsung-i2s", .of_match_table = of_match_ptr(exynos_i2s_match), - .pm = &samsung_i2s_pm, + .pm = pm_ptr(&samsung_i2s_pm), }, }; diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index c5260e101c2a..5a02aac9b423 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -7,7 +7,6 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/jack.h> -#include <linux/gpio.h> #include <linux/module.h> #include "../codecs/wm8994.h" @@ -157,7 +156,7 @@ static struct snd_soc_dai_link littlemill_dai[] = { .name = "CPU", .stream_name = "CPU", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .ops = &littlemill_ops, SND_SOC_DAILINK_REG(cpu), }, @@ -165,7 +164,7 @@ static struct snd_soc_dai_link littlemill_dai[] = { .name = "Baseband", .stream_name = "Baseband", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .ignore_suspend = 1, .c2c_params = &baseband_params, .num_c2c_params = 1, diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 702cb4cc1ce9..1e95a2a435d4 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -7,7 +7,6 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/jack.h> -#include <linux/gpio.h> #include <linux/module.h> #include "../codecs/wm5100.h" @@ -105,7 +104,7 @@ static struct snd_soc_dai_link lowland_dai[] = { .name = "CPU", .stream_name = "CPU", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .init = lowland_wm5100_init, SND_SOC_DAILINK_REG(cpu), }, @@ -113,7 +112,7 @@ static struct snd_soc_dai_link lowland_dai[] = { .name = "Baseband", .stream_name = "Baseband", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .ignore_suspend = 1, SND_SOC_DAILINK_REG(baseband), }, @@ -121,7 +120,7 @@ static struct snd_soc_dai_link lowland_dai[] = { .name = "Sub Speaker", .stream_name = "Sub Speaker", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .ignore_suspend = 1, .c2c_params = &sub_params, .num_c2c_params = 1, diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index bbfe5fef59af..239e958b88d3 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -537,7 +537,7 @@ static struct snd_soc_dai_link midas_dai[] = { .stream_name = "HiFi Primary", .ops = &midas_aif1_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, SND_SOC_DAILINK_REG(wm1811_hifi), }, { .name = "WM1811 Voice", diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index 40ac12c07145..ab3398f39f4a 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -180,7 +180,7 @@ static struct snd_soc_dai_link odroid_card_dais[] = { .no_pcm = 1, .playback_only = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(mixer), }, { /* Secondary FE <-> BE link */ diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index def92cc09f9c..acb29cd785a9 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -106,14 +106,14 @@ static struct snd_soc_dai_link smdk_dai[] = { .stream_name = "Pri_Dai", .init = smdk_wm8994_init_paiftx, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .ops = &smdk_ops, SND_SOC_DAILINK_REG(aif1), }, { /* Sec_Fifo Playback i/f */ .name = "Sec_FIFO TX", .stream_name = "Sec_Dai", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .ops = &smdk_ops, SND_SOC_DAILINK_REG(fifo_tx), }, diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c index 5802f92ab8ba..a3f539fbde5f 100644 --- a/sound/soc/samsung/smdk_wm8994pcm.c +++ b/sound/soc/samsung/smdk_wm8994pcm.c @@ -99,7 +99,7 @@ static struct snd_soc_dai_link smdk_dai[] = { .name = "WM8994 PAIF PCM", .stream_name = "Primary PCM", .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ops = &smdk_wm8994_pcm_ops, SND_SOC_DAILINK_REG(paif_pcm), }, diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index 4bbe7bcdb845..66ef49dff1ba 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -137,7 +137,7 @@ static int snow_probe(struct platform_device *pdev) link = &priv->dai_link; link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; + SND_SOC_DAIFMT_CBC_CFC; link->name = "Primary"; link->stream_name = link->name; diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 79476e8eb680..9262e5626584 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -7,13 +7,13 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/jack.h> -#include <linux/gpio.h> +#include <linux/gpio/machine.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> #include "../codecs/wm8996.h" #include "../codecs/wm9081.h" -#define WM8996_HPSEL_GPIO 214 #define MCLK_AUDIO_RATE (512 * 48000) static int speyside_set_bias_level(struct snd_soc_card *card, @@ -105,6 +105,7 @@ static struct snd_soc_jack_pin speyside_headset_pins[] = { }, }; +static struct gpio_desc *speyside_hpsel_gpio; /* Default the headphone selection to active high */ static int speyside_jack_polarity; @@ -123,7 +124,7 @@ static void speyside_set_polarity(struct snd_soc_component *component, int polarity) { speyside_jack_polarity = !polarity; - gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity); + gpiod_direction_output(speyside_hpsel_gpio, speyside_jack_polarity); /* Re-run DAPM to make sure we're using the correct mic bias */ snd_soc_dapm_sync(snd_soc_component_get_dapm(component)); @@ -145,16 +146,22 @@ static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0); struct snd_soc_component *component = dai->component; + enum gpiod_flags flags; int ret; ret = snd_soc_dai_set_sysclk(dai, WM8996_SYSCLK_MCLK2, 32768, 0); if (ret < 0) return ret; - ret = gpio_request(WM8996_HPSEL_GPIO, "HP_SEL"); - if (ret != 0) - pr_err("Failed to request HP_SEL GPIO: %d\n", ret); - gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity); + if (speyside_jack_polarity) + flags = GPIOD_OUT_HIGH; + else + flags = GPIOD_OUT_LOW; + speyside_hpsel_gpio = devm_gpiod_get(rtd->card->dev, + "hp-sel", + flags); + if (IS_ERR(speyside_hpsel_gpio)) + return PTR_ERR(speyside_hpsel_gpio); ret = snd_soc_card_jack_new_pins(rtd->card, "Headset", SND_JACK_LINEOUT | SND_JACK_HEADSET | @@ -210,7 +217,7 @@ static struct snd_soc_dai_link speyside_dai[] = { .stream_name = "CPU-DSP", .init = speyside_wm0010_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, SND_SOC_DAILINK_REG(cpu_dsp), }, { @@ -218,7 +225,7 @@ static struct snd_soc_dai_link speyside_dai[] = { .stream_name = "DSP-CODEC", .init = speyside_wm8996_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .c2c_params = &dsp_codec_params, .num_c2c_params = 1, .ignore_suspend = 1, @@ -228,7 +235,7 @@ static struct snd_soc_dai_link speyside_dai[] = { .name = "Baseband", .stream_name = "Baseband", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .ignore_suspend = 1, SND_SOC_DAILINK_REG(baseband), }, @@ -325,6 +332,26 @@ static struct snd_soc_card speyside = { .late_probe = speyside_late_probe, }; +static struct gpiod_lookup_table wm8996_gpiod_table = { + /* Hardcoded device name in board file mach-crag6410.c */ + .dev_id = "speyside", + .table = { + /* + * This line was hardcoded to 214 in the global GPIO + * number space, S3C GPIO macros seems top set the + * wm8996 codec GPIO start offset to 212, so this will + * be GPIO 214 - 212 = 2 on the wm8996. + */ + GPIO_LOOKUP("wm8996", 2, "hp-sel", GPIO_ACTIVE_HIGH), + { }, + }, +}; + +static void speyside_gpiod_table_action(void *data) +{ + gpiod_remove_lookup_table(&wm8996_gpiod_table); +} + static int speyside_probe(struct platform_device *pdev) { struct snd_soc_card *card = &speyside; @@ -332,6 +359,12 @@ static int speyside_probe(struct platform_device *pdev) card->dev = &pdev->dev; + gpiod_add_lookup_table(&wm8996_gpiod_table); + ret = devm_add_action_or_reset(&pdev->dev, speyside_gpiod_table_action, + NULL); + if (ret) + return ret; + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index 2417b91a328f..b68ce7bd44ef 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -6,7 +6,6 @@ // Sylwester Nawrocki <s.nawrocki@samsung.com> #include <linux/clk.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> @@ -451,21 +450,21 @@ static struct snd_soc_dai_link tm2_dai_links[] = { .stream_name = "HiFi Primary", .ops = &tm2_aif1_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, SND_SOC_DAILINK_REG(aif1), }, { .name = "WM5110 Voice", .stream_name = "Voice call", .ops = &tm2_aif2_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .ignore_suspend = 1, SND_SOC_DAILINK_REG(voice), }, { .name = "WM5110 BT", .stream_name = "Bluetooth", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .ignore_suspend = 1, SND_SOC_DAILINK_REG(bt), }, { @@ -473,7 +472,7 @@ static struct snd_soc_dai_link tm2_dai_links[] = { .stream_name = "i2s1", .ops = &tm2_hdmi_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(hdmi), } }; diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index 2bdd81bf821a..d0f0c01365aa 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -7,7 +7,6 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/jack.h> -#include <linux/gpio.h> #include <linux/module.h> #include "../codecs/wm8962.h" @@ -119,7 +118,7 @@ static struct snd_soc_dai_link tobermory_dai[] = { .name = "CPU", .stream_name = "CPU", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .ops = &tobermory_ops, SND_SOC_DAILINK_REG(cpu), }, diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile index c296bd5a0a7c..dddc3e694256 100644 --- a/sound/soc/sdca/Makefile +++ b/sound/soc/sdca/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-soc-sdca-objs := sdca_functions.o sdca_device.o +snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c index 80d663777eb5..0244cdcdd109 100644 --- a/sound/soc/sdca/sdca_device.c +++ b/sound/soc/sdca/sdca_device.c @@ -7,6 +7,8 @@ */ #include <linux/acpi.h> +#include <linux/module.h> +#include <linux/property.h> #include <linux/soundwire/sdw.h> #include <sound/sdca.h> #include <sound/sdca_function.h> @@ -46,8 +48,7 @@ static bool sdca_device_quirk_rt712_vb(struct sdw_slave *slave) return false; for (i = 0; i < slave->sdca_data.num_functions; i++) { - if (slave->sdca_data.sdca_func[i].type == - SDCA_FUNCTION_TYPE_SMART_MIC) + if (slave->sdca_data.function[i].type == SDCA_FUNCTION_TYPE_SMART_MIC) return true; } diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 652865329968..493f390f087a 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -6,86 +6,83 @@ * https://www.mipi.org/mipi-sdca-v1-0-download */ +#define dev_fmt(fmt) "%s: " fmt, __func__ + #include <linux/acpi.h> +#include <linux/byteorder/generic.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/dev_printk.h> +#include <linux/module.h> +#include <linux/property.h> #include <linux/soundwire/sdw.h> +#include <linux/types.h> #include <sound/sdca.h> #include <sound/sdca_function.h> -static int patch_sdca_function_type(struct device *dev, - u32 interface_revision, - u32 *function_type, - const char **function_name) -{ - unsigned long function_type_patch = 0; +/* + * Should be long enough to encompass all the MIPI DisCo properties. + */ +#define SDCA_PROPERTY_LENGTH 64 +static int patch_sdca_function_type(u32 interface_revision, u32 *function_type) +{ /* * Unfortunately early SDCA specifications used different indices for Functions, - * for backwards compatibility we have to reorder the values found + * for backwards compatibility we have to reorder the values found. */ - if (interface_revision >= 0x0801) - goto skip_early_draft_order; - - switch (*function_type) { - case 1: - function_type_patch = SDCA_FUNCTION_TYPE_SMART_AMP; - break; - case 2: - function_type_patch = SDCA_FUNCTION_TYPE_SMART_MIC; - break; - case 3: - function_type_patch = SDCA_FUNCTION_TYPE_SPEAKER_MIC; - break; - case 4: - function_type_patch = SDCA_FUNCTION_TYPE_UAJ; - break; - case 5: - function_type_patch = SDCA_FUNCTION_TYPE_RJ; - break; - case 6: - function_type_patch = SDCA_FUNCTION_TYPE_HID; - break; - default: - dev_warn(dev, "%s: SDCA version %#x unsupported function type %d, skipped\n", - __func__, interface_revision, *function_type); - return -EINVAL; + if (interface_revision < 0x0801) { + switch (*function_type) { + case 1: + *function_type = SDCA_FUNCTION_TYPE_SMART_AMP; + break; + case 2: + *function_type = SDCA_FUNCTION_TYPE_SMART_MIC; + break; + case 3: + *function_type = SDCA_FUNCTION_TYPE_SPEAKER_MIC; + break; + case 4: + *function_type = SDCA_FUNCTION_TYPE_UAJ; + break; + case 5: + *function_type = SDCA_FUNCTION_TYPE_RJ; + break; + case 6: + *function_type = SDCA_FUNCTION_TYPE_HID; + break; + default: + return -EINVAL; + } } -skip_early_draft_order: - if (function_type_patch) - *function_type = function_type_patch; + return 0; +} - /* now double-check the values */ - switch (*function_type) { +static const char *get_sdca_function_name(u32 function_type) +{ + switch (function_type) { case SDCA_FUNCTION_TYPE_SMART_AMP: - *function_name = SDCA_FUNCTION_TYPE_SMART_AMP_NAME; - break; + return SDCA_FUNCTION_TYPE_SMART_AMP_NAME; case SDCA_FUNCTION_TYPE_SMART_MIC: - *function_name = SDCA_FUNCTION_TYPE_SMART_MIC_NAME; - break; + return SDCA_FUNCTION_TYPE_SMART_MIC_NAME; case SDCA_FUNCTION_TYPE_UAJ: - *function_name = SDCA_FUNCTION_TYPE_UAJ_NAME; - break; + return SDCA_FUNCTION_TYPE_UAJ_NAME; case SDCA_FUNCTION_TYPE_HID: - *function_name = SDCA_FUNCTION_TYPE_HID_NAME; - break; + return SDCA_FUNCTION_TYPE_HID_NAME; case SDCA_FUNCTION_TYPE_SIMPLE_AMP: + return SDCA_FUNCTION_TYPE_SIMPLE_AMP_NAME; case SDCA_FUNCTION_TYPE_SIMPLE_MIC: + return SDCA_FUNCTION_TYPE_SIMPLE_MIC_NAME; case SDCA_FUNCTION_TYPE_SPEAKER_MIC: + return SDCA_FUNCTION_TYPE_SPEAKER_MIC_NAME; case SDCA_FUNCTION_TYPE_RJ: + return SDCA_FUNCTION_TYPE_RJ_NAME; case SDCA_FUNCTION_TYPE_IMP_DEF: - dev_warn(dev, "%s: found unsupported SDCA function type %d, skipped\n", - __func__, *function_type); - return -EINVAL; + return SDCA_FUNCTION_TYPE_IMP_DEF_NAME; default: - dev_err(dev, "%s: found invalid SDCA function type %d, skipped\n", - __func__, *function_type); - return -EINVAL; + return NULL; } - - dev_info(dev, "%s: found SDCA function %s (type %d)\n", - __func__, *function_name, *function_type); - - return 0; } static int find_sdca_function(struct acpi_device *adev, void *data) @@ -96,26 +93,21 @@ static int find_sdca_function(struct acpi_device *adev, void *data) struct fwnode_handle *control5; /* used to identify function type */ const char *function_name; u32 function_type; - int func_index; + int function_index; u64 addr; int ret; if (sdca_data->num_functions >= SDCA_MAX_FUNCTION_COUNT) { - dev_err(dev, "%s: maximum number of functions exceeded\n", __func__); + dev_err(dev, "maximum number of functions exceeded\n"); return -EINVAL; } - /* - * The number of functions cannot exceed 8, we could use - * acpi_get_local_address() but the value is stored as u64 so - * we might as well avoid casts and intermediate levels - */ ret = acpi_get_local_u64_address(adev->handle, &addr); if (ret < 0) return ret; - if (!addr) { - dev_err(dev, "%s: no addr\n", __func__); + if (!addr || addr > 0x7) { + dev_err(dev, "invalid addr: 0x%llx\n", addr); return -ENODEV; } @@ -140,38 +132,1723 @@ static int find_sdca_function(struct acpi_device *adev, void *data) fwnode_handle_put(control5); if (ret < 0) { - dev_err(dev, "%s: the function type can only be determined from ACPI information\n", - __func__); + dev_err(dev, "function type only supported as DisCo constant\n"); return ret; } - ret = patch_sdca_function_type(dev, sdca_data->interface_revision, - &function_type, &function_name); - if (ret < 0) + ret = patch_sdca_function_type(sdca_data->interface_revision, &function_type); + if (ret < 0) { + dev_err(dev, "SDCA version %#x invalid function type %d\n", + sdca_data->interface_revision, function_type); return ret; + } + + function_name = get_sdca_function_name(function_type); + if (!function_name) { + dev_err(dev, "invalid SDCA function type %d\n", function_type); + return -EINVAL; + } + + dev_info(dev, "SDCA function %s (type %d) at 0x%llx\n", + function_name, function_type, addr); /* store results */ - func_index = sdca_data->num_functions; - sdca_data->sdca_func[func_index].adr = addr; - sdca_data->sdca_func[func_index].type = function_type; - sdca_data->sdca_func[func_index].name = function_name; + function_index = sdca_data->num_functions; + sdca_data->function[function_index].adr = addr; + sdca_data->function[function_index].type = function_type; + sdca_data->function[function_index].name = function_name; + sdca_data->function[function_index].node = function_node; sdca_data->num_functions++; return 0; } +/** + * sdca_lookup_functions - Parse sdca_device_desc for each Function + * @slave: SoundWire slave device to be processed. + * + * Iterate through the available SDCA Functions and fill in a short + * descriptor (struct sdca_function_desc) for each function, this + * information is stored along with the SoundWire slave device and + * used for adding drivers and quirks before the devices have fully + * probed. + */ void sdca_lookup_functions(struct sdw_slave *slave) { struct device *dev = &slave->dev; struct acpi_device *adev = to_acpi_device_node(dev->fwnode); if (!adev) { - dev_info(dev, "No matching ACPI device found, ignoring peripheral\n"); + dev_info(dev, "no matching ACPI device found, ignoring peripheral\n"); return; } + acpi_dev_for_each_child(adev, find_sdca_function, &slave->sdca_data); } EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA"); +struct raw_init_write { + __le32 addr; + u8 val; +} __packed; + +static int find_sdca_init_table(struct device *dev, + struct fwnode_handle *function_node, + struct sdca_function_data *function) +{ + struct raw_init_write *raw __free(kfree) = NULL; + struct sdca_init_write *init_write; + int i, num_init_writes; + + num_init_writes = fwnode_property_count_u8(function_node, + "mipi-sdca-function-initialization-table"); + if (!num_init_writes || num_init_writes == -EINVAL) { + return 0; + } else if (num_init_writes < 0) { + dev_err(dev, "%pfwP: failed to read initialization table: %d\n", + function_node, num_init_writes); + return num_init_writes; + } else if (num_init_writes % sizeof(*raw) != 0) { + dev_err(dev, "%pfwP: init table size invalid\n", function_node); + return -EINVAL; + } else if (num_init_writes > SDCA_MAX_INIT_COUNT) { + dev_err(dev, "%pfwP: maximum init table size exceeded\n", function_node); + return -EINVAL; + } + + raw = kzalloc(num_init_writes, GFP_KERNEL); + if (!raw) + return -ENOMEM; + + fwnode_property_read_u8_array(function_node, + "mipi-sdca-function-initialization-table", + (u8 *)raw, num_init_writes); + + num_init_writes /= sizeof(*raw); + + init_write = devm_kcalloc(dev, num_init_writes, sizeof(*init_write), GFP_KERNEL); + if (!init_write) + return -ENOMEM; + + for (i = 0; i < num_init_writes; i++) { + init_write[i].addr = le32_to_cpu(raw[i].addr); + init_write[i].val = raw[i].val; + } + + function->num_init_table = num_init_writes; + function->init_table = init_write; + + return 0; +} + +static const char *find_sdca_control_label(struct device *dev, + const struct sdca_entity *entity, + const struct sdca_control *control) +{ + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { + case SDCA_CTL_TYPE_S(IT, MIC_BIAS): + return SDCA_CTL_MIC_BIAS_NAME; + case SDCA_CTL_TYPE_S(IT, USAGE): + case SDCA_CTL_TYPE_S(OT, USAGE): + return SDCA_CTL_USAGE_NAME; + case SDCA_CTL_TYPE_S(IT, LATENCY): + case SDCA_CTL_TYPE_S(OT, LATENCY): + case SDCA_CTL_TYPE_S(MU, LATENCY): + case SDCA_CTL_TYPE_S(SU, LATENCY): + case SDCA_CTL_TYPE_S(FU, LATENCY): + case SDCA_CTL_TYPE_S(XU, LATENCY): + case SDCA_CTL_TYPE_S(CRU, LATENCY): + case SDCA_CTL_TYPE_S(UDMPU, LATENCY): + case SDCA_CTL_TYPE_S(MFPU, LATENCY): + case SDCA_CTL_TYPE_S(SMPU, LATENCY): + case SDCA_CTL_TYPE_S(SAPU, LATENCY): + case SDCA_CTL_TYPE_S(PPU, LATENCY): + return SDCA_CTL_LATENCY_NAME; + case SDCA_CTL_TYPE_S(IT, CLUSTERINDEX): + case SDCA_CTL_TYPE_S(CRU, CLUSTERINDEX): + case SDCA_CTL_TYPE_S(UDMPU, CLUSTERINDEX): + case SDCA_CTL_TYPE_S(MFPU, CLUSTERINDEX): + return SDCA_CTL_CLUSTERINDEX_NAME; + case SDCA_CTL_TYPE_S(IT, DATAPORT_SELECTOR): + case SDCA_CTL_TYPE_S(OT, DATAPORT_SELECTOR): + return SDCA_CTL_DATAPORT_SELECTOR_NAME; + case SDCA_CTL_TYPE_S(IT, MATCHING_GUID): + case SDCA_CTL_TYPE_S(OT, MATCHING_GUID): + case SDCA_CTL_TYPE_S(ENTITY_0, MATCHING_GUID): + return SDCA_CTL_MATCHING_GUID_NAME; + case SDCA_CTL_TYPE_S(IT, KEEP_ALIVE): + case SDCA_CTL_TYPE_S(OT, KEEP_ALIVE): + return SDCA_CTL_KEEP_ALIVE_NAME; + case SDCA_CTL_TYPE_S(IT, NDAI_STREAM): + case SDCA_CTL_TYPE_S(OT, NDAI_STREAM): + return SDCA_CTL_NDAI_STREAM_NAME; + case SDCA_CTL_TYPE_S(IT, NDAI_CATEGORY): + case SDCA_CTL_TYPE_S(OT, NDAI_CATEGORY): + return SDCA_CTL_NDAI_CATEGORY_NAME; + case SDCA_CTL_TYPE_S(IT, NDAI_CODINGTYPE): + case SDCA_CTL_TYPE_S(OT, NDAI_CODINGTYPE): + return SDCA_CTL_NDAI_CODINGTYPE_NAME; + case SDCA_CTL_TYPE_S(IT, NDAI_PACKETTYPE): + case SDCA_CTL_TYPE_S(OT, NDAI_PACKETTYPE): + return SDCA_CTL_NDAI_PACKETTYPE_NAME; + case SDCA_CTL_TYPE_S(MU, MIXER): + return SDCA_CTL_MIXER_NAME; + case SDCA_CTL_TYPE_S(SU, SELECTOR): + return SDCA_CTL_SELECTOR_NAME; + case SDCA_CTL_TYPE_S(FU, MUTE): + return SDCA_CTL_MUTE_NAME; + case SDCA_CTL_TYPE_S(FU, CHANNEL_VOLUME): + return SDCA_CTL_CHANNEL_VOLUME_NAME; + case SDCA_CTL_TYPE_S(FU, AGC): + return SDCA_CTL_AGC_NAME; + case SDCA_CTL_TYPE_S(FU, BASS_BOOST): + return SDCA_CTL_BASS_BOOST_NAME; + case SDCA_CTL_TYPE_S(FU, LOUDNESS): + return SDCA_CTL_LOUDNESS_NAME; + case SDCA_CTL_TYPE_S(FU, GAIN): + return SDCA_CTL_GAIN_NAME; + case SDCA_CTL_TYPE_S(XU, BYPASS): + case SDCA_CTL_TYPE_S(MFPU, BYPASS): + return SDCA_CTL_BYPASS_NAME; + case SDCA_CTL_TYPE_S(XU, XU_ID): + return SDCA_CTL_XU_ID_NAME; + case SDCA_CTL_TYPE_S(XU, XU_VERSION): + return SDCA_CTL_XU_VERSION_NAME; + case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): + return SDCA_CTL_FDL_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(XU, FDL_MESSAGEOFFSET): + return SDCA_CTL_FDL_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(XU, FDL_MESSAGELENGTH): + return SDCA_CTL_FDL_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(XU, FDL_STATUS): + return SDCA_CTL_FDL_STATUS_NAME; + case SDCA_CTL_TYPE_S(XU, FDL_SET_INDEX): + return SDCA_CTL_FDL_SET_INDEX_NAME; + case SDCA_CTL_TYPE_S(XU, FDL_HOST_REQUEST): + return SDCA_CTL_FDL_HOST_REQUEST_NAME; + case SDCA_CTL_TYPE_S(CS, CLOCK_VALID): + return SDCA_CTL_CLOCK_VALID_NAME; + case SDCA_CTL_TYPE_S(CS, SAMPLERATEINDEX): + return SDCA_CTL_SAMPLERATEINDEX_NAME; + case SDCA_CTL_TYPE_S(CX, CLOCK_SELECT): + return SDCA_CTL_CLOCK_SELECT_NAME; + case SDCA_CTL_TYPE_S(PDE, REQUESTED_PS): + return SDCA_CTL_REQUESTED_PS_NAME; + case SDCA_CTL_TYPE_S(PDE, ACTUAL_PS): + return SDCA_CTL_ACTUAL_PS_NAME; + case SDCA_CTL_TYPE_S(GE, SELECTED_MODE): + return SDCA_CTL_SELECTED_MODE_NAME; + case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + return SDCA_CTL_DETECTED_MODE_NAME; + case SDCA_CTL_TYPE_S(SPE, PRIVATE): + return SDCA_CTL_PRIVATE_NAME; + case SDCA_CTL_TYPE_S(SPE, PRIVACY_POLICY): + return SDCA_CTL_PRIVACY_POLICY_NAME; + case SDCA_CTL_TYPE_S(SPE, PRIVACY_LOCKSTATE): + return SDCA_CTL_PRIVACY_LOCKSTATE_NAME; + case SDCA_CTL_TYPE_S(SPE, PRIVACY_OWNER): + return SDCA_CTL_PRIVACY_OWNER_NAME; + case SDCA_CTL_TYPE_S(SPE, AUTHTX_CURRENTOWNER): + return SDCA_CTL_AUTHTX_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGEOFFSET): + return SDCA_CTL_AUTHTX_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGELENGTH): + return SDCA_CTL_AUTHTX_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(SPE, AUTHRX_CURRENTOWNER): + return SDCA_CTL_AUTHRX_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGEOFFSET): + return SDCA_CTL_AUTHRX_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGELENGTH): + return SDCA_CTL_AUTHRX_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(UDMPU, ACOUSTIC_ENERGY_LEVEL_MONITOR): + return SDCA_CTL_ACOUSTIC_ENERGY_LEVEL_MONITOR_NAME; + case SDCA_CTL_TYPE_S(UDMPU, ULTRASOUND_LOOP_GAIN): + return SDCA_CTL_ULTRASOUND_LOOP_GAIN_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_0): + return SDCA_CTL_OPAQUESET_0_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_1): + return SDCA_CTL_OPAQUESET_1_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_2): + return SDCA_CTL_OPAQUESET_2_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_3): + return SDCA_CTL_OPAQUESET_3_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_4): + return SDCA_CTL_OPAQUESET_4_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_5): + return SDCA_CTL_OPAQUESET_5_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_6): + return SDCA_CTL_OPAQUESET_6_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_7): + return SDCA_CTL_OPAQUESET_7_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_8): + return SDCA_CTL_OPAQUESET_8_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_9): + return SDCA_CTL_OPAQUESET_9_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_10): + return SDCA_CTL_OPAQUESET_10_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_11): + return SDCA_CTL_OPAQUESET_11_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_12): + return SDCA_CTL_OPAQUESET_12_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_13): + return SDCA_CTL_OPAQUESET_13_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_14): + return SDCA_CTL_OPAQUESET_14_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_15): + return SDCA_CTL_OPAQUESET_15_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_16): + return SDCA_CTL_OPAQUESET_16_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_17): + return SDCA_CTL_OPAQUESET_17_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_18): + return SDCA_CTL_OPAQUESET_18_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_19): + return SDCA_CTL_OPAQUESET_19_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_20): + return SDCA_CTL_OPAQUESET_20_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_21): + return SDCA_CTL_OPAQUESET_21_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_22): + return SDCA_CTL_OPAQUESET_22_NAME; + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_23): + return SDCA_CTL_OPAQUESET_23_NAME; + case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_READY): + return SDCA_CTL_ALGORITHM_READY_NAME; + case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_ENABLE): + return SDCA_CTL_ALGORITHM_ENABLE_NAME; + case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_PREPARE): + return SDCA_CTL_ALGORITHM_PREPARE_NAME; + case SDCA_CTL_TYPE_S(MFPU, CENTER_FREQUENCY_INDEX): + return SDCA_CTL_CENTER_FREQUENCY_INDEX_NAME; + case SDCA_CTL_TYPE_S(MFPU, ULTRASOUND_LEVEL): + return SDCA_CTL_ULTRASOUND_LEVEL_NAME; + case SDCA_CTL_TYPE_S(MFPU, AE_NUMBER): + return SDCA_CTL_AE_NUMBER_NAME; + case SDCA_CTL_TYPE_S(MFPU, AE_CURRENTOWNER): + return SDCA_CTL_AE_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGEOFFSET): + return SDCA_CTL_AE_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGELENGTH): + return SDCA_CTL_AE_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(SMPU, TRIGGER_ENABLE): + return SDCA_CTL_TRIGGER_ENABLE_NAME; + case SDCA_CTL_TYPE_S(SMPU, TRIGGER_STATUS): + return SDCA_CTL_TRIGGER_STATUS_NAME; + case SDCA_CTL_TYPE_S(SMPU, HIST_BUFFER_MODE): + return SDCA_CTL_HIST_BUFFER_MODE_NAME; + case SDCA_CTL_TYPE_S(SMPU, HIST_BUFFER_PREAMBLE): + return SDCA_CTL_HIST_BUFFER_PREAMBLE_NAME; + case SDCA_CTL_TYPE_S(SMPU, HIST_ERROR): + return SDCA_CTL_HIST_ERROR_NAME; + case SDCA_CTL_TYPE_S(SMPU, TRIGGER_EXTENSION): + return SDCA_CTL_TRIGGER_EXTENSION_NAME; + case SDCA_CTL_TYPE_S(SMPU, TRIGGER_READY): + return SDCA_CTL_TRIGGER_READY_NAME; + case SDCA_CTL_TYPE_S(SMPU, HIST_CURRENTOWNER): + return SDCA_CTL_HIST_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGEOFFSET): + return SDCA_CTL_HIST_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGELENGTH): + return SDCA_CTL_HIST_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(SMPU, DTODTX_CURRENTOWNER): + return SDCA_CTL_DTODTX_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGEOFFSET): + return SDCA_CTL_DTODTX_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGELENGTH): + return SDCA_CTL_DTODTX_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(SMPU, DTODRX_CURRENTOWNER): + return SDCA_CTL_DTODRX_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGEOFFSET): + return SDCA_CTL_DTODRX_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGELENGTH): + return SDCA_CTL_DTODRX_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(SAPU, PROTECTION_MODE): + return SDCA_CTL_PROTECTION_MODE_NAME; + case SDCA_CTL_TYPE_S(SAPU, PROTECTION_STATUS): + return SDCA_CTL_PROTECTION_STATUS_NAME; + case SDCA_CTL_TYPE_S(SAPU, OPAQUESETREQ_INDEX): + return SDCA_CTL_OPAQUESETREQ_INDEX_NAME; + case SDCA_CTL_TYPE_S(SAPU, DTODTX_CURRENTOWNER): + return SDCA_CTL_DTODTX_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGEOFFSET): + return SDCA_CTL_DTODTX_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGELENGTH): + return SDCA_CTL_DTODTX_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(SAPU, DTODRX_CURRENTOWNER): + return SDCA_CTL_DTODRX_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGEOFFSET): + return SDCA_CTL_DTODRX_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGELENGTH): + return SDCA_CTL_DTODRX_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(PPU, POSTURENUMBER): + return SDCA_CTL_POSTURENUMBER_NAME; + case SDCA_CTL_TYPE_S(PPU, POSTUREEXTENSION): + return SDCA_CTL_POSTUREEXTENSION_NAME; + case SDCA_CTL_TYPE_S(PPU, HORIZONTALBALANCE): + return SDCA_CTL_HORIZONTALBALANCE_NAME; + case SDCA_CTL_TYPE_S(PPU, VERTICALBALANCE): + return SDCA_CTL_VERTICALBALANCE_NAME; + case SDCA_CTL_TYPE_S(TG, TONE_DIVIDER): + return SDCA_CTL_TONE_DIVIDER_NAME; + case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + return SDCA_CTL_HIDTX_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGEOFFSET): + return SDCA_CTL_HIDTX_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGELENGTH): + return SDCA_CTL_HIDTX_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(HIDE, HIDRX_CURRENTOWNER): + return SDCA_CTL_HIDRX_CURRENTOWNER_NAME; + case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGEOFFSET): + return SDCA_CTL_HIDRX_MESSAGEOFFSET_NAME; + case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGELENGTH): + return SDCA_CTL_HIDRX_MESSAGELENGTH_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, COMMIT_GROUP_MASK): + return SDCA_CTL_COMMIT_GROUP_MASK_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_SDCA_VERSION): + return SDCA_CTL_FUNCTION_SDCA_VERSION_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_TYPE): + return SDCA_CTL_FUNCTION_TYPE_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_MANUFACTURER_ID): + return SDCA_CTL_FUNCTION_MANUFACTURER_ID_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ID): + return SDCA_CTL_FUNCTION_ID_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_VERSION): + return SDCA_CTL_FUNCTION_VERSION_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_ID): + return SDCA_CTL_FUNCTION_EXTENSION_ID_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_VERSION): + return SDCA_CTL_FUNCTION_EXTENSION_VERSION_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS): + return SDCA_CTL_FUNCTION_STATUS_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ACTION): + return SDCA_CTL_FUNCTION_ACTION_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_MANUFACTURER_ID): + return SDCA_CTL_DEVICE_MANUFACTURER_ID_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_PART_ID): + return SDCA_CTL_DEVICE_PART_ID_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_VERSION): + return SDCA_CTL_DEVICE_VERSION_NAME; + case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_SDCA_VERSION): + return SDCA_CTL_DEVICE_SDCA_VERSION_NAME; + default: + return devm_kasprintf(dev, GFP_KERNEL, "Imp-Def %#x", control->sel); + } +} + +static unsigned int find_sdca_control_bits(const struct sdca_entity *entity, + const struct sdca_control *control) +{ + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { + case SDCA_CTL_TYPE_S(IT, LATENCY): + case SDCA_CTL_TYPE_S(OT, LATENCY): + case SDCA_CTL_TYPE_S(MU, LATENCY): + case SDCA_CTL_TYPE_S(SU, LATENCY): + case SDCA_CTL_TYPE_S(FU, LATENCY): + case SDCA_CTL_TYPE_S(XU, LATENCY): + case SDCA_CTL_TYPE_S(XU, FDL_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(XU, FDL_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(CRU, LATENCY): + case SDCA_CTL_TYPE_S(UDMPU, LATENCY): + case SDCA_CTL_TYPE_S(MFPU, LATENCY): + case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SMPU, LATENCY): + case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SAPU, LATENCY): + case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(PPU, LATENCY): + case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGELENGTH): + return 32; + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_MANUFACTURER_ID): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ID): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_ID): + case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_MANUFACTURER_ID): + case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_PART_ID): + case SDCA_CTL_TYPE_S(IT, DATAPORT_SELECTOR): + case SDCA_CTL_TYPE_S(OT, DATAPORT_SELECTOR): + case SDCA_CTL_TYPE_S(MU, MIXER): + case SDCA_CTL_TYPE_S(FU, CHANNEL_VOLUME): + case SDCA_CTL_TYPE_S(FU, GAIN): + case SDCA_CTL_TYPE_S(XU, XU_ID): + case SDCA_CTL_TYPE_S(UDMPU, ACOUSTIC_ENERGY_LEVEL_MONITOR): + case SDCA_CTL_TYPE_S(UDMPU, ULTRASOUND_LOOP_GAIN): + case SDCA_CTL_TYPE_S(MFPU, ULTRASOUND_LEVEL): + case SDCA_CTL_TYPE_S(PPU, HORIZONTALBALANCE): + case SDCA_CTL_TYPE_S(PPU, VERTICALBALANCE): + return 16; + case SDCA_CTL_TYPE_S(FU, MUTE): + case SDCA_CTL_TYPE_S(FU, AGC): + case SDCA_CTL_TYPE_S(FU, BASS_BOOST): + case SDCA_CTL_TYPE_S(FU, LOUDNESS): + case SDCA_CTL_TYPE_S(XU, BYPASS): + case SDCA_CTL_TYPE_S(MFPU, BYPASS): + return 1; + default: + return 8; + } +} + +static enum sdca_control_datatype +find_sdca_control_datatype(const struct sdca_entity *entity, + const struct sdca_control *control) +{ + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { + case SDCA_CTL_TYPE_S(XU, BYPASS): + case SDCA_CTL_TYPE_S(MFPU, BYPASS): + case SDCA_CTL_TYPE_S(FU, MUTE): + case SDCA_CTL_TYPE_S(FU, AGC): + case SDCA_CTL_TYPE_S(FU, BASS_BOOST): + case SDCA_CTL_TYPE_S(FU, LOUDNESS): + return SDCA_CTL_DATATYPE_ONEBIT; + case SDCA_CTL_TYPE_S(IT, LATENCY): + case SDCA_CTL_TYPE_S(OT, LATENCY): + case SDCA_CTL_TYPE_S(MU, LATENCY): + case SDCA_CTL_TYPE_S(SU, LATENCY): + case SDCA_CTL_TYPE_S(FU, LATENCY): + case SDCA_CTL_TYPE_S(XU, LATENCY): + case SDCA_CTL_TYPE_S(CRU, LATENCY): + case SDCA_CTL_TYPE_S(UDMPU, LATENCY): + case SDCA_CTL_TYPE_S(MFPU, LATENCY): + case SDCA_CTL_TYPE_S(SMPU, LATENCY): + case SDCA_CTL_TYPE_S(SAPU, LATENCY): + case SDCA_CTL_TYPE_S(PPU, LATENCY): + case SDCA_CTL_TYPE_S(SU, SELECTOR): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_0): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_1): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_2): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_3): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_4): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_5): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_6): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_7): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_8): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_9): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_10): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_11): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_12): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_13): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_14): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_15): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_16): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_17): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_18): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_19): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_20): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_21): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_22): + case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_23): + case SDCA_CTL_TYPE_S(SAPU, PROTECTION_MODE): + case SDCA_CTL_TYPE_S(SMPU, HIST_BUFFER_PREAMBLE): + case SDCA_CTL_TYPE_S(XU, FDL_HOST_REQUEST): + case SDCA_CTL_TYPE_S(XU, XU_ID): + case SDCA_CTL_TYPE_S(CX, CLOCK_SELECT): + case SDCA_CTL_TYPE_S(TG, TONE_DIVIDER): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_MANUFACTURER_ID): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ID): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_ID): + case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_MANUFACTURER_ID): + case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_PART_ID): + case SDCA_CTL_TYPE_S(XU, FDL_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(XU, FDL_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGELENGTH): + case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGEOFFSET): + case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGELENGTH): + return SDCA_CTL_DATATYPE_INTEGER; + case SDCA_CTL_TYPE_S(IT, MIC_BIAS): + case SDCA_CTL_TYPE_S(SMPU, HIST_BUFFER_MODE): + case SDCA_CTL_TYPE_S(PDE, REQUESTED_PS): + case SDCA_CTL_TYPE_S(PDE, ACTUAL_PS): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_TYPE): + return SDCA_CTL_DATATYPE_SPEC_ENCODED_VALUE; + case SDCA_CTL_TYPE_S(XU, XU_VERSION): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_SDCA_VERSION): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_VERSION): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_VERSION): + case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_VERSION): + case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_SDCA_VERSION): + return SDCA_CTL_DATATYPE_BCD; + case SDCA_CTL_TYPE_S(FU, CHANNEL_VOLUME): + case SDCA_CTL_TYPE_S(FU, GAIN): + case SDCA_CTL_TYPE_S(MU, MIXER): + case SDCA_CTL_TYPE_S(PPU, HORIZONTALBALANCE): + case SDCA_CTL_TYPE_S(PPU, VERTICALBALANCE): + case SDCA_CTL_TYPE_S(MFPU, ULTRASOUND_LEVEL): + case SDCA_CTL_TYPE_S(UDMPU, ACOUSTIC_ENERGY_LEVEL_MONITOR): + case SDCA_CTL_TYPE_S(UDMPU, ULTRASOUND_LOOP_GAIN): + return SDCA_CTL_DATATYPE_Q7P8DB; + case SDCA_CTL_TYPE_S(IT, USAGE): + case SDCA_CTL_TYPE_S(OT, USAGE): + case SDCA_CTL_TYPE_S(IT, CLUSTERINDEX): + case SDCA_CTL_TYPE_S(CRU, CLUSTERINDEX): + case SDCA_CTL_TYPE_S(UDMPU, CLUSTERINDEX): + case SDCA_CTL_TYPE_S(MFPU, CLUSTERINDEX): + case SDCA_CTL_TYPE_S(MFPU, CENTER_FREQUENCY_INDEX): + case SDCA_CTL_TYPE_S(MFPU, AE_NUMBER): + case SDCA_CTL_TYPE_S(SAPU, OPAQUESETREQ_INDEX): + case SDCA_CTL_TYPE_S(XU, FDL_SET_INDEX): + case SDCA_CTL_TYPE_S(CS, SAMPLERATEINDEX): + case SDCA_CTL_TYPE_S(GE, SELECTED_MODE): + case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + return SDCA_CTL_DATATYPE_BYTEINDEX; + case SDCA_CTL_TYPE_S(PPU, POSTURENUMBER): + return SDCA_CTL_DATATYPE_POSTURENUMBER; + case SDCA_CTL_TYPE_S(IT, DATAPORT_SELECTOR): + case SDCA_CTL_TYPE_S(OT, DATAPORT_SELECTOR): + return SDCA_CTL_DATATYPE_DP_INDEX; + case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_READY): + case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_ENABLE): + case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_PREPARE): + case SDCA_CTL_TYPE_S(SAPU, PROTECTION_STATUS): + case SDCA_CTL_TYPE_S(SMPU, TRIGGER_ENABLE): + case SDCA_CTL_TYPE_S(SMPU, TRIGGER_STATUS): + case SDCA_CTL_TYPE_S(SMPU, TRIGGER_READY): + case SDCA_CTL_TYPE_S(SPE, PRIVACY_POLICY): + case SDCA_CTL_TYPE_S(SPE, PRIVACY_OWNER): + return SDCA_CTL_DATATYPE_BITINDEX; + case SDCA_CTL_TYPE_S(IT, KEEP_ALIVE): + case SDCA_CTL_TYPE_S(OT, KEEP_ALIVE): + case SDCA_CTL_TYPE_S(IT, NDAI_STREAM): + case SDCA_CTL_TYPE_S(OT, NDAI_STREAM): + case SDCA_CTL_TYPE_S(IT, NDAI_CATEGORY): + case SDCA_CTL_TYPE_S(OT, NDAI_CATEGORY): + case SDCA_CTL_TYPE_S(IT, NDAI_CODINGTYPE): + case SDCA_CTL_TYPE_S(OT, NDAI_CODINGTYPE): + case SDCA_CTL_TYPE_S(IT, NDAI_PACKETTYPE): + case SDCA_CTL_TYPE_S(OT, NDAI_PACKETTYPE): + case SDCA_CTL_TYPE_S(SMPU, HIST_ERROR): + case SDCA_CTL_TYPE_S(XU, FDL_STATUS): + case SDCA_CTL_TYPE_S(CS, CLOCK_VALID): + case SDCA_CTL_TYPE_S(SPE, PRIVACY_LOCKSTATE): + case SDCA_CTL_TYPE_S(ENTITY_0, COMMIT_GROUP_MASK): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS): + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ACTION): + case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): + case SDCA_CTL_TYPE_S(SPE, AUTHTX_CURRENTOWNER): + case SDCA_CTL_TYPE_S(SPE, AUTHRX_CURRENTOWNER): + case SDCA_CTL_TYPE_S(MFPU, AE_CURRENTOWNER): + case SDCA_CTL_TYPE_S(SMPU, HIST_CURRENTOWNER): + case SDCA_CTL_TYPE_S(SMPU, DTODTX_CURRENTOWNER): + case SDCA_CTL_TYPE_S(SMPU, DTODRX_CURRENTOWNER): + case SDCA_CTL_TYPE_S(SAPU, DTODTX_CURRENTOWNER): + case SDCA_CTL_TYPE_S(SAPU, DTODRX_CURRENTOWNER): + case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + case SDCA_CTL_TYPE_S(HIDE, HIDRX_CURRENTOWNER): + return SDCA_CTL_DATATYPE_BITMAP; + case SDCA_CTL_TYPE_S(IT, MATCHING_GUID): + case SDCA_CTL_TYPE_S(OT, MATCHING_GUID): + case SDCA_CTL_TYPE_S(ENTITY_0, MATCHING_GUID): + return SDCA_CTL_DATATYPE_GUID; + default: + return SDCA_CTL_DATATYPE_IMPDEF; + } +} + +static int find_sdca_control_range(struct device *dev, + struct fwnode_handle *control_node, + struct sdca_control_range *range) +{ + u8 *range_list; + int num_range; + u16 *limits; + int i; + + num_range = fwnode_property_count_u8(control_node, "mipi-sdca-control-range"); + if (!num_range || num_range == -EINVAL) + return 0; + else if (num_range < 0) + return num_range; + + range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL); + if (!range_list) + return -ENOMEM; + + fwnode_property_read_u8_array(control_node, "mipi-sdca-control-range", + range_list, num_range); + + limits = (u16 *)range_list; + + range->cols = le16_to_cpu(limits[0]); + range->rows = le16_to_cpu(limits[1]); + range->data = (u32 *)&limits[2]; + + num_range = (num_range - (2 * sizeof(*limits))) / sizeof(*range->data); + if (num_range != range->cols * range->rows) + return -EINVAL; + + for (i = 0; i < num_range; i++) + range->data[i] = le32_to_cpu(range->data[i]); + + return 0; +} + +/* + * TODO: Add support for -cn- properties, allowing different channels to have + * different defaults etc. + */ +static int find_sdca_entity_control(struct device *dev, struct sdca_entity *entity, + struct fwnode_handle *control_node, + struct sdca_control *control) +{ + u32 tmp; + int ret; + + ret = fwnode_property_read_u32(control_node, "mipi-sdca-control-access-mode", &tmp); + if (ret) { + dev_err(dev, "%s: control %#x: access mode missing: %d\n", + entity->label, control->sel, ret); + return ret; + } + + control->mode = tmp; + + ret = fwnode_property_read_u32(control_node, "mipi-sdca-control-access-layer", &tmp); + if (ret) { + dev_err(dev, "%s: control %#x: access layer missing: %d\n", + entity->label, control->sel, ret); + return ret; + } + + control->layers = tmp; + + switch (control->mode) { + case SDCA_ACCESS_MODE_DC: + ret = fwnode_property_read_u32(control_node, + "mipi-sdca-control-dc-value", + &tmp); + if (ret) { + dev_err(dev, "%s: control %#x: dc value missing: %d\n", + entity->label, control->sel, ret); + return ret; + } + + control->value = tmp; + control->has_fixed = true; + break; + case SDCA_ACCESS_MODE_RW: + case SDCA_ACCESS_MODE_DUAL: + ret = fwnode_property_read_u32(control_node, + "mipi-sdca-control-default-value", + &tmp); + if (!ret) { + control->value = tmp; + control->has_default = true; + } + + ret = fwnode_property_read_u32(control_node, + "mipi-sdca-control-fixed-value", + &tmp); + if (!ret) { + if (control->has_default && control->value != tmp) { + dev_err(dev, + "%s: control %#x: default and fixed value don't match\n", + entity->label, control->sel); + return -EINVAL; + } + + control->value = tmp; + control->has_fixed = true; + } + + control->deferrable = fwnode_property_read_bool(control_node, + "mipi-sdca-control-deferrable"); + break; + default: + break; + } + + ret = find_sdca_control_range(dev, control_node, &control->range); + if (ret) { + dev_err(dev, "%s: control %#x: range missing: %d\n", + entity->label, control->sel, ret); + return ret; + } + + ret = fwnode_property_read_u64(control_node, "mipi-sdca-control-cn-list", + &control->cn_list); + if (ret == -EINVAL) { + /* Spec allows not specifying cn-list if only the first number is used */ + control->cn_list = 0x1; + } else if (ret || !control->cn_list) { + dev_err(dev, "%s: control %#x: cn list missing: %d\n", + entity->label, control->sel, ret); + return ret; + } + + ret = fwnode_property_read_u32(control_node, + "mipi-sdca-control-interrupt-position", + &tmp); + if (!ret) + control->interrupt_position = tmp; + + control->label = find_sdca_control_label(dev, entity, control); + if (!control->label) + return -ENOMEM; + + control->type = find_sdca_control_datatype(entity, control); + control->nbits = find_sdca_control_bits(entity, control); + + dev_info(dev, "%s: %s: control %#x mode %#x layers %#x cn %#llx int %d value %#x %s\n", + entity->label, control->label, control->sel, + control->mode, control->layers, control->cn_list, + control->interrupt_position, control->value, + control->deferrable ? "deferrable" : ""); + + return 0; +} + +static int find_sdca_entity_controls(struct device *dev, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +{ + struct sdca_control *controls; + int num_controls; + u64 control_list; + int control_sel; + int i, ret; + + ret = fwnode_property_read_u64(entity_node, "mipi-sdca-control-list", &control_list); + if (ret == -EINVAL) { + /* Allow missing control lists, assume no controls. */ + dev_warn(dev, "%s: missing control list\n", entity->label); + return 0; + } else if (ret) { + dev_err(dev, "%s: failed to read control list: %d\n", entity->label, ret); + return ret; + } else if (!control_list) { + return 0; + } + + num_controls = hweight64(control_list); + controls = devm_kcalloc(dev, num_controls, sizeof(*controls), GFP_KERNEL); + if (!controls) + return -ENOMEM; + + i = 0; + for_each_set_bit(control_sel, (unsigned long *)&control_list, + BITS_PER_TYPE(control_list)) { + struct fwnode_handle *control_node; + char control_property[SDCA_PROPERTY_LENGTH]; + + /* DisCo uses upper-case for hex numbers */ + snprintf(control_property, sizeof(control_property), + "mipi-sdca-control-0x%X-subproperties", control_sel); + + control_node = fwnode_get_named_child_node(entity_node, control_property); + if (!control_node) { + dev_err(dev, "%s: control node %s not found\n", + entity->label, control_property); + return -EINVAL; + } + + controls[i].sel = control_sel; + + ret = find_sdca_entity_control(dev, entity, control_node, &controls[i]); + fwnode_handle_put(control_node); + if (ret) + return ret; + + i++; + } + + entity->num_controls = num_controls; + entity->controls = controls; + + return 0; +} + +static bool find_sdca_iot_dataport(struct sdca_entity_iot *terminal) +{ + switch (terminal->type) { + case SDCA_TERM_TYPE_GENERIC: + case SDCA_TERM_TYPE_ULTRASOUND: + case SDCA_TERM_TYPE_CAPTURE_DIRECT_PCM_MIC: + case SDCA_TERM_TYPE_RAW_PDM_MIC: + case SDCA_TERM_TYPE_SPEECH: + case SDCA_TERM_TYPE_VOICE: + case SDCA_TERM_TYPE_SECONDARY_PCM_MIC: + case SDCA_TERM_TYPE_ACOUSTIC_CONTEXT_AWARENESS: + case SDCA_TERM_TYPE_DTOD_STREAM: + case SDCA_TERM_TYPE_REFERENCE_STREAM: + case SDCA_TERM_TYPE_SENSE_CAPTURE: + case SDCA_TERM_TYPE_STREAMING_MIC: + case SDCA_TERM_TYPE_OPTIMIZATION_STREAM: + case SDCA_TERM_TYPE_PDM_RENDER_STREAM: + case SDCA_TERM_TYPE_COMPANION_DATA: + return true; + default: + return false; + } +} + +static int find_sdca_entity_iot(struct device *dev, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +{ + struct sdca_entity_iot *terminal = &entity->iot; + u32 tmp; + int ret; + + ret = fwnode_property_read_u32(entity_node, "mipi-sdca-terminal-type", &tmp); + if (ret) { + dev_err(dev, "%s: terminal type missing: %d\n", entity->label, ret); + return ret; + } + + terminal->type = tmp; + terminal->is_dataport = find_sdca_iot_dataport(terminal); + + ret = fwnode_property_read_u32(entity_node, + "mipi-sdca-terminal-reference-number", &tmp); + if (!ret) + terminal->reference = tmp; + + ret = fwnode_property_read_u32(entity_node, + "mipi-sdca-terminal-connector-type", &tmp); + if (!ret) + terminal->connector = tmp; + + ret = fwnode_property_read_u32(entity_node, + "mipi-sdca-terminal-transducer-count", &tmp); + if (!ret) + terminal->num_transducer = tmp; + + dev_info(dev, "%s: terminal type %#x ref %#x conn %#x count %d\n", + entity->label, terminal->type, terminal->reference, + terminal->connector, terminal->num_transducer); + + return 0; +} + +static int find_sdca_entity_cs(struct device *dev, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +{ + struct sdca_entity_cs *clock = &entity->cs; + u32 tmp; + int ret; + + ret = fwnode_property_read_u32(entity_node, "mipi-sdca-cs-type", &tmp); + if (ret) { + dev_err(dev, "%s: clock type missing: %d\n", entity->label, ret); + return ret; + } + + clock->type = tmp; + + ret = fwnode_property_read_u32(entity_node, + "mipi-sdca-clock-valid-max-delay", &tmp); + if (!ret) + clock->max_delay = tmp; + + dev_info(dev, "%s: clock type %#x delay %d\n", entity->label, + clock->type, clock->max_delay); + + return 0; +} + +static int find_sdca_entity_pde(struct device *dev, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +{ + static const int mult_delay = 3; + struct sdca_entity_pde *power = &entity->pde; + u32 *delay_list __free(kfree) = NULL; + struct sdca_pde_delay *delays; + int num_delays; + int i, j; + + num_delays = fwnode_property_count_u32(entity_node, + "mipi-sdca-powerdomain-transition-max-delay"); + if (num_delays <= 0) { + dev_err(dev, "%s: max delay list missing: %d\n", + entity->label, num_delays); + return -EINVAL; + } else if (num_delays % mult_delay != 0) { + dev_err(dev, "%s: delays not multiple of %d\n", + entity->label, mult_delay); + return -EINVAL; + } else if (num_delays > SDCA_MAX_DELAY_COUNT) { + dev_err(dev, "%s: maximum number of transition delays exceeded\n", + entity->label); + return -EINVAL; + } + + /* There are 3 values per delay */ + delays = devm_kcalloc(dev, num_delays / mult_delay, + sizeof(*delays), GFP_KERNEL); + if (!delays) + return -ENOMEM; + + delay_list = kcalloc(num_delays, sizeof(*delay_list), GFP_KERNEL); + if (!delay_list) + return -ENOMEM; + + fwnode_property_read_u32_array(entity_node, + "mipi-sdca-powerdomain-transition-max-delay", + delay_list, num_delays); + + num_delays /= mult_delay; + + for (i = 0, j = 0; i < num_delays; i++) { + delays[i].from_ps = delay_list[j++]; + delays[i].to_ps = delay_list[j++]; + delays[i].us = delay_list[j++]; + + dev_info(dev, "%s: from %#x to %#x delay %dus\n", entity->label, + delays[i].from_ps, delays[i].to_ps, delays[i].us); + } + + power->num_max_delay = num_delays; + power->max_delay = delays; + + return 0; +} + +struct raw_ge_mode { + u8 val; + u8 num_controls; + struct { + u8 id; + u8 sel; + u8 cn; + __le32 val; + } __packed controls[] __counted_by(num_controls); +} __packed; + +static int find_sdca_entity_ge(struct device *dev, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +{ + struct sdca_entity_ge *group = &entity->ge; + u8 *affected_list __free(kfree) = NULL; + u8 *affected_iter; + int num_affected; + int i, j; + + num_affected = fwnode_property_count_u8(entity_node, + "mipi-sdca-ge-selectedmode-controls-affected"); + if (!num_affected) { + return 0; + } else if (num_affected < 0) { + dev_err(dev, "%s: failed to read affected controls: %d\n", + entity->label, num_affected); + return num_affected; + } else if (num_affected > SDCA_MAX_AFFECTED_COUNT) { + dev_err(dev, "%s: maximum affected controls size exceeded\n", + entity->label); + return -EINVAL; + } + + affected_list = kcalloc(num_affected, sizeof(*affected_list), GFP_KERNEL); + if (!affected_list) + return -ENOMEM; + + fwnode_property_read_u8_array(entity_node, + "mipi-sdca-ge-selectedmode-controls-affected", + affected_list, num_affected); + + group->num_modes = *affected_list; + affected_iter = affected_list + 1; + + group->modes = devm_kcalloc(dev, group->num_modes, sizeof(*group->modes), + GFP_KERNEL); + if (!group->modes) + return -ENOMEM; + + for (i = 0; i < group->num_modes; i++) { + struct raw_ge_mode *raw = (struct raw_ge_mode *)affected_iter; + struct sdca_ge_mode *mode = &group->modes[i]; + + affected_iter += sizeof(*raw); + if (affected_iter > affected_list + num_affected) + goto bad_list; + + mode->val = raw->val; + mode->num_controls = raw->num_controls; + + affected_iter += mode->num_controls * sizeof(raw->controls[0]); + if (affected_iter > affected_list + num_affected) + goto bad_list; + + mode->controls = devm_kcalloc(dev, mode->num_controls, + sizeof(*mode->controls), GFP_KERNEL); + if (!mode->controls) + return -ENOMEM; + + for (j = 0; j < mode->num_controls; j++) { + mode->controls[j].id = raw->controls[j].id; + mode->controls[j].sel = raw->controls[j].sel; + mode->controls[j].cn = raw->controls[j].cn; + mode->controls[j].val = le32_to_cpu(raw->controls[j].val); + } + } + + return 0; + +bad_list: + dev_err(dev, "%s: malformed affected controls list\n", entity->label); + return -EINVAL; +} + +static int find_sdca_entity(struct device *dev, + struct fwnode_handle *function_node, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +{ + u32 tmp; + int ret; + + ret = fwnode_property_read_string(entity_node, "mipi-sdca-entity-label", + &entity->label); + if (ret) { + dev_err(dev, "%pfwP: entity %#x: label missing: %d\n", + function_node, entity->id, ret); + return ret; + } + + ret = fwnode_property_read_u32(entity_node, "mipi-sdca-entity-type", &tmp); + if (ret) { + dev_err(dev, "%s: type missing: %d\n", entity->label, ret); + return ret; + } + + entity->type = tmp; + + dev_info(dev, "%s: entity %#x type %#x\n", + entity->label, entity->id, entity->type); + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + case SDCA_ENTITY_TYPE_OT: + ret = find_sdca_entity_iot(dev, entity_node, entity); + break; + case SDCA_ENTITY_TYPE_CS: + ret = find_sdca_entity_cs(dev, entity_node, entity); + break; + case SDCA_ENTITY_TYPE_PDE: + ret = find_sdca_entity_pde(dev, entity_node, entity); + break; + case SDCA_ENTITY_TYPE_GE: + ret = find_sdca_entity_ge(dev, entity_node, entity); + break; + default: + break; + } + if (ret) + return ret; + + ret = find_sdca_entity_controls(dev, entity_node, entity); + if (ret) + return ret; + + return 0; +} + +static int find_sdca_entities(struct device *dev, + struct fwnode_handle *function_node, + struct sdca_function_data *function) +{ + u32 *entity_list __free(kfree) = NULL; + struct sdca_entity *entities; + int num_entities; + int i, ret; + + num_entities = fwnode_property_count_u32(function_node, + "mipi-sdca-entity-id-list"); + if (num_entities <= 0) { + dev_err(dev, "%pfwP: entity id list missing: %d\n", + function_node, num_entities); + return -EINVAL; + } else if (num_entities > SDCA_MAX_ENTITY_COUNT) { + dev_err(dev, "%pfwP: maximum number of entities exceeded\n", + function_node); + return -EINVAL; + } + + /* Add 1 to make space for Entity 0 */ + entities = devm_kcalloc(dev, num_entities + 1, sizeof(*entities), GFP_KERNEL); + if (!entities) + return -ENOMEM; + + entity_list = kcalloc(num_entities, sizeof(*entity_list), GFP_KERNEL); + if (!entity_list) + return -ENOMEM; + + fwnode_property_read_u32_array(function_node, "mipi-sdca-entity-id-list", + entity_list, num_entities); + + for (i = 0; i < num_entities; i++) + entities[i].id = entity_list[i]; + + /* now read subproperties */ + for (i = 0; i < num_entities; i++) { + char entity_property[SDCA_PROPERTY_LENGTH]; + struct fwnode_handle *entity_node; + + /* DisCo uses upper-case for hex numbers */ + snprintf(entity_property, sizeof(entity_property), + "mipi-sdca-entity-id-0x%X-subproperties", entities[i].id); + + entity_node = fwnode_get_named_child_node(function_node, entity_property); + if (!entity_node) { + dev_err(dev, "%pfwP: entity node %s not found\n", + function_node, entity_property); + return -EINVAL; + } + + ret = find_sdca_entity(dev, function_node, entity_node, &entities[i]); + fwnode_handle_put(entity_node); + if (ret) + return ret; + } + + /* + * Add Entity 0 at end of the array, makes it easy to skip during + * all the Entity searches involved in creating connections. + */ + entities[num_entities].label = "entity0"; + + ret = find_sdca_entity_controls(dev, function_node, &entities[num_entities]); + if (ret) + return ret; + + function->num_entities = num_entities + 1; + function->entities = entities; + + return 0; +} + +static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function, + const char *entity_label) +{ + int i; + + for (i = 0; i < function->num_entities; i++) { + struct sdca_entity *entity = &function->entities[i]; + + if (!strcmp(entity->label, entity_label)) + return entity; + } + + return NULL; +} + +static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function, + const int id) +{ + int i; + + for (i = 0; i < function->num_entities; i++) { + struct sdca_entity *entity = &function->entities[i]; + + if (entity->id == id) + return entity; + } + + return NULL; +} + +static int find_sdca_entity_connection_iot(struct device *dev, + struct sdca_function_data *function, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +{ + struct sdca_entity_iot *terminal = &entity->iot; + struct fwnode_handle *clock_node; + struct sdca_entity *clock_entity; + const char *clock_label; + int ret; + + clock_node = fwnode_get_named_child_node(entity_node, + "mipi-sdca-terminal-clock-connection"); + if (!clock_node) + return 0; + + ret = fwnode_property_read_string(clock_node, "mipi-sdca-entity-label", + &clock_label); + if (ret) { + dev_err(dev, "%s: clock label missing: %d\n", entity->label, ret); + fwnode_handle_put(clock_node); + return ret; + } + + clock_entity = find_sdca_entity_by_label(function, clock_label); + if (!clock_entity) { + dev_err(dev, "%s: failed to find clock with label %s\n", + entity->label, clock_label); + fwnode_handle_put(clock_node); + return -EINVAL; + } + + terminal->clock = clock_entity; + + dev_info(dev, "%s -> %s\n", clock_entity->label, entity->label); + + fwnode_handle_put(clock_node); + return 0; +} + +static int find_sdca_entity_connection_pde(struct device *dev, + struct sdca_function_data *function, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +{ + struct sdca_entity_pde *power = &entity->pde; + u32 *managed_list __free(kfree) = NULL; + struct sdca_entity **managed; + int num_managed; + int i; + + num_managed = fwnode_property_count_u32(entity_node, + "mipi-sdca-powerdomain-managed-list"); + if (!num_managed) { + return 0; + } else if (num_managed < 0) { + dev_err(dev, "%s: managed list missing: %d\n", entity->label, num_managed); + return num_managed; + } else if (num_managed > SDCA_MAX_ENTITY_COUNT) { + dev_err(dev, "%s: maximum number of managed entities exceeded\n", + entity->label); + return -EINVAL; + } + + managed = devm_kcalloc(dev, num_managed, sizeof(*managed), GFP_KERNEL); + if (!managed) + return -ENOMEM; + + managed_list = kcalloc(num_managed, sizeof(*managed_list), GFP_KERNEL); + if (!managed_list) + return -ENOMEM; + + fwnode_property_read_u32_array(entity_node, + "mipi-sdca-powerdomain-managed-list", + managed_list, num_managed); + + for (i = 0; i < num_managed; i++) { + managed[i] = find_sdca_entity_by_id(function, managed_list[i]); + if (!managed[i]) { + dev_err(dev, "%s: failed to find entity with id %#x\n", + entity->label, managed_list[i]); + return -EINVAL; + } + + dev_info(dev, "%s -> %s\n", managed[i]->label, entity->label); + } + + power->num_managed = num_managed; + power->managed = managed; + + return 0; +} + +static int find_sdca_entity_connection_ge(struct device *dev, + struct sdca_function_data *function, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +{ + int i, j; + + for (i = 0; i < entity->ge.num_modes; i++) { + struct sdca_ge_mode *mode = &entity->ge.modes[i]; + + for (j = 0; j < mode->num_controls; j++) { + struct sdca_ge_control *affected = &mode->controls[j]; + struct sdca_entity *managed; + + managed = find_sdca_entity_by_id(function, affected->id); + if (!managed) { + dev_err(dev, "%s: failed to find entity with id %#x\n", + entity->label, affected->id); + return -EINVAL; + } + + if (managed->group && managed->group != entity) { + dev_err(dev, + "%s: entity controlled by two groups %s, %s\n", + managed->label, managed->group->label, + entity->label); + return -EINVAL; + } + + managed->group = entity; + } + } + + return 0; +} + +static int find_sdca_entity_connection(struct device *dev, + struct sdca_function_data *function, + struct fwnode_handle *entity_node, + struct sdca_entity *entity) +{ + struct sdca_entity **pins; + int num_pins, pin; + u64 pin_list; + int i, ret; + + switch (entity->type) { + case SDCA_ENTITY_TYPE_IT: + case SDCA_ENTITY_TYPE_OT: + ret = find_sdca_entity_connection_iot(dev, function, + entity_node, entity); + break; + case SDCA_ENTITY_TYPE_PDE: + ret = find_sdca_entity_connection_pde(dev, function, + entity_node, entity); + break; + case SDCA_ENTITY_TYPE_GE: + ret = find_sdca_entity_connection_ge(dev, function, + entity_node, entity); + break; + default: + ret = 0; + break; + } + if (ret) + return ret; + + ret = fwnode_property_read_u64(entity_node, "mipi-sdca-input-pin-list", &pin_list); + if (ret == -EINVAL) { + /* Allow missing pin lists, assume no pins. */ + dev_warn(dev, "%s: missing pin list\n", entity->label); + return 0; + } else if (ret) { + dev_err(dev, "%s: failed to read pin list: %d\n", entity->label, ret); + return ret; + } else if (pin_list & BIT(0)) { + /* + * Each bit set in the pin-list refers to an entity_id in this + * Function. Entity 0 is an illegal connection since it is used + * for Function-level configurations. + */ + dev_err(dev, "%s: pin 0 used as input\n", entity->label); + return -EINVAL; + } else if (!pin_list) { + return 0; + } + + num_pins = hweight64(pin_list); + pins = devm_kcalloc(dev, num_pins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + i = 0; + for_each_set_bit(pin, (unsigned long *)&pin_list, BITS_PER_TYPE(pin_list)) { + char pin_property[SDCA_PROPERTY_LENGTH]; + struct fwnode_handle *connected_node; + struct sdca_entity *connected_entity; + const char *connected_label; + + snprintf(pin_property, sizeof(pin_property), "mipi-sdca-input-pin-%d", pin); + + connected_node = fwnode_get_named_child_node(entity_node, pin_property); + if (!connected_node) { + dev_err(dev, "%s: pin node %s not found\n", + entity->label, pin_property); + return -EINVAL; + } + + ret = fwnode_property_read_string(connected_node, "mipi-sdca-entity-label", + &connected_label); + if (ret) { + dev_err(dev, "%s: pin %d label missing: %d\n", + entity->label, pin, ret); + fwnode_handle_put(connected_node); + return ret; + } + + connected_entity = find_sdca_entity_by_label(function, connected_label); + if (!connected_entity) { + dev_err(dev, "%s: failed to find entity with label %s\n", + entity->label, connected_label); + fwnode_handle_put(connected_node); + return -EINVAL; + } + + pins[i] = connected_entity; + + dev_info(dev, "%s -> %s\n", connected_entity->label, entity->label); + + i++; + fwnode_handle_put(connected_node); + } + + entity->num_sources = num_pins; + entity->sources = pins; + + return 0; +} + +static int find_sdca_connections(struct device *dev, + struct fwnode_handle *function_node, + struct sdca_function_data *function) +{ + int i; + + /* Entity 0 cannot have connections */ + for (i = 0; i < function->num_entities - 1; i++) { + struct sdca_entity *entity = &function->entities[i]; + char entity_property[SDCA_PROPERTY_LENGTH]; + struct fwnode_handle *entity_node; + int ret; + + /* DisCo uses upper-case for hex numbers */ + snprintf(entity_property, sizeof(entity_property), + "mipi-sdca-entity-id-0x%X-subproperties", + entity->id); + + entity_node = fwnode_get_named_child_node(function_node, entity_property); + if (!entity_node) { + dev_err(dev, "%pfwP: entity node %s not found\n", + function_node, entity_property); + return -EINVAL; + } + + ret = find_sdca_entity_connection(dev, function, entity_node, entity); + fwnode_handle_put(entity_node); + if (ret) + return ret; + } + + return 0; +} + +static int find_sdca_cluster_channel(struct device *dev, + struct sdca_cluster *cluster, + struct fwnode_handle *channel_node, + struct sdca_channel *channel) +{ + u32 tmp; + int ret; + + ret = fwnode_property_read_u32(channel_node, "mipi-sdca-cluster-channel-id", &tmp); + if (ret) { + dev_err(dev, "cluster %#x: missing channel id: %d\n", + cluster->id, ret); + return ret; + } + + channel->id = tmp; + + ret = fwnode_property_read_u32(channel_node, + "mipi-sdca-cluster-channel-purpose", + &tmp); + if (ret) { + dev_err(dev, "cluster %#x: channel %#x: missing purpose: %d\n", + cluster->id, channel->id, ret); + return ret; + } + + channel->purpose = tmp; + + ret = fwnode_property_read_u32(channel_node, + "mipi-sdca-cluster-channel-relationship", + &tmp); + if (ret) { + dev_err(dev, "cluster %#x: channel %#x: missing relationship: %d\n", + cluster->id, channel->id, ret); + return ret; + } + + channel->relationship = tmp; + + dev_info(dev, "cluster %#x: channel id %#x purpose %#x relationship %#x\n", + cluster->id, channel->id, channel->purpose, channel->relationship); + + return 0; +} + +static int find_sdca_cluster_channels(struct device *dev, + struct fwnode_handle *cluster_node, + struct sdca_cluster *cluster) +{ + struct sdca_channel *channels; + u32 num_channels; + int i, ret; + + ret = fwnode_property_read_u32(cluster_node, "mipi-sdca-channel-count", + &num_channels); + if (ret < 0) { + dev_err(dev, "cluster %#x: failed to read channel list: %d\n", + cluster->id, ret); + return ret; + } else if (num_channels > SDCA_MAX_CHANNEL_COUNT) { + dev_err(dev, "cluster %#x: maximum number of channels exceeded\n", + cluster->id); + return -EINVAL; + } + + channels = devm_kcalloc(dev, num_channels, sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + for (i = 0; i < num_channels; i++) { + char channel_property[SDCA_PROPERTY_LENGTH]; + struct fwnode_handle *channel_node; + + /* DisCo uses upper-case for hex numbers */ + snprintf(channel_property, sizeof(channel_property), + "mipi-sdca-channel-%d-subproperties", i + 1); + + channel_node = fwnode_get_named_child_node(cluster_node, channel_property); + if (!channel_node) { + dev_err(dev, "cluster %#x: channel node %s not found\n", + cluster->id, channel_property); + return -EINVAL; + } + + ret = find_sdca_cluster_channel(dev, cluster, channel_node, &channels[i]); + fwnode_handle_put(channel_node); + if (ret) + return ret; + } + + cluster->num_channels = num_channels; + cluster->channels = channels; + + return 0; +} + +static int find_sdca_clusters(struct device *dev, + struct fwnode_handle *function_node, + struct sdca_function_data *function) +{ + u32 *cluster_list __free(kfree) = NULL; + struct sdca_cluster *clusters; + int num_clusters; + int i, ret; + + num_clusters = fwnode_property_count_u32(function_node, "mipi-sdca-cluster-id-list"); + if (!num_clusters || num_clusters == -EINVAL) { + return 0; + } else if (num_clusters < 0) { + dev_err(dev, "%pfwP: failed to read cluster id list: %d\n", + function_node, num_clusters); + return num_clusters; + } else if (num_clusters > SDCA_MAX_CLUSTER_COUNT) { + dev_err(dev, "%pfwP: maximum number of clusters exceeded\n", function_node); + return -EINVAL; + } + + clusters = devm_kcalloc(dev, num_clusters, sizeof(*clusters), GFP_KERNEL); + if (!clusters) + return -ENOMEM; + + cluster_list = kcalloc(num_clusters, sizeof(*cluster_list), GFP_KERNEL); + if (!cluster_list) + return -ENOMEM; + + fwnode_property_read_u32_array(function_node, "mipi-sdca-cluster-id-list", + cluster_list, num_clusters); + + for (i = 0; i < num_clusters; i++) + clusters[i].id = cluster_list[i]; + + /* now read subproperties */ + for (i = 0; i < num_clusters; i++) { + char cluster_property[SDCA_PROPERTY_LENGTH]; + struct fwnode_handle *cluster_node; + + /* DisCo uses upper-case for hex numbers */ + snprintf(cluster_property, sizeof(cluster_property), + "mipi-sdca-cluster-id-0x%X-subproperties", clusters[i].id); + + cluster_node = fwnode_get_named_child_node(function_node, cluster_property); + if (!cluster_node) { + dev_err(dev, "%pfwP: cluster node %s not found\n", + function_node, cluster_property); + return -EINVAL; + } + + ret = find_sdca_cluster_channels(dev, cluster_node, &clusters[i]); + fwnode_handle_put(cluster_node); + if (ret) + return ret; + } + + function->num_clusters = num_clusters; + function->clusters = clusters; + + return 0; +} + +/** + * sdca_parse_function - parse ACPI DisCo for a Function + * @dev: Pointer to device against which function data will be allocated. + * @function_desc: Pointer to the Function short descriptor. + * @function: Pointer to the Function information, to be populated. + * + * Return: Returns 0 for success. + */ +int sdca_parse_function(struct device *dev, + struct sdca_function_desc *function_desc, + struct sdca_function_data *function) +{ + u32 tmp; + int ret; + + function->desc = function_desc; + + ret = fwnode_property_read_u32(function_desc->node, + "mipi-sdca-function-busy-max-delay", &tmp); + if (!ret) + function->busy_max_delay = tmp; + + dev_info(dev, "%pfwP: name %s delay %dus\n", function->desc->node, + function->desc->name, function->busy_max_delay); + + ret = find_sdca_init_table(dev, function_desc->node, function); + if (ret) + return ret; + + ret = find_sdca_entities(dev, function_desc->node, function); + if (ret) + return ret; + + ret = find_sdca_connections(dev, function_desc->node, function); + if (ret) + return ret; + + ret = find_sdca_clusters(dev, function_desc->node, function); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA"); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SDCA library"); diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c new file mode 100644 index 000000000000..4b78188cfceb --- /dev/null +++ b/sound/soc/sdca/sdca_regmap.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +/* + * The MIPI SDCA specification is available for public downloads at + * https://www.mipi.org/mipi-sdca-v1-0-download + */ + +#include <linux/bitops.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/types.h> +#include <sound/sdca.h> +#include <sound/sdca_function.h> +#include <sound/sdca_regmap.h> + +static struct sdca_entity * +function_find_entity(struct sdca_function_data *function, unsigned int reg) +{ + int i; + + for (i = 0; i < function->num_entities; i++) + if (SDW_SDCA_CTL_ENT(reg) == function->entities[i].id) + return &function->entities[i]; + + return NULL; +} + +static struct sdca_control * +entity_find_control(struct sdca_entity *entity, unsigned int reg) +{ + int i; + + for (i = 0; i < entity->num_controls; i++) { + if (SDW_SDCA_CTL_CSEL(reg) == entity->controls[i].sel) + return &entity->controls[i]; + } + + return NULL; +} + +static struct sdca_control * +function_find_control(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_entity *entity; + + entity = function_find_entity(function, reg); + if (!entity) + return NULL; + + return entity_find_control(entity, reg); +} + +/** + * sdca_regmap_readable - return if a given SDCA Control is readable + * @function: Pointer to the Function information. + * @reg: Register address/Control to be processed. + * + * Return: Returns true if the register is readable. + */ +bool sdca_regmap_readable(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_control *control; + + if (!SDW_SDCA_VALID_CTL(reg)) + return false; + + control = function_find_control(function, reg); + if (!control) + return false; + + switch (control->mode) { + case SDCA_ACCESS_MODE_RW: + case SDCA_ACCESS_MODE_RO: + case SDCA_ACCESS_MODE_DUAL: + case SDCA_ACCESS_MODE_RW1S: + case SDCA_ACCESS_MODE_RW1C: + /* No access to registers marked solely for device use */ + return control->layers & ~SDCA_ACCESS_LAYER_DEVICE; + default: + return false; + } +} +EXPORT_SYMBOL_NS(sdca_regmap_readable, "SND_SOC_SDCA"); + +/** + * sdca_regmap_writeable - return if a given SDCA Control is writeable + * @function: Pointer to the Function information. + * @reg: Register address/Control to be processed. + * + * Return: Returns true if the register is writeable. + */ +bool sdca_regmap_writeable(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_control *control; + + if (!SDW_SDCA_VALID_CTL(reg)) + return false; + + control = function_find_control(function, reg); + if (!control) + return false; + + switch (control->mode) { + case SDCA_ACCESS_MODE_RW: + case SDCA_ACCESS_MODE_DUAL: + case SDCA_ACCESS_MODE_RW1S: + case SDCA_ACCESS_MODE_RW1C: + /* No access to registers marked solely for device use */ + return control->layers & ~SDCA_ACCESS_LAYER_DEVICE; + default: + return false; + } +} +EXPORT_SYMBOL_NS(sdca_regmap_writeable, "SND_SOC_SDCA"); + +/** + * sdca_regmap_volatile - return if a given SDCA Control is volatile + * @function: Pointer to the Function information. + * @reg: Register address/Control to be processed. + * + * Return: Returns true if the register is volatile. + */ +bool sdca_regmap_volatile(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_control *control; + + if (!SDW_SDCA_VALID_CTL(reg)) + return false; + + control = function_find_control(function, reg); + if (!control) + return false; + + switch (control->mode) { + case SDCA_ACCESS_MODE_RO: + case SDCA_ACCESS_MODE_RW1S: + case SDCA_ACCESS_MODE_RW1C: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_NS(sdca_regmap_volatile, "SND_SOC_SDCA"); + +/** + * sdca_regmap_deferrable - return if a given SDCA Control is deferrable + * @function: Pointer to the Function information. + * @reg: Register address/Control to be processed. + * + * Return: Returns true if the register is deferrable. + */ +bool sdca_regmap_deferrable(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_control *control; + + if (!SDW_SDCA_VALID_CTL(reg)) + return false; + + control = function_find_control(function, reg); + if (!control) + return false; + + return control->deferrable; +} +EXPORT_SYMBOL_NS(sdca_regmap_deferrable, "SND_SOC_SDCA"); + +/** + * sdca_regmap_mbq_size - return size in bytes of a given SDCA Control + * @function: Pointer to the Function information. + * @reg: Register address/Control to be processed. + * + * Return: Returns the size in bytes of the Control. + */ +int sdca_regmap_mbq_size(struct sdca_function_data *function, unsigned int reg) +{ + struct sdca_control *control; + + if (!SDW_SDCA_VALID_CTL(reg)) + return -EINVAL; + + control = function_find_control(function, reg); + if (!control) + return false; + + return clamp_val(control->nbits / BITS_PER_BYTE, sizeof(u8), sizeof(u32)); +} +EXPORT_SYMBOL_NS(sdca_regmap_mbq_size, "SND_SOC_SDCA"); + +/** + * sdca_regmap_count_constants - count the number of DisCo constant Controls + * @dev: Pointer to the device. + * @function: Pointer to the Function information, to be parsed. + * + * This function returns the number of DisCo constant Controls present + * in a function. Typically this information will be used to populate + * the regmap defaults array, allowing drivers to access the values of + * DisCo constants as any other physical register. + * + * Return: Returns number of DisCo constant controls, or a negative error + * code on failure. + */ +int sdca_regmap_count_constants(struct device *dev, + struct sdca_function_data *function) +{ + int nconsts = 0; + int i, j; + + for (i = 0; i < function->num_entities; i++) { + struct sdca_entity *entity = &function->entities[i]; + + for (j = 0; j < entity->num_controls; j++) { + if (entity->controls[j].mode == SDCA_ACCESS_MODE_DC) + nconsts += hweight64(entity->controls[j].cn_list); + } + } + + return nconsts; +} +EXPORT_SYMBOL_NS(sdca_regmap_count_constants, "SND_SOC_SDCA"); + +/** + * sdca_regmap_populate_constants - fill an array with DisCo constant values + * @dev: Pointer to the device. + * @function: Pointer to the Function information, to be parsed. + * @consts: Pointer to the array which should be filled with the DisCo + * constant values. + * + * This function will populate a regmap struct reg_default array with + * the values of the DisCo constants for a given Function. This + * allows to access the values of DisCo constants the same as any + * other physical register. + * + * Return: Returns the number of constants populated on success, a negative + * error code on failure. + */ +int sdca_regmap_populate_constants(struct device *dev, + struct sdca_function_data *function, + struct reg_default *consts) +{ + int i, j, k; + + for (i = 0, k = 0; i < function->num_entities; i++) { + struct sdca_entity *entity = &function->entities[i]; + + for (j = 0; j < entity->num_controls; j++) { + struct sdca_control *control = &entity->controls[j]; + int cn; + + if (control->mode != SDCA_ACCESS_MODE_DC) + continue; + + for_each_set_bit(cn, (unsigned long *)&control->cn_list, + BITS_PER_TYPE(control->cn_list)) { + consts[k].reg = SDW_SDCA_CTL(function->desc->adr, + entity->id, + control->sel, cn); + consts[k].def = control->value; + k++; + } + } + } + + return k; +} +EXPORT_SYMBOL_NS(sdca_regmap_populate_constants, "SND_SOC_SDCA"); + +/** + * sdca_regmap_write_defaults - write out DisCo defaults to device + * @dev: Pointer to the device. + * @regmap: Pointer to the Function register map. + * @function: Pointer to the Function information, to be parsed. + * + * This function will write out to the hardware all the DisCo default and + * fixed value controls. This will cause them to be populated into the cache, + * and subsequent handling can be done through a cache sync. + * + * Return: Returns zero on success, and a negative error code on failure. + */ +int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap, + struct sdca_function_data *function) +{ + int i, j; + int ret; + + for (i = 0; i < function->num_entities; i++) { + struct sdca_entity *entity = &function->entities[i]; + + for (j = 0; j < entity->num_controls; j++) { + struct sdca_control *control = &entity->controls[j]; + int cn; + + if (control->mode == SDCA_ACCESS_MODE_DC) + continue; + + if (!control->has_default && !control->has_fixed) + continue; + + for_each_set_bit(cn, (unsigned long *)&control->cn_list, + BITS_PER_TYPE(control->cn_list)) { + unsigned int reg; + + reg = SDW_SDCA_CTL(function->desc->adr, entity->id, + control->sel, cn); + + ret = regmap_write(regmap, reg, control->value); + if (ret) + return ret; + } + } + } + + return 0; +} +EXPORT_SYMBOL_NS(sdca_regmap_write_defaults, "SND_SOC_SDCA"); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SDCA library"); diff --git a/sound/soc/sdw_utils/soc_sdw_cs_amp.c b/sound/soc/sdw_utils/soc_sdw_cs_amp.c index a0bb626c5cb8..4b6181cf2971 100644 --- a/sound/soc/sdw_utils/soc_sdw_cs_amp.c +++ b/sound/soc/sdw_utils/soc_sdw_cs_amp.c @@ -15,6 +15,7 @@ #include <sound/soc_sdw_utils.h> #define CODEC_NAME_SIZE 8 +#define CS_AMP_CHANNELS_PER_AMP 4 int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { @@ -48,6 +49,51 @@ int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai } EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_rtd_init, "SND_SOC_SDW_UTILS"); +int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) +{ + const struct snd_soc_dai_link *dai_link = rtd->dai_link; + const struct snd_soc_dai_link_ch_map *ch_map; + const struct snd_soc_dai_link_component *codec_dlc; + struct snd_soc_dai *codec_dai; + u8 ch_slot[8] = {}; + unsigned int amps_per_bus, ch_per_amp, mask; + int i, ret; + + WARN_ON(dai_link->num_cpus > ARRAY_SIZE(ch_slot)); + + /* + * CS35L56 has 4 TX channels. When the capture is aggregated the + * same bus slots will be allocated to all the amps on a bus. Only + * one amp on that bus can be transmitting in each slot so divide + * the available 4 slots between all the amps on a bus. + */ + amps_per_bus = dai_link->num_codecs / dai_link->num_cpus; + if ((amps_per_bus == 0) || (amps_per_bus > CS_AMP_CHANNELS_PER_AMP)) { + dev_err(rtd->card->dev, "Illegal num_codecs:%u / num_cpus:%u\n", + dai_link->num_codecs, dai_link->num_cpus); + return -EINVAL; + } + + ch_per_amp = CS_AMP_CHANNELS_PER_AMP / amps_per_bus; + + for_each_rtd_ch_maps(rtd, i, ch_map) { + codec_dlc = snd_soc_link_to_codec(rtd->dai_link, i); + codec_dai = snd_soc_find_dai(codec_dlc); + mask = GENMASK(ch_per_amp - 1, 0) << ch_slot[ch_map->cpu]; + + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, mask, 4, 32); + if (ret < 0) { + dev_err(rtd->card->dev, "Failed to set TDM slot:%d\n", ret); + return ret; + } + + ch_slot[ch_map->cpu] += ch_per_amp; + } + + return 0; +} +EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_feedback_rtd_init, "SND_SOC_SDW_UTILS"); + int asoc_sdw_cs_amp_init(struct snd_soc_card *card, struct snd_soc_dai_link *dai_links, struct asoc_sdw_codec_info *info, diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 937fa3ce59df..5175818ff2c1 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -488,10 +488,10 @@ struct asoc_sdw_codec_info codec_info_list[] = { .part_id = 0x3556, .dais = { { - .direction = {true, true}, + .direction = {true, false}, .dai_name = "cs35l56-sdw1", .dai_type = SOC_SDW_DAI_TYPE_AMP, - .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, + .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, .init = asoc_sdw_cs_amp_init, .rtd_init = asoc_sdw_cs_spk_rtd_init, .controls = generic_spk_controls, @@ -499,8 +499,15 @@ struct asoc_sdw_codec_info codec_info_list[] = { .widgets = generic_spk_widgets, .num_widgets = ARRAY_SIZE(generic_spk_widgets), }, + { + .direction = {false, true}, + .dai_name = "cs35l56-sdw1c", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, + .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, + }, }, - .dai_num = 1, + .dai_num = 2, }, { .part_id = 0x4242, @@ -579,8 +586,20 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_type = SOC_SDW_DAI_TYPE_JACK, .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, }, + { + .direction = {true, false}, + .dai_name = "sdw-mockup-aif1", + .dai_type = SOC_SDW_DAI_TYPE_AMP, + .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + }, + { + .direction = {false, true}, + .dai_name = "sdw-mockup-aif1", + .dai_type = SOC_SDW_DAI_TYPE_MIC, + .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, + }, }, - .dai_num = 1, + .dai_num = 3, }, { .part_id = 0xaa55, /* headset codec mockup */ diff --git a/sound/soc/soc-card.c b/sound/soc/soc-card.c index 8e9546fe7428..235427d69061 100644 --- a/sound/soc/soc-card.c +++ b/sound/soc/soc-card.c @@ -15,18 +15,8 @@ static inline int _soc_card_ret(struct snd_soc_card *card, const char *func, int ret) { - switch (ret) { - case -EPROBE_DEFER: - case -ENOTSUPP: - case 0: - break; - default: - dev_err(card->dev, - "ASoC: error at %s on %s: %d\n", - func, card->name, ret); - } - - return ret; + return snd_soc_ret(card->dev, ret, + "at %s() on %s\n", func, card->name); } struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, @@ -219,7 +209,7 @@ int snd_soc_card_set_bias_level(struct snd_soc_card *card, { int ret = 0; - if (card && card->set_bias_level) + if (card->set_bias_level) ret = card->set_bias_level(card, dapm, level); return soc_card_ret(card, ret); @@ -231,7 +221,7 @@ int snd_soc_card_set_bias_level_post(struct snd_soc_card *card, { int ret = 0; - if (card && card->set_bias_level_post) + if (card->set_bias_level_post) ret = card->set_bias_level_post(card, dapm, level); return soc_card_ret(card, ret); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index b67ef78f405c..25f5e543ae8d 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -13,32 +13,20 @@ #include <sound/soc.h> #include <linux/bitops.h> -#define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret, -1) -#define soc_component_ret_reg_rw(dai, ret, reg) _soc_component_ret(dai, __func__, ret, reg) -static inline int _soc_component_ret(struct snd_soc_component *component, - const char *func, int ret, int reg) -{ - /* Positive/Zero values are not errors */ - if (ret >= 0) - return ret; - - /* Negative values might be errors */ - switch (ret) { - case -EPROBE_DEFER: - case -ENOTSUPP: - break; - default: - if (reg == -1) - dev_err(component->dev, - "ASoC: error at %s on %s: %d\n", - func, component->name, ret); - else - dev_err(component->dev, - "ASoC: error at %s on %s for register: [0x%08x] %d\n", - func, component->name, reg, ret); - } +#define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret) +static inline int _soc_component_ret(struct snd_soc_component *component, const char *func, int ret) +{ + return snd_soc_ret(component->dev, ret, + "at %s() on %s\n", func, component->name); +} - return ret; +#define soc_component_ret_reg_rw(dai, ret, reg) _soc_component_ret_reg_rw(dai, __func__, ret, reg) +static inline int _soc_component_ret_reg_rw(struct snd_soc_component *component, + const char *func, int ret, int reg) +{ + return snd_soc_ret(component->dev, ret, + "at %s() on %s for register: [0x%08x]\n", + func, component->name, reg); } static inline int soc_component_field_shift(struct snd_soc_component *component, diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 563dc0767c17..01d1d6bee28c 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -148,7 +148,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) snd_soc_dpcm_mutex_lock(fe); /* calculate valid and active FE <-> BE dpcms */ - dpcm_process_paths(fe, stream, &list, 1); + dpcm_add_paths(fe, stream, &list); fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a1dace4bb616..3f97d1f132c6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -32,6 +32,7 @@ #include <linux/of_graph.h> #include <linux/dmi.h> #include <linux/acpi.h> +#include <linux/string_choices.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -430,7 +431,7 @@ void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd) codec_dai->driver->playback.stream_name, snd_soc_dai_stream_active(codec_dai, playback) ? "active" : "inactive", - rtd->pop_wait ? "yes" : "no"); + str_yes_no(rtd->pop_wait)); /* are we waiting on this codec DAI stream */ if (rtd->pop_wait == 1) { @@ -1449,23 +1450,46 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, { struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; + unsigned int ext_fmt; unsigned int i; int ret; if (!dai_fmt) return 0; + /* + * dai_fmt has 4 types + * 1. SND_SOC_DAIFMT_FORMAT_MASK + * 2. SND_SOC_DAIFMT_CLOCK + * 3. SND_SOC_DAIFMT_INV + * 4. SND_SOC_DAIFMT_CLOCK_PROVIDER + * + * 4. CLOCK_PROVIDER is set from Codec perspective in dai_fmt. So it will be flipped + * when this function calls set_fmt() for CPU (CBx_CFx -> Bx_Cx). see below. + * This mean, we can't set CPU/Codec both are clock consumer for example. + * New idea handles 4. in each dai->ext_fmt. It can keep compatibility. + * + * Legacy + * dai_fmt includes 1, 2, 3, 4 + * + * New idea + * dai_fmt includes 1, 2, 3 + * ext_fmt includes 4 + */ for_each_rtd_codec_dais(rtd, i, codec_dai) { - ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt); + ext_fmt = rtd->dai_link->codecs[i].ext_fmt; + ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt | ext_fmt); if (ret != 0 && ret != -ENOTSUPP) return ret; } /* Flip the polarity for the "CPU" end of link */ + /* Will effect only for 4. SND_SOC_DAIFMT_CLOCK_PROVIDER */ dai_fmt = snd_soc_daifmt_clock_provider_flipped(dai_fmt); for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); + ext_fmt = rtd->dai_link->cpus[i].ext_fmt; + ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt | ext_fmt); if (ret != 0 && ret != -ENOTSUPP) return ret; } @@ -1644,18 +1668,8 @@ static int soc_probe_component(struct snd_soc_card *card, ret = snd_soc_dapm_add_routes(dapm, component->driver->dapm_routes, component->driver->num_dapm_routes); - if (ret < 0) { - if (card->disable_route_checks) { - dev_info(card->dev, - "%s: disable_route_checks set, ignoring errors on add_routes\n", - __func__); - } else { - dev_err(card->dev, - "%s: snd_soc_dapm_add_routes failed: %d\n", - __func__, ret); - goto err_probe; - } - } + if (ret < 0) + goto err_probe; /* see for_each_card_components */ list_add(&component->card_list, &card->component_dev_list); @@ -1874,7 +1888,6 @@ static void append_dmi_string(struct snd_soc_card *card, const char *str) /** * snd_soc_set_dmi_name() - Register DMI names to card * @card: The card to register DMI names - * @flavour: The flavour "differentiator" for the card amongst its peers. * * An Intel machine driver may be used by many different devices but are * difficult for userspace to differentiate, since machine drivers usually @@ -1902,7 +1915,7 @@ static void append_dmi_string(struct snd_soc_card *card, const char *str) * * Returns 0 on success, otherwise a negative error code. */ -int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) +static int snd_soc_set_dmi_name(struct snd_soc_card *card) { const char *vendor, *product, *board; @@ -1946,16 +1959,16 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) return 0; } - /* Add flavour to dmi long name */ - if (flavour) - append_dmi_string(card, flavour); - /* set the card long name */ card->long_name = card->dmi_longname; return 0; } -EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); +#else +static inline int snd_soc_set_dmi_name(struct snd_soc_card *card) +{ + return 0; +} #endif /* CONFIG_DMI */ static void soc_check_tplg_fes(struct snd_soc_card *card) @@ -2234,18 +2247,8 @@ static int snd_soc_bind_card(struct snd_soc_card *card) ret = snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); - if (ret < 0) { - if (card->disable_route_checks) { - dev_info(card->dev, - "%s: disable_route_checks set, ignoring errors on add_routes\n", - __func__); - } else { - dev_err(card->dev, - "%s: snd_soc_dapm_add_routes failed: %d\n", - __func__, ret); - goto probe_end; - } - } + if (ret < 0) + goto probe_end; ret = snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, card->num_of_dapm_routes); @@ -2253,7 +2256,7 @@ static int snd_soc_bind_card(struct snd_soc_card *card) goto probe_end; /* try to set some sane longname if DMI is available */ - snd_soc_set_dmi_name(card, NULL); + snd_soc_set_dmi_name(card); soc_setup_card_name(card, card->snd_card->shortname, card->name, NULL); @@ -3043,7 +3046,7 @@ int snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop) unsigned int i, nb_controls; int ret; - if (!of_property_read_bool(dev->of_node, prop)) + if (!of_property_present(dev->of_node, prop)) return 0; strings = devm_kcalloc(dev, nb_controls_max, @@ -3117,23 +3120,17 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np, if (rx_mask) snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask); - if (of_property_read_bool(np, "dai-tdm-slot-num")) { - ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); - if (ret) - return ret; - - if (slots) - *slots = val; - } - - if (of_property_read_bool(np, "dai-tdm-slot-width")) { - ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); - if (ret) - return ret; + ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); + if (ret && ret != -EINVAL) + return ret; + if (!ret && slots) + *slots = val; - if (slot_width) - *slot_width = val; - } + ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); + if (ret && ret != -EINVAL) + return ret; + if (!ret && slot_width) + *slot_width = val; return 0; } @@ -3389,6 +3386,9 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np, char prop[128]; unsigned int bit, frame; + if (!np) + return 0; + if (!prefix) prefix = ""; @@ -3397,12 +3397,12 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np, * check "[prefix]frame-master" */ snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); - bit = of_property_read_bool(np, prop); + bit = of_property_present(np, prop); if (bit && bitclkmaster) *bitclkmaster = of_parse_phandle(np, prop, 0); snprintf(prop, sizeof(prop), "%sframe-master", prefix); - frame = of_property_read_bool(np, prop); + frame = of_property_present(np, prop); if (frame && framemaster) *framemaster = of_parse_phandle(np, prop, 0); diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 34ba1a93a4c9..a210089747d0 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -14,22 +14,8 @@ static inline int _soc_dai_ret(const struct snd_soc_dai *dai, const char *func, int ret) { - /* Positive, Zero values are not errors */ - if (ret >= 0) - return ret; - - /* Negative values might be errors */ - switch (ret) { - case -EPROBE_DEFER: - case -ENOTSUPP: - break; - default: - dev_err(dai->dev, - "ASoC: error at %s on %s: %d\n", - func, dai->name, ret); - } - - return ret; + return snd_soc_ret(dai->dev, ret, + "at %s() on %s\n", func, dai->name); } /* @@ -275,10 +261,11 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, if (dai->driver->ops && dai->driver->ops->xlate_tdm_slot_mask) - dai->driver->ops->xlate_tdm_slot_mask(slots, - &tx_mask, &rx_mask); + ret = dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); else - snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); + ret = snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); + if (ret) + goto err; for_each_pcm_streams(stream) snd_soc_dai_tdm_mask_set(dai, stream, *tdm_mask[stream]); @@ -287,6 +274,7 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, dai->driver->ops->set_tdm_slot) ret = dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, slots, slot_width); +err: return soc_dai_ret(dai, ret); } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); @@ -360,6 +348,30 @@ int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); +int snd_soc_dai_prepare(struct snd_soc_dai *dai, + struct snd_pcm_substream *substream) +{ + int ret = 0; + + if (!snd_soc_dai_stream_valid(dai, substream->stream)) + return 0; + + if (dai->driver->ops && + dai->driver->ops->prepare) + ret = dai->driver->ops->prepare(substream, dai); + + return soc_dai_ret(dai, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_dai_prepare); + +int snd_soc_dai_mute_is_ctrled_at_trigger(struct snd_soc_dai *dai) +{ + if (dai->driver->ops) + return dai->driver->ops->mute_unmute_on_trigger; + + return 0; +} + /** * snd_soc_dai_digital_mute - configure DAI system or master clock. * @dai: DAI @@ -577,14 +589,9 @@ int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream) int i, ret; for_each_rtd_dais(rtd, i, dai) { - if (!snd_soc_dai_stream_valid(dai, substream->stream)) - continue; - if (dai->driver->ops && - dai->driver->ops->prepare) { - ret = dai->driver->ops->prepare(substream, dai); - if (ret < 0) - return soc_dai_ret(dai, ret); - } + ret = snd_soc_dai_prepare(dai, substream); + if (ret < 0) + return ret; } return 0; @@ -621,7 +628,7 @@ int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, if (ret < 0) break; - if (dai->driver->ops && dai->driver->ops->mute_unmute_on_trigger) + if (snd_soc_dai_mute_is_ctrled_at_trigger(dai)) snd_soc_dai_digital_mute(dai, 0, substream->stream); soc_dai_mark_push(dai, substream, trigger); @@ -634,7 +641,7 @@ int snd_soc_pcm_dai_trigger(struct snd_pcm_substream *substream, if (rollback && !soc_dai_mark_match(dai, substream, trigger)) continue; - if (dai->driver->ops && dai->driver->ops->mute_unmute_on_trigger) + if (snd_soc_dai_mute_is_ctrled_at_trigger(dai)) snd_soc_dai_digital_mute(dai, 1, substream->stream); r = soc_dai_trigger(dai, substream, cmd); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 99521c784a9b..b7818388984e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -317,7 +317,6 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card) snd_soc_dapm_mutex_unlock(card); } -EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty); /* create a new dapm widget */ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( @@ -730,7 +729,7 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, if (ret != 0) goto out; - if (!card || dapm != &card->dapm) + if (dapm != &card->dapm) ret = snd_soc_dapm_force_bias_level(dapm, level); if (ret != 0) @@ -1744,9 +1743,8 @@ static void dapm_seq_run(struct snd_soc_card *card, soc_dapm_async_complete(d); } -static void dapm_widget_update(struct snd_soc_card *card) +static void dapm_widget_update(struct snd_soc_card *card, struct snd_soc_dapm_update *update) { - struct snd_soc_dapm_update *update = card->update; struct snd_soc_dapm_widget_list *wlist; struct snd_soc_dapm_widget *w = NULL; unsigned int wi; @@ -1952,7 +1950,8 @@ static bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm) * o Input pin to Output pin (bypass, sidetone) * o DAC to ADC (loopback). */ -static int dapm_power_widgets(struct snd_soc_card *card, int event) +static int dapm_power_widgets(struct snd_soc_card *card, int event, + struct snd_soc_dapm_update *update) { struct snd_soc_dapm_widget *w; struct snd_soc_dapm_context *d; @@ -2060,7 +2059,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) /* Power down widgets first; try to avoid amplifying pops. */ dapm_seq_run(card, &down_list, event, false); - dapm_widget_update(card); + dapm_widget_update(card, update); /* Now power up. */ dapm_seq_run(card, &up_list, event, true); @@ -2333,7 +2332,9 @@ static void soc_dapm_connect_path(struct snd_soc_dapm_path *path, /* test and update the power status of a mux widget */ static int soc_dapm_mux_update_power(struct snd_soc_card *card, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) + struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_update *update, + int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; int found = 0; @@ -2354,7 +2355,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card, } if (found) - dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP, update); return found; } @@ -2367,9 +2368,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, int ret; snd_soc_dapm_mutex_lock(card); - card->update = update; - ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); - card->update = NULL; + ret = soc_dapm_mux_update_power(card, kcontrol, update, mux, e); snd_soc_dapm_mutex_unlock(card); if (ret > 0) snd_soc_dpcm_runtime_update(card); @@ -2380,6 +2379,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); /* test and update the power status of a mixer or switch widget */ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_update *update, int connect, int rconnect) { struct snd_soc_dapm_path *path; @@ -2419,7 +2419,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, } if (found) - dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP, update); return found; } @@ -2432,9 +2432,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, int ret; snd_soc_dapm_mutex_lock(card); - card->update = update; - ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1); - card->update = NULL; + ret = soc_dapm_mixer_update_power(card, kcontrol, update, connect, -1); snd_soc_dapm_mutex_unlock(card); if (ret > 0) snd_soc_dpcm_runtime_update(card); @@ -2690,7 +2688,7 @@ int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm) if (!snd_soc_card_is_instantiated(dapm->card)) return 0; - return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); + return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP, NULL); } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_unlocked); @@ -2783,7 +2781,6 @@ int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream, return ret; } -EXPORT_SYMBOL_GPL(snd_soc_dapm_update_dai); int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char *s) { @@ -3360,7 +3357,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) dapm_debugfs_add_widget(w); } - dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP, NULL); snd_soc_dapm_mutex_unlock(card); return 0; } @@ -3449,6 +3446,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, unsigned int val, rval = 0; int connect, rconnect = -1, change, reg_change = 0; struct snd_soc_dapm_update update = {}; + struct snd_soc_dapm_update *pupdate = NULL; int ret = 0; val = (ucontrol->value.integer.value[0] & mask); @@ -3497,13 +3495,9 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, update.reg = reg; update.mask = mask << shift; update.val = val; - card->update = &update; + pupdate = &update; } - - ret = soc_dapm_mixer_update_power(card, kcontrol, connect, - rconnect); - - card->update = NULL; + ret = soc_dapm_mixer_update_power(card, kcontrol, pupdate, connect, rconnect); } snd_soc_dapm_mutex_unlock(card); @@ -3570,6 +3564,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, unsigned int val, change, reg_change = 0; unsigned int mask; struct snd_soc_dapm_update update = {}; + struct snd_soc_dapm_update *pupdate = NULL; int ret = 0; if (item[0] >= e->items) @@ -3597,12 +3592,9 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.reg = e->reg; update.mask = mask; update.val = val; - card->update = &update; + pupdate = &update; } - - ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); - - card->update = NULL; + ret = soc_dapm_mux_update_power(card, kcontrol, pupdate, item[0], e); } snd_soc_dapm_mutex_unlock(card); @@ -4013,6 +4005,18 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMU: + snd_soc_dapm_widget_for_each_source_path(w, path) { + source = path->source->priv; + + snd_soc_dai_prepare(source, substream); + } + + snd_soc_dapm_widget_for_each_sink_path(w, path) { + sink = path->sink->priv; + + snd_soc_dai_prepare(sink, substream); + } + snd_soc_dapm_widget_for_each_sink_path(w, path) { sink = path->sink->priv; @@ -4516,7 +4520,7 @@ static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, for_each_rtd_dais(rtd, i, dai) soc_dapm_dai_stream_event(dai, stream, event); - dapm_power_widgets(rtd->card, event); + dapm_power_widgets(rtd->card, event, NULL); } /** @@ -4853,7 +4857,6 @@ void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, /* see for_each_card_dapms */ list_add(&dapm->list, &card->dapm_list); } -EXPORT_SYMBOL_GPL(snd_soc_dapm_init); static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm) { diff --git a/sound/soc/soc-link.c b/sound/soc/soc-link.c index 7f1f1bc717e2..02fd68f2e702 100644 --- a/sound/soc/soc-link.c +++ b/sound/soc/soc-link.c @@ -12,22 +12,8 @@ static inline int _soc_link_ret(struct snd_soc_pcm_runtime *rtd, const char *func, int ret) { - /* Positive, Zero values are not errors */ - if (ret >= 0) - return ret; - - /* Negative values might be errors */ - switch (ret) { - case -EPROBE_DEFER: - case -ENOTSUPP: - break; - default: - dev_err(rtd->dev, - "ASoC: error at %s on %s: %d\n", - func, rtd->dai_link->name, ret); - } - - return ret; + return snd_soc_ret(rtd->dev, ret, + "at %s() on %s\n", func, rtd->dai_link->name); } /* diff --git a/sound/soc/soc-ops-test.c b/sound/soc/soc-ops-test.c new file mode 100644 index 000000000000..dc1e482bba6a --- /dev/null +++ b/sound/soc/soc-ops-test.c @@ -0,0 +1,541 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2025 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <kunit/device.h> +#include <kunit/test.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/string.h> +#include <sound/asound.h> +#include <sound/control.h> +#include <sound/soc.h> +#include <sound/soc-component.h> + +enum soc_ops_test_control_layout { + SOC_OPS_TEST_SINGLE, + SOC_OPS_TEST_DOUBLE, + SOC_OPS_TEST_DOUBLE_R, +}; + +#define TEST_MC(clayout, xmin, xmax, xpmax, xsign, xinvert) \ + .mc = { \ + .min = xmin, .max = xmax, .platform_max = xpmax, \ + .reg = 0, .shift = 0, .sign_bit = xsign, .invert = xinvert, \ + .rreg = SOC_OPS_TEST_##clayout == SOC_OPS_TEST_DOUBLE_R ? 1 : 0, \ + .rshift = SOC_OPS_TEST_##clayout == SOC_OPS_TEST_DOUBLE ? 16 : 0, \ + } + +#define TEST_UINFO(clayout, ctype, cmin, cmax) \ + .uinfo = { \ + .type = SNDRV_CTL_ELEM_TYPE_##ctype, \ + .count = SOC_OPS_TEST_##clayout == SOC_OPS_TEST_SINGLE ? 1 : 2, \ + .value.integer.min = cmin, \ + .value.integer.max = cmax, \ + } + +#define ITEST(cname, clayout, ctype, cfunc, cmin, cmax, \ + xmin, xmax, xpmax, xsign, xinvert) \ + { \ + .name = cname, \ + .func_name = #cfunc, \ + .layout = SOC_OPS_TEST_##clayout, \ + .info = snd_soc_info_##cfunc, \ + TEST_MC(clayout, xmin, xmax, xpmax, xsign, xinvert), \ + TEST_UINFO(clayout, ctype, cmin, cmax), \ + } + +#define ATEST(clayout, cfunc, cctl, cret, cinit, \ + xmask, xreg, xmin, xmax, xpmax, xsign, xinvert) \ + { \ + .func_name = #cfunc, \ + .layout = SOC_OPS_TEST_##clayout, \ + .put = snd_soc_put_##cfunc, \ + .get = snd_soc_get_##cfunc, \ + TEST_MC(clayout, xmin, xmax, xpmax, xsign, xinvert), \ + .lctl = cctl, .rctl = cctl, \ + .lmask = SOC_OPS_TEST_##clayout == SOC_OPS_TEST_DOUBLE ? \ + (xmask) | (xmask) << 16 : (xmask), \ + .rmask = SOC_OPS_TEST_##clayout == SOC_OPS_TEST_DOUBLE_R ? (xmask) : 0, \ + .init = cinit ? 0xFFFFFFFF : 0x00000000, \ + .lreg = SOC_OPS_TEST_##clayout == SOC_OPS_TEST_DOUBLE ? \ + (xreg) | (xreg) << 16 : (xreg), \ + .rreg = SOC_OPS_TEST_##clayout == SOC_OPS_TEST_DOUBLE_R ? (xreg) : 0, \ + .ret = cret, \ + } + +struct soc_ops_test_priv { + struct kunit *test; + + struct snd_soc_component component; +}; + +struct info_test_param { + const char * const name; + const char * const func_name; + enum soc_ops_test_control_layout layout; + struct soc_mixer_control mc; + int (*info)(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *info); + + struct snd_ctl_elem_info uinfo; +}; + +struct access_test_param { + const char * const func_name; + enum soc_ops_test_control_layout layout; + struct soc_mixer_control mc; + int (*put)(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *value); + int (*get)(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *value); + + unsigned int init; + unsigned int lmask; + unsigned int rmask; + unsigned int lreg; + unsigned int rreg; + long lctl; + long rctl; + int ret; +}; + +static const struct info_test_param all_info_test_params[] = { + // Handling of volume control name for types + ITEST("Test Control", SINGLE, BOOLEAN, volsw, 0, 1, 0, 1, 0, 0, 0), + ITEST("Test Volume", SINGLE, INTEGER, volsw, 0, 1, 0, 1, 0, 0, 0), + ITEST("Test Volume Control", SINGLE, BOOLEAN, volsw, 0, 1, 0, 1, 0, 0, 0), + ITEST("Test Control", DOUBLE_R, BOOLEAN, volsw, 0, 1, 0, 1, 0, 0, 0), + ITEST("Test Volume", DOUBLE_R, INTEGER, volsw, 0, 1, 0, 1, 0, 0, 0), + ITEST("Test Volume Control", DOUBLE_R, BOOLEAN, volsw, 0, 1, 0, 1, 0, 0, 0), + ITEST("Test Control", DOUBLE, BOOLEAN, volsw, 0, 1, 0, 1, 0, 0, 0), + ITEST("Test Volume", DOUBLE, INTEGER, volsw, 0, 1, 0, 1, 0, 0, 0), + ITEST("Test Volume Control", DOUBLE, BOOLEAN, volsw, 0, 1, 0, 1, 0, 0, 0), + ITEST("Test Control", SINGLE, BOOLEAN, volsw, 0, 1, 0, 1, 0, 0, 1), + ITEST("Test Volume", SINGLE, INTEGER, volsw, 0, 1, 0, 1, 0, 0, 1), + ITEST("Test Volume Control", SINGLE, BOOLEAN, volsw, 0, 1, 0, 1, 0, 0, 1), + ITEST("Test Control", DOUBLE, BOOLEAN, volsw, 0, 1, 0, 1, 0, 0, 1), + ITEST("Test Volume", DOUBLE, INTEGER, volsw, 0, 1, 0, 1, 0, 0, 1), + ITEST("Test Volume Control", DOUBLE, BOOLEAN, volsw, 0, 1, 0, 1, 0, 0, 1), + ITEST("Test Control", SINGLE, INTEGER, volsw, 0, 2, 0, 2, 0, 0, 0), + ITEST("Test Volume", SINGLE, INTEGER, volsw, 0, 2, 0, 2, 0, 0, 0), + ITEST("Test Volume Control", SINGLE, INTEGER, volsw, 0, 2, 0, 2, 0, 0, 0), + ITEST("Test Control", SINGLE, INTEGER, volsw, 0, 1, 0, 2, 1, 0, 0), + ITEST("Test Volume", SINGLE, INTEGER, volsw, 0, 1, 0, 2, 1, 0, 0), + ITEST("Test Volume Control", SINGLE, INTEGER, volsw, 0, 1, 0, 2, 1, 0, 0), + // Negative minimums + ITEST("Test Control", SINGLE, INTEGER, volsw, 0, 20, -10, 10, 0, 4, 0), + ITEST("Test Control", SINGLE, INTEGER, volsw, 0, 15, -10, 10, 15, 4, 0), + ITEST("Test Control", SINGLE, INTEGER, volsw, 0, 20, -10, 10, 0, 4, 1), + ITEST("Test Control", SINGLE, INTEGER, volsw, 0, 15, -10, 10, 15, 4, 1), + // SX control volume control naming + ITEST("Test Control", SINGLE, BOOLEAN, volsw_sx, 0, 1, 0xF, 1, 0, 0, 0), + ITEST("Test Volume", SINGLE, INTEGER, volsw_sx, 0, 1, 0xF, 1, 0, 0, 0), + ITEST("Test Volume Control", SINGLE, BOOLEAN, volsw_sx, 0, 1, 0xF, 1, 0, 0, 0), + ITEST("Test Control", SINGLE, INTEGER, volsw_sx, 0, 4, 0xE, 4, 0, 0, 0), + ITEST("Test Volume", SINGLE, INTEGER, volsw_sx, 0, 4, 0xE, 4, 0, 0, 0), + ITEST("Test Volume Control", SINGLE, INTEGER, volsw_sx, 0, 4, 0xE, 4, 0, 0, 0), + ITEST("Test Control", SINGLE, INTEGER, volsw_sx, 0, 3, 0xE, 4, 3, 0, 0), + ITEST("Test Volume", SINGLE, INTEGER, volsw_sx, 0, 3, 0xE, 4, 3, 0, 0), + ITEST("Test Volume Control", SINGLE, INTEGER, volsw_sx, 0, 3, 0xE, 4, 3, 0, 0), +}; + +static const struct access_test_param all_access_test_params[] = { + // Single positive value controls + ATEST(SINGLE, volsw, 10, 1, false, 0x1F, 0x0A, 0, 20, 0, 0, 0), + ATEST(SINGLE, volsw, 0, 0, false, 0x1F, 0x00, 0, 20, 0, 0, 0), + ATEST(SINGLE, volsw, 20, 1, false, 0x1F, 0x14, 0, 20, 0, 0, 0), + ATEST(SINGLE, volsw, 10, 1, false, 0x1F, 0x0A, 0, 20, 15, 0, 0), + ATEST(SINGLE, volsw, 25, -22, false, 0x1F, 0x00, 0, 20, 15, 0, 0), + ATEST(SINGLE, volsw, 15, 1, false, 0x1F, 0x0F, 0, 20, 15, 0, 0), + // Inverted single positive value controls + ATEST(SINGLE, volsw, 10, 1, false, 0x1F, 0x0A, 0, 20, 0, 0, 1), + ATEST(SINGLE, volsw, 0, 1, false, 0x1F, 0x14, 0, 20, 0, 0, 1), + ATEST(SINGLE, volsw, 20, 0, false, 0x1F, 0x00, 0, 20, 0, 0, 1), + ATEST(SINGLE, volsw, 10, 1, false, 0x1F, 0x0A, 0, 20, 15, 0, 1), + ATEST(SINGLE, volsw, 25, -22, false, 0x1F, 0x00, 0, 20, 15, 0, 1), + ATEST(SINGLE, volsw, 15, 1, false, 0x1F, 0x05, 0, 20, 15, 0, 1), + ATEST(SINGLE, volsw, 10, 1, true, 0x1F, 0x0A, 0, 20, 0, 0, 0), + ATEST(SINGLE, volsw, 0, 1, true, 0x1F, 0x00, 0, 20, 0, 0, 0), + ATEST(SINGLE, volsw, 20, 1, true, 0x1F, 0x14, 0, 20, 0, 0, 0), + ATEST(SINGLE, volsw, 10, 1, true, 0x1F, 0x0A, 0, 20, 15, 0, 0), + ATEST(SINGLE, volsw, 25, -22, true, 0x1F, 0x00, 0, 20, 15, 0, 0), + ATEST(SINGLE, volsw, 15, 1, true, 0x1F, 0x0F, 0, 20, 15, 0, 0), + // Single negative value controls + ATEST(SINGLE, volsw, 10, 0, false, 0x1F, 0x00, -10, 10, 0, 4, 0), + ATEST(SINGLE, volsw, 0, 1, false, 0x1F, 0x16, -10, 10, 0, 4, 0), + ATEST(SINGLE, volsw, 20, 1, false, 0x1F, 0x0A, -10, 10, 0, 4, 0), + ATEST(SINGLE, volsw, 10, 0, false, 0x1F, 0x00, -10, 10, 15, 4, 0), + ATEST(SINGLE, volsw, 25, -22, false, 0x1F, 0x00, -10, 10, 15, 4, 0), + ATEST(SINGLE, volsw, 15, 1, false, 0x1F, 0x05, -10, 10, 15, 4, 0), + // Single non-zero minimum positive value controls + ATEST(SINGLE, volsw, 10, 1, false, 0x1F, 0x14, 10, 30, 0, 0, 0), + ATEST(SINGLE, volsw, 0, 1, false, 0x1F, 0x0A, 10, 30, 0, 0, 0), + ATEST(SINGLE, volsw, 20, 1, false, 0x1F, 0x1E, 10, 30, 0, 0, 0), + ATEST(SINGLE, volsw, 10, 1, false, 0x1F, 0x14, 10, 30, 15, 0, 0), + ATEST(SINGLE, volsw, 25, -22, false, 0x1F, 0x00, 10, 30, 15, 0, 0), + ATEST(SINGLE, volsw, 15, 1, false, 0x1F, 0x19, 10, 30, 15, 0, 0), + // Inverted single non-zero minimum positive value controls + ATEST(SINGLE, volsw, 10, 1, false, 0x1F, 0x14, 10, 30, 0, 0, 1), + ATEST(SINGLE, volsw, 0, 1, false, 0x1F, 0x1E, 10, 30, 0, 0, 1), + ATEST(SINGLE, volsw, 20, 1, false, 0x1F, 0x0A, 10, 30, 0, 0, 1), + ATEST(SINGLE, volsw, 10, 1, false, 0x1F, 0x14, 10, 30, 15, 0, 1), + ATEST(SINGLE, volsw, 25, -22, false, 0x1F, 0x00, 10, 30, 15, 0, 1), + ATEST(SINGLE, volsw, 15, 1, false, 0x1F, 0x0F, 10, 30, 15, 0, 1), + // Double register positive value controls + ATEST(DOUBLE_R, volsw, 10, 1, false, 0x1F, 0x0A, 0, 20, 0, 0, 0), + ATEST(DOUBLE_R, volsw, 0, 0, false, 0x1F, 0x00, 0, 20, 0, 0, 0), + ATEST(DOUBLE_R, volsw, 20, 1, false, 0x1F, 0x14, 0, 20, 0, 0, 0), + ATEST(DOUBLE_R, volsw, 10, 1, false, 0x1F, 0x0A, 0, 20, 15, 0, 0), + ATEST(DOUBLE_R, volsw, 25, -22, false, 0x1F, 0x00, 0, 20, 15, 0, 0), + ATEST(DOUBLE_R, volsw, 15, 1, false, 0x1F, 0x0F, 0, 20, 15, 0, 0), + // Double register negative value controls + ATEST(DOUBLE_R, volsw, 10, 0, false, 0x1F, 0x00, -10, 10, 0, 4, 0), + ATEST(DOUBLE_R, volsw, 0, 1, false, 0x1F, 0x16, -10, 10, 0, 4, 0), + ATEST(DOUBLE_R, volsw, 20, 1, false, 0x1F, 0x0A, -10, 10, 0, 4, 0), + ATEST(DOUBLE_R, volsw, 10, 0, false, 0x1F, 0x00, -10, 10, 15, 4, 0), + ATEST(DOUBLE_R, volsw, 25, -22, false, 0x1F, 0x00, -10, 10, 15, 4, 0), + ATEST(DOUBLE_R, volsw, 15, 1, false, 0x1F, 0x05, -10, 10, 15, 4, 0), + ATEST(DOUBLE_R, volsw, 10, 1, true, 0x1F, 0x00, -10, 10, 0, 4, 0), + ATEST(DOUBLE_R, volsw, 0, 1, true, 0x1F, 0x16, -10, 10, 0, 4, 0), + ATEST(DOUBLE_R, volsw, 20, 1, true, 0x1F, 0x0A, -10, 10, 0, 4, 0), + ATEST(DOUBLE_R, volsw, 10, 1, true, 0x1F, 0x00, -10, 10, 15, 4, 0), + ATEST(DOUBLE_R, volsw, 25, -22, true, 0x1F, 0x00, -10, 10, 15, 4, 0), + ATEST(DOUBLE_R, volsw, 15, 1, true, 0x1F, 0x05, -10, 10, 15, 4, 0), + // Inverted double register negative value controls + ATEST(DOUBLE_R, volsw, 10, 1, true, 0x1F, 0x00, -10, 10, 0, 4, 1), + ATEST(DOUBLE_R, volsw, 0, 1, true, 0x1F, 0x0A, -10, 10, 0, 4, 1), + ATEST(DOUBLE_R, volsw, 20, 1, true, 0x1F, 0x16, -10, 10, 0, 4, 1), + ATEST(DOUBLE_R, volsw, 10, 1, true, 0x1F, 0x00, -10, 10, 15, 4, 1), + ATEST(DOUBLE_R, volsw, 25, -22, true, 0x1F, 0x00, -10, 10, 15, 4, 1), + ATEST(DOUBLE_R, volsw, 15, 1, true, 0x1F, 0x1B, -10, 10, 15, 4, 1), + // Double register non-zero minimum positive value controls + ATEST(DOUBLE_R, volsw, 10, 1, false, 0x1F, 0x14, 10, 30, 0, 0, 0), + ATEST(DOUBLE_R, volsw, 0, 1, false, 0x1F, 0x0A, 10, 30, 0, 0, 0), + ATEST(DOUBLE_R, volsw, 20, 1, false, 0x1F, 0x1E, 10, 30, 0, 0, 0), + ATEST(DOUBLE_R, volsw, 10, 1, false, 0x1F, 0x14, 10, 30, 15, 0, 0), + ATEST(DOUBLE_R, volsw, 25, -22, false, 0x1F, 0x00, 10, 30, 15, 0, 0), + ATEST(DOUBLE_R, volsw, 15, 1, false, 0x1F, 0x19, 10, 30, 15, 0, 0), + // Double shift positive value controls + ATEST(DOUBLE, volsw, 10, 1, false, 0x1F, 0x0A, 0, 20, 0, 0, 0), + ATEST(DOUBLE, volsw, 0, 0, false, 0x1F, 0x00, 0, 20, 0, 0, 0), + ATEST(DOUBLE, volsw, 20, 1, false, 0x1F, 0x14, 0, 20, 0, 0, 0), + ATEST(DOUBLE, volsw, 10, 1, false, 0x1F, 0x0A, 0, 20, 15, 0, 0), + ATEST(DOUBLE, volsw, 25, -22, false, 0x1F, 0x00, 0, 20, 15, 0, 0), + ATEST(DOUBLE, volsw, 15, 1, false, 0x1F, 0x0F, 0, 20, 15, 0, 0), + // Double shift negative value controls + ATEST(DOUBLE, volsw, 10, 0, false, 0x1F, 0x00, -10, 10, 0, 4, 0), + ATEST(DOUBLE, volsw, 0, 1, false, 0x1F, 0x16, -10, 10, 0, 4, 0), + ATEST(DOUBLE, volsw, 20, 1, false, 0x1F, 0x0A, -10, 10, 0, 4, 0), + ATEST(DOUBLE, volsw, 10, 0, false, 0x1F, 0x00, -10, 10, 15, 4, 0), + ATEST(DOUBLE, volsw, 25, -22, false, 0x1F, 0x00, -10, 10, 15, 4, 0), + ATEST(DOUBLE, volsw, 15, 1, false, 0x1F, 0x05, -10, 10, 15, 4, 0), + // Inverted double shift negative value controls + ATEST(DOUBLE, volsw, 10, 0, false, 0x1F, 0x00, -10, 10, 0, 4, 1), + ATEST(DOUBLE, volsw, 0, 1, false, 0x1F, 0x0A, -10, 10, 0, 4, 1), + ATEST(DOUBLE, volsw, 20, 1, false, 0x1F, 0x16, -10, 10, 0, 4, 1), + ATEST(DOUBLE, volsw, 10, 0, false, 0x1F, 0x00, -10, 10, 15, 4, 1), + ATEST(DOUBLE, volsw, 25, -22, false, 0x1F, 0x00, -10, 10, 15, 4, 1), + ATEST(DOUBLE, volsw, 15, 1, false, 0x1F, 0x1B, -10, 10, 15, 4, 1), + // Double shift non-zero minimum positive value controls + ATEST(DOUBLE, volsw, 10, 1, false, 0x1F, 0x14, 10, 30, 0, 0, 0), + ATEST(DOUBLE, volsw, 0, 1, false, 0x1F, 0x0A, 10, 30, 0, 0, 0), + ATEST(DOUBLE, volsw, 20, 1, false, 0x1F, 0x1E, 10, 30, 0, 0, 0), + ATEST(DOUBLE, volsw, 10, 1, false, 0x1F, 0x14, 10, 30, 15, 0, 0), + ATEST(DOUBLE, volsw, 25, -22, false, 0x1F, 0x00, 10, 30, 15, 0, 0), + ATEST(DOUBLE, volsw, 15, 1, false, 0x1F, 0x19, 10, 30, 15, 0, 0), + ATEST(DOUBLE, volsw, 10, 1, true, 0x1F, 0x14, 10, 30, 0, 0, 0), + ATEST(DOUBLE, volsw, 0, 1, true, 0x1F, 0x0A, 10, 30, 0, 0, 0), + ATEST(DOUBLE, volsw, 20, 1, true, 0x1F, 0x1E, 10, 30, 0, 0, 0), + ATEST(DOUBLE, volsw, 10, 1, true, 0x1F, 0x14, 10, 30, 15, 0, 0), + ATEST(DOUBLE, volsw, 25, -22, true, 0x1F, 0x00, 10, 30, 15, 0, 0), + ATEST(DOUBLE, volsw, 15, 1, true, 0x1F, 0x19, 10, 30, 15, 0, 0), + // Single SX all values + ATEST(SINGLE, volsw_sx, 0, 1, false, 0xF, 0x0F, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 1, 0, false, 0xF, 0x00, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 2, 1, false, 0xF, 0x01, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 3, 1, false, 0xF, 0x02, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 4, 1, false, 0xF, 0x03, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 5, -22, false, 0xF, 0x00, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 0, 0, true, 0xF, 0x0F, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 1, 1, true, 0xF, 0x00, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 2, 1, true, 0xF, 0x01, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 3, 1, true, 0xF, 0x02, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 4, 1, true, 0xF, 0x03, 0x0F, 4, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 5, -22, true, 0xF, 0x00, 0x0F, 4, 0, 0, 0), + // Inverted single SX all values + ATEST(SINGLE, volsw_sx, 0, 1, false, 0x1F, 0x03, 0x0F, 4, 0, 0, 1), + ATEST(SINGLE, volsw_sx, 1, 1, false, 0x1F, 0x02, 0x0F, 4, 0, 0, 1), + ATEST(SINGLE, volsw_sx, 2, 1, false, 0x1F, 0x01, 0x0F, 4, 0, 0, 1), + ATEST(SINGLE, volsw_sx, 3, 0, false, 0x1F, 0x00, 0x0F, 4, 0, 0, 1), + ATEST(SINGLE, volsw_sx, 4, 1, false, 0x1F, 0x0F, 0x0F, 4, 0, 0, 1), + ATEST(SINGLE, volsw_sx, 5, -22, false, 0x1F, 0x00, 0x0F, 4, 0, 0, 1), + // Single SX select values + ATEST(SINGLE, volsw_sx, 0, 1, false, 0xFF, 0x88, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 1, 1, false, 0xFF, 0x89, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 119, 1, false, 0xFF, 0xFF, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 120, 0, false, 0xFF, 0x00, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 121, 1, false, 0xFF, 0x01, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 143, 1, false, 0xFF, 0x17, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 144, 1, false, 0xFF, 0x18, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 145, -22, false, 0xFF, 0x00, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 0, 1, true, 0xFF, 0x88, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 1, 1, true, 0xFF, 0x89, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 119, 0, true, 0xFF, 0xFF, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 120, 1, true, 0xFF, 0x00, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 121, 1, true, 0xFF, 0x01, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 143, 1, true, 0xFF, 0x17, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 144, 1, true, 0xFF, 0x18, 0x88, 144, 0, 0, 0), + ATEST(SINGLE, volsw_sx, 145, -22, true, 0xFF, 0x00, 0x88, 144, 0, 0, 0), + // Double shift SX select values + ATEST(DOUBLE, volsw_sx, 0, 1, true, 0xFF, 0x88, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE, volsw_sx, 1, 1, true, 0xFF, 0x89, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE, volsw_sx, 119, 0, true, 0xFF, 0xFF, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE, volsw_sx, 120, 1, true, 0xFF, 0x00, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE, volsw_sx, 121, 1, true, 0xFF, 0x01, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE, volsw_sx, 143, 1, true, 0xFF, 0x17, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE, volsw_sx, 144, 1, true, 0xFF, 0x18, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE, volsw_sx, 145, -22, true, 0xFF, 0x00, 0x88, 144, 0, 0, 0), + // Double register SX select values + ATEST(DOUBLE_R, volsw_sx, 0, 1, true, 0xFF, 0x88, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE_R, volsw_sx, 1, 1, true, 0xFF, 0x89, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE_R, volsw_sx, 119, 0, true, 0xFF, 0xFF, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE_R, volsw_sx, 120, 1, true, 0xFF, 0x00, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE_R, volsw_sx, 121, 1, true, 0xFF, 0x01, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE_R, volsw_sx, 143, 1, true, 0xFF, 0x17, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE_R, volsw_sx, 144, 1, true, 0xFF, 0x18, 0x88, 144, 0, 0, 0), + ATEST(DOUBLE_R, volsw_sx, 145, -22, true, 0xFF, 0x00, 0x88, 144, 0, 0, 0), +}; + +static const char *control_type_str(const snd_ctl_elem_type_t type) +{ + switch (type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + return "bool"; + case SNDRV_CTL_ELEM_TYPE_INTEGER: + return "int"; + default: + return "unknown"; + } +} + +static const char *control_layout_str(const enum soc_ops_test_control_layout layout) +{ + switch (layout) { + case SOC_OPS_TEST_SINGLE: + return "single"; + case SOC_OPS_TEST_DOUBLE: + return "double"; + case SOC_OPS_TEST_DOUBLE_R: + return "double_r"; + default: + return "unknown"; + } +}; + +static int mock_regmap_read(void *context, const void *reg_buf, + const size_t reg_size, void *val_buf, + size_t val_size) +{ + struct soc_ops_test_priv *priv = context; + + KUNIT_FAIL(priv->test, "Unexpected bus read"); + + return -EIO; +} + +static int mock_regmap_gather_write(void *context, + const void *reg_buf, size_t reg_size, + const void *val_buf, size_t val_size) +{ + struct soc_ops_test_priv *priv = context; + + KUNIT_FAIL(priv->test, "Unexpected bus gather_write"); + + return -EIO; +} + +static int mock_regmap_write(void *context, const void *val_buf, + size_t val_size) +{ + struct soc_ops_test_priv *priv = context; + + KUNIT_FAIL(priv->test, "Unexpected bus write"); + + return -EIO; +} + +static const struct regmap_bus mock_regmap_bus = { + .read = mock_regmap_read, + .write = mock_regmap_write, + .gather_write = mock_regmap_gather_write, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +static const struct regmap_config mock_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .max_register = 0x1, + .cache_type = REGCACHE_FLAT, +}; + +static int soc_ops_test_init(struct kunit *test) +{ + struct soc_ops_test_priv *priv; + struct regmap *regmap; + struct device *dev; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->test = test; + + dev = kunit_device_register(test, "soc_ops_test_drv"); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + regmap = devm_regmap_init(dev, &mock_regmap_bus, priv, &mock_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* No actual hardware, we just use the cache */ + regcache_cache_only(regmap, true); + + priv->component.dev = dev; + priv->component.regmap = regmap; + mutex_init(&priv->component.io_mutex); + + test->priv = priv; + + return 0; +} + +static void soc_ops_test_exit(struct kunit *test) +{ + struct soc_ops_test_priv *priv = test->priv; + + kunit_device_unregister(test, priv->component.dev); +} + +static void info_test_desc(const struct info_test_param *param, char *desc) +{ + snprintf(desc, KUNIT_PARAM_DESC_SIZE, + "%s %s %s: ctl range: %ld->%ld, reg range: %d->%d(%d), sign: %d, inv: %d", + control_layout_str(param->layout), param->func_name, + control_type_str(param->uinfo.type), + param->uinfo.value.integer.min, param->uinfo.value.integer.max, + param->mc.min, param->mc.max, param->mc.platform_max, + param->mc.sign_bit, param->mc.invert); +} + +static void soc_ops_test_info(struct kunit *test) +{ + struct soc_ops_test_priv *priv = test->priv; + const struct info_test_param *param = test->param_value; + const struct snd_ctl_elem_info *target = ¶m->uinfo; + struct snd_ctl_elem_info result; + struct snd_kcontrol kctl = { + .private_data = &priv->component, + .private_value = (unsigned long)¶m->mc, + }; + int ret; + + strscpy(kctl.id.name, param->name, sizeof(kctl.id.name)); + + ret = param->info(&kctl, &result); + KUNIT_ASSERT_FALSE(test, ret); + + KUNIT_EXPECT_EQ(test, result.count, target->count); + KUNIT_EXPECT_EQ(test, result.type, target->type); + KUNIT_EXPECT_EQ(test, result.value.integer.min, target->value.integer.min); + KUNIT_EXPECT_EQ(test, result.value.integer.max, target->value.integer.max); +} + +static void access_test_desc(const struct access_test_param *param, char *desc) +{ + if (param->ret < 0) { + snprintf(desc, KUNIT_PARAM_DESC_SIZE, + "%s %s: %ld,%ld -> range: %d->%d(%d), sign: %d, inv: %d -> err: %d", + control_layout_str(param->layout), param->func_name, + param->lctl, param->rctl, + param->mc.min, param->mc.max, param->mc.platform_max, + param->mc.sign_bit, param->mc.invert, + param->ret); + } else { + snprintf(desc, KUNIT_PARAM_DESC_SIZE, + "%s %s: %ld,%ld -> range: %d->%d(%d), sign: %d, inv: %d -> %#x,%#x", + control_layout_str(param->layout), param->func_name, + param->lctl, param->rctl, + param->mc.min, param->mc.max, param->mc.platform_max, + param->mc.sign_bit, param->mc.invert, + param->lreg, param->rreg); + } +} + +static void soc_ops_test_access(struct kunit *test) +{ + struct soc_ops_test_priv *priv = test->priv; + const struct access_test_param *param = test->param_value; + struct snd_kcontrol kctl = { + .private_data = &priv->component, + .private_value = (unsigned long)¶m->mc, + }; + struct snd_ctl_elem_value result; + unsigned int val; + int ret; + + ret = regmap_write(priv->component.regmap, 0x0, param->init); + KUNIT_ASSERT_FALSE(test, ret); + ret = regmap_write(priv->component.regmap, 0x1, param->init); + KUNIT_ASSERT_FALSE(test, ret); + + result.value.integer.value[0] = param->lctl; + result.value.integer.value[1] = param->rctl; + + ret = param->put(&kctl, &result); + KUNIT_ASSERT_EQ(test, ret, param->ret); + if (ret < 0) + return; + + ret = regmap_read(priv->component.regmap, 0x0, &val); + KUNIT_ASSERT_FALSE(test, ret); + KUNIT_EXPECT_EQ(test, val, (param->init & ~param->lmask) | param->lreg); + + ret = regmap_read(priv->component.regmap, 0x1, &val); + KUNIT_ASSERT_FALSE(test, ret); + KUNIT_EXPECT_EQ(test, val, (param->init & ~param->rmask) | param->rreg); + + result.value.integer.value[0] = 0; + result.value.integer.value[1] = 0; + + ret = param->get(&kctl, &result); + KUNIT_ASSERT_GE(test, ret, 0); + + KUNIT_EXPECT_EQ(test, result.value.integer.value[0], param->lctl); + if (param->layout != SOC_OPS_TEST_SINGLE) + KUNIT_EXPECT_EQ(test, result.value.integer.value[1], param->rctl); + else + KUNIT_EXPECT_EQ(test, result.value.integer.value[1], 0); +} + +KUNIT_ARRAY_PARAM(all_info_tests, all_info_test_params, info_test_desc); +KUNIT_ARRAY_PARAM(all_access_tests, all_access_test_params, access_test_desc); + +static struct kunit_case soc_ops_test_cases[] = { + KUNIT_CASE_PARAM(soc_ops_test_info, all_info_tests_gen_params), + KUNIT_CASE_PARAM(soc_ops_test_access, all_access_tests_gen_params), + {} +}; + +static struct kunit_suite soc_ops_test_suite = { + .name = "soc-ops", + .init = soc_ops_test_init, + .exit = soc_ops_test_exit, + .test_cases = soc_ops_test_cases, +}; + +kunit_test_suites(&soc_ops_test_suite); + +MODULE_DESCRIPTION("ASoC soc-ops kunit test"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 19928f098d8d..8d4dd11c9aef 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -24,7 +24,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dpcm.h> #include <sound/initval.h> /** @@ -38,7 +37,7 @@ * Returns 0 for success. */ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + struct snd_ctl_elem_info *uinfo) { struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; @@ -57,7 +56,7 @@ EXPORT_SYMBOL_GPL(snd_soc_info_enum_double); * Returns 0 for success. */ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; @@ -88,7 +87,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_enum_double); * Returns 0 for success. */ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; @@ -111,95 +110,183 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); -/** - * snd_soc_read_signed - Read a codec register and interpret as signed value - * @component: component - * @reg: Register to read - * @mask: Mask to use after shifting the register value - * @shift: Right shift of register value - * @sign_bit: Bit that describes if a number is negative or not. - * @signed_val: Pointer to where the read value should be stored - * - * This functions reads a codec register. The register value is shifted right - * by 'shift' bits and masked with the given 'mask'. Afterwards it translates - * the given registervalue into a signed integer if sign_bit is non-zero. - * - * Returns 0 on sucess, otherwise an error value - */ -static int snd_soc_read_signed(struct snd_soc_component *component, - unsigned int reg, unsigned int mask, unsigned int shift, - unsigned int sign_bit, int *signed_val) +static int soc_mixer_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val, + unsigned int mask, unsigned int shift, int max) +{ + int val = (reg_val >> shift) & mask; + + if (mc->sign_bit) + val = sign_extend32(val, mc->sign_bit); + + val -= mc->min; + + if (mc->invert) + val = max - val; + + return val & mask; +} + +static unsigned int soc_mixer_ctl_to_reg(struct soc_mixer_control *mc, int val, + unsigned int mask, unsigned int shift, + int max) +{ + unsigned int reg_val; + + if (mc->invert) + val = max - val; + + reg_val = val + mc->min; + + return (reg_val & mask) << shift; +} + +static int soc_mixer_valid_ctl(struct soc_mixer_control *mc, long val, int max) { + if (val < 0) + return -EINVAL; + + if (mc->platform_max && val > mc->platform_max) + return -EINVAL; + + if (val > max) + return -EINVAL; + + return 0; +} + +static int soc_mixer_mask(struct soc_mixer_control *mc) +{ + if (mc->sign_bit) + return GENMASK(mc->sign_bit, 0); + else + return GENMASK(fls(mc->max) - 1, 0); +} + +static int soc_mixer_sx_mask(struct soc_mixer_control *mc) +{ + // min + max will take us 1-bit over the size of the mask + return GENMASK(fls(mc->min + mc->max) - 2, 0); +} + +static int soc_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo, + struct soc_mixer_control *mc, int max) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + + if (max == 1) { + /* Even two value controls ending in Volume should be integer */ + const char *vol_string = strstr(kcontrol->id.name, " Volume"); + + if (!vol_string || strcmp(vol_string, " Volume")) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + } + + if (mc->platform_max && mc->platform_max < max) + max = mc->platform_max; + + uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = max; + + return 0; +} + +static int soc_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + struct soc_mixer_control *mc, int mask, int max) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + unsigned int val1, val_mask; + unsigned int val2 = 0; + bool double_r = false; int ret; - unsigned int val; - val = snd_soc_component_read(component, reg); - val = (val >> shift) & mask; + ret = soc_mixer_valid_ctl(mc, ucontrol->value.integer.value[0], max); + if (ret) + return ret; - if (!sign_bit) { - *signed_val = val; - return 0; + val1 = soc_mixer_ctl_to_reg(mc, ucontrol->value.integer.value[0], + mask, mc->shift, max); + val_mask = mask << mc->shift; + + if (snd_soc_volsw_is_stereo(mc)) { + ret = soc_mixer_valid_ctl(mc, ucontrol->value.integer.value[1], max); + if (ret) + return ret; + + if (mc->reg == mc->rreg) { + val1 |= soc_mixer_ctl_to_reg(mc, + ucontrol->value.integer.value[1], + mask, mc->rshift, max); + val_mask |= mask << mc->rshift; + } else { + val2 = soc_mixer_ctl_to_reg(mc, + ucontrol->value.integer.value[1], + mask, mc->shift, max); + double_r = true; + } } - /* non-negative number */ - if (!(val & BIT(sign_bit))) { - *signed_val = val; - return 0; + ret = snd_soc_component_update_bits(component, mc->reg, val_mask, val1); + if (ret < 0) + return ret; + + if (double_r) { + int err = snd_soc_component_update_bits(component, mc->rreg, + val_mask, val2); + /* Don't drop change flag */ + if (err) + return err; } - ret = val; + return ret; +} - /* - * The register most probably does not contain a full-sized int. - * Instead we have an arbitrary number of bits in a signed - * representation which has to be translated into a full-sized int. - * This is done by filling up all bits above the sign-bit. - */ - ret |= ~((int)(BIT(sign_bit) - 1)); +static int soc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + struct soc_mixer_control *mc, int mask, int max) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + unsigned int reg_val; + int val; + + reg_val = snd_soc_component_read(component, mc->reg); + val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->shift, max); + + ucontrol->value.integer.value[0] = val; - *signed_val = ret; + if (snd_soc_volsw_is_stereo(mc)) { + if (mc->reg == mc->rreg) { + val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->rshift, max); + } else { + reg_val = snd_soc_component_read(component, mc->rreg); + val = soc_mixer_reg_to_ctl(mc, reg_val, mask, mc->shift, max); + } + + ucontrol->value.integer.value[1] = val; + } return 0; } /** - * snd_soc_info_volsw - single mixer info callback + * snd_soc_info_volsw - single mixer info callback with range. * @kcontrol: mixer control * @uinfo: control element information * - * Callback to provide information about a single mixer control, or a double - * mixer control that spans 2 registers. + * Callback to provide information, with a range, about a single mixer control, + * or a double mixer control that spans 2 registers. * * Returns 0 for success. */ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + struct snd_ctl_elem_info *uinfo) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - const char *vol_string = NULL; - int max; - max = uinfo->value.integer.max = mc->max - mc->min; - if (mc->platform_max && mc->platform_max < max) - max = mc->platform_max; - - if (max == 1) { - /* Even two value controls ending in Volume should always be integer */ - vol_string = strstr(kcontrol->id.name, " Volume"); - if (vol_string && !strcmp(vol_string, " Volume")) - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - } else { - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - } - - uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = max; - - return 0; + return soc_info_volsw(kcontrol, uinfo, mc, mc->max - mc->min); } EXPORT_SYMBOL_GPL(snd_soc_info_volsw); @@ -221,165 +308,50 @@ int snd_soc_info_volsw_sx(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - int max; - - if (mc->platform_max) - max = mc->platform_max; - else - max = mc->max; - - if (max == 1 && !strstr(kcontrol->id.name, " Volume")) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = max; - - return 0; + return soc_info_volsw(kcontrol, uinfo, mc, mc->max); } EXPORT_SYMBOL_GPL(snd_soc_info_volsw_sx); /** - * snd_soc_get_volsw - single mixer get callback + * snd_soc_get_volsw - single mixer get callback with range * @kcontrol: mixer control * @ucontrol: control element information * - * Callback to get the value of a single mixer control, or a double mixer - * control that spans 2 registers. + * Callback to get the value, within a range, of a single mixer control, or a + * double mixer control that spans 2 registers. * * Returns 0 for success. */ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - int sign_bit = mc->sign_bit; - unsigned int mask = (1ULL << fls(max)) - 1; - unsigned int invert = mc->invert; - int val; - int ret; - - if (sign_bit) - mask = BIT(sign_bit + 1) - 1; - - ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[0] = val - min; - if (invert) - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; + unsigned int mask = soc_mixer_mask(mc); - if (snd_soc_volsw_is_stereo(mc)) { - if (reg == reg2) - ret = snd_soc_read_signed(component, reg, mask, rshift, - sign_bit, &val); - else - ret = snd_soc_read_signed(component, reg2, mask, shift, - sign_bit, &val); - if (ret) - return ret; - - ucontrol->value.integer.value[1] = val - min; - if (invert) - ucontrol->value.integer.value[1] = - max - ucontrol->value.integer.value[1]; - } - - return 0; + return soc_get_volsw(kcontrol, ucontrol, mc, mask, mc->max - mc->min); } EXPORT_SYMBOL_GPL(snd_soc_get_volsw); /** - * snd_soc_put_volsw - single mixer put callback + * snd_soc_put_volsw - single mixer put callback with range * @kcontrol: mixer control * @ucontrol: control element information * - * Callback to set the value of a single mixer control, or a double mixer - * control that spans 2 registers. + * Callback to set the value , within a range, of a single mixer control, or + * a double mixer control that spans 2 registers. * * Returns 0 for success. */ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - unsigned int sign_bit = mc->sign_bit; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - int err, ret; - bool type_2r = false; - unsigned int val2 = 0; - unsigned int val, val_mask; - - if (sign_bit) - mask = BIT(sign_bit + 1) - 1; - - if (ucontrol->value.integer.value[0] < 0) - return -EINVAL; - val = ucontrol->value.integer.value[0]; - if (mc->platform_max && ((int)val + min) > mc->platform_max) - return -EINVAL; - if (val > max - min) - return -EINVAL; - val = (val + min) & mask; - if (invert) - val = max - val; - val_mask = mask << shift; - val = val << shift; - if (snd_soc_volsw_is_stereo(mc)) { - if (ucontrol->value.integer.value[1] < 0) - return -EINVAL; - val2 = ucontrol->value.integer.value[1]; - if (mc->platform_max && ((int)val2 + min) > mc->platform_max) - return -EINVAL; - if (val2 > max - min) - return -EINVAL; - val2 = (val2 + min) & mask; - if (invert) - val2 = max - val2; - if (reg == reg2) { - val_mask |= mask << rshift; - val |= val2 << rshift; - } else { - val2 = val2 << shift; - type_2r = true; - } - } - err = snd_soc_component_update_bits(component, reg, val_mask, val); - if (err < 0) - return err; - ret = err; - - if (type_2r) { - err = snd_soc_component_update_bits(component, reg2, val_mask, - val2); - /* Don't discard any error code or drop change flag */ - if (ret == 0 || err < 0) { - ret = err; - } - } + unsigned int mask = soc_mixer_mask(mc); - return ret; + return soc_put_volsw(kcontrol, ucontrol, mc, mask, mc->max - mc->min); } EXPORT_SYMBOL_GPL(snd_soc_put_volsw); @@ -394,30 +366,13 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw); * Returns 0 for success. */ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - unsigned int mask = (1U << (fls(min + max) - 1)) - 1; - unsigned int val; - - val = snd_soc_component_read(component, reg); - ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask; - - if (snd_soc_volsw_is_stereo(mc)) { - val = snd_soc_component_read(component, reg2); - val = ((val >> rshift) - min) & mask; - ucontrol->value.integer.value[1] = val; - } + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mask = soc_mixer_sx_mask(mc); - return 0; + return soc_get_volsw(kcontrol, ucontrol, mc, mask, mc->max); } EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); @@ -433,212 +388,40 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int rshift = mc->rshift; - int max = mc->max; - int min = mc->min; - unsigned int mask = (1U << (fls(min + max) - 1)) - 1; - int err = 0; - int ret; - unsigned int val, val_mask; - - if (ucontrol->value.integer.value[0] < 0) - return -EINVAL; - val = ucontrol->value.integer.value[0]; - if (mc->platform_max && val > mc->platform_max) - return -EINVAL; - if (val > max) - return -EINVAL; - val_mask = mask << shift; - val = (val + min) & mask; - val = val << shift; - - err = snd_soc_component_update_bits(component, reg, val_mask, val); - if (err < 0) - return err; - ret = err; - - if (snd_soc_volsw_is_stereo(mc)) { - unsigned int val2 = ucontrol->value.integer.value[1]; - - if (mc->platform_max && val2 > mc->platform_max) - return -EINVAL; - if (val2 > max) - return -EINVAL; - - val_mask = mask << rshift; - val2 = (val2 + min) & mask; - val2 = val2 << rshift; - - err = snd_soc_component_update_bits(component, reg2, val_mask, - val2); + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mask = soc_mixer_sx_mask(mc); - /* Don't discard any error code or drop change flag */ - if (ret == 0 || err < 0) { - ret = err; - } - } - return ret; + return soc_put_volsw(kcontrol, ucontrol, mc, mask, mc->max); } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx); -/** - * snd_soc_info_volsw_range - single mixer info callback with range. - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information, within a range, about a single - * mixer control. - * - * returns 0 for success. - */ -int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static int snd_soc_clip_to_platform_max(struct snd_kcontrol *kctl) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int platform_max; - int min = mc->min; + struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; + struct snd_ctl_elem_value uctl; + int ret; if (!mc->platform_max) - mc->platform_max = mc->max; - platform_max = mc->platform_max; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = platform_max - min; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range); - -/** - * snd_soc_put_volsw_range - single mixer put value callback with range. - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to set the value, within a range, for a single mixer control. - * - * Returns 0 for success. - */ -int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - unsigned int reg = mc->reg; - unsigned int rreg = mc->rreg; - unsigned int shift = mc->shift; - int min = mc->min; - int max = mc->max; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - unsigned int val, val_mask; - int err, ret, tmp; - - tmp = ucontrol->value.integer.value[0]; - if (tmp < 0) - return -EINVAL; - if (mc->platform_max && tmp > mc->platform_max) - return -EINVAL; - if (tmp > mc->max - mc->min) - return -EINVAL; + return 0; - if (invert) - val = (max - ucontrol->value.integer.value[0]) & mask; - else - val = ((ucontrol->value.integer.value[0] + min) & mask); - val_mask = mask << shift; - val = val << shift; + ret = kctl->get(kctl, &uctl); + if (ret < 0) + return ret; - err = snd_soc_component_update_bits(component, reg, val_mask, val); - if (err < 0) - return err; - ret = err; + if (uctl.value.integer.value[0] > mc->platform_max) + uctl.value.integer.value[0] = mc->platform_max; - if (snd_soc_volsw_is_stereo(mc)) { - tmp = ucontrol->value.integer.value[1]; - if (tmp < 0) - return -EINVAL; - if (mc->platform_max && tmp > mc->platform_max) - return -EINVAL; - if (tmp > mc->max - mc->min) - return -EINVAL; + if (snd_soc_volsw_is_stereo(mc) && + uctl.value.integer.value[1] > mc->platform_max) + uctl.value.integer.value[1] = mc->platform_max; - if (invert) - val = (max - ucontrol->value.integer.value[1]) & mask; - else - val = ((ucontrol->value.integer.value[1] + min) & mask); - val_mask = mask << shift; - val = val << shift; - - err = snd_soc_component_update_bits(component, rreg, val_mask, - val); - /* Don't discard any error code or drop change flag */ - if (ret == 0 || err < 0) { - ret = err; - } - } - - return ret; -} -EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range); - -/** - * snd_soc_get_volsw_range - single mixer get callback with range - * @kcontrol: mixer control - * @ucontrol: control element information - * - * Callback to get the value, within a range, of a single mixer control. - * - * Returns 0 for success. - */ -int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int rreg = mc->rreg; - unsigned int shift = mc->shift; - int min = mc->min; - int max = mc->max; - unsigned int mask = (1 << fls(max)) - 1; - unsigned int invert = mc->invert; - unsigned int val; - - val = snd_soc_component_read(component, reg); - ucontrol->value.integer.value[0] = (val >> shift) & mask; - if (invert) - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; - else - ucontrol->value.integer.value[0] = - ucontrol->value.integer.value[0] - min; - - if (snd_soc_volsw_is_stereo(mc)) { - val = snd_soc_component_read(component, rreg); - ucontrol->value.integer.value[1] = (val >> shift) & mask; - if (invert) - ucontrol->value.integer.value[1] = - max - ucontrol->value.integer.value[1]; - else - ucontrol->value.integer.value[1] = - ucontrol->value.integer.value[1] - min; - } + ret = kctl->put(kctl, &uctl); + if (ret < 0) + return ret; return 0; } -EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); /** * snd_soc_limit_volume - Set new limit to an existing volume control. @@ -649,8 +432,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range); * * Return 0 for success, else error. */ -int snd_soc_limit_volume(struct snd_soc_card *card, - const char *name, int max) +int snd_soc_limit_volume(struct snd_soc_card *card, const char *name, int max) { struct snd_kcontrol *kctl; int ret = -EINVAL; @@ -661,12 +443,15 @@ int snd_soc_limit_volume(struct snd_soc_card *card, kctl = snd_soc_card_get_kcontrol(card, name); if (kctl) { - struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kctl->private_value; + if (max <= mc->max - mc->min) { mc->platform_max = max; - ret = 0; + ret = snd_soc_clip_to_platform_max(kctl); } } + return ret; } EXPORT_SYMBOL_GPL(snd_soc_limit_volume); @@ -726,8 +511,8 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_bytes *params = (void *)kcontrol->private_value; - int ret, len; unsigned int val, mask; + int ret, len; if (!component->regmap || !params->num_regs) return -EINVAL; @@ -758,15 +543,13 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, break; case 2: mask = ~params->mask; - ret = regmap_parse_val(component->regmap, - &mask, &mask); + ret = regmap_parse_val(component->regmap, &mask, &mask); if (ret != 0) return ret; ((u16 *)data)[0] &= mask; - ret = regmap_parse_val(component->regmap, - &val, &val); + ret = regmap_parse_val(component->regmap, &val, &val); if (ret != 0) return ret; @@ -774,15 +557,13 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, break; case 4: mask = ~params->mask; - ret = regmap_parse_val(component->regmap, - &mask, &mask); + ret = regmap_parse_val(component->regmap, &mask, &mask); if (ret != 0) return ret; ((u32 *)data)[0] &= mask; - ret = regmap_parse_val(component->regmap, - &val, &val); + ret = regmap_parse_val(component->regmap, &val, &val); if (ret != 0) return ret; @@ -798,7 +579,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_bytes_put); int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *ucontrol) + struct snd_ctl_elem_info *ucontrol) { struct soc_bytes_ext *params = (void *)kcontrol->private_value; @@ -810,7 +591,7 @@ int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext); int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) + unsigned int size, unsigned int __user *tlv) { struct soc_bytes_ext *params = (void *)kcontrol->private_value; unsigned int count = size < params->max ? size : params->max; @@ -826,6 +607,7 @@ int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, ret = params->put(kcontrol, tlv, count); break; } + return ret; } EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback); @@ -835,17 +617,19 @@ EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback); * @kcontrol: mreg control * @uinfo: control element information * - * Callback to provide information of a control that can - * span multiple codec registers which together - * forms a single signed value in a MSB/LSB manner. + * Callback to provide information of a control that can span multiple + * codec registers which together forms a single signed value. Note + * that unlike the non-xr variant of sx controls these may or may not + * include the sign bit, depending on nbits, and there is no shift. * * Returns 0 for success. */ int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) + struct snd_ctl_elem_info *uinfo) { struct soc_mreg_control *mc = (struct soc_mreg_control *)kcontrol->private_value; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = mc->min; @@ -860,16 +644,17 @@ EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx); * @kcontrol: mreg control * @ucontrol: control element information * - * Callback to get the value of a control that can span - * multiple codec registers which together forms a single - * signed value in a MSB/LSB manner. The control supports - * specifying total no of bits used to allow for bitfields - * across the multiple codec registers. + * Callback to get the value of a control that can span multiple codec + * registers which together forms a single signed value. The control + * supports specifying total no of bits used to allow for bitfields + * across the multiple codec registers. Note that unlike the non-xr + * variant of sx controls these may or may not include the sign bit, + * depending on nbits, and there is no shift. * * Returns 0 for success. */ int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mreg_control *mc = @@ -877,23 +662,21 @@ int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol, unsigned int regbase = mc->regbase; unsigned int regcount = mc->regcount; unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; - unsigned int regwmask = (1UL<<regwshift)-1; - unsigned int invert = mc->invert; - unsigned long mask = (1UL<<mc->nbits)-1; - long min = mc->min; - long max = mc->max; + unsigned int regwmask = GENMASK(regwshift - 1, 0); + unsigned long mask = GENMASK(mc->nbits - 1, 0); long val = 0; unsigned int i; for (i = 0; i < regcount; i++) { - unsigned int regval = snd_soc_component_read(component, regbase+i); - val |= (regval & regwmask) << (regwshift*(regcount-i-1)); + unsigned int regval = snd_soc_component_read(component, regbase + i); + + val |= (regval & regwmask) << (regwshift * (regcount - i - 1)); } val &= mask; - if (min < 0 && val > max) + if (mc->min < 0 && val > mc->max) val |= ~mask; - if (invert) - val = max - val; + if (mc->invert) + val = mc->max - val; ucontrol->value.integer.value[0] = val; return 0; @@ -905,16 +688,17 @@ EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx); * @kcontrol: mreg control * @ucontrol: control element information * - * Callback to set the value of a control that can span - * multiple codec registers which together forms a single - * signed value in a MSB/LSB manner. The control supports - * specifying total no of bits used to allow for bitfields - * across the multiple codec registers. + * Callback to set the value of a control that can span multiple codec + * registers which together forms a single signed value. The control + * supports specifying total no of bits used to allow for bitfields + * across the multiple codec registers. Note that unlike the non-xr + * variant of sx controls these may or may not include the sign bit, + * depending on nbits, and there is no shift. * * Returns 0 for success. */ int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mreg_control *mc = @@ -922,24 +706,25 @@ int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol, unsigned int regbase = mc->regbase; unsigned int regcount = mc->regcount; unsigned int regwshift = component->val_bytes * BITS_PER_BYTE; - unsigned int regwmask = (1UL<<regwshift)-1; - unsigned int invert = mc->invert; - unsigned long mask = (1UL<<mc->nbits)-1; - long max = mc->max; + unsigned int regwmask = GENMASK(regwshift - 1, 0); + unsigned long mask = GENMASK(mc->nbits - 1, 0); long val = ucontrol->value.integer.value[0]; int ret = 0; unsigned int i; if (val < mc->min || val > mc->max) return -EINVAL; - if (invert) - val = max - val; + if (mc->invert) + val = mc->max - val; val &= mask; for (i = 0; i < regcount; i++) { - unsigned int regval = (val >> (regwshift*(regcount-i-1))) & regwmask; - unsigned int regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask; - int err = snd_soc_component_update_bits(component, regbase+i, + unsigned int regval = (val >> (regwshift * (regcount - i - 1))) & + regwmask; + unsigned int regmask = (mask >> (regwshift * (regcount - i - 1))) & + regwmask; + int err = snd_soc_component_update_bits(component, regbase + i, regmask, regval); + if (err < 0) return err; if (err > 0) @@ -960,22 +745,21 @@ EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx); * Returns 0 for success. */ int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int shift = mc->shift; - unsigned int mask = 1 << shift; unsigned int invert = mc->invert != 0; + unsigned int mask = BIT(mc->shift); unsigned int val; - val = snd_soc_component_read(component, reg); + val = snd_soc_component_read(component, mc->reg); val &= mask; - if (shift != 0 && val != 0) - val = val >> shift; + if (mc->shift != 0 && val != 0) + val = val >> mc->shift; + ucontrol->value.enumerated.item[0] = val ^ invert; return 0; @@ -993,24 +777,22 @@ EXPORT_SYMBOL_GPL(snd_soc_get_strobe); * Returns 1 for success. */ int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) + struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int shift = mc->shift; - unsigned int mask = 1 << shift; - unsigned int invert = mc->invert != 0; unsigned int strobe = ucontrol->value.enumerated.item[0] != 0; + unsigned int invert = mc->invert != 0; + unsigned int mask = BIT(mc->shift); unsigned int val1 = (strobe ^ invert) ? mask : 0; unsigned int val2 = (strobe ^ invert) ? 0 : mask; - int err; + int ret; - err = snd_soc_component_update_bits(component, reg, mask, val1); - if (err < 0) - return err; + ret = snd_soc_component_update_bits(component, mc->reg, mask, val1); + if (ret < 0) + return ret; - return snd_soc_component_update_bits(component, reg, mask, val2); + return snd_soc_component_update_bits(component, mc->reg, mask, val2); } EXPORT_SYMBOL_GPL(snd_soc_put_strobe); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 1150455619aa..4308a6cbb2e6 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -30,28 +30,13 @@ static inline int _soc_pcm_ret(struct snd_soc_pcm_runtime *rtd, const char *func, int ret) { - /* Positive, Zero values are not errors */ - if (ret >= 0) - return ret; - - /* Negative values might be errors */ - switch (ret) { - case -EPROBE_DEFER: - case -ENOTSUPP: - case -EINVAL: - break; - default: - dev_err(rtd->dev, - "ASoC: error at %s on %s: %d\n", - func, rtd->dai_link->name, ret); - } - - return ret; + return snd_soc_ret(rtd->dev, ret, + "at %s() on %s\n", func, rtd->dai_link->name); } /* is the current PCM operation for this FE ? */ #if 0 -static int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream) +static int snd_soc_dpcm_can_fe_update(struct snd_soc_pcm_runtime *fe, int stream) { if (fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) return 1; @@ -60,7 +45,7 @@ static int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream #endif /* is the current PCM operation for this BE ? */ -static int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, +static int snd_soc_dpcm_can_be_update(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream) { if ((fe->dpcm[stream].runtime_update == SND_SOC_DPCM_UPDATE_FE) || @@ -159,7 +144,6 @@ static inline const char *soc_codec_dai_name(struct snd_soc_pcm_runtime *rtd) return (rtd)->dai_link->num_codecs == 1 ? snd_soc_rtd_to_codec(rtd, 0)->name : "multicodec"; } -#ifdef CONFIG_DEBUG_FS static const char *dpcm_state_string(enum snd_soc_dpcm_state state) { switch (state) { @@ -188,6 +172,7 @@ static const char *dpcm_state_string(enum snd_soc_dpcm_state state) return "unknown"; } +#ifdef CONFIG_DEBUG_FS static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, int stream, char *buf, size_t size) { @@ -253,11 +238,9 @@ static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, int stream; char *buf; - if (fe->dai_link->num_cpus > 1) { - dev_err(fe->dev, + if (fe->dai_link->num_cpus > 1) + return snd_soc_ret(fe->dev, -EINVAL, "%s doesn't support Multi CPU yet\n", __func__); - return -EINVAL; - } buf = kmalloc(out_count, GFP_KERNEL); if (!buf) @@ -417,8 +400,7 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) } /* DPCM stream event, send event to FE and all active BEs. */ -int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, - int event) +void dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event) { struct snd_soc_dpcm *dpcm; @@ -439,8 +421,6 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, } snd_soc_dapm_stream_event(fe, dir, event); - - return 0; } static void soc_pcm_set_dai_params(struct snd_soc_dai *dai, @@ -475,12 +455,9 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream, ret = snd_pcm_hw_constraint_single(substream->runtime, \ SNDRV_PCM_HW_PARAM_##NAME,\ soc_dai->symmetric_##name); \ - if (ret < 0) { \ - dev_err(soc_dai->dev, \ - "ASoC: Unable to apply %s constraint: %d\n",\ - #name, ret); \ - return ret; \ - } \ + if (ret < 0) \ + return snd_soc_ret(soc_dai->dev, ret, \ + "Unable to apply %s constraint\n", #name); \ } __soc_pcm_apply_symmetry(rate, RATE); @@ -511,12 +488,11 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream, for_each_rtd_cpu_dais(rtd, i, cpu_dai) \ if (!snd_soc_dai_is_dummy(cpu_dai) && \ cpu_dai->symmetric_##xxx && \ - cpu_dai->symmetric_##xxx != d.symmetric_##xxx) { \ - dev_err(rtd->dev, "ASoC: unmatched %s symmetry: %s:%d - %s:%d\n", \ - #xxx, cpu_dai->name, cpu_dai->symmetric_##xxx, \ - d.name, d.symmetric_##xxx); \ - return -EINVAL; \ - } + cpu_dai->symmetric_##xxx != d.symmetric_##xxx) \ + return snd_soc_ret(rtd->dev, -EINVAL, \ + "unmatched %s symmetry: %s:%d - %s:%d\n", \ + #xxx, cpu_dai->name, cpu_dai->symmetric_##xxx, \ + d.name, d.symmetric_##xxx); /* reject unmatched parameters when applying symmetry */ __soc_pcm_params_symmetry(rate); @@ -627,13 +603,8 @@ static void soc_pcm_hw_update_chan(struct snd_pcm_hardware *hw, static void soc_pcm_hw_update_format(struct snd_pcm_hardware *hw, const struct snd_soc_pcm_stream *p) { - hw->formats &= p->formats; -} - -static void soc_pcm_hw_update_subformat(struct snd_pcm_hardware *hw, - const struct snd_soc_pcm_stream *p) -{ - hw->subformats &= p->subformats; + hw->formats &= p->formats; + hw->subformats &= p->subformats; } /** @@ -674,7 +645,6 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, soc_pcm_hw_update_chan(hw, cpu_stream); soc_pcm_hw_update_rate(hw, cpu_stream); soc_pcm_hw_update_format(hw, cpu_stream); - soc_pcm_hw_update_subformat(hw, cpu_stream); } cpu_chan_min = hw->channels_min; cpu_chan_max = hw->channels_max; @@ -696,7 +666,6 @@ int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, soc_pcm_hw_update_chan(hw, codec_stream); soc_pcm_hw_update_rate(hw, codec_stream); soc_pcm_hw_update_format(hw, codec_stream); - soc_pcm_hw_update_subformat(hw, codec_stream); } /* Verify both a valid CPU DAI and a valid CODEC DAI were found */ @@ -861,9 +830,8 @@ static int soc_hw_sanity_check(struct snd_pcm_substream *substream) return 0; config_err: - dev_err(dev, "ASoC: %s <-> %s No matching %s\n", - name_codec, name_cpu, err_msg); - return -EINVAL; + return snd_soc_ret(dev, -EINVAL, + "%s <-> %s No matching %s\n", name_codec, name_cpu, err_msg); } /* @@ -981,12 +949,18 @@ static int __soc_pcm_prepare(struct snd_soc_pcm_runtime *rtd, SND_SOC_DAPM_STREAM_START); for_each_rtd_dais(rtd, i, dai) { - if (dai->driver->ops && !dai->driver->ops->mute_unmute_on_trigger) + if (!snd_soc_dai_mute_is_ctrled_at_trigger(dai)) snd_soc_dai_digital_mute(dai, 0, substream->stream); } out: - return soc_pcm_ret(rtd, ret); + /* + * Don't use soc_pcm_ret() on .prepare callback to lower error log severity + * + * We don't want to log an error since we do not want to give userspace a way to do a + * denial-of-service attack on the syslog / diskspace. + */ + return ret; } /* PCM prepare ops for non-DPCM streams */ @@ -998,6 +972,13 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) snd_soc_dpcm_mutex_lock(rtd); ret = __soc_pcm_prepare(rtd, substream); snd_soc_dpcm_mutex_unlock(rtd); + + /* + * Don't use soc_pcm_ret() on .prepare callback to lower error log severity + * + * We don't want to log an error since we do not want to give userspace a way to do a + * denial-of-service attack on the syslog / diskspace. + */ return ret; } @@ -1026,7 +1007,7 @@ static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd, soc_pcm_set_dai_params(dai, NULL); if (snd_soc_dai_stream_active(dai, substream->stream) == 1) { - if (dai->driver->ops && !dai->driver->ops->mute_unmute_on_trigger) + if (!snd_soc_dai_mute_is_ctrled_at_trigger(dai)) snd_soc_dai_digital_mute(dai, 1, substream->stream); } } @@ -1074,10 +1055,10 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) * function can also be called multiple times and can allocate buffers * (using snd_pcm_lib_* ). It's non-atomic. */ -static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_substream *substream, +static int __soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; struct snd_pcm_hw_params tmp_params; @@ -1183,7 +1164,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, int ret; snd_soc_dpcm_mutex_lock(rtd); - ret = __soc_pcm_hw_params(rtd, substream, params); + ret = __soc_pcm_hw_params(substream, params); snd_soc_dpcm_mutex_unlock(rtd); return ret; } @@ -1313,19 +1294,18 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, snd_soc_dpcm_mutex_assert_held(fe); /* only add new dpcms */ - for_each_dpcm_be(fe, stream, dpcm) { - if (dpcm->be == be && dpcm->fe == fe) + for_each_dpcm_be(fe, stream, dpcm) + if (dpcm->be == be) return 0; - } fe_substream = snd_soc_dpcm_get_substream(fe, stream); be_substream = snd_soc_dpcm_get_substream(be, stream); - if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic) { - dev_err(be->dev, "%s: FE is atomic but BE is nonatomic, invalid configuration\n", - __func__); - return -EINVAL; - } + if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic) + return snd_soc_ret(be->dev, -EINVAL, + "%s: %s is atomic but %s is nonatomic, invalid configuration\n", + __func__, fe->dai_link->name, be->dai_link->name); + if (fe_substream->pcm->nonatomic && !be_substream->pcm->nonatomic) { dev_dbg(be->dev, "FE is nonatomic but BE is not, forcing BE as nonatomic\n"); be_substream->pcm->nonatomic = 1; @@ -1495,11 +1475,9 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe, struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0); int paths; - if (fe->dai_link->num_cpus > 1) { - dev_err(fe->dev, + if (fe->dai_link->num_cpus > 1) + return snd_soc_ret(fe->dev, -EINVAL, "%s doesn't support Multi CPU yet\n", __func__); - return -EINVAL; - } /* get number of valid DAI paths and their widgets */ paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list, @@ -1565,8 +1543,8 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, return prune; } -static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, - struct snd_soc_dapm_widget_list **list_) +int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, + struct snd_soc_dapm_widget_list **list_) { struct snd_soc_card *card = fe->card; struct snd_soc_dapm_widget_list *list = *list_; @@ -1631,19 +1609,6 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, return new; } -/* - * Find the corresponding BE DAIs that source or sink audio to this - * FE substream. - */ -int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, - int stream, struct snd_soc_dapm_widget_list **list, int new) -{ - if (new) - return dpcm_add_paths(fe, stream, list); - else - return dpcm_prune_paths(fe, stream, list); -} - void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1667,13 +1632,13 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, return; /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; if (be->dpcm[stream].users == 0) { - dev_err(be->dev, "ASoC: no users %s at close - state %d\n", + dev_err(be->dev, "ASoC: no users %s at close - state %s\n", snd_pcm_direction_name(stream), - be->dpcm[stream].state); + dpcm_state_string(be->dpcm[stream].state)); continue; } @@ -1717,14 +1682,14 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) } /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; /* first time the dpcm is open ? */ if (be->dpcm[stream].users == DPCM_MAX_BE_USERS) { - dev_err(be->dev, "ASoC: too many users %s at open %d\n", + dev_err(be->dev, "ASoC: too many users %s at open %s\n", snd_pcm_direction_name(stream), - be->dpcm[stream].state); + dpcm_state_string(be->dpcm[stream].state)); continue; } @@ -1743,9 +1708,9 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) if (err < 0) { be->dpcm[stream].users--; if (be->dpcm[stream].users < 0) - dev_err(be->dev, "ASoC: no users %s at unwind %d\n", + dev_err(be->dev, "ASoC: no users %s at unwind %s\n", snd_pcm_direction_name(stream), - be->dpcm[stream].state); + dpcm_state_string(be->dpcm[stream].state)); be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; goto unwind; @@ -1793,7 +1758,6 @@ static void dpcm_runtime_setup_fe(struct snd_pcm_substream *substream) soc_pcm_hw_update_rate(hw, cpu_stream); soc_pcm_hw_update_chan(hw, cpu_stream); soc_pcm_hw_update_format(hw, cpu_stream); - soc_pcm_hw_update_subformat(hw, cpu_stream); } } @@ -1831,7 +1795,6 @@ static void dpcm_runtime_setup_be_format(struct snd_pcm_substream *substream) codec_stream = snd_soc_dai_get_pcm_stream(dai, stream); soc_pcm_hw_update_format(hw, codec_stream); - soc_pcm_hw_update_subformat(hw, codec_stream); } } } @@ -2044,7 +2007,7 @@ void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) snd_soc_dpcm_get_substream(be, stream); /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; /* only free hw when no longer used - check all FEs */ @@ -2110,7 +2073,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) be_substream = snd_soc_dpcm_get_substream(be, stream); /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; /* copy params for each dpcm */ @@ -2138,7 +2101,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: hw_params BE %s\n", be->dai_link->name); - ret = __soc_pcm_hw_params(be, be_substream, &hw_params); + ret = __soc_pcm_hw_params(be_substream, &hw_params); if (ret < 0) goto unwind; @@ -2155,7 +2118,7 @@ unwind: be = dpcm->be; be_substream = snd_soc_dpcm_get_substream(be, stream); - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; /* only allow hw_free() if no connected FEs are running */ @@ -2194,7 +2157,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, params_channels(params), params_format(params)); /* call hw_params on the frontend */ - ret = __soc_pcm_hw_params(fe, substream, params); + ret = __soc_pcm_hw_params(substream, params); if (ret < 0) dpcm_be_dai_hw_free(fe, stream); else @@ -2225,7 +2188,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, snd_pcm_stream_lock_irqsave_nested(be_substream, flags); /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) goto next; dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n", @@ -2390,23 +2353,23 @@ static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream, ret = soc_pcm_trigger(substream, cmd); if (ret < 0) - return ret; + goto end; ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); - return ret; } - /* call trigger on the frontend after the backend. */ - ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); - if (ret < 0) - return ret; - - dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", - fe->dai_link->name, cmd); + else { + ret = dpcm_be_dai_trigger(fe, substream->stream, cmd); + if (ret < 0) + goto end; - ret = soc_pcm_trigger(substream, cmd); + dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n", + fe->dai_link->name, cmd); - return ret; + ret = soc_pcm_trigger(substream, cmd); + } +end: + return snd_soc_ret(fe->dev, ret, "trigger FE cmd: %d failed\n", cmd); } static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) @@ -2414,46 +2377,17 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream); int stream = substream->stream; int ret = 0; + int fe_first; enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; switch (trigger) { case SND_SOC_DPCM_TRIGGER_PRE: - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_DRAIN: - ret = dpcm_dai_trigger_fe_be(substream, cmd, true); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = dpcm_dai_trigger_fe_be(substream, cmd, false); - break; - default: - ret = -EINVAL; - break; - } + fe_first = true; break; case SND_SOC_DPCM_TRIGGER_POST: - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_DRAIN: - ret = dpcm_dai_trigger_fe_be(substream, cmd, false); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = dpcm_dai_trigger_fe_be(substream, cmd, true); - break; - default: - ret = -EINVAL; - break; - } + fe_first = false; break; default: dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd, @@ -2462,12 +2396,26 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd) goto out; } - if (ret < 0) { - dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n", - cmd, ret); - goto out; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_DRAIN: + ret = dpcm_dai_trigger_fe_be(substream, cmd, fe_first); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = dpcm_dai_trigger_fe_be(substream, cmd, !fe_first); + break; + default: + ret = -EINVAL; + break; } + if (ret < 0) + goto out; + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -2517,7 +2465,7 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) snd_soc_dpcm_get_substream(be, stream); /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; if (!snd_soc_dpcm_can_be_prepared(fe, be, stream)) @@ -2539,7 +2487,13 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; } - return soc_pcm_ret(fe, ret); + /* + * Don't use soc_pcm_ret() on .prepare callback to lower error log severity + * + * We don't want to log an error since we do not want to give userspace a way to do a + * denial-of-service attack on the syslog / diskspace. + */ + return ret; } static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) @@ -2579,7 +2533,13 @@ out: dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); snd_soc_dpcm_mutex_unlock(fe); - return soc_pcm_ret(fe, ret); + /* + * Don't use soc_pcm_ret() on .prepare callback to lower error log severity + * + * We don't want to log an error since we do not want to give userspace a way to do a + * denial-of-service attack on the syslog / diskspace. + */ + return ret; } static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) @@ -2612,8 +2572,8 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) /* Only start the BE if the FE is ready */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_FREE || fe->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE) { - dev_err(fe->dev, "ASoC: FE %s is not ready %d\n", - fe->dai_link->name, fe->dpcm[stream].state); + dev_err(fe->dev, "ASoC: FE %s is not ready %s\n", + fe->dai_link->name, dpcm_state_string(fe->dpcm[stream].state)); ret = -EINVAL; goto disconnect; } @@ -2663,7 +2623,7 @@ disconnect: struct snd_soc_pcm_runtime *be = dpcm->be; /* is this op for this BE ? */ - if (!snd_soc_dpcm_be_can_update(fe, be, stream)) + if (!snd_soc_dpcm_can_be_update(fe, be, stream)) continue; if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_CLOSE || @@ -2683,11 +2643,9 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) if (!fe->dai_link->dynamic) return 0; - if (fe->dai_link->num_cpus > 1) { - dev_err(fe->dev, + if (fe->dai_link->num_cpus > 1) + return snd_soc_ret(fe->dev, -EINVAL, "%s doesn't support Multi CPU yet\n", __func__); - return -EINVAL; - } /* only check active links */ if (!snd_soc_dai_active(snd_soc_rtd_to_cpu(fe, 0))) @@ -2714,7 +2672,14 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) return paths; /* update any playback/capture paths */ - count = dpcm_process_paths(fe, stream, &list, new); + /* + * Find the corresponding BE DAIs that source or sink audio to this + * FE substream. + */ + if (new) + count = dpcm_add_paths(fe, stream, &list); + else + count = dpcm_prune_paths(fe, stream, &list); if (count) { dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); if (new) @@ -2758,7 +2723,8 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) out: snd_soc_dpcm_mutex_unlock(card); - return ret; + + return snd_soc_ret(card->dev, ret, "%s() failed\n", __func__); } EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update); @@ -2805,7 +2771,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) goto open_end; /* calculate valid and active FE <-> BE dpcms */ - dpcm_process_paths(fe, stream, &list, 1); + dpcm_add_paths(fe, stream, &list); ret = dpcm_fe_dai_startup(fe_substream); if (ret < 0) @@ -2832,10 +2798,9 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd, int has_capture = 0; int i; - if (dai_link->dynamic && dai_link->num_cpus > 1) { - dev_err(rtd->dev, "DPCM doesn't support Multi CPU for Front-Ends yet\n"); - return -EINVAL; - } + if (dai_link->dynamic && dai_link->num_cpus > 1) + return snd_soc_ret(rtd->dev, -EINVAL, + "DPCM doesn't support Multi CPU for Front-Ends yet\n"); /* Adapt stream for codec2codec links */ cpu_capture = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_CAPTURE); @@ -2877,12 +2842,9 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd, if (dai_link->capture_only) has_playback = 0; - if (!has_playback && !has_capture) { - dev_err(rtd->dev, "substream %s has no playback, no capture\n", - dai_link->stream_name); - - return -EINVAL; - } + if (!has_playback && !has_capture) + return snd_soc_ret(rtd->dev, -EINVAL, + "substream %s has no playback, no capture\n", dai_link->stream_name); *playback = has_playback; *capture = has_capture; @@ -2922,11 +2884,10 @@ static int soc_create_pcm(struct snd_pcm **pcm, ret = snd_pcm_new(rtd->card->snd_card, new_name, rtd->id, playback, capture, pcm); } - if (ret < 0) { - dev_err(rtd->card->dev, "ASoC: can't create pcm %s for dailink %s: %d\n", - new_name, rtd->dai_link->name, ret); - return ret; - } + if (ret < 0) + return snd_soc_ret(rtd->dev, ret, + "can't create pcm %s for dailink %s\n", new_name, rtd->dai_link->name); + dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n", rtd->id, new_name); return 0; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 43003d2d3666..7b0b8531bb32 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -131,8 +131,8 @@ static const struct snd_soc_tplg_kcontrol_ops io_ops[] = { snd_soc_put_enum_double, NULL}, {SND_SOC_TPLG_CTL_BYTES, snd_soc_bytes_get, snd_soc_bytes_put, snd_soc_bytes_info}, - {SND_SOC_TPLG_CTL_RANGE, snd_soc_get_volsw_range, - snd_soc_put_volsw_range, snd_soc_info_volsw_range}, + {SND_SOC_TPLG_CTL_RANGE, snd_soc_get_volsw, + snd_soc_put_volsw, snd_soc_info_volsw}, {SND_SOC_TPLG_CTL_VOLSW_XR_SX, snd_soc_get_xr_sx, snd_soc_put_xr_sx, snd_soc_info_xr_sx}, {SND_SOC_TPLG_CTL_STROBE, snd_soc_get_strobe, @@ -220,15 +220,6 @@ static int get_widget_id(int tplg_type) return -EINVAL; } -static inline void soc_bind_err(struct soc_tplg *tplg, - struct snd_soc_tplg_ctl_hdr *hdr, int index) -{ - dev_err(tplg->dev, - "ASoC: invalid control type (g,p,i) %d:%d:%d index %d at 0x%lx\n", - hdr->ops.get, hdr->ops.put, hdr->ops.info, index, - soc_tplg_get_offset(tplg)); -} - static inline void soc_control_err(struct soc_tplg *tplg, struct snd_soc_tplg_ctl_hdr *hdr, const char *name) { @@ -678,6 +669,7 @@ static int soc_tplg_control_dmixer_create(struct soc_tplg *tplg, struct snd_kcon sm->min = le32_to_cpu(mc->min); sm->invert = le32_to_cpu(mc->invert); sm->platform_max = le32_to_cpu(mc->platform_max); + sm->num_channels = le32_to_cpu(mc->num_channels); /* map io handlers */ err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg); @@ -992,35 +984,26 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, return -EINVAL; } - switch (le32_to_cpu(control_hdr->ops.info)) { - case SND_SOC_TPLG_CTL_VOLSW: - case SND_SOC_TPLG_CTL_STROBE: - case SND_SOC_TPLG_CTL_VOLSW_SX: - case SND_SOC_TPLG_CTL_VOLSW_XR_SX: - case SND_SOC_TPLG_CTL_RANGE: - case SND_SOC_TPLG_DAPM_CTL_VOLSW: - case SND_SOC_TPLG_DAPM_CTL_PIN: + switch (le32_to_cpu(control_hdr->type)) { + case SND_SOC_TPLG_TYPE_MIXER: ret = soc_tplg_dmixer_create(tplg, le32_to_cpu(hdr->payload_size)); break; - case SND_SOC_TPLG_CTL_ENUM: - case SND_SOC_TPLG_CTL_ENUM_VALUE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + case SND_SOC_TPLG_TYPE_ENUM: ret = soc_tplg_denum_create(tplg, le32_to_cpu(hdr->payload_size)); break; - case SND_SOC_TPLG_CTL_BYTES: + case SND_SOC_TPLG_TYPE_BYTES: ret = soc_tplg_dbytes_create(tplg, le32_to_cpu(hdr->payload_size)); break; default: - soc_bind_err(tplg, control_hdr, i); - return -EINVAL; + ret = -EINVAL; + break; } + if (ret < 0) { - dev_err(tplg->dev, "ASoC: invalid control\n"); + dev_err(tplg->dev, "ASoC: invalid control type: %d, index: %d at 0x%lx\n", + control_hdr->type, i, soc_tplg_get_offset(tplg)); return ret; } - } return 0; @@ -1101,14 +1084,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, } ret = snd_soc_dapm_add_routes(dapm, route, 1); - if (ret) { - if (!dapm->card->disable_route_checks) { - dev_err(tplg->dev, "ASoC: dapm_add_routes failed: %d\n", ret); - break; - } - dev_info(tplg->dev, - "ASoC: disable_route_checks set, ignoring dapm_add_routes errors\n"); - } + if (ret) + break; } return ret; @@ -1190,13 +1167,9 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, for (i = 0; i < le32_to_cpu(w->num_kcontrols); i++) { control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; - switch (le32_to_cpu(control_hdr->ops.info)) { - case SND_SOC_TPLG_CTL_VOLSW: - case SND_SOC_TPLG_CTL_STROBE: - case SND_SOC_TPLG_CTL_VOLSW_SX: - case SND_SOC_TPLG_CTL_VOLSW_XR_SX: - case SND_SOC_TPLG_CTL_RANGE: - case SND_SOC_TPLG_DAPM_CTL_VOLSW: + + switch (le32_to_cpu(control_hdr->type)) { + case SND_SOC_TPLG_TYPE_MIXER: /* volume mixer */ kc[i].index = mixer_count; kcontrol_type[i] = SND_SOC_TPLG_TYPE_MIXER; @@ -1205,11 +1178,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, if (ret < 0) goto hdr_err; break; - case SND_SOC_TPLG_CTL_ENUM: - case SND_SOC_TPLG_CTL_ENUM_VALUE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: - case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: + case SND_SOC_TPLG_TYPE_ENUM: /* enumerated mixer */ kc[i].index = enum_count; kcontrol_type[i] = SND_SOC_TPLG_TYPE_ENUM; @@ -1218,7 +1187,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, if (ret < 0) goto hdr_err; break; - case SND_SOC_TPLG_CTL_BYTES: + case SND_SOC_TPLG_TYPE_BYTES: /* bytes control */ kc[i].index = bytes_count; kcontrol_type[i] = SND_SOC_TPLG_TYPE_BYTES; diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index aa93e77ac937..5e3e4f14c392 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -7,7 +7,7 @@ // Author: Mark Brown <broonie@opensource.wolfsonmicro.com> // Liam Girdwood <lrg@slimlogic.co.uk> -#include <linux/platform_device.h> +#include <linux/device/faux.h> #include <linux/export.h> #include <linux/math.h> #include <sound/core.h> @@ -15,6 +15,33 @@ #include <sound/pcm_params.h> #include <sound/soc.h> +int snd_soc_ret(const struct device *dev, int ret, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + /* Positive, Zero values are not errors */ + if (ret >= 0) + return ret; + + /* Negative values might be errors */ + switch (ret) { + case -EPROBE_DEFER: + case -ENOTSUPP: + case -EOPNOTSUPP: + break; + default: + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + dev_err(dev, "ASoC error (%d): %pV", ret, &vaf); + } + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_ret); + int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots) { return sample_size * channels * tdm_slots; @@ -235,48 +262,38 @@ struct snd_soc_dai_link_component snd_soc_dummy_dlc = { }; EXPORT_SYMBOL_GPL(snd_soc_dummy_dlc); -static int snd_soc_dummy_probe(struct platform_device *pdev) +static int snd_soc_dummy_probe(struct faux_device *fdev) { int ret; - ret = devm_snd_soc_register_component(&pdev->dev, + ret = devm_snd_soc_register_component(&fdev->dev, &dummy_codec, &dummy_dai, 1); if (ret < 0) return ret; - ret = devm_snd_soc_register_component(&pdev->dev, &dummy_platform, + ret = devm_snd_soc_register_component(&fdev->dev, &dummy_platform, NULL, 0); return ret; } -static struct platform_driver soc_dummy_driver = { - .driver = { - .name = "snd-soc-dummy", - }, +static struct faux_device_ops soc_dummy_ops = { .probe = snd_soc_dummy_probe, }; -static struct platform_device *soc_dummy_dev; +static struct faux_device *soc_dummy_dev; int __init snd_soc_util_init(void) { - int ret; - - soc_dummy_dev = - platform_device_register_simple("snd-soc-dummy", -1, NULL, 0); - if (IS_ERR(soc_dummy_dev)) - return PTR_ERR(soc_dummy_dev); + soc_dummy_dev = faux_device_create("snd-soc-dummy", NULL, + &soc_dummy_ops); + if (!soc_dummy_dev) + return -ENODEV; - ret = platform_driver_register(&soc_dummy_driver); - if (ret != 0) - platform_device_unregister(soc_dummy_dev); - - return ret; + return 0; } void snd_soc_util_exit(void) { - platform_driver_unregister(&soc_dummy_driver); - platform_device_unregister(soc_dummy_dev); + faux_device_destroy(soc_dummy_dev); } diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index f4cafe801017..3ea82fa72e35 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -32,6 +32,7 @@ config SND_SOC_SOF_AMD_COMMON config SND_SOC_SOF_AMD_RENOIR tristate "SOF support for RENOIR" depends on SND_SOC_SOF_PCI + depends on AMD_NODE select SND_SOC_SOF_AMD_COMMON help Select this option for SOF support on AMD Renoir platform @@ -39,6 +40,7 @@ config SND_SOC_SOF_AMD_RENOIR config SND_SOC_SOF_AMD_VANGOGH tristate "SOF support for VANGOGH" depends on SND_SOC_SOF_PCI + depends on AMD_NODE select SND_SOC_SOF_AMD_COMMON help Select this option for SOF support @@ -49,6 +51,7 @@ config SND_SOC_SOF_AMD_VANGOGH config SND_SOC_SOF_AMD_REMBRANDT tristate "SOF support for REMBRANDT" depends on SND_SOC_SOF_PCI + depends on AMD_NODE select SND_SOC_SOF_AMD_COMMON help Select this option for SOF support on AMD Rembrandt platform @@ -81,6 +84,7 @@ config SND_SOC_SOF_AMD_SOUNDWIRE config SND_SOC_SOF_AMD_ACP63 tristate "SOF support for ACP6.3 platform" depends on SND_SOC_SOF_PCI + depends on AMD_NODE select SND_SOC_SOF_AMD_COMMON select SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE help @@ -92,6 +96,7 @@ config SND_SOC_SOF_AMD_ACP63 config SND_SOC_SOF_AMD_ACP70 tristate "SOF support for ACP7.0 platform" depends on SND_SOC_SOF_PCI + depends on AMD_NODE select SND_SOC_SOF_AMD_COMMON help Select this option for SOF support on diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 5f371d9263f3..22d4b807e1bb 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -165,8 +165,9 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) int dsp_msg, dsp_ack; unsigned int status; - if (sdev->first_boot && sdev->fw_state != SOF_FW_BOOT_COMPLETE) { + if (unlikely(sdev->first_boot && sdev->fw_state != SOF_FW_BOOT_COMPLETE)) { acp_mailbox_read(sdev, sdev->dsp_box.offset, &status, sizeof(status)); + if ((status & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { snd_sof_dsp_panic(sdev, sdev->dsp_box.offset + sizeof(status), true); @@ -188,13 +189,21 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write); if (dsp_ack) { - spin_lock_irq(&sdev->ipc_lock); - /* handle immediate reply from DSP core */ - acp_dsp_ipc_get_reply(sdev); - snd_sof_ipc_reply(sdev, 0); - /* set the done bit */ - acp_dsp_ipc_dsp_done(sdev); - spin_unlock_irq(&sdev->ipc_lock); + if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) { + spin_lock_irq(&sdev->ipc_lock); + + /* handle immediate reply from DSP core */ + acp_dsp_ipc_get_reply(sdev); + snd_sof_ipc_reply(sdev, 0); + /* set the done bit */ + acp_dsp_ipc_dsp_done(sdev); + + spin_unlock_irq(&sdev->ipc_lock); + } else { + dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_BOOT_COMPLETE: %#x\n", + dsp_ack); + } + ipc_irq = true; } diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 33648ff8b833..7c6d647fa253 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -16,6 +16,8 @@ #include <linux/module.h> #include <linux/pci.h> +#include <asm/amd_node.h> + #include "../ops.h" #include "acp.h" #include "acp-dsp-offset.h" @@ -27,6 +29,7 @@ MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug"); static struct acp_quirk_entry quirk_valve_galileo = { .signed_fw_image = true, .skip_iram_dram_size_mod = true, + .post_fw_run_delay = true, }; const struct dmi_system_id acp_sof_quirk_table[] = { @@ -42,24 +45,6 @@ const struct dmi_system_id acp_sof_quirk_table[] = { }; EXPORT_SYMBOL_GPL(acp_sof_quirk_table); -static int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data) -{ - pci_write_config_dword(dev, 0x60, smn_addr); - pci_write_config_dword(dev, 0x64, data); - - return 0; -} - -static int smn_read(struct pci_dev *dev, u32 smn_addr) -{ - u32 data = 0; - - pci_write_config_dword(dev, 0x60, smn_addr); - pci_read_config_dword(dev, 0x64, &data); - - return data; -} - static void init_dma_descriptor(struct acp_dev_data *adata) { struct snd_sof_dev *sdev = adata->dev; @@ -208,11 +193,11 @@ int configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr, static int psp_mbox_ready(struct acp_dev_data *adata, bool ack) { struct snd_sof_dev *sdev = adata->dev; - int ret; - u32 data; + int ret, data; + + ret = read_poll_timeout(smn_read_register, data, data > 0 && data & MBOX_READY_MASK, + MBOX_DELAY_US, ACP_PSP_TIMEOUT_US, false, MP0_C2PMSG_114_REG); - ret = read_poll_timeout(smn_read, data, data & MBOX_READY_MASK, MBOX_DELAY_US, - ACP_PSP_TIMEOUT_US, false, adata->smn_dev, MP0_C2PMSG_114_REG); if (!ret) return 0; @@ -240,8 +225,8 @@ static int psp_send_cmd(struct acp_dev_data *adata, int cmd) return -EINVAL; /* Get a non-zero Doorbell value from PSP */ - ret = read_poll_timeout(smn_read, data, data, MBOX_DELAY_US, ACP_PSP_TIMEOUT_US, false, - adata->smn_dev, MP0_C2PMSG_73_REG); + ret = read_poll_timeout(smn_read_register, data, data > 0, MBOX_DELAY_US, + ACP_PSP_TIMEOUT_US, false, MP0_C2PMSG_73_REG); if (ret) { dev_err(sdev->dev, "Failed to get Doorbell from MBOX %x\n", MP0_C2PMSG_73_REG); @@ -253,10 +238,14 @@ static int psp_send_cmd(struct acp_dev_data *adata, int cmd) if (ret) return ret; - smn_write(adata->smn_dev, MP0_C2PMSG_114_REG, cmd); + ret = amd_smn_write(0, MP0_C2PMSG_114_REG, cmd); + if (ret) + return ret; /* Ring the Doorbell for PSP */ - smn_write(adata->smn_dev, MP0_C2PMSG_73_REG, data); + ret = amd_smn_write(0, MP0_C2PMSG_73_REG, data); + if (ret) + return ret; /* Check MBOX ready as PSP ack */ ret = psp_mbox_ready(adata, 1); @@ -770,16 +759,10 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) adata->pci_rev = pci->revision; mutex_init(&adata->acp_lock); sdev->pdata->hw_pdata = adata; - adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL); - if (!adata->smn_dev) { - dev_err(sdev->dev, "Failed to get host bridge device\n"); - ret = -ENODEV; - goto unregister_dev; - } ret = acp_init(sdev); if (ret < 0) - goto free_smn_dev; + goto unregister_dev; sdev->ipc_irq = pci->irq; ret = request_threaded_irq(sdev->ipc_irq, acp_irq_handler, acp_irq_thread, @@ -787,7 +770,7 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "failed to register IRQ %d\n", sdev->ipc_irq); - goto free_smn_dev; + goto unregister_dev; } /* scan SoundWire capabilities exposed by DSDT */ @@ -800,7 +783,6 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: SoundWire probe error\n"); free_irq(sdev->ipc_irq, sdev); - pci_dev_put(adata->smn_dev); return ret; } @@ -846,8 +828,6 @@ skip_soundwire: free_ipc_irq: free_irq(sdev->ipc_irq, sdev); -free_smn_dev: - pci_dev_put(adata->smn_dev); unregister_dev: platform_device_unregister(adata->dmic_dev); return ret; @@ -858,9 +838,6 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev) { struct acp_dev_data *adata = sdev->pdata->hw_pdata; - if (adata->smn_dev) - pci_dev_put(adata->smn_dev); - if (adata->sdw) amd_sof_sdw_exit(sdev); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 800594440f73..d084db34eed8 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -197,7 +197,6 @@ struct acp_dsp_stream { struct sof_amd_acp_desc { const char *name; - unsigned int host_bridge_id; u32 pgfsm_base; u32 ext_intr_enb; u32 ext_intr_cntl; @@ -220,6 +219,7 @@ struct sof_amd_acp_desc { struct acp_quirk_entry { bool signed_fw_image; bool skip_iram_dram_size_mod; + bool post_fw_run_delay; }; /* Common device data struct for ACP devices */ @@ -255,7 +255,6 @@ struct acp_dev_data { struct dma_descriptor dscr_info[ACP_MAX_DESC]; struct acp_dsp_stream stream_buf[ACP_MAX_STREAM]; struct acp_dsp_stream *dtrace_stream; - struct pci_dev *smn_dev; struct acp_dsp_stream *probe_stream; bool enable_fw_debug; bool is_dram_in_use; diff --git a/sound/soc/sof/amd/pci-acp63.c b/sound/soc/sof/amd/pci-acp63.c index ffe7c755d655..21ffdfdcf03d 100644 --- a/sound/soc/sof/amd/pci-acp63.c +++ b/sound/soc/sof/amd/pci-acp63.c @@ -28,7 +28,6 @@ #define ACP6x_REG_END 0x125C000 static const struct sof_amd_acp_desc acp63_chip_info = { - .host_bridge_id = HOST_BRIDGE_ACP63, .pgfsm_base = ACP6X_PGFSM_BASE, .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB, .ext_intr_cntl = ACP6X_EXTERNAL_INTR_CNTL, @@ -105,7 +104,7 @@ static struct pci_driver snd_sof_pci_amd_acp63_driver = { .probe = acp63_pci_probe, .remove = acp63_pci_remove, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_amd_acp63_driver); diff --git a/sound/soc/sof/amd/pci-acp70.c b/sound/soc/sof/amd/pci-acp70.c index 3647ec992e95..8fa1170a2161 100644 --- a/sound/soc/sof/amd/pci-acp70.c +++ b/sound/soc/sof/amd/pci-acp70.c @@ -28,7 +28,6 @@ #define ACP70_REG_END 0x125C000 static const struct sof_amd_acp_desc acp70_chip_info = { - .host_bridge_id = HOST_BRIDGE_ACP70, .pgfsm_base = ACP70_PGFSM_BASE, .ext_intr_enb = ACP70_EXTERNAL_INTR_ENB, .ext_intr_cntl = ACP70_EXTERNAL_INTR_CNTL, @@ -101,7 +100,7 @@ static struct pci_driver snd_sof_pci_amd_acp70_driver = { .probe = acp70_pci_probe, .remove = acp70_pci_remove, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_amd_acp70_driver); diff --git a/sound/soc/sof/amd/pci-rmb.c b/sound/soc/sof/amd/pci-rmb.c index cbb4d5282664..0233b6ba2d2e 100644 --- a/sound/soc/sof/amd/pci-rmb.c +++ b/sound/soc/sof/amd/pci-rmb.c @@ -28,7 +28,6 @@ #define ACP6X_FUTURE_REG_ACLK_0 0x1854 static const struct sof_amd_acp_desc rembrandt_chip_info = { - .host_bridge_id = HOST_BRIDGE_RMB, .pgfsm_base = ACP6X_PGFSM_BASE, .ext_intr_stat = ACP6X_EXT_INTR_STAT, .dsp_intr_base = ACP6X_DSP_SW_INTR_BASE, diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c index b7d558cb1fd7..2b7fbcf11b55 100644 --- a/sound/soc/sof/amd/pci-rn.c +++ b/sound/soc/sof/amd/pci-rn.c @@ -28,7 +28,6 @@ #define ACP3X_FUTURE_REG_ACLK_0 0x1860 static const struct sof_amd_acp_desc renoir_chip_info = { - .host_bridge_id = HOST_BRIDGE_CZN, .pgfsm_base = ACP3X_PGFSM_BASE, .ext_intr_stat = ACP3X_EXT_INTR_STAT, .dsp_intr_base = ACP3X_DSP_SW_INTR_BASE, @@ -98,7 +97,7 @@ static struct pci_driver snd_sof_pci_amd_rn_driver = { .probe = acp_pci_rn_probe, .remove = acp_pci_rn_remove, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_amd_rn_driver); diff --git a/sound/soc/sof/amd/pci-vangogh.c b/sound/soc/sof/amd/pci-vangogh.c index 53f64d6bc91b..6ef692becfb9 100644 --- a/sound/soc/sof/amd/pci-vangogh.c +++ b/sound/soc/sof/amd/pci-vangogh.c @@ -13,11 +13,9 @@ #include <linux/module.h> #include <linux/pci.h> -#include <linux/platform_device.h> #include <sound/sof.h> #include <sound/soc-acpi.h> -#include "../ops.h" #include "../sof-pci-dev.h" #include "../../amd/mach-config.h" #include "acp.h" @@ -27,7 +25,6 @@ static const struct sof_amd_acp_desc vangogh_chip_info = { .name = "vangogh", - .host_bridge_id = HOST_BRIDGE_VGH, .pgfsm_base = ACP5X_PGFSM_BASE, .ext_intr_stat = ACP5X_EXT_INTR_STAT, .dsp_intr_base = ACP5X_DSP_SW_INTR_BASE, @@ -93,7 +90,7 @@ static struct pci_driver snd_sof_pci_amd_vgh_driver = { .probe = acp_pci_vgh_probe, .remove = acp_pci_vgh_remove, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_amd_vgh_driver); diff --git a/sound/soc/sof/amd/vangogh.c b/sound/soc/sof/amd/vangogh.c index 8e2672106ac6..6ed5f9aaa414 100644 --- a/sound/soc/sof/amd/vangogh.c +++ b/sound/soc/sof/amd/vangogh.c @@ -11,13 +11,10 @@ * Hardware interface for Audio DSP on Vangogh platform */ -#include <linux/platform_device.h> +#include <linux/delay.h> #include <linux/module.h> -#include "../ops.h" -#include "../sof-audio.h" #include "acp.h" -#include "acp-dsp-offset.h" #define I2S_HS_INSTANCE 0 #define I2S_BT_INSTANCE 1 @@ -136,6 +133,20 @@ static struct snd_soc_dai_driver vangogh_sof_dai[] = { }, }; +static int sof_vangogh_post_fw_run_delay(struct snd_sof_dev *sdev) +{ + /* + * Resuming from suspend in some cases my cause the DSP firmware + * to enter an unrecoverable faulty state. Delaying a bit any host + * to DSP transmission right after firmware boot completion seems + * to resolve the issue. + */ + if (!sdev->first_boot) + usleep_range(100, 150); + + return 0; +} + /* Vangogh ops */ struct snd_sof_dsp_ops sof_vangogh_ops; EXPORT_SYMBOL_NS(sof_vangogh_ops, "SND_SOC_SOF_AMD_COMMON"); @@ -157,6 +168,9 @@ int sof_vangogh_ops_init(struct snd_sof_dev *sdev) if (quirks->signed_fw_image) sof_vangogh_ops.load_firmware = acp_sof_load_signed_firmware; + + if (quirks->post_fw_run_delay) + sof_vangogh_ops.post_fw_run = sof_vangogh_post_fw_run_delay; } return 0; diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig index 4751b04d5e6f..327e2df94a58 100644 --- a/sound/soc/sof/imx/Kconfig +++ b/sound/soc/sof/imx/Kconfig @@ -32,22 +32,13 @@ config SND_SOC_SOF_IMX8 Say Y if you have such a device. If unsure select "N". -config SND_SOC_SOF_IMX8M - tristate "SOF support for i.MX8M" +config SND_SOC_SOF_IMX9 + tristate "SOF support for i.MX9" depends on IMX_DSP select SND_SOC_SOF_IMX_COMMON help - This adds support for Sound Open Firmware for NXP i.MX8M platforms. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_SOF_IMX8ULP - tristate "SOF support for i.MX8ULP" - depends on IMX_DSP - select SND_SOC_SOF_IMX_COMMON - help - This adds support for Sound Open Firmware for NXP i.MX8ULP platforms. - Say Y if you have such a device. + This adds support for Sound Open Firmware for NXP i.MX9 platforms. + Say Y if you need such a device. If unsure select "N". endif ## SND_SOC_SOF_IMX_TOPLEVEL diff --git a/sound/soc/sof/imx/Makefile b/sound/soc/sof/imx/Makefile index be0bf0736dfa..74b5ecad8fe8 100644 --- a/sound/soc/sof/imx/Makefile +++ b/sound/soc/sof/imx/Makefile @@ -1,11 +1,9 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) snd-sof-imx8-y := imx8.o -snd-sof-imx8m-y := imx8m.o -snd-sof-imx8ulp-y := imx8ulp.o +snd-sof-imx9-y := imx9.o snd-sof-imx-common-y := imx-common.o obj-$(CONFIG_SND_SOC_SOF_IMX8) += snd-sof-imx8.o -obj-$(CONFIG_SND_SOC_SOF_IMX8M) += snd-sof-imx8m.o -obj-$(CONFIG_SND_SOC_SOF_IMX8ULP) += snd-sof-imx8ulp.o +obj-$(CONFIG_SND_SOC_SOF_IMX9) += snd-sof-imx9.o obj-$(CONFIG_SND_SOC_SOF_IMX_COMMON) += imx-common.o diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index fce6d9cf6a6b..62bf707aa909 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -1,11 +1,16 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) // -// Copyright 2020 NXP +// Copyright 2020-2025 NXP // // Common helpers for the audio DSP on i.MX8 +#include <linux/firmware/imx/dsp.h> #include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> +#include <linux/pm_domain.h> #include <sound/sof/xtensa.h> + #include "../ops.h" #include "imx-common.h" @@ -74,5 +79,428 @@ void imx8_dump(struct snd_sof_dev *sdev, u32 flags) } EXPORT_SYMBOL(imx8_dump); +static void imx_handle_reply(struct imx_dsp_ipc *ipc) +{ + struct snd_sof_dev *sdev; + unsigned long flags; + + sdev = imx_dsp_get_data(ipc); + + spin_lock_irqsave(&sdev->ipc_lock, flags); + snd_sof_ipc_process_reply(sdev, 0); + spin_unlock_irqrestore(&sdev->ipc_lock, flags); +} + +static void imx_handle_request(struct imx_dsp_ipc *ipc) +{ + struct snd_sof_dev *sdev; + u32 panic_code; + + sdev = imx_dsp_get_data(ipc); + + if (get_chip_info(sdev)->ipc_info.has_panic_code) { + sof_mailbox_read(sdev, sdev->debug_box.offset + 0x4, + &panic_code, + sizeof(panic_code)); + + if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, panic_code, true); + return; + } + } + + snd_sof_ipc_msgs_rx(sdev); +} + +static struct imx_dsp_ops imx_ipc_ops = { + .handle_reply = imx_handle_reply, + .handle_request = imx_handle_request, +}; + +static int imx_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct imx_common_data *common = sdev->pdata->hw_pdata; + + sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size); + imx_dsp_ring_doorbell(common->ipc_handle, 0x0); + + return 0; +} + +static int imx_get_bar_index(struct snd_sof_dev *sdev, u32 type) +{ + switch (type) { + case SOF_FW_BLK_TYPE_IRAM: + case SOF_FW_BLK_TYPE_SRAM: + return type; + default: + return -EINVAL; + } +} + +static int imx_get_mailbox_offset(struct snd_sof_dev *sdev) +{ + return get_chip_info(sdev)->ipc_info.boot_mbox_offset; +} + +static int imx_get_window_offset(struct snd_sof_dev *sdev, u32 id) +{ + return get_chip_info(sdev)->ipc_info.window_offset; +} + +static int imx_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target) +{ + sdev->dsp_power_state = *target; + + return 0; +} + +static int imx_common_resume(struct snd_sof_dev *sdev) +{ + struct imx_common_data *common; + int ret, i; + + common = sdev->pdata->hw_pdata; + + ret = clk_bulk_prepare_enable(common->clk_num, common->clks); + if (ret) + dev_err(sdev->dev, "failed to enable clocks: %d\n", ret); + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_request_channel(common->ipc_handle, i); + + /* done. If need be, core will be started by SOF core immediately after */ + return 0; +} + +static int imx_common_suspend(struct snd_sof_dev *sdev) +{ + struct imx_common_data *common; + int i, ret; + + common = sdev->pdata->hw_pdata; + + ret = imx_chip_core_shutdown(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to shutdown core: %d\n", ret); + return ret; + } + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_free_channel(common->ipc_handle, i); + + clk_bulk_disable_unprepare(common->clk_num, common->clks); + + return 0; +} + +static int imx_runtime_resume(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D0, + }; + int ret; + + ret = imx_common_resume(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to runtime common resume: %d\n", ret); + return ret; + } + + return snd_sof_dsp_set_power_state(sdev, &target_state); +} + +static int imx_resume(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D0, + }; + int ret; + + ret = imx_common_resume(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to common resume: %d\n", ret); + return ret; + } + + if (pm_runtime_suspended(sdev->dev)) { + pm_runtime_disable(sdev->dev); + pm_runtime_set_active(sdev->dev); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_enable(sdev->dev); + pm_runtime_idle(sdev->dev); + } + + return snd_sof_dsp_set_power_state(sdev, &target_state); +} + +static int imx_runtime_suspend(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_state = { + .state = SOF_DSP_PM_D3, + }; + int ret; + + ret = imx_common_suspend(sdev); + if (ret < 0) + dev_err(sdev->dev, "failed to runtime common suspend: %d\n", ret); + + return snd_sof_dsp_set_power_state(sdev, &target_state); +} + +static int imx_suspend(struct snd_sof_dev *sdev, unsigned int target_state) +{ + const struct sof_dsp_power_state target_power_state = { + .state = target_state, + }; + int ret; + + if (!pm_runtime_suspended(sdev->dev)) { + ret = imx_common_suspend(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to common suspend: %d\n", ret); + return ret; + } + } + + return snd_sof_dsp_set_power_state(sdev, &target_power_state); +} + +static int imx_region_name_to_blk_type(const char *region_name) +{ + if (!strcmp(region_name, "iram")) + return SOF_FW_BLK_TYPE_IRAM; + else if (!strcmp(region_name, "dram")) + return SOF_FW_BLK_TYPE_DRAM; + else if (!strcmp(region_name, "sram")) + return SOF_FW_BLK_TYPE_SRAM; + else + return -EINVAL; +} + +static int imx_parse_ioremap_memory(struct snd_sof_dev *sdev) +{ + const struct imx_chip_info *chip_info; + struct reserved_mem *reserved; + struct platform_device *pdev; + struct device_node *res_np; + phys_addr_t base, size; + struct resource *res; + int i, blk_type, ret; + + pdev = to_platform_device(sdev->dev); + chip_info = get_chip_info(sdev); + + for (i = 0; chip_info->memory[i].name; i++) { + blk_type = imx_region_name_to_blk_type(chip_info->memory[i].name); + if (blk_type < 0) + return dev_err_probe(sdev->dev, blk_type, + "no blk type for region %s\n", + chip_info->memory[i].name); + + if (!chip_info->memory[i].reserved) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + chip_info->memory[i].name); + if (!res) + return dev_err_probe(sdev->dev, -ENODEV, + "failed to fetch %s resource\n", + chip_info->memory[i].name); + + base = res->start; + size = resource_size(res); + } else { + ret = of_property_match_string(pdev->dev.of_node, + "memory-region-names", + chip_info->memory[i].name); + if (ret < 0) + return dev_err_probe(sdev->dev, ret, + "no valid index for %s\n", + chip_info->memory[i].name); + + res_np = of_parse_phandle(pdev->dev.of_node, + "memory-region", + ret); + if (!res_np) + return dev_err_probe(sdev->dev, -ENODEV, + "failed to parse phandle %s\n", + chip_info->memory[i].name); + + reserved = of_reserved_mem_lookup(res_np); + of_node_put(res_np); + if (!reserved) + return dev_err_probe(sdev->dev, -ENODEV, + "failed to get %s reserved\n", + chip_info->memory[i].name); + + base = reserved->base; + size = reserved->size; + } + + sdev->bar[blk_type] = devm_ioremap(sdev->dev, base, size); + if (!sdev->bar[blk_type]) + return dev_err_probe(sdev->dev, + -ENOMEM, + "failed to ioremap %s region\n", + chip_info->memory[i].name); + } + + return 0; +} + +static void imx_unregister_action(void *data) +{ + struct imx_common_data *common; + struct snd_sof_dev *sdev; + + sdev = data; + common = sdev->pdata->hw_pdata; + + if (get_chip_info(sdev)->has_dma_reserved) + of_reserved_mem_device_release(sdev->dev); + + platform_device_unregister(common->ipc_dev); +} + +static int imx_probe(struct snd_sof_dev *sdev) +{ + struct dev_pm_domain_attach_data domain_data = { + .pd_names = NULL, /* no filtering */ + .pd_flags = PD_FLAG_DEV_LINK_ON, + }; + struct imx_common_data *common; + struct platform_device *pdev; + int ret; + + pdev = to_platform_device(sdev->dev); + + common = devm_kzalloc(sdev->dev, sizeof(*common), GFP_KERNEL); + if (!common) + return dev_err_probe(sdev->dev, -ENOMEM, + "failed to allocate common data\n"); + sdev->pdata->hw_pdata = common; + + common->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp", + PLATFORM_DEVID_NONE, + pdev, sizeof(*pdev)); + if (IS_ERR(common->ipc_dev)) + return dev_err_probe(sdev->dev, PTR_ERR(common->ipc_dev), + "failed to create IPC device\n"); + + if (get_chip_info(sdev)->has_dma_reserved) { + ret = of_reserved_mem_device_init_by_name(sdev->dev, + pdev->dev.of_node, + "dma"); + if (ret) { + platform_device_unregister(common->ipc_dev); + + return dev_err_probe(sdev->dev, ret, + "failed to bind DMA region\n"); + } + } + + /* let the devres API take care of the cleanup */ + ret = devm_add_action_or_reset(sdev->dev, + imx_unregister_action, + sdev); + if (ret) + return dev_err_probe(sdev->dev, ret, "failed to add devm action\n"); + + common->ipc_handle = dev_get_drvdata(&common->ipc_dev->dev); + if (!common->ipc_handle) + return dev_err_probe(sdev->dev, -EPROBE_DEFER, + "failed to fetch IPC handle\n"); + + ret = imx_parse_ioremap_memory(sdev); + if (ret < 0) + return dev_err_probe(sdev->dev, ret, + "failed to parse/ioremap memory regions\n"); + + if (!sdev->dev->pm_domain) { + ret = devm_pm_domain_attach_list(sdev->dev, + &domain_data, &common->pd_list); + if (ret < 0) + return dev_err_probe(sdev->dev, ret, "failed to attach PDs\n"); + } + + ret = devm_clk_bulk_get_all(sdev->dev, &common->clks); + if (ret < 0) + return dev_err_probe(sdev->dev, ret, "failed to fetch clocks\n"); + common->clk_num = ret; + + ret = clk_bulk_prepare_enable(common->clk_num, common->clks); + if (ret < 0) + return dev_err_probe(sdev->dev, ret, "failed to enable clocks\n"); + + common->ipc_handle->ops = &imx_ipc_ops; + imx_dsp_set_data(common->ipc_handle, sdev); + + sdev->num_cores = 1; + sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; + sdev->dsp_box.offset = get_chip_info(sdev)->ipc_info.boot_mbox_offset; + + return imx_chip_probe(sdev); +} + +static void imx_remove(struct snd_sof_dev *sdev) +{ + struct imx_common_data *common; + int ret; + + common = sdev->pdata->hw_pdata; + + if (!pm_runtime_suspended(sdev->dev)) { + ret = imx_chip_core_shutdown(sdev); + if (ret < 0) + dev_err(sdev->dev, "failed to shutdown core: %d\n", ret); + + clk_bulk_disable_unprepare(common->clk_num, common->clks); + } +} + +const struct snd_sof_dsp_ops sof_imx_ops = { + .probe = imx_probe, + .remove = imx_remove, + + .run = imx_chip_core_kick, + .reset = imx_chip_core_reset, + + .block_read = sof_block_read, + .block_write = sof_block_write, + + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + + .send_msg = imx_send_msg, + .get_mailbox_offset = imx_get_mailbox_offset, + .get_window_offset = imx_get_window_offset, + + .ipc_msg_data = sof_ipc_msg_data, + .set_stream_data_offset = sof_set_stream_data_offset, + + .get_bar_index = imx_get_bar_index, + .load_firmware = snd_sof_load_firmware_memcpy, + + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, + + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + + .runtime_suspend = imx_runtime_suspend, + .runtime_resume = imx_runtime_resume, + .suspend = imx_suspend, + .resume = imx_resume, + + .set_power_state = imx_set_power_state, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; +EXPORT_SYMBOL(sof_imx_ops); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF helpers for IMX platforms"); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 13d7f3ef675e..9bd711dbb5d0 100644 --- a/sound/soc/sof/imx/imx-common.h +++ b/sound/soc/sof/imx/imx-common.h @@ -4,10 +4,159 @@ #define __IMX_COMMON_H__ #include <linux/clk.h> +#include <linux/of_platform.h> +#include <sound/sof/xtensa.h> + +#include "../sof-of-dev.h" +#include "../ops.h" #define EXCEPT_MAX_HDR_SIZE 0x400 #define IMX8_STACK_DUMP_SIZE 32 +/* chip_info refers to the data stored in struct sof_dev_desc's chip_info */ +#define get_chip_info(sdev)\ + ((const struct imx_chip_info *)((sdev)->pdata->desc->chip_info)) + +/* chip_pdata refers to the data stored in struct imx_common_data's chip_pdata */ +#define get_chip_pdata(sdev)\ + (((struct imx_common_data *)((sdev)->pdata->hw_pdata))->chip_pdata) + +/* can be used if: + * 1) The only supported IPC version is IPC3. + * 2) The default paths/FW name match values below. + * + * otherwise, just explicitly declare the structure + */ +#define IMX_SOF_DEV_DESC(mach_name, of_machs, \ + mach_chip_info, mach_ops, mach_ops_init) \ +static struct sof_dev_desc sof_of_##mach_name##_desc = { \ + .of_machines = of_machs, \ + .chip_info = mach_chip_info, \ + .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), \ + .ipc_default = SOF_IPC_TYPE_3, \ + .default_fw_path = { \ + [SOF_IPC_TYPE_3] = "imx/sof", \ + }, \ + .default_tplg_path = { \ + [SOF_IPC_TYPE_3] = "imx/sof-tplg", \ + }, \ + .default_fw_filename = { \ + [SOF_IPC_TYPE_3] = "sof-" #mach_name ".ri", \ + }, \ + .ops = mach_ops, \ + .ops_init = mach_ops_init, \ +} + +/* to be used alongside IMX_SOF_DEV_DESC() */ +#define IMX_SOF_DEV_DESC_NAME(mach_name) sof_of_##mach_name##_desc + +/* dai driver entry w/ playback and capture caps. If one direction is missing + * then set the channels to 0. + */ +#define IMX_SOF_DAI_DRV_ENTRY(dai_name, pb_cmin, pb_cmax, cap_cmin, cap_cmax) \ +{ \ + .name = dai_name, \ + .playback = { \ + .channels_min = pb_cmin, \ + .channels_max = pb_cmax, \ + }, \ + .capture = { \ + .channels_min = cap_cmin, \ + .channels_max = cap_cmax, \ + }, \ +} + +/* use if playback and capture have the same min/max channel count */ +#define IMX_SOF_DAI_DRV_ENTRY_BIDIR(dai_name, cmin, cmax)\ + IMX_SOF_DAI_DRV_ENTRY(dai_name, cmin, cmax, cmin, cmax) + +struct imx_ipc_info { + /* true if core is able to write a panic code to the debug box */ + bool has_panic_code; + /* offset to mailbox in which firmware initially writes FW_READY */ + int boot_mbox_offset; + /* offset to region at which the mailboxes start */ + int window_offset; +}; + +struct imx_chip_ops { + /* called after clocks and PDs are enabled */ + int (*probe)(struct snd_sof_dev *sdev); + /* used directly by the SOF core */ + int (*core_kick)(struct snd_sof_dev *sdev); + /* called during suspend()/remove() before clocks are disabled */ + int (*core_shutdown)(struct snd_sof_dev *sdev); + /* used directly by the SOF core */ + int (*core_reset)(struct snd_sof_dev *sdev); +}; + +struct imx_memory_info { + const char *name; + bool reserved; +}; + +struct imx_chip_info { + struct imx_ipc_info ipc_info; + /* does the chip have a reserved memory region for DMA? */ + bool has_dma_reserved; + struct imx_memory_info *memory; + struct snd_soc_dai_driver *drv; + int num_drv; + /* optional */ + const struct imx_chip_ops *ops; +}; + +struct imx_common_data { + struct platform_device *ipc_dev; + struct imx_dsp_ipc *ipc_handle; + /* core may have no clocks */ + struct clk_bulk_data *clks; + int clk_num; + /* core may have no PDs */ + struct dev_pm_domain_list *pd_list; + void *chip_pdata; +}; + +static inline int imx_chip_core_kick(struct snd_sof_dev *sdev) +{ + const struct imx_chip_ops *ops = get_chip_info(sdev)->ops; + + if (ops && ops->core_kick) + return ops->core_kick(sdev); + + return 0; +} + +static inline int imx_chip_core_shutdown(struct snd_sof_dev *sdev) +{ + const struct imx_chip_ops *ops = get_chip_info(sdev)->ops; + + if (ops && ops->core_shutdown) + return ops->core_shutdown(sdev); + + return 0; +} + +static inline int imx_chip_core_reset(struct snd_sof_dev *sdev) +{ + const struct imx_chip_ops *ops = get_chip_info(sdev)->ops; + + if (ops && ops->core_reset) + return ops->core_reset(sdev); + + return 0; +} + +static inline int imx_chip_probe(struct snd_sof_dev *sdev) +{ + const struct imx_chip_ops *ops = get_chip_info(sdev)->ops; + + if (ops && ops->probe) + return ops->probe(sdev); + + return 0; +} + void imx8_get_registers(struct snd_sof_dev *sdev, struct sof_ipc_dsp_oops_xtensa *xoops, struct sof_ipc_panic_info *panic_info, @@ -15,4 +164,6 @@ void imx8_get_registers(struct snd_sof_dev *sdev, void imx8_dump(struct snd_sof_dev *sdev, u32 flags); +extern const struct snd_sof_dsp_ops sof_imx_ops; + #endif diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 0b85b29d1067..ab07512e511d 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -1,140 +1,72 @@ // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) // -// Copyright 2019 NXP +// Copyright 2019-2025 NXP // // Author: Daniel Baluta <daniel.baluta@nxp.com> // // Hardware interface for audio DSP on i.MX8 -#include <linux/firmware.h> -#include <linux/of_platform.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/pm_domain.h> - -#include <linux/module.h> -#include <sound/sof.h> -#include <sound/sof/xtensa.h> -#include <linux/firmware/imx/ipc.h> -#include <linux/firmware/imx/dsp.h> +#include <dt-bindings/firmware/imx/rsrc.h> +#include <linux/arm-smccc.h> #include <linux/firmware/imx/svc/misc.h> -#include <dt-bindings/firmware/imx/rsrc.h> -#include "../ops.h" -#include "../sof-of-dev.h" -#include "imx-common.h" +#include <linux/mfd/syscon.h> -/* DSP memories */ -#define IRAM_OFFSET 0x10000 -#define IRAM_SIZE (2 * 1024) -#define DRAM0_OFFSET 0x0 -#define DRAM0_SIZE (32 * 1024) -#define DRAM1_OFFSET 0x8000 -#define DRAM1_SIZE (32 * 1024) -#define SYSRAM_OFFSET 0x18000 -#define SYSRAM_SIZE (256 * 1024) -#define SYSROM_OFFSET 0x58000 -#define SYSROM_SIZE (192 * 1024) +#include "imx-common.h" +/* imx8/imx8x macros */ #define RESET_VECTOR_VADDR 0x596f8000 -#define MBOX_OFFSET 0x800000 -#define MBOX_SIZE 0x1000 - -struct imx8_priv { - struct device *dev; - struct snd_sof_dev *sdev; - - /* DSP IPC handler */ - struct imx_dsp_ipc *dsp_ipc; - struct platform_device *ipc_dev; - - /* System Controller IPC handler */ - struct imx_sc_ipc *sc_ipc; - - /* Power domain handling */ - int num_domains; - struct device **pd_dev; - struct device_link **link; - - struct clk_bulk_data *clks; - int clk_num; -}; - -static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev) -{ - return MBOX_OFFSET; -} - -static int imx8_get_window_offset(struct snd_sof_dev *sdev, u32 id) -{ - return MBOX_OFFSET; -} - -static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc) -{ - struct imx8_priv *priv = imx_dsp_get_data(ipc); - unsigned long flags; - - spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - snd_sof_ipc_process_reply(priv->sdev, 0); - spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); -} - -static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc) -{ - struct imx8_priv *priv = imx_dsp_get_data(ipc); - u32 p; /* panic code */ - - /* Read the message from the debug box. */ - sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p)); - - /* Check to see if the message is a panic code (0x0dead***) */ - if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) - snd_sof_dsp_panic(priv->sdev, p, true); - else - snd_sof_ipc_msgs_rx(priv->sdev); -} - -static struct imx_dsp_ops dsp_ops = { - .handle_reply = imx8_dsp_handle_reply, - .handle_request = imx8_dsp_handle_request, +/* imx8m macros */ +#define IMX8M_DAP_DEBUG 0x28800000 +#define IMX8M_DAP_DEBUG_SIZE (64 * 1024) +#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020) +#define IMX8M_PWRCTL_CORERESET BIT(16) + +#define AudioDSP_REG0 0x100 +#define AudioDSP_REG1 0x104 +#define AudioDSP_REG2 0x108 +#define AudioDSP_REG3 0x10c + +#define AudioDSP_REG2_RUNSTALL BIT(5) + +/* imx8ulp macros */ +#define FSL_SIP_HIFI_XRDC 0xc200000e +#define SYSCTRL0 0x8 +#define EXECUTE_BIT BIT(13) +#define RESET_BIT BIT(16) +#define HIFI4_CLK_BIT BIT(17) +#define PB_CLK_BIT BIT(18) +#define PLAT_CLK_BIT BIT(19) +#define DEBUG_LOGIC_BIT BIT(25) + +struct imx8m_chip_data { + void __iomem *dap; + struct regmap *regmap; }; -static int imx8_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) -{ - struct imx8_priv *priv = sdev->pdata->hw_pdata; - - sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, - msg->msg_size); - imx_dsp_ring_doorbell(priv->dsp_ipc, 0); - - return 0; -} - /* * DSP control. */ static int imx8x_run(struct snd_sof_dev *sdev) { - struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata; int ret; - ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP, + ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP, IMX_SC_C_OFS_SEL, 1); if (ret < 0) { dev_err(sdev->dev, "Error system address offset source select\n"); return ret; } - ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP, + ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP, IMX_SC_C_OFS_AUDIO, 0x80); if (ret < 0) { dev_err(sdev->dev, "Error system address offset of AUDIO\n"); return ret; } - ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP, + ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP, IMX_SC_C_OFS_PERIPH, 0x5A); if (ret < 0) { dev_err(sdev->dev, "Error system address offset of PERIPH %d\n", @@ -142,14 +74,14 @@ static int imx8x_run(struct snd_sof_dev *sdev) return ret; } - ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP, + ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP, IMX_SC_C_OFS_IRQ, 0x51); if (ret < 0) { dev_err(sdev->dev, "Error system address offset of IRQ\n"); return ret; } - imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true, + imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, true, RESET_VECTOR_VADDR); return 0; @@ -157,17 +89,16 @@ static int imx8x_run(struct snd_sof_dev *sdev) static int imx8_run(struct snd_sof_dev *sdev) { - struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata; int ret; - ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP, + ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP, IMX_SC_C_OFS_SEL, 0); if (ret < 0) { dev_err(sdev->dev, "Error system address offset source select\n"); return ret; } - imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true, + imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, true, RESET_VECTOR_VADDR); return 0; @@ -175,429 +106,273 @@ static int imx8_run(struct snd_sof_dev *sdev) static int imx8_probe(struct snd_sof_dev *sdev) { - struct platform_device *pdev = - container_of(sdev->dev, struct platform_device, dev); - struct device_node *np = pdev->dev.of_node; - struct device_node *res_node; - struct resource *mmio; - struct imx8_priv *priv; - struct resource res; - u32 base, size; - int ret = 0; - int i; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - sdev->num_cores = 1; - sdev->pdata->hw_pdata = priv; - priv->dev = sdev->dev; - priv->sdev = sdev; - - /* power up device associated power domains */ - priv->num_domains = of_count_phandle_with_args(np, "power-domains", - "#power-domain-cells"); - if (priv->num_domains < 0) { - dev_err(sdev->dev, "no power-domains property in %pOF\n", np); - return priv->num_domains; - } - - priv->pd_dev = devm_kmalloc_array(&pdev->dev, priv->num_domains, - sizeof(*priv->pd_dev), GFP_KERNEL); - if (!priv->pd_dev) - return -ENOMEM; - - priv->link = devm_kmalloc_array(&pdev->dev, priv->num_domains, - sizeof(*priv->link), GFP_KERNEL); - if (!priv->link) - return -ENOMEM; - - for (i = 0; i < priv->num_domains; i++) { - priv->pd_dev[i] = dev_pm_domain_attach_by_id(&pdev->dev, i); - if (IS_ERR(priv->pd_dev[i])) { - ret = PTR_ERR(priv->pd_dev[i]); - goto exit_unroll_pm; - } - priv->link[i] = device_link_add(&pdev->dev, priv->pd_dev[i], - DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | - DL_FLAG_RPM_ACTIVE); - if (!priv->link[i]) { - ret = -ENOMEM; - dev_pm_domain_detach(priv->pd_dev[i], false); - goto exit_unroll_pm; - } - } - - ret = imx_scu_get_handle(&priv->sc_ipc); - if (ret) { - dev_err(sdev->dev, "Cannot obtain SCU handle (err = %d)\n", - ret); - goto exit_unroll_pm; - } + struct imx_sc_ipc *sc_ipc_handle; + struct imx_common_data *common; + int ret; - priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp", - PLATFORM_DEVID_NONE, - pdev, sizeof(*pdev)); - if (IS_ERR(priv->ipc_dev)) { - ret = PTR_ERR(priv->ipc_dev); - goto exit_unroll_pm; - } + common = sdev->pdata->hw_pdata; - priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev); - if (!priv->dsp_ipc) { - /* DSP IPC driver not probed yet, try later */ - ret = -EPROBE_DEFER; - dev_err(sdev->dev, "Failed to get drvdata\n"); - goto exit_pdev_unregister; - } + ret = imx_scu_get_handle(&sc_ipc_handle); + if (ret < 0) + return dev_err_probe(sdev->dev, ret, + "failed to fetch SC IPC handle\n"); - imx_dsp_set_data(priv->dsp_ipc, priv); - priv->dsp_ipc->ops = &dsp_ops; - - /* DSP base */ - mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (mmio) { - base = mmio->start; - size = resource_size(mmio); - } else { - dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n"); - ret = -EINVAL; - goto exit_pdev_unregister; - } + common->chip_pdata = sc_ipc_handle; - sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); - if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { - dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", - base, size); - ret = -ENODEV; - goto exit_pdev_unregister; - } - sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM; + return 0; +} - res_node = of_parse_phandle(np, "memory-region", 0); - if (!res_node) { - dev_err(&pdev->dev, "failed to get memory region node\n"); - ret = -ENODEV; - goto exit_pdev_unregister; - } +static int imx8m_reset(struct snd_sof_dev *sdev) +{ + struct imx8m_chip_data *chip; + u32 pwrctl; - ret = of_address_to_resource(res_node, 0, &res); - of_node_put(res_node); - if (ret) { - dev_err(&pdev->dev, "failed to get reserved region address\n"); - goto exit_pdev_unregister; - } + chip = get_chip_pdata(sdev); - sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start, - resource_size(&res)); - if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { - dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n", - base, size); - ret = -ENOMEM; - goto exit_pdev_unregister; - } - sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; + /* put DSP into reset and stall */ + pwrctl = readl(chip->dap + IMX8M_DAP_PWRCTL); + pwrctl |= IMX8M_PWRCTL_CORERESET; + writel(pwrctl, chip->dap + IMX8M_DAP_PWRCTL); - /* set default mailbox offset for FW ready message */ - sdev->dsp_box.offset = MBOX_OFFSET; + /* keep reset asserted for 10 cycles */ + usleep_range(1, 2); - ret = devm_clk_bulk_get_all(sdev->dev, &priv->clks); - if (ret < 0) { - dev_err(sdev->dev, "failed to fetch clocks: %d\n", ret); - goto exit_pdev_unregister; - } - priv->clk_num = ret; + regmap_update_bits(chip->regmap, AudioDSP_REG2, + AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL); - ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks); - if (ret < 0) { - dev_err(sdev->dev, "failed to enable clocks: %d\n", ret); - goto exit_pdev_unregister; - } + /* take the DSP out of reset and keep stalled for FW loading */ + pwrctl = readl(chip->dap + IMX8M_DAP_PWRCTL); + pwrctl &= ~IMX8M_PWRCTL_CORERESET; + writel(pwrctl, chip->dap + IMX8M_DAP_PWRCTL); return 0; - -exit_pdev_unregister: - platform_device_unregister(priv->ipc_dev); -exit_unroll_pm: - while (--i >= 0) { - device_link_del(priv->link[i]); - dev_pm_domain_detach(priv->pd_dev[i], false); - } - - return ret; } -static void imx8_remove(struct snd_sof_dev *sdev) +static int imx8m_run(struct snd_sof_dev *sdev) { - struct imx8_priv *priv = sdev->pdata->hw_pdata; - int i; + struct imx8m_chip_data *chip = get_chip_pdata(sdev); - clk_bulk_disable_unprepare(priv->clk_num, priv->clks); - platform_device_unregister(priv->ipc_dev); - - for (i = 0; i < priv->num_domains; i++) { - device_link_del(priv->link[i]); - dev_pm_domain_detach(priv->pd_dev[i], false); - } -} + regmap_update_bits(chip->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0); -/* on i.MX8 there is 1 to 1 match between type and BAR idx */ -static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type) -{ - /* Only IRAM and SRAM bars are valid */ - switch (type) { - case SOF_FW_BLK_TYPE_IRAM: - case SOF_FW_BLK_TYPE_SRAM: - return type; - default: - return -EINVAL; - } + return 0; } -static void imx8_suspend(struct snd_sof_dev *sdev) +static int imx8m_probe(struct snd_sof_dev *sdev) { - int i; - struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; + struct imx_common_data *common; + struct imx8m_chip_data *chip; - for (i = 0; i < DSP_MU_CHAN_NUM; i++) - imx_dsp_free_channel(priv->dsp_ipc, i); + common = sdev->pdata->hw_pdata; - clk_bulk_disable_unprepare(priv->clk_num, priv->clks); -} + chip = devm_kzalloc(sdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return dev_err_probe(sdev->dev, -ENOMEM, + "failed to allocate chip data\n"); -static int imx8_resume(struct snd_sof_dev *sdev) -{ - struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; - int ret; - int i; + chip->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE); + if (!chip->dap) + return dev_err_probe(sdev->dev, -ENODEV, + "failed to ioremap DAP\n"); - ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks); - if (ret < 0) { - dev_err(sdev->dev, "failed to enable clocks: %d\n", ret); - return ret; - } + chip->regmap = syscon_regmap_lookup_by_phandle(sdev->dev->of_node, "fsl,dsp-ctrl"); + if (IS_ERR(chip->regmap)) + return dev_err_probe(sdev->dev, PTR_ERR(chip->regmap), + "failed to fetch dsp ctrl regmap\n"); - for (i = 0; i < DSP_MU_CHAN_NUM; i++) - imx_dsp_request_channel(priv->dsp_ipc, i); + common->chip_pdata = chip; return 0; } -static int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev) +static int imx8ulp_run(struct snd_sof_dev *sdev) { - int ret; - const struct sof_dsp_power_state target_dsp_state = { - .state = SOF_DSP_PM_D0, - }; + struct regmap *regmap = get_chip_pdata(sdev); - ret = imx8_resume(sdev); - if (ret < 0) - return ret; - - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} + /* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */ + regmap_update_bits(regmap, SYSCTRL0, RESET_BIT, 0); -static int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev) -{ - const struct sof_dsp_power_state target_dsp_state = { - .state = SOF_DSP_PM_D3, - }; + /* Reset HiFi4 DSP Debug logic: 1 debug reset, 0 out of reset*/ + regmap_update_bits(regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0); - imx8_suspend(sdev); + /* Stall HIFI4 DSP Execution: 1 stall, 0 run */ + regmap_update_bits(regmap, SYSCTRL0, EXECUTE_BIT, 0); - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); + return 0; } -static int imx8_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) +static int imx8ulp_reset(struct snd_sof_dev *sdev) { - const struct sof_dsp_power_state target_dsp_state = { - .state = target_state, - }; + struct arm_smccc_res smc_res; + struct regmap *regmap; - if (!pm_runtime_suspended(sdev->dev)) - imx8_suspend(sdev); + regmap = get_chip_pdata(sdev); - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} + /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */ + regmap_update_bits(regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT); -static int imx8_dsp_resume(struct snd_sof_dev *sdev) -{ - int ret; - const struct sof_dsp_power_state target_dsp_state = { - .state = SOF_DSP_PM_D0, - }; + /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */ + regmap_update_bits(regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT); - ret = imx8_resume(sdev); - if (ret < 0) - return ret; + /* HiFi4 Clock Enable: 1 enabled, 0 disabled */ + regmap_update_bits(regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT); - if (pm_runtime_suspended(sdev->dev)) { - pm_runtime_disable(sdev->dev); - pm_runtime_set_active(sdev->dev); - pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_enable(sdev->dev); - pm_runtime_idle(sdev->dev); - } + regmap_update_bits(regmap, SYSCTRL0, RESET_BIT, RESET_BIT); - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} + usleep_range(1, 2); -static struct snd_soc_dai_driver imx8_dai[] = { -{ - .name = "esai0", - .playback = { - .channels_min = 1, - .channels_max = 8, - }, - .capture = { - .channels_min = 1, - .channels_max = 8, - }, -}, -{ - .name = "sai1", - .playback = { - .channels_min = 1, - .channels_max = 32, - }, - .capture = { - .channels_min = 1, - .channels_max = 32, - }, -}, -}; + /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */ + regmap_update_bits(regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT); + usleep_range(1, 2); -static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev, - const struct sof_dsp_power_state *target_state) -{ - sdev->dsp_power_state = *target_state; + arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_res); - return 0; + return smc_res.a0; } -/* i.MX8 ops */ -static const struct snd_sof_dsp_ops sof_imx8_ops = { - /* probe and remove */ - .probe = imx8_probe, - .remove = imx8_remove, - /* DSP core boot */ - .run = imx8_run, - - /* Block IO */ - .block_read = sof_block_read, - .block_write = sof_block_write, +static int imx8ulp_probe(struct snd_sof_dev *sdev) +{ + struct imx_common_data *common; + struct regmap *regmap; - /* Mailbox IO */ - .mailbox_read = sof_mailbox_read, - .mailbox_write = sof_mailbox_write, + common = sdev->pdata->hw_pdata; - /* ipc */ - .send_msg = imx8_send_msg, - .get_mailbox_offset = imx8_get_mailbox_offset, - .get_window_offset = imx8_get_window_offset, + regmap = syscon_regmap_lookup_by_phandle(sdev->dev->of_node, "fsl,dsp-ctrl"); + if (IS_ERR(regmap)) + return dev_err_probe(sdev->dev, PTR_ERR(regmap), + "failed to fetch dsp ctrl regmap\n"); - .ipc_msg_data = sof_ipc_msg_data, - .set_stream_data_offset = sof_set_stream_data_offset, + common->chip_pdata = regmap; - .get_bar_index = imx8_get_bar_index, + return 0; +} - /* firmware loading */ - .load_firmware = snd_sof_load_firmware_memcpy, +static struct snd_soc_dai_driver imx8_dai[] = { + IMX_SOF_DAI_DRV_ENTRY_BIDIR("esai0", 1, 8), + IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai1", 1, 32), +}; - /* Debug information */ - .dbg_dump = imx8_dump, - .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, +static struct snd_soc_dai_driver imx8m_dai[] = { + IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai1", 1, 32), + IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai2", 1, 32), + IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai3", 1, 32), + IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai5", 1, 32), + IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai6", 1, 32), + IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai7", 1, 32), + IMX_SOF_DAI_DRV_ENTRY("micfil", 0, 0, 1, 8), +}; - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, +static struct snd_soc_dai_driver imx8ulp_dai[] = { + IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai5", 1, 32), + IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai6", 1, 32), +}; - /* Firmware ops */ - .dsp_arch_ops = &sof_xtensa_arch_ops, +static struct snd_sof_dsp_ops sof_imx8_ops; - /* DAI drivers */ - .drv = imx8_dai, - .num_drv = ARRAY_SIZE(imx8_dai), +static int imx8_ops_init(struct snd_sof_dev *sdev) +{ + /* first copy from template */ + memcpy(&sof_imx8_ops, &sof_imx_ops, sizeof(sof_imx_ops)); - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + /* then set common imx8 ops */ + sof_imx8_ops.dbg_dump = imx8_dump; + sof_imx8_ops.dsp_arch_ops = &sof_xtensa_arch_ops; + sof_imx8_ops.debugfs_add_region_item = + snd_sof_debugfs_add_region_item_iomem; - /* PM */ - .runtime_suspend = imx8_dsp_runtime_suspend, - .runtime_resume = imx8_dsp_runtime_resume, + /* ... and finally set DAI driver */ + sof_imx8_ops.drv = get_chip_info(sdev)->drv; + sof_imx8_ops.num_drv = get_chip_info(sdev)->num_drv; - .suspend = imx8_dsp_suspend, - .resume = imx8_dsp_resume, + return 0; +} - .set_power_state = imx8_dsp_set_power_state, +static const struct imx_chip_ops imx8_chip_ops = { + .probe = imx8_probe, + .core_kick = imx8_run, }; -/* i.MX8X ops */ -static const struct snd_sof_dsp_ops sof_imx8x_ops = { - /* probe and remove */ - .probe = imx8_probe, - .remove = imx8_remove, - /* DSP core boot */ - .run = imx8x_run, - - /* Block IO */ - .block_read = sof_block_read, - .block_write = sof_block_write, - - /* Mailbox IO */ - .mailbox_read = sof_mailbox_read, - .mailbox_write = sof_mailbox_write, - - /* ipc */ - .send_msg = imx8_send_msg, - .get_mailbox_offset = imx8_get_mailbox_offset, - .get_window_offset = imx8_get_window_offset, - - .ipc_msg_data = sof_ipc_msg_data, - .set_stream_data_offset = sof_set_stream_data_offset, +static const struct imx_chip_ops imx8x_chip_ops = { + .probe = imx8_probe, + .core_kick = imx8x_run, +}; - .get_bar_index = imx8_get_bar_index, +static const struct imx_chip_ops imx8m_chip_ops = { + .probe = imx8m_probe, + .core_kick = imx8m_run, + .core_reset = imx8m_reset, +}; - /* firmware loading */ - .load_firmware = snd_sof_load_firmware_memcpy, +static const struct imx_chip_ops imx8ulp_chip_ops = { + .probe = imx8ulp_probe, + .core_kick = imx8ulp_run, + .core_reset = imx8ulp_reset, +}; - /* Debug information */ - .dbg_dump = imx8_dump, - .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, +static struct imx_memory_info imx8_memory_regions[] = { + { .name = "iram", .reserved = false }, + { .name = "sram", .reserved = true }, + { } +}; - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, +static struct imx_memory_info imx8m_memory_regions[] = { + { .name = "iram", .reserved = false }, + { .name = "sram", .reserved = true }, + { } +}; - /* Firmware ops */ - .dsp_arch_ops = &sof_xtensa_arch_ops, +static struct imx_memory_info imx8ulp_memory_regions[] = { + { .name = "iram", .reserved = false }, + { .name = "sram", .reserved = true }, + { } +}; - /* DAI drivers */ +static const struct imx_chip_info imx8_chip_info = { + .ipc_info = { + .has_panic_code = true, + .boot_mbox_offset = 0x800000, + .window_offset = 0x800000, + }, + .memory = imx8_memory_regions, .drv = imx8_dai, .num_drv = ARRAY_SIZE(imx8_dai), + .ops = &imx8_chip_ops, +}; - /* PM */ - .runtime_suspend = imx8_dsp_runtime_suspend, - .runtime_resume = imx8_dsp_runtime_resume, - - .suspend = imx8_dsp_suspend, - .resume = imx8_dsp_resume, +static const struct imx_chip_info imx8x_chip_info = { + .ipc_info = { + .has_panic_code = true, + .boot_mbox_offset = 0x800000, + .window_offset = 0x800000, + }, + .memory = imx8_memory_regions, + .drv = imx8_dai, + .num_drv = ARRAY_SIZE(imx8_dai), + .ops = &imx8x_chip_ops, +}; - .set_power_state = imx8_dsp_set_power_state, +static const struct imx_chip_info imx8m_chip_info = { + .ipc_info = { + .has_panic_code = true, + .boot_mbox_offset = 0x800000, + .window_offset = 0x800000, + }, + .memory = imx8m_memory_regions, + .drv = imx8m_dai, + .num_drv = ARRAY_SIZE(imx8m_dai), + .ops = &imx8m_chip_ops, +}; - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP +static const struct imx_chip_info imx8ulp_chip_info = { + .ipc_info = { + .has_panic_code = true, + .boot_mbox_offset = 0x800000, + .window_offset = 0x800000, + }, + .has_dma_reserved = true, + .memory = imx8ulp_memory_regions, + .drv = imx8ulp_dai, + .num_drv = ARRAY_SIZE(imx8ulp_dai), + .ops = &imx8ulp_chip_ops, }; static struct snd_sof_of_mach sof_imx8_machs[] = { @@ -607,50 +382,70 @@ static struct snd_sof_of_mach sof_imx8_machs[] = { .drv_name = "asoc-audio-graph-card2", }, { + .compatible = "fsl,imx8qxp-mek-wcpu", + .sof_tplg_filename = "sof-imx8-wm8962.tplg", + .drv_name = "asoc-audio-graph-card2", + }, + { .compatible = "fsl,imx8qm-mek", .sof_tplg_filename = "sof-imx8-wm8960.tplg", .drv_name = "asoc-audio-graph-card2", }, - {} -}; - -static struct sof_dev_desc sof_of_imx8qxp_desc = { - .of_machines = sof_imx8_machs, - .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), - .ipc_default = SOF_IPC_TYPE_3, - .default_fw_path = { - [SOF_IPC_TYPE_3] = "imx/sof", + { + .compatible = "fsl,imx8qm-mek-revd", + .sof_tplg_filename = "sof-imx8-wm8962.tplg", + .drv_name = "asoc-audio-graph-card2", }, - .default_tplg_path = { - [SOF_IPC_TYPE_3] = "imx/sof-tplg", + { + .compatible = "fsl,imx8qxp-mek-bb", + .sof_tplg_filename = "sof-imx8-cs42888.tplg", + .drv_name = "asoc-audio-graph-card2", }, - .default_fw_filename = { - [SOF_IPC_TYPE_3] = "sof-imx8x.ri", + { + .compatible = "fsl,imx8qm-mek-bb", + .sof_tplg_filename = "sof-imx8-cs42888.tplg", + .drv_name = "asoc-audio-graph-card2", }, - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8x_ops, -}; - -static struct sof_dev_desc sof_of_imx8qm_desc = { - .of_machines = sof_imx8_machs, - .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), - .ipc_default = SOF_IPC_TYPE_3, - .default_fw_path = { - [SOF_IPC_TYPE_3] = "imx/sof", + { + .compatible = "fsl,imx8mp-evk", + .sof_tplg_filename = "sof-imx8mp-wm8960.tplg", + .drv_name = "asoc-audio-graph-card2", }, - .default_tplg_path = { - [SOF_IPC_TYPE_3] = "imx/sof-tplg", + { + .compatible = "fsl,imx8mp-evk-revb4", + .sof_tplg_filename = "sof-imx8mp-wm8962.tplg", + .drv_name = "asoc-audio-graph-card2", }, - .default_fw_filename = { - [SOF_IPC_TYPE_3] = "sof-imx8.ri", + { + .compatible = "fsl,imx8ulp-evk", + .sof_tplg_filename = "sof-imx8ulp-btsco.tplg", + .drv_name = "asoc-audio-graph-card2", }, - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8_ops, + {} }; +IMX_SOF_DEV_DESC(imx8, sof_imx8_machs, &imx8_chip_info, &sof_imx8_ops, imx8_ops_init); +IMX_SOF_DEV_DESC(imx8x, sof_imx8_machs, &imx8x_chip_info, &sof_imx8_ops, imx8_ops_init); +IMX_SOF_DEV_DESC(imx8m, sof_imx8_machs, &imx8m_chip_info, &sof_imx8_ops, imx8_ops_init); +IMX_SOF_DEV_DESC(imx8ulp, sof_imx8_machs, &imx8ulp_chip_info, &sof_imx8_ops, imx8_ops_init); + static const struct of_device_id sof_of_imx8_ids[] = { - { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc}, - { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc}, + { + .compatible = "fsl,imx8qxp-dsp", + .data = &IMX_SOF_DEV_DESC_NAME(imx8x), + }, + { + .compatible = "fsl,imx8qm-dsp", + .data = &IMX_SOF_DEV_DESC_NAME(imx8), + }, + { + .compatible = "fsl,imx8mp-dsp", + .data = &IMX_SOF_DEV_DESC_NAME(imx8m), + }, + { + .compatible = "fsl,imx8ulp-dsp", + .data = &IMX_SOF_DEV_DESC_NAME(imx8ulp), + }, { } }; MODULE_DEVICE_TABLE(of, sof_of_imx8_ids); @@ -661,7 +456,7 @@ static struct platform_driver snd_sof_of_imx8_driver = { .remove = sof_of_remove, .driver = { .name = "sof-audio-of-imx8", - .pm = &sof_of_pm, + .pm = pm_ptr(&sof_of_pm), .of_match_table = sof_of_imx8_ids, }, }; diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c deleted file mode 100644 index ff42743efa79..000000000000 --- a/sound/soc/sof/imx/imx8m.c +++ /dev/null @@ -1,519 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -// -// Copyright 2020 NXP -// -// Author: Daniel Baluta <daniel.baluta@nxp.com> -// -// Hardware interface for audio DSP on i.MX8M - -#include <linux/bits.h> -#include <linux/firmware.h> -#include <linux/mfd/syscon.h> -#include <linux/of_platform.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/regmap.h> - -#include <linux/module.h> -#include <sound/sof.h> -#include <sound/sof/xtensa.h> -#include <linux/firmware/imx/dsp.h> - -#include "../ops.h" -#include "../sof-of-dev.h" -#include "imx-common.h" - -#define MBOX_OFFSET 0x800000 -#define MBOX_SIZE 0x1000 - -/* DAP registers */ -#define IMX8M_DAP_DEBUG 0x28800000 -#define IMX8M_DAP_DEBUG_SIZE (64 * 1024) -#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020) -#define IMX8M_PWRCTL_CORERESET BIT(16) - -/* DSP audio mix registers */ -#define AudioDSP_REG0 0x100 -#define AudioDSP_REG1 0x104 -#define AudioDSP_REG2 0x108 -#define AudioDSP_REG3 0x10c - -#define AudioDSP_REG2_RUNSTALL BIT(5) - -struct imx8m_priv { - struct device *dev; - struct snd_sof_dev *sdev; - - /* DSP IPC handler */ - struct imx_dsp_ipc *dsp_ipc; - struct platform_device *ipc_dev; - - struct clk_bulk_data *clks; - int clk_num; - - void __iomem *dap; - struct regmap *regmap; -}; - -static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) -{ - return MBOX_OFFSET; -} - -static int imx8m_get_window_offset(struct snd_sof_dev *sdev, u32 id) -{ - return MBOX_OFFSET; -} - -static void imx8m_dsp_handle_reply(struct imx_dsp_ipc *ipc) -{ - struct imx8m_priv *priv = imx_dsp_get_data(ipc); - unsigned long flags; - - spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - snd_sof_ipc_process_reply(priv->sdev, 0); - spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); -} - -static void imx8m_dsp_handle_request(struct imx_dsp_ipc *ipc) -{ - struct imx8m_priv *priv = imx_dsp_get_data(ipc); - u32 p; /* Panic code */ - - /* Read the message from the debug box. */ - sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p)); - - /* Check to see if the message is a panic code (0x0dead***) */ - if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) - snd_sof_dsp_panic(priv->sdev, p, true); - else - snd_sof_ipc_msgs_rx(priv->sdev); -} - -static struct imx_dsp_ops imx8m_dsp_ops = { - .handle_reply = imx8m_dsp_handle_reply, - .handle_request = imx8m_dsp_handle_request, -}; - -static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) -{ - struct imx8m_priv *priv = sdev->pdata->hw_pdata; - - sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, - msg->msg_size); - imx_dsp_ring_doorbell(priv->dsp_ipc, 0); - - return 0; -} - -/* - * DSP control. - */ -static int imx8m_run(struct snd_sof_dev *sdev) -{ - struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; - - regmap_update_bits(priv->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0); - - return 0; -} - -static int imx8m_reset(struct snd_sof_dev *sdev) -{ - struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; - u32 pwrctl; - - /* put DSP into reset and stall */ - pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); - pwrctl |= IMX8M_PWRCTL_CORERESET; - writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); - - /* keep reset asserted for 10 cycles */ - usleep_range(1, 2); - - regmap_update_bits(priv->regmap, AudioDSP_REG2, - AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL); - - /* take the DSP out of reset and keep stalled for FW loading */ - pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); - pwrctl &= ~IMX8M_PWRCTL_CORERESET; - writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); - - return 0; -} - -static int imx8m_probe(struct snd_sof_dev *sdev) -{ - struct platform_device *pdev = - container_of(sdev->dev, struct platform_device, dev); - struct device_node *np = pdev->dev.of_node; - struct device_node *res_node; - struct resource *mmio; - struct imx8m_priv *priv; - struct resource res; - u32 base, size; - int ret = 0; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - sdev->num_cores = 1; - sdev->pdata->hw_pdata = priv; - priv->dev = sdev->dev; - priv->sdev = sdev; - - priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp", - PLATFORM_DEVID_NONE, - pdev, sizeof(*pdev)); - if (IS_ERR(priv->ipc_dev)) - return PTR_ERR(priv->ipc_dev); - - priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev); - if (!priv->dsp_ipc) { - /* DSP IPC driver not probed yet, try later */ - ret = -EPROBE_DEFER; - dev_err(sdev->dev, "Failed to get drvdata\n"); - goto exit_pdev_unregister; - } - - imx_dsp_set_data(priv->dsp_ipc, priv); - priv->dsp_ipc->ops = &imx8m_dsp_ops; - - /* DSP base */ - mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (mmio) { - base = mmio->start; - size = resource_size(mmio); - } else { - dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n"); - ret = -EINVAL; - goto exit_pdev_unregister; - } - - priv->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE); - if (!priv->dap) { - dev_err(sdev->dev, "error: failed to map DAP debug memory area"); - ret = -ENODEV; - goto exit_pdev_unregister; - } - - sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); - if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { - dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", - base, size); - ret = -ENODEV; - goto exit_pdev_unregister; - } - sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM; - - res_node = of_parse_phandle(np, "memory-region", 0); - if (!res_node) { - dev_err(&pdev->dev, "failed to get memory region node\n"); - ret = -ENODEV; - goto exit_pdev_unregister; - } - - ret = of_address_to_resource(res_node, 0, &res); - of_node_put(res_node); - if (ret) { - dev_err(&pdev->dev, "failed to get reserved region address\n"); - goto exit_pdev_unregister; - } - - sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start, - resource_size(&res)); - if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { - dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n", - base, size); - ret = -ENOMEM; - goto exit_pdev_unregister; - } - sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; - - /* set default mailbox offset for FW ready message */ - sdev->dsp_box.offset = MBOX_OFFSET; - - priv->regmap = syscon_regmap_lookup_by_phandle(np, "fsl,dsp-ctrl"); - if (IS_ERR(priv->regmap)) { - dev_err(sdev->dev, "cannot find dsp-ctrl registers"); - ret = PTR_ERR(priv->regmap); - goto exit_pdev_unregister; - } - - ret = devm_clk_bulk_get_all(sdev->dev, &priv->clks); - if (ret < 0) { - dev_err(sdev->dev, "failed to fetch clocks: %d\n", ret); - goto exit_pdev_unregister; - } - priv->clk_num = ret; - - ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks); - if (ret < 0) { - dev_err(sdev->dev, "failed to enable clocks: %d\n", ret); - goto exit_pdev_unregister; - } - - return 0; - -exit_pdev_unregister: - platform_device_unregister(priv->ipc_dev); - return ret; -} - -static void imx8m_remove(struct snd_sof_dev *sdev) -{ - struct imx8m_priv *priv = sdev->pdata->hw_pdata; - - clk_bulk_disable_unprepare(priv->clk_num, priv->clks); - platform_device_unregister(priv->ipc_dev); -} - -/* on i.MX8 there is 1 to 1 match between type and BAR idx */ -static int imx8m_get_bar_index(struct snd_sof_dev *sdev, u32 type) -{ - /* Only IRAM and SRAM bars are valid */ - switch (type) { - case SOF_FW_BLK_TYPE_IRAM: - case SOF_FW_BLK_TYPE_SRAM: - return type; - default: - return -EINVAL; - } -} - -static struct snd_soc_dai_driver imx8m_dai[] = { -{ - .name = "sai1", - .playback = { - .channels_min = 1, - .channels_max = 32, - }, - .capture = { - .channels_min = 1, - .channels_max = 32, - }, -}, -{ - .name = "sai3", - .playback = { - .channels_min = 1, - .channels_max = 32, - }, - .capture = { - .channels_min = 1, - .channels_max = 32, - }, -}, -{ - .name = "micfil", - .capture = { - .channels_min = 1, - .channels_max = 8, - }, -}, -}; - -static int imx8m_dsp_set_power_state(struct snd_sof_dev *sdev, - const struct sof_dsp_power_state *target_state) -{ - sdev->dsp_power_state = *target_state; - - return 0; -} - -static int imx8m_resume(struct snd_sof_dev *sdev) -{ - struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; - int ret; - int i; - - ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks); - if (ret < 0) { - dev_err(sdev->dev, "failed to enable clocks: %d\n", ret); - return ret; - } - - for (i = 0; i < DSP_MU_CHAN_NUM; i++) - imx_dsp_request_channel(priv->dsp_ipc, i); - - return 0; -} - -static void imx8m_suspend(struct snd_sof_dev *sdev) -{ - struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; - int i; - - for (i = 0; i < DSP_MU_CHAN_NUM; i++) - imx_dsp_free_channel(priv->dsp_ipc, i); - - clk_bulk_disable_unprepare(priv->clk_num, priv->clks); -} - -static int imx8m_dsp_runtime_resume(struct snd_sof_dev *sdev) -{ - int ret; - const struct sof_dsp_power_state target_dsp_state = { - .state = SOF_DSP_PM_D0, - }; - - ret = imx8m_resume(sdev); - if (ret < 0) - return ret; - - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} - -static int imx8m_dsp_runtime_suspend(struct snd_sof_dev *sdev) -{ - const struct sof_dsp_power_state target_dsp_state = { - .state = SOF_DSP_PM_D3, - }; - - imx8m_suspend(sdev); - - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} - -static int imx8m_dsp_resume(struct snd_sof_dev *sdev) -{ - int ret; - const struct sof_dsp_power_state target_dsp_state = { - .state = SOF_DSP_PM_D0, - }; - - ret = imx8m_resume(sdev); - if (ret < 0) - return ret; - - if (pm_runtime_suspended(sdev->dev)) { - pm_runtime_disable(sdev->dev); - pm_runtime_set_active(sdev->dev); - pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_enable(sdev->dev); - pm_runtime_idle(sdev->dev); - } - - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} - -static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) -{ - const struct sof_dsp_power_state target_dsp_state = { - .state = target_state, - }; - - if (!pm_runtime_suspended(sdev->dev)) - imx8m_suspend(sdev); - - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} - -/* i.MX8 ops */ -static const struct snd_sof_dsp_ops sof_imx8m_ops = { - /* probe and remove */ - .probe = imx8m_probe, - .remove = imx8m_remove, - /* DSP core boot */ - .run = imx8m_run, - .reset = imx8m_reset, - - /* Block IO */ - .block_read = sof_block_read, - .block_write = sof_block_write, - - /* Mailbox IO */ - .mailbox_read = sof_mailbox_read, - .mailbox_write = sof_mailbox_write, - - /* ipc */ - .send_msg = imx8m_send_msg, - .get_mailbox_offset = imx8m_get_mailbox_offset, - .get_window_offset = imx8m_get_window_offset, - - .ipc_msg_data = sof_ipc_msg_data, - .set_stream_data_offset = sof_set_stream_data_offset, - - .get_bar_index = imx8m_get_bar_index, - - /* firmware loading */ - .load_firmware = snd_sof_load_firmware_memcpy, - - /* Debug information */ - .dbg_dump = imx8_dump, - .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - /* Firmware ops */ - .dsp_arch_ops = &sof_xtensa_arch_ops, - - /* DAI drivers */ - .drv = imx8m_dai, - .num_drv = ARRAY_SIZE(imx8m_dai), - - .suspend = imx8m_dsp_suspend, - .resume = imx8m_dsp_resume, - - .runtime_suspend = imx8m_dsp_runtime_suspend, - .runtime_resume = imx8m_dsp_runtime_resume, - - .set_power_state = imx8m_dsp_set_power_state, - - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, -}; - -static struct snd_sof_of_mach sof_imx8mp_machs[] = { - { - .compatible = "fsl,imx8mp-evk", - .sof_tplg_filename = "sof-imx8mp-wm8960.tplg", - .drv_name = "asoc-audio-graph-card2", - }, - {} -}; - -static struct sof_dev_desc sof_of_imx8mp_desc = { - .of_machines = sof_imx8mp_machs, - .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), - .ipc_default = SOF_IPC_TYPE_3, - .default_fw_path = { - [SOF_IPC_TYPE_3] = "imx/sof", - }, - .default_tplg_path = { - [SOF_IPC_TYPE_3] = "imx/sof-tplg", - }, - .default_fw_filename = { - [SOF_IPC_TYPE_3] = "sof-imx8m.ri", - }, - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8m_ops, -}; - -static const struct of_device_id sof_of_imx8m_ids[] = { - { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc}, - { } -}; -MODULE_DEVICE_TABLE(of, sof_of_imx8m_ids); - -/* DT driver definition */ -static struct platform_driver snd_sof_of_imx8m_driver = { - .probe = sof_of_probe, - .remove = sof_of_remove, - .driver = { - .name = "sof-audio-of-imx8m", - .pm = &sof_of_pm, - .of_match_table = sof_of_imx8m_ids, - }, -}; -module_platform_driver(snd_sof_of_imx8m_driver); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION("SOF support for IMX8M platforms"); -MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); diff --git a/sound/soc/sof/imx/imx8ulp.c b/sound/soc/sof/imx/imx8ulp.c deleted file mode 100644 index 6965791ab6ef..000000000000 --- a/sound/soc/sof/imx/imx8ulp.c +++ /dev/null @@ -1,521 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -// -// Copyright 2021-2022 NXP -// -// Author: Peng Zhang <peng.zhang_8@nxp.com> -// -// Hardware interface for audio DSP on i.MX8ULP - -#include <linux/arm-smccc.h> -#include <linux/clk.h> -#include <linux/firmware.h> -#include <linux/firmware/imx/dsp.h> -#include <linux/firmware/imx/ipc.h> -#include <linux/firmware/imx/svc/misc.h> -#include <linux/mfd/syscon.h> -#include <linux/module.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/of_platform.h> -#include <linux/of_reserved_mem.h> - -#include <sound/sof.h> -#include <sound/sof/xtensa.h> - -#include "../ops.h" -#include "../sof-of-dev.h" -#include "imx-common.h" - -#define FSL_SIP_HIFI_XRDC 0xc200000e - -/* SIM Domain register */ -#define SYSCTRL0 0x8 -#define EXECUTE_BIT BIT(13) -#define RESET_BIT BIT(16) -#define HIFI4_CLK_BIT BIT(17) -#define PB_CLK_BIT BIT(18) -#define PLAT_CLK_BIT BIT(19) -#define DEBUG_LOGIC_BIT BIT(25) - -#define MBOX_OFFSET 0x800000 -#define MBOX_SIZE 0x1000 - -struct imx8ulp_priv { - struct device *dev; - struct snd_sof_dev *sdev; - - /* DSP IPC handler */ - struct imx_dsp_ipc *dsp_ipc; - struct platform_device *ipc_dev; - - struct regmap *regmap; - struct clk_bulk_data *clks; - int clk_num; -}; - -static void imx8ulp_sim_lpav_start(struct imx8ulp_priv *priv) -{ - /* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */ - regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, 0); - - /* Reset HiFi4 DSP Debug logic: 1 debug reset, 0 out of reset*/ - regmap_update_bits(priv->regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0); - - /* Stall HIFI4 DSP Execution: 1 stall, 0 run */ - regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, 0); -} - -static int imx8ulp_get_mailbox_offset(struct snd_sof_dev *sdev) -{ - return MBOX_OFFSET; -} - -static int imx8ulp_get_window_offset(struct snd_sof_dev *sdev, u32 id) -{ - return MBOX_OFFSET; -} - -static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc) -{ - struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); - unsigned long flags; - - spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - - snd_sof_ipc_process_reply(priv->sdev, 0); - - spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); -} - -static void imx8ulp_dsp_handle_request(struct imx_dsp_ipc *ipc) -{ - struct imx8ulp_priv *priv = imx_dsp_get_data(ipc); - u32 p; /* panic code */ - - /* Read the message from the debug box. */ - sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p)); - - /* Check to see if the message is a panic code (0x0dead***) */ - if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) - snd_sof_dsp_panic(priv->sdev, p, true); - else - snd_sof_ipc_msgs_rx(priv->sdev); -} - -static struct imx_dsp_ops dsp_ops = { - .handle_reply = imx8ulp_dsp_handle_reply, - .handle_request = imx8ulp_dsp_handle_request, -}; - -static int imx8ulp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) -{ - struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; - - sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, - msg->msg_size); - imx_dsp_ring_doorbell(priv->dsp_ipc, 0); - - return 0; -} - -static int imx8ulp_run(struct snd_sof_dev *sdev) -{ - struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; - - imx8ulp_sim_lpav_start(priv); - - return 0; -} - -static int imx8ulp_reset(struct snd_sof_dev *sdev) -{ - struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; - struct arm_smccc_res smc_resource; - - /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */ - regmap_update_bits(priv->regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT); - - /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */ - regmap_update_bits(priv->regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT); - - /* HiFi4 Clock Enable: 1 enabled, 0 disabled */ - regmap_update_bits(priv->regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT); - - regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, RESET_BIT); - usleep_range(1, 2); - - /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */ - regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT); - usleep_range(1, 2); - - arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_resource); - - return 0; -} - -static int imx8ulp_probe(struct snd_sof_dev *sdev) -{ - struct platform_device *pdev = - container_of(sdev->dev, struct platform_device, dev); - struct device_node *np = pdev->dev.of_node; - struct device_node *res_node; - struct resource *mmio; - struct imx8ulp_priv *priv; - struct resource res; - u32 base, size; - int ret = 0; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - sdev->num_cores = 1; - sdev->pdata->hw_pdata = priv; - priv->dev = sdev->dev; - priv->sdev = sdev; - - /* System integration module(SIM) control dsp configuration */ - priv->regmap = syscon_regmap_lookup_by_phandle(np, "fsl,dsp-ctrl"); - if (IS_ERR(priv->regmap)) - return PTR_ERR(priv->regmap); - - priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp", - PLATFORM_DEVID_NONE, - pdev, sizeof(*pdev)); - if (IS_ERR(priv->ipc_dev)) - return PTR_ERR(priv->ipc_dev); - - priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev); - if (!priv->dsp_ipc) { - /* DSP IPC driver not probed yet, try later */ - ret = -EPROBE_DEFER; - dev_err(sdev->dev, "Failed to get drvdata\n"); - goto exit_pdev_unregister; - } - - imx_dsp_set_data(priv->dsp_ipc, priv); - priv->dsp_ipc->ops = &dsp_ops; - - /* DSP base */ - mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (mmio) { - base = mmio->start; - size = resource_size(mmio); - } else { - dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n"); - ret = -EINVAL; - goto exit_pdev_unregister; - } - - sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); - if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { - dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", - base, size); - ret = -ENODEV; - goto exit_pdev_unregister; - } - sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM; - - res_node = of_parse_phandle(np, "memory-reserved", 0); - if (!res_node) { - dev_err(&pdev->dev, "failed to get memory region node\n"); - ret = -ENODEV; - goto exit_pdev_unregister; - } - - ret = of_address_to_resource(res_node, 0, &res); - of_node_put(res_node); - if (ret) { - dev_err(&pdev->dev, "failed to get reserved region address\n"); - goto exit_pdev_unregister; - } - - sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start, - resource_size(&res)); - if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { - dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n", - base, size); - ret = -ENOMEM; - goto exit_pdev_unregister; - } - sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM; - - /* set default mailbox offset for FW ready message */ - sdev->dsp_box.offset = MBOX_OFFSET; - - ret = of_reserved_mem_device_init(sdev->dev); - if (ret) { - dev_err(&pdev->dev, "failed to init reserved memory region %d\n", ret); - goto exit_pdev_unregister; - } - - ret = devm_clk_bulk_get_all(sdev->dev, &priv->clks); - if (ret < 0) { - dev_err(sdev->dev, "failed to fetch clocks: %d\n", ret); - goto exit_pdev_unregister; - } - priv->clk_num = ret; - - ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks); - if (ret < 0) { - dev_err(sdev->dev, "failed to enable clocks: %d\n", ret); - goto exit_pdev_unregister; - } - - return 0; - -exit_pdev_unregister: - platform_device_unregister(priv->ipc_dev); - - return ret; -} - -static void imx8ulp_remove(struct snd_sof_dev *sdev) -{ - struct imx8ulp_priv *priv = sdev->pdata->hw_pdata; - - clk_bulk_disable_unprepare(priv->clk_num, priv->clks); - platform_device_unregister(priv->ipc_dev); -} - -/* on i.MX8 there is 1 to 1 match between type and BAR idx */ -static int imx8ulp_get_bar_index(struct snd_sof_dev *sdev, u32 type) -{ - return type; -} - -static int imx8ulp_suspend(struct snd_sof_dev *sdev) -{ - int i; - struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata; - - /*Stall DSP, release in .run() */ - regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT); - - for (i = 0; i < DSP_MU_CHAN_NUM; i++) - imx_dsp_free_channel(priv->dsp_ipc, i); - - clk_bulk_disable_unprepare(priv->clk_num, priv->clks); - - return 0; -} - -static int imx8ulp_resume(struct snd_sof_dev *sdev) -{ - struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata; - int i, ret; - - ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks); - if (ret < 0) { - dev_err(sdev->dev, "failed to enable clocks: %d\n", ret); - return ret; - } - - for (i = 0; i < DSP_MU_CHAN_NUM; i++) - imx_dsp_request_channel(priv->dsp_ipc, i); - - return 0; -} - -static int imx8ulp_dsp_runtime_resume(struct snd_sof_dev *sdev) -{ - const struct sof_dsp_power_state target_dsp_state = { - .state = SOF_DSP_PM_D0, - .substate = 0, - }; - - imx8ulp_resume(sdev); - - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} - -static int imx8ulp_dsp_runtime_suspend(struct snd_sof_dev *sdev) -{ - const struct sof_dsp_power_state target_dsp_state = { - .state = SOF_DSP_PM_D3, - .substate = 0, - }; - - imx8ulp_suspend(sdev); - - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} - -static int imx8ulp_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) -{ - const struct sof_dsp_power_state target_dsp_state = { - .state = target_state, - .substate = 0, - }; - - if (!pm_runtime_suspended(sdev->dev)) - imx8ulp_suspend(sdev); - - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} - -static int imx8ulp_dsp_resume(struct snd_sof_dev *sdev) -{ - const struct sof_dsp_power_state target_dsp_state = { - .state = SOF_DSP_PM_D0, - .substate = 0, - }; - - imx8ulp_resume(sdev); - - if (pm_runtime_suspended(sdev->dev)) { - pm_runtime_disable(sdev->dev); - pm_runtime_set_active(sdev->dev); - pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_enable(sdev->dev); - pm_runtime_idle(sdev->dev); - } - - return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); -} - -static struct snd_soc_dai_driver imx8ulp_dai[] = { - { - .name = "sai5", - .playback = { - .channels_min = 1, - .channels_max = 32, - }, - .capture = { - .channels_min = 1, - .channels_max = 32, - }, - }, - { - .name = "sai6", - .playback = { - .channels_min = 1, - .channels_max = 32, - }, - .capture = { - .channels_min = 1, - .channels_max = 32, - }, - }, -}; - -static int imx8ulp_dsp_set_power_state(struct snd_sof_dev *sdev, - const struct sof_dsp_power_state *target_state) -{ - sdev->dsp_power_state = *target_state; - - return 0; -} - -/* i.MX8 ops */ -static const struct snd_sof_dsp_ops sof_imx8ulp_ops = { - /* probe and remove */ - .probe = imx8ulp_probe, - .remove = imx8ulp_remove, - /* DSP core boot */ - .run = imx8ulp_run, - .reset = imx8ulp_reset, - - /* Block IO */ - .block_read = sof_block_read, - .block_write = sof_block_write, - - /* Module IO */ - .read64 = sof_io_read64, - - /* Mailbox IO */ - .mailbox_read = sof_mailbox_read, - .mailbox_write = sof_mailbox_write, - - /* ipc */ - .send_msg = imx8ulp_send_msg, - .get_mailbox_offset = imx8ulp_get_mailbox_offset, - .get_window_offset = imx8ulp_get_window_offset, - - .ipc_msg_data = sof_ipc_msg_data, - .set_stream_data_offset = sof_set_stream_data_offset, - - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - - /* module loading */ - .get_bar_index = imx8ulp_get_bar_index, - /* firmware loading */ - .load_firmware = snd_sof_load_firmware_memcpy, - - /* Debug information */ - .dbg_dump = imx8_dump, - - /* Firmware ops */ - .dsp_arch_ops = &sof_xtensa_arch_ops, - - /* DAI drivers */ - .drv = imx8ulp_dai, - .num_drv = ARRAY_SIZE(imx8ulp_dai), - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - - /* PM */ - .runtime_suspend = imx8ulp_dsp_runtime_suspend, - .runtime_resume = imx8ulp_dsp_runtime_resume, - - .suspend = imx8ulp_dsp_suspend, - .resume = imx8ulp_dsp_resume, - - .set_power_state = imx8ulp_dsp_set_power_state, -}; - -static struct snd_sof_of_mach sof_imx8ulp_machs[] = { - { - .compatible = "fsl,imx8ulp-evk", - .sof_tplg_filename = "sof-imx8ulp-btsco.tplg", - .drv_name = "asoc-audio-graph-card2", - }, - {} -}; - -static struct sof_dev_desc sof_of_imx8ulp_desc = { - .of_machines = sof_imx8ulp_machs, - .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), - .ipc_default = SOF_IPC_TYPE_3, - .default_fw_path = { - [SOF_IPC_TYPE_3] = "imx/sof", - }, - .default_tplg_path = { - [SOF_IPC_TYPE_3] = "imx/sof-tplg", - }, - .default_fw_filename = { - [SOF_IPC_TYPE_3] = "sof-imx8ulp.ri", - }, - .nocodec_tplg_filename = "sof-imx8ulp-nocodec.tplg", - .ops = &sof_imx8ulp_ops, -}; - -static const struct of_device_id sof_of_imx8ulp_ids[] = { - { .compatible = "fsl,imx8ulp-dsp", .data = &sof_of_imx8ulp_desc}, - { } -}; -MODULE_DEVICE_TABLE(of, sof_of_imx8ulp_ids); - -/* DT driver definition */ -static struct platform_driver snd_sof_of_imx8ulp_driver = { - .probe = sof_of_probe, - .remove = sof_of_remove, - .driver = { - .name = "sof-audio-of-imx8ulp", - .pm = &sof_of_pm, - .of_match_table = sof_of_imx8ulp_ids, - }, -}; -module_platform_driver(snd_sof_of_imx8ulp_driver); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_DESCRIPTION("SOF support for IMX8ULP platforms"); -MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); diff --git a/sound/soc/sof/imx/imx9.c b/sound/soc/sof/imx/imx9.c new file mode 100644 index 000000000000..853155d5990a --- /dev/null +++ b/sound/soc/sof/imx/imx9.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +/* + * Copyright 2025 NXP + */ + +#include <linux/arm-smccc.h> + +#include "imx-common.h" + +#define IMX_SIP_SRC 0xC2000005 +#define IMX_SIP_SRC_M_RESET_ADDR_SET 0x03 + +#define IMX95_CPU_VEC_FLAGS_BOOT BIT(29) + +#define IMX_SIP_LMM 0xC200000F +#define IMX_SIP_LMM_BOOT 0x0 +#define IMX_SIP_LMM_SHUTDOWN 0x1 + +#define IMX95_M7_LM_ID 0x1 + +static struct snd_soc_dai_driver imx95_dai[] = { + IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai3", 1, 32), +}; + +static struct snd_sof_dsp_ops sof_imx9_ops; + +static int imx95_ops_init(struct snd_sof_dev *sdev) +{ + /* first copy from template */ + memcpy(&sof_imx9_ops, &sof_imx_ops, sizeof(sof_imx_ops)); + + /* ... and finally set DAI driver */ + sof_imx9_ops.drv = get_chip_info(sdev)->drv; + sof_imx9_ops.num_drv = get_chip_info(sdev)->num_drv; + + return 0; +} + +static int imx95_chip_probe(struct snd_sof_dev *sdev) +{ + struct arm_smccc_res smc_res; + struct platform_device *pdev; + struct resource *res; + + pdev = to_platform_device(sdev->dev); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (!res) + return dev_err_probe(sdev->dev, -ENODEV, + "failed to fetch SRAM region\n"); + + /* set core boot reset address */ + arm_smccc_smc(IMX_SIP_SRC, IMX_SIP_SRC_M_RESET_ADDR_SET, res->start, + IMX95_CPU_VEC_FLAGS_BOOT, 0, 0, 0, 0, &smc_res); + + return smc_res.a0; +} + +static int imx95_core_kick(struct snd_sof_dev *sdev) +{ + struct arm_smccc_res smc_res; + + arm_smccc_smc(IMX_SIP_LMM, IMX_SIP_LMM_BOOT, + IMX95_M7_LM_ID, 0, 0, 0, 0, 0, &smc_res); + + return smc_res.a0; +} + +static int imx95_core_shutdown(struct snd_sof_dev *sdev) +{ + struct arm_smccc_res smc_res; + + arm_smccc_smc(IMX_SIP_LMM, IMX_SIP_LMM_SHUTDOWN, + IMX95_M7_LM_ID, 0, 0, 0, 0, 0, &smc_res); + + return smc_res.a0; +} + +static const struct imx_chip_ops imx95_chip_ops = { + .probe = imx95_chip_probe, + .core_kick = imx95_core_kick, + .core_shutdown = imx95_core_shutdown, +}; + +static struct imx_memory_info imx95_memory_regions[] = { + { .name = "sram", .reserved = false }, + { } +}; + +static const struct imx_chip_info imx95_chip_info = { + .ipc_info = { + .boot_mbox_offset = 0x6001000, + .window_offset = 0x6000000, + }, + .has_dma_reserved = true, + .memory = imx95_memory_regions, + .drv = imx95_dai, + .num_drv = ARRAY_SIZE(imx95_dai), + .ops = &imx95_chip_ops, +}; + +static struct snd_sof_of_mach sof_imx9_machs[] = { + { + .compatible = "fsl,imx95-19x19-evk", + .sof_tplg_filename = "sof-imx95-wm8962.tplg", + .drv_name = "asoc-audio-graph-card2", + }, + { + } +}; + +IMX_SOF_DEV_DESC(imx95, sof_imx9_machs, &imx95_chip_info, &sof_imx9_ops, imx95_ops_init); + +static const struct of_device_id sof_of_imx9_ids[] = { + { + .compatible = "fsl,imx95-cm7-sof", + .data = &IMX_SOF_DEV_DESC_NAME(imx95), + }, + { + }, +}; +MODULE_DEVICE_TABLE(of, sof_of_imx9_ids); + +static struct platform_driver snd_sof_of_imx9_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-imx9", + .pm = pm_ptr(&sof_of_pm), + .of_match_table = sof_of_imx9_ids, + }, +}; +module_platform_driver(snd_sof_of_imx9_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("SOF driver for imx9 platforms"); +MODULE_AUTHOR("Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>"); diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index f40daa616803..675f9fc92dde 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -34,7 +34,7 @@ snd-sof-pci-intel-icl-y := pci-icl.o icl.o snd-sof-pci-intel-tgl-y := pci-tgl.o tgl.o snd-sof-pci-intel-mtl-y := pci-mtl.o mtl.o snd-sof-pci-intel-lnl-y := pci-lnl.o lnl.o -snd-sof-pci-intel-ptl-y := pci-ptl.o +snd-sof-pci-intel-ptl-y := pci-ptl.o ptl.o obj-$(CONFIG_SND_SOC_SOF_MERRIFIELD) += snd-sof-pci-intel-tng.o obj-$(CONFIG_SND_SOC_SOF_INTEL_SKL) += snd-sof-pci-intel-skl.o diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index 30e981c558c6..0d364bcdcfa9 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -78,20 +78,20 @@ void atom_dump(struct snd_sof_dev *sdev, u32 flags) imrd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRD); dev_err(sdev->dev, "error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n", - (panic & SHIM_IPCX_BUSY) ? "yes" : "no", - (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic); + str_yes_no(panic & SHIM_IPCX_BUSY), + str_yes_no(panic & SHIM_IPCX_DONE), panic); dev_err(sdev->dev, "error: mask host: pending %s complete %s raw 0x%llx\n", - (imrx & SHIM_IMRX_BUSY) ? "yes" : "no", - (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx); + str_yes_no(imrx & SHIM_IMRX_BUSY), + str_yes_no(imrx & SHIM_IMRX_DONE), imrx); dev_err(sdev->dev, "error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n", - (status & SHIM_IPCD_BUSY) ? "yes" : "no", - (status & SHIM_IPCD_DONE) ? "yes" : "no", status); + str_yes_no(status & SHIM_IPCD_BUSY), + str_yes_no(status & SHIM_IPCD_DONE), status); dev_err(sdev->dev, "error: mask DSP: pending %s complete %s raw 0x%llx\n", - (imrd & SHIM_IMRD_BUSY) ? "yes" : "no", - (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd); + str_yes_no(imrd & SHIM_IMRD_BUSY), + str_yes_no(imrd & SHIM_IMRD_DONE), imrd); } EXPORT_SYMBOL_NS(atom_dump, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP"); diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index c4d92f3508b6..f1287d509835 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -266,20 +266,20 @@ static void bdw_dump(struct snd_sof_dev *sdev, u32 flags) imrd = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRD); dev_err(sdev->dev, "error: ipc host -> DSP: pending %s complete %s raw 0x%8.8x\n", - (panic & SHIM_IPCX_BUSY) ? "yes" : "no", - (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic); + str_yes_no(panic & SHIM_IPCX_BUSY), + str_yes_no(panic & SHIM_IPCX_DONE), panic); dev_err(sdev->dev, "error: mask host: pending %s complete %s raw 0x%8.8x\n", - (imrx & SHIM_IMRX_BUSY) ? "yes" : "no", - (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx); + str_yes_no(imrx & SHIM_IMRX_BUSY), + str_yes_no(imrx & SHIM_IMRX_DONE), imrx); dev_err(sdev->dev, "error: ipc DSP -> host: pending %s complete %s raw 0x%8.8x\n", - (status & SHIM_IPCD_BUSY) ? "yes" : "no", - (status & SHIM_IPCD_DONE) ? "yes" : "no", status); + str_yes_no(status & SHIM_IPCD_BUSY), + str_yes_no(status & SHIM_IPCD_DONE), status); dev_err(sdev->dev, "error: mask DSP: pending %s complete %s raw 0x%8.8x\n", - (imrd & SHIM_IMRD_BUSY) ? "yes" : "no", - (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd); + str_yes_no(imrd & SHIM_IMRD_BUSY), + str_yes_no(imrd & SHIM_IMRD_DONE), imrd); } /* @@ -410,8 +410,7 @@ static int bdw_probe(struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; - struct platform_device *pdev = - container_of(sdev->dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(sdev->dev); const struct sof_intel_dsp_desc *chip; struct resource *mmio; u32 base, size; @@ -687,7 +686,7 @@ static struct platform_driver snd_sof_acpi_intel_bdw_driver = { .remove = sof_acpi_remove, .driver = { .name = "sof-audio-acpi-intel-bdw", - .pm = &sof_acpi_pm, + .pm = pm_ptr(&sof_acpi_pm), .acpi_match_table = sof_broadwell_match, }, }; @@ -695,6 +694,5 @@ module_platform_driver(snd_sof_acpi_intel_bdw_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for Broadwell platforms"); -MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HIFI_EP_IPC"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_ACPI_DEV"); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 536d4c89d2f0..18208f77b84d 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -109,8 +109,7 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; - struct platform_device *pdev = - container_of(sdev->dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(sdev->dev); const struct sof_intel_dsp_desc *chip; struct resource *mmio; u32 base, size; @@ -468,7 +467,7 @@ static struct platform_driver snd_sof_acpi_intel_byt_driver = { .remove = sof_acpi_remove, .driver = { .name = "sof-audio-acpi-intel-byt", - .pm = &sof_acpi_pm, + .pm = pm_ptr(&sof_acpi_pm), .acpi_match_table = sof_baytrail_match, }, }; @@ -476,7 +475,6 @@ module_platform_driver(snd_sof_acpi_intel_byt_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for Baytrail/Cherrytrail"); -MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HIFI_EP_IPC"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_ACPI_DEV"); MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_ATOM_HIFI_EP"); diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 568f3dfe822f..2f9925830d1d 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -454,6 +454,7 @@ int hda_codec_i915_exit(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS_GPL(hda_codec_i915_exit, "SND_SOC_SOF_HDA_AUDIO_CODEC_I915"); +MODULE_SOFTDEP("pre: snd-hda-codec-hdmi"); #endif MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 0db2a3e554fb..883d0d3bae9e 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -318,7 +318,7 @@ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, i case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: ret = hda_link_dma_cleanup(substream, hext_stream, dai, - cmd == SNDRV_PCM_TRIGGER_STOP ? false : true); + cmd != SNDRV_PCM_TRIGGER_STOP); if (ret < 0) { dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__); return ret; @@ -503,6 +503,12 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, int ret; int i; + if (!w) { + dev_err(cpu_dai->dev, "%s widget not found, check amp link num in the topology\n", + cpu_dai->name); + return -EINVAL; + } + ops = hda_dai_get_ops(substream, cpu_dai); if (!ops) { dev_err(cpu_dai->dev, "DAI widget ops not set\n"); @@ -582,6 +588,12 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, */ for_each_rtd_cpu_dais(rtd, i, dai) { w = snd_soc_dai_get_widget(dai, substream->stream); + if (!w) { + dev_err(cpu_dai->dev, + "%s widget not found, check amp link num in the topology\n", + dai->name); + return -EINVAL; + } ipc4_copier = widget_to_copier(w); memcpy(&ipc4_copier->dma_config_tlv[cpu_dai_id], dma_config_tlv, sizeof(*dma_config_tlv)); diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index fe627bcb0531..ce561fe52bd5 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -16,6 +16,7 @@ #include <linux/bitfield.h> #include <linux/module.h> +#include <linux/string_choices.h> #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) @@ -42,6 +43,7 @@ * @shim_offset: offset to SHIM register base * @ip_offset: offset to IP register base * @shim_vs_offset: offset to vendor-specific (VS) SHIM base + * @mic_privacy_mask: bitmask of sublinks where mic privacy is applied */ struct hdac_ext2_link { struct hdac_ext_link hext_link; @@ -65,6 +67,8 @@ struct hdac_ext2_link { u32 shim_offset; u32 ip_offset; u32 shim_vs_offset; + + unsigned long mic_privacy_mask; }; #define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link) @@ -90,6 +94,13 @@ struct hdac_ext2_link { #define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100 #define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00 +/* Microphone privacy */ +#define AZX_REG_INTEL_VS_SHIM_PVCCS 0x10 +#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE BIT(0) +#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG BIT(8) +#define AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS BIT(9) +#define AZX_REG_INTEL_VS_SHIM_PVCCS_FMDIS BIT(10) + /* HDAML section - this part follows sequences in the hardware specification, * including naming conventions and the use of the hdaml_ prefix. * The code is intentionally minimal with limited dependencies on frameworks or @@ -696,6 +707,20 @@ static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, } ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); + if ((h2link->mic_privacy_mask & BIT(sublink)) && !ret) { + u16 __iomem *pvccs = h2link->base_ptr + + h2link->shim_vs_offset + + sublink * h2link->instance_offset + + AZX_REG_INTEL_VS_SHIM_PVCCS; + u16 val = readw(pvccs); + + writew(val | AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs); + + if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS) + dev_dbg(bus->dev, + "sublink %d (%d:%d): Mic privacy is enabled\n", + sublink, alt, elid); + } skip_init: if (eml_lock) @@ -742,6 +767,16 @@ static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid if (--h2link->sublink_ref_count[sublink] > 0) goto skip_shutdown; } + + if (h2link->mic_privacy_mask & BIT(sublink)) { + u16 __iomem *pvccs = h2link->base_ptr + + h2link->shim_vs_offset + + sublink * h2link->instance_offset + + AZX_REG_INTEL_VS_SHIM_PVCCS; + + writew(readw(pvccs) & ~AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHGIE, pvccs); + } + ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink); skip_shutdown: @@ -987,6 +1022,98 @@ int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool e } EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, "SND_SOC_SOF_HDA_MLINK"); +void hdac_bus_eml_set_mic_privacy_mask(struct hdac_bus *bus, bool alt, int elid, + unsigned long mask) +{ + struct hdac_ext2_link *h2link; + + if (!mask) + return; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return; + + if (__fls(mask) > h2link->slcount) { + dev_warn(bus->dev, + "%s: invalid sublink mask for %d:%d, slcount %d: %#lx\n", + __func__, alt, elid, h2link->slcount, mask); + return; + } + + dev_dbg(bus->dev, "sublink mask for %d:%d, slcount %d: %#lx\n", alt, + elid, h2link->slcount, mask); + + h2link->mic_privacy_mask = mask; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_set_mic_privacy_mask, "SND_SOC_SOF_HDA_MLINK"); + +bool hdac_bus_eml_is_mic_privacy_changed(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + bool changed = false; + u16 __iomem *pvccs; + int i; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return false; + + /* The change in privacy state needs to be acked for each link */ + for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) { + u16 val; + + if (h2link->sublink_ref_count[i] == 0) + continue; + + pvccs = h2link->base_ptr + + h2link->shim_vs_offset + + i * h2link->instance_offset + + AZX_REG_INTEL_VS_SHIM_PVCCS; + + val = readw(pvccs); + if (val & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTSCHG) { + writew(val, pvccs); + changed = true; + } + } + + return changed; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_is_mic_privacy_changed, "SND_SOC_SOF_HDA_MLINK"); + +bool hdac_bus_eml_get_mic_privacy_state(struct hdac_bus *bus, bool alt, int elid) +{ + struct hdac_ext2_link *h2link; + u16 __iomem *pvccs; + bool state; + int i; + + h2link = find_ext2_link(bus, alt, elid); + if (!h2link) + return false; + + for_each_set_bit(i, &h2link->mic_privacy_mask, h2link->slcount) { + if (h2link->sublink_ref_count[i] == 0) + continue; + + /* Return the privacy state from the first active link */ + pvccs = h2link->base_ptr + + h2link->shim_vs_offset + + i * h2link->instance_offset + + AZX_REG_INTEL_VS_SHIM_PVCCS; + + state = readw(pvccs) & AZX_REG_INTEL_VS_SHIM_PVCCS_MDSTS; + dev_dbg(bus->dev, "alt: %d, elid: %d: Mic privacy is %s\n", alt, + elid, str_enabled_disabled(state)); + + return state; + } + + return false; +} +EXPORT_SYMBOL_NS(hdac_bus_eml_get_mic_privacy_state, "SND_SOC_SOF_HDA_MLINK"); + #endif MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 5b5e484f9acf..1dd8d2092c3b 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -37,6 +37,11 @@ static bool hda_disable_rewinds; module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444); MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds"); +static int hda_force_pause_support = -1; +module_param_named(force_pause_support, hda_force_pause_support, int, 0444); +MODULE_PARM_DESC(force_pause_support, + "Pause support: -1: Use default, 0: Disable, 1: Enable (default -1)"); + u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate) { switch (rate) { @@ -240,6 +245,16 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, if (hda_always_enable_dmi_l1 && direction == SNDRV_PCM_STREAM_CAPTURE) runtime->hw.info &= ~SNDRV_PCM_INFO_PAUSE; + /* + * Do not advertise the PAUSE support if it is forced to be disabled via + * module parameter or if the pause_supported is false for the PCM + * device + */ + if (hda_force_pause_support == 0 || + (hda_force_pause_support == -1 && + !spcm->stream[substream->stream].pause_supported)) + runtime->hw.info &= ~SNDRV_PCM_INFO_PAUSE; + if (hda_always_enable_dmi_l1 || direction == SNDRV_PCM_STREAM_PLAYBACK || spcm->stream[substream->stream].d0i3_compatible) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index f991785f727e..6b1ada566476 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -63,6 +63,11 @@ static int sdw_params_stream(struct device *dev, struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, params_data->substream->stream); struct snd_sof_dai_config_data data = { 0 }; + if (!w) { + dev_err(dev, "%s widget not found, check amp link num in the topology\n", + d->name); + return -EINVAL; + } data.dai_index = (params_data->link_id << 8) | d->id; data.dai_data = params_data->alh_stream_id; data.dai_node_id = data.dai_data; @@ -347,6 +352,27 @@ void hda_sdw_process_wakeen_common(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS(hda_sdw_process_wakeen_common, "SND_SOC_SOF_INTEL_HDA_GENERIC"); +static bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev) +{ + const struct sof_intel_dsp_desc *chip; + + chip = get_chip_info(sdev->pdata); + if (chip && chip->check_mic_privacy_irq) + return chip->check_mic_privacy_irq(sdev, true, + AZX_REG_ML_LEPTR_ID_SDW); + + return false; +} + +static void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) +{ + const struct sof_intel_dsp_desc *chip; + + chip = get_chip_info(sdev->pdata); + if (chip && chip->process_mic_privacy) + chip->process_mic_privacy(sdev, true, AZX_REG_ML_LEPTR_ID_SDW); +} + #else /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */ static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) { @@ -378,6 +404,13 @@ static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) return false; } +static inline bool hda_dsp_sdw_check_mic_privacy_irq(struct snd_sof_dev *sdev) +{ + return false; +} + +static inline void hda_dsp_sdw_process_mic_privacy(struct snd_sof_dev *sdev) { } + #endif /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */ /* pre fw run operations */ @@ -673,7 +706,13 @@ static irqreturn_t hda_dsp_interrupt_thread(int irq, void *context) if (hda_dsp_check_sdw_irq(sdev)) { trace_sof_intel_hda_irq(sdev, "sdw"); + hda_dsp_sdw_thread(irq, hdev->sdw); + + if (hda_dsp_sdw_check_mic_privacy_irq(sdev)) { + trace_sof_intel_hda_irq(sdev, "mic privacy"); + hda_dsp_sdw_process_mic_privacy(sdev); + } } if (hda_sdw_check_wakeen_irq(sdev)) { @@ -1307,22 +1346,8 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) /* report to machine driver if any DMICs are found */ mach->mach_params.dmic_num = check_dmic_num(sdev); - if (sdw_mach_found) { - /* - * DMICs use up to 4 pins and are typically pin-muxed with SoundWire - * link 2 and 3, or link 1 and 2, thus we only try to enable dmics - * if all conditions are true: - * a) 2 or fewer links are used by SoundWire - * b) the NHLT table reports the presence of microphones - */ - if (hweight_long(mach->link_mask) <= 2) - dmic_fixup = true; - else - mach->mach_params.dmic_num = 0; - } else { - if (mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER) - dmic_fixup = true; - } + if (sdw_mach_found || mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER) + dmic_fixup = true; if (tplg_fixup && dmic_fixup && diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index ee4ccc1a5490..76154627fc17 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -913,10 +913,6 @@ extern struct snd_sof_dsp_ops sof_tgl_ops; int sof_tgl_ops_init(struct snd_sof_dev *sdev); extern struct snd_sof_dsp_ops sof_icl_ops; int sof_icl_ops_init(struct snd_sof_dev *sdev); -extern struct snd_sof_dsp_ops sof_mtl_ops; -int sof_mtl_ops_init(struct snd_sof_dev *sdev); -extern struct snd_sof_dsp_ops sof_lnl_ops; -int sof_lnl_ops_init(struct snd_sof_dev *sdev); extern const struct sof_intel_dsp_desc skl_chip_info; extern const struct sof_intel_dsp_desc apl_chip_info; diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c index 793d8539821d..2f3222040f98 100644 --- a/sound/soc/sof/intel/lnl.c +++ b/sound/soc/sof/intel/lnl.c @@ -20,17 +20,6 @@ #include "lnl.h" #include <sound/hda-mlink.h> -/* LunarLake ops */ -struct snd_sof_dsp_ops sof_lnl_ops; -EXPORT_SYMBOL_NS(sof_lnl_ops, "SND_SOC_SOF_INTEL_LNL"); - -static const struct snd_sof_debugfs_map lnl_dsp_debugfs[] = { - {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, - {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS}, - {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, - {"fw_regs", HDA_DSP_BAR, MTL_SRAM_WINDOW_OFFSET(0), 0x1000, SOF_DEBUGFS_ACCESS_D0_ONLY}, -}; - /* this helps allows the DSP to setup DMIC/SSP */ static int hdac_bus_offload_dmic_ssp(struct hdac_bus *bus, bool enable) { @@ -111,94 +100,50 @@ static int lnl_dsp_post_fw_run(struct snd_sof_dev *sdev) return 0; } -int sof_lnl_ops_init(struct snd_sof_dev *sdev) +int sof_lnl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops) { - struct sof_ipc4_fw_data *ipc4_data; + int ret; - /* common defaults */ - memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); + ret = sof_mtl_set_ops(sdev, dsp_ops); + if (ret) + return ret; /* probe/remove */ if (!sdev->dspless_mode_selected) { - sof_lnl_ops.probe = lnl_hda_dsp_probe; - sof_lnl_ops.remove = lnl_hda_dsp_remove; + dsp_ops->probe = lnl_hda_dsp_probe; + dsp_ops->remove = lnl_hda_dsp_remove; } - /* shutdown */ - sof_lnl_ops.shutdown = hda_dsp_shutdown; - - /* doorbell */ - sof_lnl_ops.irq_thread = mtl_ipc_irq_thread; - - /* ipc */ - sof_lnl_ops.send_msg = mtl_ipc_send_msg; - sof_lnl_ops.get_mailbox_offset = mtl_dsp_ipc_get_mailbox_offset; - sof_lnl_ops.get_window_offset = mtl_dsp_ipc_get_window_offset; - - /* debug */ - sof_lnl_ops.debug_map = lnl_dsp_debugfs; - sof_lnl_ops.debug_map_count = ARRAY_SIZE(lnl_dsp_debugfs); - sof_lnl_ops.dbg_dump = mtl_dsp_dump; - sof_lnl_ops.ipc_dump = mtl_ipc_dump; - - /* pre/post fw run */ - sof_lnl_ops.pre_fw_run = mtl_dsp_pre_fw_run; - sof_lnl_ops.post_fw_run = lnl_dsp_post_fw_run; - - /* parse platform specific extended manifest */ - sof_lnl_ops.parse_platform_ext_manifest = NULL; - - /* dsp core get/put */ - /* TODO: add core_get and core_put */ + /* post fw run */ + dsp_ops->post_fw_run = lnl_dsp_post_fw_run; /* PM */ if (!sdev->dspless_mode_selected) { - sof_lnl_ops.resume = lnl_hda_dsp_resume; - sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume; + dsp_ops->resume = lnl_hda_dsp_resume; + dsp_ops->runtime_resume = lnl_hda_dsp_runtime_resume; } - /* dsp core get/put */ - sof_lnl_ops.core_get = mtl_dsp_core_get; - sof_lnl_ops.core_put = mtl_dsp_core_put; - - sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); - if (!sdev->private) - return -ENOMEM; - - ipc4_data = sdev->private; - ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; - - ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; - - ipc4_data->fw_context_save = true; - - /* External library loading support */ - ipc4_data->load_library = hda_dsp_ipc4_load_library; - - /* set DAI ops */ - hda_set_dai_drv_ops(sdev, &sof_lnl_ops); - - sof_lnl_ops.set_power_state = hda_dsp_set_power_state_ipc4; - return 0; -}; -EXPORT_SYMBOL_NS(sof_lnl_ops_init, "SND_SOC_SOF_INTEL_LNL"); +} +EXPORT_SYMBOL_NS(sof_lnl_set_ops, "SND_SOC_SOF_INTEL_LNL"); /* Check if an SDW IRQ occurred */ -static bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev) +bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); return hdac_bus_eml_check_interrupt(bus, true, AZX_REG_ML_LEPTR_ID_SDW); } +EXPORT_SYMBOL_NS(lnl_dsp_check_sdw_irq, "SND_SOC_SOF_INTEL_LNL"); -static int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev) +int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev) { mtl_disable_ipc_interrupts(sdev); return mtl_enable_interrupts(sdev, false); } +EXPORT_SYMBOL_NS(lnl_dsp_disable_interrupts, "SND_SOC_SOF_INTEL_LNL"); -static bool lnl_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) +bool lnl_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); u16 wake_sts; @@ -214,6 +159,7 @@ static bool lnl_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) /* filter out the range of SDIs that can be set for SoundWire */ return wake_sts & GENMASK(SDW_MAX_DEVICES, SDW_INTEL_DEV_NUM_IDA_MIN); } +EXPORT_SYMBOL_NS(lnl_sdw_check_wakeen_irq, "SND_SOC_SOF_INTEL_LNL"); const struct sof_intel_dsp_desc lnl_chip_info = { .cores_num = 5, @@ -239,26 +185,5 @@ const struct sof_intel_dsp_desc lnl_chip_info = { .hw_ip_version = SOF_INTEL_ACE_2_0, }; -const struct sof_intel_dsp_desc ptl_chip_info = { - .cores_num = 5, - .init_core_mask = BIT(0), - .host_managed_cores_mask = BIT(0), - .ipc_req = MTL_DSP_REG_HFIPCXIDR, - .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY, - .ipc_ack = MTL_DSP_REG_HFIPCXIDA, - .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE, - .ipc_ctl = MTL_DSP_REG_HFIPCXCTL, - .rom_status_reg = LNL_DSP_REG_HFDSC, - .rom_init_timeout = 300, - .ssp_count = MTL_SSP_COUNT, - .d0i3_offset = MTL_HDA_VS_D0I3C, - .read_sdw_lcount = hda_sdw_check_lcount_ext, - .check_sdw_irq = lnl_dsp_check_sdw_irq, - .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq, - .check_ipc_irq = mtl_dsp_check_ipc_irq, - .cl_init = mtl_dsp_cl_init, - .power_down_dsp = mtl_power_down_dsp, - .disable_interrupts = lnl_dsp_disable_interrupts, - .hw_ip_version = SOF_INTEL_ACE_3_0, -}; -EXPORT_SYMBOL_NS(ptl_chip_info, "SND_SOC_SOF_INTEL_LNL"); +MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL"); +MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK"); diff --git a/sound/soc/sof/intel/lnl.h b/sound/soc/sof/intel/lnl.h index 79101af84b2e..2837f818ac51 100644 --- a/sound/soc/sof/intel/lnl.h +++ b/sound/soc/sof/intel/lnl.h @@ -12,4 +12,10 @@ #define LNL_DSP_REG_HFDSC 0x160200 /* DSP core0 status */ #define LNL_DSP_REG_HFDEC 0x160204 /* DSP core0 error */ +int sof_lnl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops); + +bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev); +int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev); +bool lnl_sdw_check_wakeen_irq(struct snd_sof_dev *sdev); + #endif /* __SOF_INTEL_LNL_H */ diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index d07c68f431ba..2242c96bfa51 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -11,6 +11,7 @@ #include <linux/debugfs.h> #include <linux/firmware.h> +#include <linux/string_choices.h> #include <sound/sof/ipc4/header.h> #include <trace/events/sof_intel.h> #include "../ipc4-priv.h" @@ -95,7 +96,7 @@ static bool mtl_dsp_check_sdw_irq(struct snd_sof_dev *sdev) return false; } -int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc4_msg *msg_data = msg->msg_data; @@ -121,7 +122,6 @@ int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) return 0; } -EXPORT_SYMBOL_NS(mtl_ipc_send_msg, "SND_SOC_SOF_INTEL_MTL"); void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev) { @@ -176,7 +176,7 @@ static void mtl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable) HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US); if (ret < 0) dev_err(sdev->dev, "failed to set SoundWire IPC interrupt %s\n", - enable ? "enable" : "disable"); + str_enable_disable(enable)); } int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) @@ -209,7 +209,7 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US); if (ret < 0) { dev_err(sdev->dev, "failed to %s Host IPC and/or SOUNDWIRE\n", - enable ? "enable" : "disable"); + str_enable_disable(enable)); return ret; } @@ -228,7 +228,7 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US); if (ret < 0) { dev_err(sdev->dev, "failed to set Host IPC interrupt %s\n", - enable ? "enable" : "disable"); + str_enable_disable(enable)); return ret; } @@ -237,7 +237,7 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) EXPORT_SYMBOL_NS(mtl_enable_interrupts, "SND_SOC_SOF_INTEL_MTL"); /* pre fw run operations */ -int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) +static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; u32 dsphfpwrsts; @@ -297,9 +297,8 @@ int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) return ret; } -EXPORT_SYMBOL_NS(mtl_dsp_pre_fw_run, "SND_SOC_SOF_INTEL_MTL"); -int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) +static int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) { int ret; @@ -324,9 +323,8 @@ int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) hda_sdw_int_enable(sdev, true); return 0; } -EXPORT_SYMBOL_NS(mtl_dsp_post_fw_run, "SND_SOC_SOF_INTEL_MTL"); -void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags) +static void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags) { char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR; u32 fwsts; @@ -342,7 +340,6 @@ void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags) sof_ipc4_intel_dump_telemetry_state(sdev, flags); } -EXPORT_SYMBOL_NS(mtl_dsp_dump, "SND_SOC_SOF_INTEL_MTL"); static bool mtl_dsp_primary_core_is_enabled(struct snd_sof_dev *sdev) { @@ -558,7 +555,7 @@ err: } EXPORT_SYMBOL_NS(mtl_dsp_cl_init, "SND_SOC_SOF_INTEL_MTL"); -irqreturn_t mtl_ipc_irq_thread(int irq, void *context) +static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) { struct sof_ipc4_msg notification_data = {{ 0 }}; struct snd_sof_dev *sdev = context; @@ -640,21 +637,18 @@ irqreturn_t mtl_ipc_irq_thread(int irq, void *context) return IRQ_HANDLED; } -EXPORT_SYMBOL_NS(mtl_ipc_irq_thread, "SND_SOC_SOF_INTEL_MTL"); -int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) +static int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) { return MTL_DSP_MBOX_UPLINK_OFFSET; } -EXPORT_SYMBOL_NS(mtl_dsp_ipc_get_mailbox_offset, "SND_SOC_SOF_INTEL_MTL"); -int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) +static int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) { return MTL_SRAM_WINDOW_OFFSET(id); } -EXPORT_SYMBOL_NS(mtl_dsp_ipc_get_window_offset, "SND_SOC_SOF_INTEL_MTL"); -void mtl_ipc_dump(struct snd_sof_dev *sdev) +static void mtl_ipc_dump(struct snd_sof_dev *sdev) { u32 hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl; @@ -670,7 +664,6 @@ void mtl_ipc_dump(struct snd_sof_dev *sdev) "Host IPC initiator: %#x|%#x|%#x, target: %#x|%#x|%#x, ctl: %#x\n", hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl); } -EXPORT_SYMBOL_NS(mtl_ipc_dump, "SND_SOC_SOF_INTEL_MTL"); static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev) { @@ -679,7 +672,7 @@ static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev) return mtl_enable_interrupts(sdev, false); } -int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core) +static int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core) { const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; @@ -691,9 +684,8 @@ int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core) return 0; } -EXPORT_SYMBOL_NS(mtl_dsp_core_get, "SND_SOC_SOF_INTEL_MTL"); -int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core) +static int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core) { const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; int ret; @@ -709,45 +701,41 @@ int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core) return 0; } -EXPORT_SYMBOL_NS(mtl_dsp_core_put, "SND_SOC_SOF_INTEL_MTL"); -/* Meteorlake ops */ -struct snd_sof_dsp_ops sof_mtl_ops; - -int sof_mtl_ops_init(struct snd_sof_dev *sdev) +int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops) { struct sof_ipc4_fw_data *ipc4_data; /* common defaults */ - memcpy(&sof_mtl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); + memcpy(dsp_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); /* shutdown */ - sof_mtl_ops.shutdown = hda_dsp_shutdown; + dsp_ops->shutdown = hda_dsp_shutdown; /* doorbell */ - sof_mtl_ops.irq_thread = mtl_ipc_irq_thread; + dsp_ops->irq_thread = mtl_ipc_irq_thread; /* ipc */ - sof_mtl_ops.send_msg = mtl_ipc_send_msg; - sof_mtl_ops.get_mailbox_offset = mtl_dsp_ipc_get_mailbox_offset; - sof_mtl_ops.get_window_offset = mtl_dsp_ipc_get_window_offset; + dsp_ops->send_msg = mtl_ipc_send_msg; + dsp_ops->get_mailbox_offset = mtl_dsp_ipc_get_mailbox_offset; + dsp_ops->get_window_offset = mtl_dsp_ipc_get_window_offset; /* debug */ - sof_mtl_ops.debug_map = mtl_dsp_debugfs; - sof_mtl_ops.debug_map_count = ARRAY_SIZE(mtl_dsp_debugfs); - sof_mtl_ops.dbg_dump = mtl_dsp_dump; - sof_mtl_ops.ipc_dump = mtl_ipc_dump; + dsp_ops->debug_map = mtl_dsp_debugfs; + dsp_ops->debug_map_count = ARRAY_SIZE(mtl_dsp_debugfs); + dsp_ops->dbg_dump = mtl_dsp_dump; + dsp_ops->ipc_dump = mtl_ipc_dump; /* pre/post fw run */ - sof_mtl_ops.pre_fw_run = mtl_dsp_pre_fw_run; - sof_mtl_ops.post_fw_run = mtl_dsp_post_fw_run; + dsp_ops->pre_fw_run = mtl_dsp_pre_fw_run; + dsp_ops->post_fw_run = mtl_dsp_post_fw_run; /* parse platform specific extended manifest */ - sof_mtl_ops.parse_platform_ext_manifest = NULL; + dsp_ops->parse_platform_ext_manifest = NULL; /* dsp core get/put */ - sof_mtl_ops.core_get = mtl_dsp_core_get; - sof_mtl_ops.core_put = mtl_dsp_core_put; + dsp_ops->core_get = mtl_dsp_core_get; + dsp_ops->core_put = mtl_dsp_core_put; sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); if (!sdev->private) @@ -763,13 +751,14 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev) /* External library loading support */ ipc4_data->load_library = hda_dsp_ipc4_load_library; - /* set DAI ops */ - hda_set_dai_drv_ops(sdev, &sof_mtl_ops); + dsp_ops->set_power_state = hda_dsp_set_power_state_ipc4; - sof_mtl_ops.set_power_state = hda_dsp_set_power_state_ipc4; + /* set DAI ops */ + hda_set_dai_drv_ops(sdev, dsp_ops); return 0; -}; +} +EXPORT_SYMBOL_NS(sof_mtl_set_ops, "SND_SOC_SOF_INTEL_MTL"); const struct sof_intel_dsp_desc mtl_chip_info = { .cores_num = 3, diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index 9ab4b21c960e..e01a1536709e 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -122,26 +122,13 @@ #define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0) bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev); -int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev); void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev); int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable); -int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev); -int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev); -void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags); - int mtl_power_down_dsp(struct snd_sof_dev *sdev); int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); -irqreturn_t mtl_ipc_irq_thread(int irq, void *context); - -int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev); -int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id); - -void mtl_ipc_dump(struct snd_sof_dev *sdev); - -int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core); -int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core); +int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops); diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c index 94ab3c61e3f7..0bf7ee753bc3 100644 --- a/sound/soc/sof/intel/pci-apl.c +++ b/sound/soc/sof/intel/pci-apl.c @@ -99,7 +99,7 @@ static struct pci_driver snd_sof_pci_intel_apl_driver = { .remove = sof_pci_remove, .shutdown = sof_pci_shutdown, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_intel_apl_driver); diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c index 739c352c3860..de48640024e4 100644 --- a/sound/soc/sof/intel/pci-cnl.c +++ b/sound/soc/sof/intel/pci-cnl.c @@ -137,7 +137,7 @@ static struct pci_driver snd_sof_pci_intel_cnl_driver = { .remove = sof_pci_remove, .shutdown = sof_pci_shutdown, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_intel_cnl_driver); diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c index 8545ab95eac8..fd219e654844 100644 --- a/sound/soc/sof/intel/pci-icl.c +++ b/sound/soc/sof/intel/pci-icl.c @@ -102,7 +102,7 @@ static struct pci_driver snd_sof_pci_intel_icl_driver = { .remove = sof_pci_remove, .shutdown = sof_pci_shutdown, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_intel_icl_driver); diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c index 8d4d74ac4398..ae379c23f008 100644 --- a/sound/soc/sof/intel/pci-lnl.c +++ b/sound/soc/sof/intel/pci-lnl.c @@ -18,7 +18,15 @@ /* platform specific devices */ #include "hda.h" -#include "mtl.h" +#include "lnl.h" + +/* LunarLake ops */ +static struct snd_sof_dsp_ops sof_lnl_ops; + +static int sof_lnl_ops_init(struct snd_sof_dev *sdev) +{ + return sof_lnl_set_ops(sdev, &sof_lnl_ops); +} static const struct sof_dev_desc lnl_desc = { .use_acpi_target_states = true, @@ -64,7 +72,7 @@ static struct pci_driver snd_sof_pci_intel_lnl_driver = { .remove = sof_pci_remove, .shutdown = sof_pci_shutdown, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_intel_lnl_driver); @@ -73,6 +81,4 @@ MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for LunarLake platforms"); MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC"); MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON"); -MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL"); -MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK"); MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c index 71f711cf8599..7b2533999195 100644 --- a/sound/soc/sof/intel/pci-mtl.c +++ b/sound/soc/sof/intel/pci-mtl.c @@ -20,6 +20,14 @@ #include "hda.h" #include "mtl.h" +/* Meteorlake ops */ +static struct snd_sof_dsp_ops sof_mtl_ops; + +static int sof_mtl_ops_init(struct snd_sof_dev *sdev) +{ + return sof_mtl_set_ops(sdev, &sof_mtl_ops); +} + static const struct sof_dev_desc mtl_desc = { .use_acpi_target_states = true, .machines = snd_soc_acpi_intel_mtl_machines, @@ -127,7 +135,7 @@ static struct pci_driver snd_sof_pci_intel_mtl_driver = { .remove = sof_pci_remove, .shutdown = sof_pci_shutdown, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_intel_mtl_driver); diff --git a/sound/soc/sof/intel/pci-ptl.c b/sound/soc/sof/intel/pci-ptl.c index 0aacdfac9fb4..7d4c46f56931 100644 --- a/sound/soc/sof/intel/pci-ptl.c +++ b/sound/soc/sof/intel/pci-ptl.c @@ -16,7 +16,15 @@ /* platform specific devices */ #include "hda.h" -#include "mtl.h" +#include "ptl.h" + +/* PantherLake ops */ +static struct snd_sof_dsp_ops sof_ptl_ops; + +static int sof_ptl_ops_init(struct snd_sof_dev *sdev) +{ + return sof_ptl_set_ops(sdev, &sof_ptl_ops); +} static const struct sof_dev_desc ptl_desc = { .use_acpi_target_states = true, @@ -43,13 +51,14 @@ static const struct sof_dev_desc ptl_desc = { [SOF_IPC_TYPE_4] = "sof-ptl.ri", }, .nocodec_tplg_filename = "sof-ptl-nocodec.tplg", - .ops = &sof_lnl_ops, - .ops_init = sof_lnl_ops_init, + .ops = &sof_ptl_ops, + .ops_init = sof_ptl_ops_init, }; /* PCI IDs */ static const struct pci_device_id sof_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, HDA_PTL, &ptl_desc) }, /* PTL */ + { PCI_DEVICE_DATA(INTEL, HDA_PTL_H, &ptl_desc) }, /* PTL-H */ { 0, } }; MODULE_DEVICE_TABLE(pci, sof_pci_ids); @@ -62,7 +71,7 @@ static struct pci_driver snd_sof_pci_intel_ptl_driver = { .remove = sof_pci_remove, .shutdown = sof_pci_shutdown, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_intel_ptl_driver); @@ -71,7 +80,4 @@ MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for PantherLake platforms"); MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_GENERIC"); MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HDA_COMMON"); -MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_LNL"); -MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL"); -MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK"); MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); diff --git a/sound/soc/sof/intel/pci-skl.c b/sound/soc/sof/intel/pci-skl.c index bd9daae51e4c..a16945dc35f7 100644 --- a/sound/soc/sof/intel/pci-skl.c +++ b/sound/soc/sof/intel/pci-skl.c @@ -83,7 +83,7 @@ static struct pci_driver snd_sof_pci_intel_skl_driver = { .remove = sof_pci_remove, .shutdown = sof_pci_shutdown, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_intel_skl_driver); diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index f76a7197f6ca..437c43819825 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -311,7 +311,7 @@ static struct pci_driver snd_sof_pci_intel_tgl_driver = { .remove = sof_pci_remove, .shutdown = sof_pci_shutdown, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_intel_tgl_driver); diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index b585ac4a85c2..0c11cc1fd820 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -238,14 +238,13 @@ static struct pci_driver snd_sof_pci_intel_tng_driver = { .remove = sof_pci_remove, .shutdown = sof_pci_shutdown, .driver = { - .pm = &sof_pci_pm, + .pm = pm_ptr(&sof_pci_pm), }, }; module_pci_driver(snd_sof_pci_intel_tng_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for Tangier platforms"); -MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_HIFI_EP_IPC"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_ATOM_HIFI_EP"); diff --git a/sound/soc/sof/intel/ptl.c b/sound/soc/sof/intel/ptl.c new file mode 100644 index 000000000000..8fa4bdceedd9 --- /dev/null +++ b/sound/soc/sof/intel/ptl.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2025 Intel Corporation + +/* + * Hardware interface for audio DSP on PantherLake. + */ + +#include <sound/hda_register.h> +#include <sound/hda-mlink.h> +#include <sound/sof/ipc4/header.h> +#include "../ipc4-priv.h" +#include "../ops.h" +#include "hda.h" +#include "hda-ipc.h" +#include "../sof-audio.h" +#include "mtl.h" +#include "lnl.h" +#include "ptl.h" + +static bool sof_ptl_check_mic_privacy_irq(struct snd_sof_dev *sdev, bool alt, + int elid) +{ + if (!alt || elid != AZX_REG_ML_LEPTR_ID_SDW) + return false; + + return hdac_bus_eml_is_mic_privacy_changed(sof_to_bus(sdev), alt, elid); +} + +static void sof_ptl_process_mic_privacy(struct snd_sof_dev *sdev, bool alt, + int elid) +{ + bool state; + + if (!alt || elid != AZX_REG_ML_LEPTR_ID_SDW) + return; + + state = hdac_bus_eml_get_mic_privacy_state(sof_to_bus(sdev), alt, elid); + + sof_ipc4_mic_privacy_state_change(sdev, state); +} + +static void sof_ptl_set_mic_privacy(struct snd_sof_dev *sdev, + struct sof_ipc4_intel_mic_privacy_cap *caps) +{ + u32 micpvcp; + + if (!caps || !caps->capabilities_length) + return; + + micpvcp = caps->capabilities[0]; + + /* No need to set the mic privacy if it is not enabled or forced */ + if (!(micpvcp & PTL_MICPVCP_DDZE_ENABLED) || + micpvcp & PTL_MICPVCP_DDZE_FORCED) + return; + + hdac_bus_eml_set_mic_privacy_mask(sof_to_bus(sdev), true, + AZX_REG_ML_LEPTR_ID_SDW, + PTL_MICPVCP_GET_SDW_MASK(micpvcp)); +} + +int sof_ptl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops) +{ + struct sof_ipc4_fw_data *ipc4_data; + int ret; + + ret = sof_lnl_set_ops(sdev, dsp_ops); + if (ret) + return ret; + + ipc4_data = sdev->private; + ipc4_data->intel_configure_mic_privacy = sof_ptl_set_mic_privacy; + + return 0; +}; +EXPORT_SYMBOL_NS(sof_ptl_set_ops, "SND_SOC_SOF_INTEL_PTL"); + +const struct sof_intel_dsp_desc ptl_chip_info = { + .cores_num = 5, + .init_core_mask = BIT(0), + .host_managed_cores_mask = BIT(0), + .ipc_req = MTL_DSP_REG_HFIPCXIDR, + .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY, + .ipc_ack = MTL_DSP_REG_HFIPCXIDA, + .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE, + .ipc_ctl = MTL_DSP_REG_HFIPCXCTL, + .rom_status_reg = LNL_DSP_REG_HFDSC, + .rom_init_timeout = 300, + .ssp_count = MTL_SSP_COUNT, + .d0i3_offset = MTL_HDA_VS_D0I3C, + .read_sdw_lcount = hda_sdw_check_lcount_ext, + .check_sdw_irq = lnl_dsp_check_sdw_irq, + .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq, + .check_ipc_irq = mtl_dsp_check_ipc_irq, + .check_mic_privacy_irq = sof_ptl_check_mic_privacy_irq, + .process_mic_privacy = sof_ptl_process_mic_privacy, + .cl_init = mtl_dsp_cl_init, + .power_down_dsp = mtl_power_down_dsp, + .disable_interrupts = lnl_dsp_disable_interrupts, + .hw_ip_version = SOF_INTEL_ACE_3_0, +}; + +MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL"); +MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_LNL"); +MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK"); diff --git a/sound/soc/sof/intel/ptl.h b/sound/soc/sof/intel/ptl.h new file mode 100644 index 000000000000..6a7ef11f411e --- /dev/null +++ b/sound/soc/sof/intel/ptl.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2025 Intel Corporation + */ + +#ifndef __SOF_INTEL_PTL_H +#define __SOF_INTEL_PTL_H + +#define PTL_MICPVCP_DDZE_FORCED BIT(16) +#define PTL_MICPVCP_DDZE_ENABLED BIT(17) +#define PTL_MICPVCP_DDZLS_SDW GENMASK(26, 20) +#define PTL_MICPVCP_GET_SDW_MASK(x) (((x) & PTL_MICPVCP_DDZLS_SDW) >> 20) + +int sof_ptl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops); + +#endif /* __SOF_INTEL_PTL_H */ diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index 8709b750a11e..d4372f0bff7e 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -193,6 +193,8 @@ struct sof_intel_dsp_desc { bool (*check_sdw_wakeen_irq)(struct snd_sof_dev *sdev); void (*sdw_process_wakeen)(struct snd_sof_dev *sdev); bool (*check_ipc_irq)(struct snd_sof_dev *sdev); + bool (*check_mic_privacy_irq)(struct snd_sof_dev *sdev, bool alt, int elid); + void (*process_mic_privacy)(struct snd_sof_dev *sdev, bool alt, int elid); int (*power_down_dsp)(struct snd_sof_dev *sdev); int (*disable_interrupts)(struct snd_sof_dev *sdev); int (*cl_init)(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 1c1b8f595367..90ef5d99f626 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -117,22 +117,23 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, if (platform_params->cont_update_posn) pcm.params.cont_update_posn = 1; - dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag); + spcm_dbg(spcm, substream->stream, "stream_tag %d\n", + pcm.params.stream_tag); /* send hw_params IPC to the DSP */ ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm), &ipc_params_reply, sizeof(ipc_params_reply)); if (ret < 0) { - dev_err(component->dev, "HW params ipc failed for stream %d\n", - pcm.params.stream_tag); + spcm_err(spcm, substream->stream, + "STREAM_PCM_PARAMS ipc failed for stream_tag %d\n", + pcm.params.stream_tag); return ret; } ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[substream->stream], ipc_params_reply.posn_offset); if (ret < 0) { - dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n", - __func__, spcm->pcm.pcm_id); + spcm_err(spcm, substream->stream, "invalid stream data offset\n"); return ret; } @@ -171,7 +172,7 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; break; default: - dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd); + spcm_err(spcm, substream->stream, "Unhandled trigger cmd %d\n", cmd); return -EINVAL; } diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index e98b53b67d12..473d416bc910 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2386,28 +2386,16 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) { struct snd_sof_widget *swidget; - struct snd_sof_pcm *spcm; - int dir, ret; + int ret; /* * free all PCMs and their associated DAPM widgets if their connected DAPM widget * list is not NULL. This should only be true for paused streams at this point. * This is equivalent to the handling of FE DAI suspend trigger for running streams. */ - list_for_each_entry(spcm, &sdev->pcm_list, list) { - for_each_pcm_streams(dir) { - struct snd_pcm_substream *substream = spcm->stream[dir].substream; - - if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored) - continue; - - if (spcm->stream[dir].list) { - ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); - if (ret < 0) - return ret; - } - } - } + ret = sof_pcm_free_all_streams(sdev); + if (ret) + return ret; /* * free any left over DAI widgets. This is equivalent to the handling of suspend trigger diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 7de5e3d285e7..4a194a705ace 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -801,20 +801,16 @@ int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev) return -EINVAL; } - if (ready->flags & SOF_IPC_INFO_BUILD) { + if (ready->flags & SOF_IPC_INFO_BUILD) dev_info(sdev->dev, "Firmware debug build %d on %s-%s - options:\n" " GDB: %s\n" " lock debug: %s\n" " lock vdebug: %s\n", v->build, v->date, v->time, - (ready->flags & SOF_IPC_INFO_GDB) ? - "enabled" : "disabled", - (ready->flags & SOF_IPC_INFO_LOCKS) ? - "enabled" : "disabled", - (ready->flags & SOF_IPC_INFO_LOCKSV) ? - "enabled" : "disabled"); - } + str_enabled_disabled(ready->flags & SOF_IPC_INFO_GDB), + str_enabled_disabled(ready->flags & SOF_IPC_INFO_LOCKS), + str_enabled_disabled(ready->flags & SOF_IPC_INFO_LOCKSV)); /* copy the fw_version into debugfs at first boot */ memcpy(&sdev->fw_version, v, sizeof(*v)); diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index bcdb33d03682..d2f534d65edf 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -169,21 +169,14 @@ static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev) return payload_offset; } -static int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev, - unsigned long lib_id, const guid_t *uuid) +static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id, + const char *lib_filename, bool optional) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_fw_library *fw_lib; - const char *fw_filename; ssize_t payload_offset; int ret, i, err; - if (!sdev->pdata->fw_lib_prefix) { - dev_err(sdev->dev, - "Library loading is not supported due to not set library path\n"); - return -EINVAL; - } - if (!ipc4_data->load_library) { dev_err(sdev->dev, "Library loading is not supported on this platform\n"); return -EOPNOTSUPP; @@ -193,21 +186,26 @@ static int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev, if (!fw_lib) return -ENOMEM; - fw_filename = kasprintf(GFP_KERNEL, "%s/%pUL.bin", - sdev->pdata->fw_lib_prefix, uuid); - if (!fw_filename) { - ret = -ENOMEM; - goto free_fw_lib; - } - - ret = request_firmware(&fw_lib->sof_fw.fw, fw_filename, sdev->dev); - if (ret < 0) { - dev_err(sdev->dev, "Library file '%s' is missing\n", fw_filename); - goto free_filename; + if (optional) { + ret = firmware_request_nowarn(&fw_lib->sof_fw.fw, lib_filename, + sdev->dev); + if (ret < 0) { + /* optional library, override the error */ + ret = 0; + goto free_fw_lib; + } } else { - dev_dbg(sdev->dev, "Library file '%s' loaded\n", fw_filename); + ret = request_firmware(&fw_lib->sof_fw.fw, lib_filename, + sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "Library file '%s' is missing\n", + lib_filename); + goto free_fw_lib; + } } + dev_dbg(sdev->dev, "Library file '%s' loaded\n", lib_filename); + payload_offset = sof_ipc4_fw_parse_ext_man(sdev, fw_lib); if (payload_offset <= 0) { if (!payload_offset) @@ -251,22 +249,117 @@ static int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev, if (unlikely(ret)) goto release; - kfree(fw_filename); - return 0; release: release_firmware(fw_lib->sof_fw.fw); /* Allocated within sof_ipc4_fw_parse_ext_man() */ devm_kfree(sdev->dev, fw_lib->modules); -free_filename: - kfree(fw_filename); free_fw_lib: devm_kfree(sdev->dev, fw_lib); return ret; } +/** + * sof_ipc4_complete_split_release - loads the library parts of a split firmware + * @sdev: SOF device + * + * With IPC4 the firmware can be a single binary or a split release. + * - single binary: only the basefw + * - split release: basefw and two libraries (openmodules, debug) + * + * With split firmware release it is also allowed that for example only the + * debug library is present (the openmodules content is built in the basefw). + * + * To handle the permutations try to load the openmodules then the debug + * libraries as optional ones after the basefw boot. + * + * The libraries for the split release are stored alongside the basefw on the + * filesystem. + */ +int sof_ipc4_complete_split_release(struct snd_sof_dev *sdev) +{ + static const char * const lib_bundle[] = { "openmodules", "debug" }; + const char *fw_filename = sdev->pdata->fw_filename; + const char *lib_filename, *p; + size_t lib_name_base_size; + unsigned long lib_id = 1; + char *lib_name_base; + int i; + + p = strstr(fw_filename, ".ri"); + if (!p || strlen(p) != 3) { + dev_info(sdev->dev, + "%s: Firmware name '%s' is missing .ri extension\n", + __func__, fw_filename); + return 0; + } + + /* Space for the firmware basename + '\0', without the extension */ + lib_name_base_size = strlen(fw_filename) - 2; + lib_name_base = kzalloc(lib_name_base_size, GFP_KERNEL); + if (!lib_name_base) + return -ENOMEM; + + /* + * strscpy will 0 terminate the copied string, removing the '.ri' from + * the end of the fw_filename, for example: + * fw_filename: "sof-ptl.ri\0" + * lib_name_base: "sof-ptl\0" + */ + strscpy(lib_name_base, fw_filename, lib_name_base_size); + + for (i = 0; i < ARRAY_SIZE(lib_bundle); i++) { + int ret; + + lib_filename = kasprintf(GFP_KERNEL, "%s/%s-%s.ri", + sdev->pdata->fw_filename_prefix, + lib_name_base, lib_bundle[i]); + if (!lib_filename) { + kfree(lib_name_base); + return -ENOMEM; + } + + ret = sof_ipc4_load_library(sdev, lib_id, lib_filename, true); + if (ret) + dev_warn(sdev->dev, "%s: Failed to load %s: %d\n", + __func__, lib_filename, ret); + else + lib_id++; + + kfree(lib_filename); + } + + kfree(lib_name_base); + + return 0; +} + +static int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev, + unsigned long lib_id, const guid_t *uuid) +{ + const char *lib_filename; + int ret; + + if (!sdev->pdata->fw_lib_prefix) { + dev_err(sdev->dev, + "Library loading is not supported due to not set library path\n"); + return -EINVAL; + } + + lib_filename = kasprintf(GFP_KERNEL, "%s/%pUL.bin", + sdev->pdata->fw_lib_prefix, uuid); + if (!lib_filename) + return -ENOMEM; + + ret = sof_ipc4_load_library(sdev, lib_id, lib_filename, false); + + kfree(lib_filename); + + return ret; +} + struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev, const guid_t *uuid) { @@ -409,6 +502,39 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) offset += sizeof(*tuple) + tuple->size; } + /* Get the hardware configuration */ + msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID); + msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID); + msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_HW_CONFIG_GET); + + msg.data_size = sdev->ipc->max_payload_size; + + ret = iops->set_get_data(sdev, &msg, msg.data_size, false); + if (ret) + goto out; + + offset = 0; + while (offset < msg.data_size) { + tuple = (struct sof_ipc4_tuple *)((u8 *)msg.data_ptr + offset); + + switch (tuple->type) { + case SOF_IPC4_HW_CFG_INTEL_MIC_PRIVACY_CAPS: + if (ipc4_data->intel_configure_mic_privacy) { + struct sof_ipc4_intel_mic_privacy_cap *caps; + + caps = (struct sof_ipc4_intel_mic_privacy_cap *)tuple->value; + ipc4_data->intel_configure_mic_privacy(sdev, caps); + } + break; + default: + break; + } + + offset += sizeof(*tuple) + tuple->size; + } + out: kfree(msg.data_ptr); diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 18fff2df76f9..1a2841899ff5 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -313,7 +313,7 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, set_fifo_size = false; break; default: - dev_err(sdev->dev, "Unexpected state %d", state); + spcm_err(spcm, direction, "Unexpected pipeline state %d\n", state); return -EINVAL; } @@ -333,8 +333,8 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, struct sof_ipc4_pipeline *pipeline = pipe_widget->private; if (!pipeline->use_chain_dma) { - dev_err(sdev->dev, - "All pipelines in chained DMA stream should have use_chain_dma attribute set."); + spcm_err(spcm, direction, + "All pipelines in chained DMA path should have use_chain_dma attribute set."); return -EINVAL; } @@ -389,12 +389,12 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, int ret; int i; - dev_dbg(sdev->dev, "trigger cmd: %d state: %d\n", cmd, state); - spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; + spcm_dbg(spcm, substream->stream, "cmd: %d, state: %d\n", cmd, state); + pipeline_list = &spcm->stream[substream->stream].pipeline_list; /* nothing to trigger if the list is empty */ @@ -465,7 +465,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, */ ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list); if (ret < 0) { - dev_err(sdev->dev, "failed to pause all pipelines\n"); + spcm_err(spcm, substream->stream, "failed to pause all pipelines\n"); goto free; } @@ -494,7 +494,9 @@ skip_pause_transition: /* else set the RUNNING/RESET state in the DSP */ ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list); if (ret < 0) { - dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state); + spcm_err(spcm, substream->stream, + "failed to set final state %d for all pipelines\n", + state); /* * workaround: if the firmware is crashed while setting the * pipelines to reset state we must ignore the error code and @@ -610,12 +612,11 @@ static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev, * Copier does not change sampling rate, so we * need to only consider the input pin information. */ + be_rate = pin_fmts[0].audio_fmt.sampling_frequency; for (i = 0; i < num_input_formats; i++) { unsigned int val = pin_fmts[i].audio_fmt.sampling_frequency; - if (i == 0) - be_rate = val; - else if (val != be_rate) + if (val != be_rate) single_be_rate = false; if (val == fe_rate) { diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index ea3323b90343..58b032820683 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -11,6 +11,7 @@ #include <linux/idr.h> #include <sound/sof/ext_manifest4.h> +#include <sound/sof/ipc4/header.h> #include "sof-priv.h" /* The DSP window indices are fixed */ @@ -89,6 +90,8 @@ struct sof_ipc4_fw_data { int (*load_library)(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib, bool reload); + void (*intel_configure_mic_privacy)(struct snd_sof_dev *sdev, + struct sof_ipc4_intel_mic_privacy_cap *caps); struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */ }; @@ -101,6 +104,7 @@ extern const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops; int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 state); int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core); +int sof_ipc4_complete_split_release(struct snd_sof_dev *sdev); int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev); int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev); struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev, @@ -117,4 +121,6 @@ void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev, size_t sof_ipc4_find_debug_slot_offset_by_type(struct snd_sof_dev *sdev, u32 slot_type); +void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state); + #endif diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index b55eb977e443..540ba140e155 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -7,6 +7,7 @@ // // #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <uapi/sound/sof/tokens.h> #include <sound/pcm_params.h> #include <sound/sof/ext_manifest4.h> @@ -765,10 +766,16 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) } list_for_each_entry(w, &sdev->widget_list, list) { - if (w->widget->sname && + struct snd_sof_dai *alh_dai; + + if (!WIDGET_IS_DAI(w->id) || !w->widget->sname || strcmp(w->widget->sname, swidget->widget->sname)) continue; + alh_dai = w->private; + if (alh_dai->type != SOF_DAI_INTEL_ALH) + continue; + blob->alh_cfg.device_count++; } @@ -1801,8 +1808,8 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_copier_data *copier_data; int input_fmt_index, output_fmt_index; - struct snd_pcm_hw_params ref_params; struct sof_ipc4_copier *ipc4_copier; + struct snd_pcm_hw_params *ref_params __free(kfree) = NULL; struct snd_sof_dai *dai; u32 gtw_cfg_config_length; u32 dma_config_tlv_size = 0; @@ -1815,15 +1822,19 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, bool single_output_bitdepth; int i; - dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id); - switch (swidget->id) { case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; struct sof_ipc4_gtw_attributes *gtw_attr; - struct snd_sof_widget *pipe_widget; - struct sof_ipc4_pipeline *pipeline; + + dev_dbg(sdev->dev, + "Host copier %s, type %d, ChainDMA: %s, stream_tag: %d\n", + swidget->widget->name, swidget->id, + str_yes_no(pipeline->use_chain_dma), + platform_params->stream_tag); /* parse the deep buffer dma size */ ret = sof_update_ipc_object(scomp, &deep_buffer_dma_ms, @@ -1840,9 +1851,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt; - pipe_widget = swidget->spipe->pipe_widget; - pipeline = pipe_widget->private; - if (pipeline->use_chain_dma) { u32 host_dma_id; u32 fifo_size; @@ -1878,9 +1886,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, * for capture. */ if (dir == SNDRV_PCM_STREAM_PLAYBACK) - ref_params = *fe_params; + ref_params = kmemdup(fe_params, sizeof(*ref_params), GFP_KERNEL); else - ref_params = *pipeline_params; + ref_params = kmemdup(pipeline_params, sizeof(*ref_params), GFP_KERNEL); + if (!ref_params) + return -ENOMEM; copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; copier_data->gtw_cfg.node_id |= @@ -1896,6 +1906,10 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + dev_dbg(sdev->dev, "Dai copier %s, type %d, ChainDMA: %s\n", + swidget->widget->name, swidget->id, + str_yes_no(pipeline->use_chain_dma)); + if (pipeline->use_chain_dma) return 0; @@ -1913,8 +1927,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, * In case of capture the ref_params returned will be used to * find the input configuration of the copier. */ - ref_params = *fe_params; - ret = sof_ipc4_prepare_dai_copier(sdev, dai, &ref_params, dir); + ref_params = kmemdup(fe_params, sizeof(*ref_params), GFP_KERNEL); + if (!ref_params) + return -ENOMEM; + + ret = sof_ipc4_prepare_dai_copier(sdev, dai, ref_params, dir); if (ret < 0) return ret; @@ -1923,16 +1940,22 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, * input configuration of the copier. */ if (dir == SNDRV_PCM_STREAM_PLAYBACK) - ref_params = *pipeline_params; + memcpy(ref_params, pipeline_params, sizeof(*ref_params)); break; } case snd_soc_dapm_buffer: { + dev_dbg(sdev->dev, "Module copier %s, type %d\n", + swidget->widget->name, swidget->id); + ipc4_copier = (struct sof_ipc4_copier *)swidget->private; copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt; - ref_params = *pipeline_params; + + ref_params = kmemdup(pipeline_params, sizeof(*ref_params), GFP_KERNEL); + if (!ref_params) + return -ENOMEM; break; } @@ -1945,7 +1968,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, /* set input and output audio formats */ input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config, - &ref_params, available_fmt); + ref_params, available_fmt); if (input_fmt_index < 0) return input_fmt_index; @@ -2061,11 +2084,13 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, list_for_each_entry(w, &sdev->widget_list, list) { u32 node_type; - if (w->widget->sname && + if (!WIDGET_IS_DAI(w->id) || !w->widget->sname || strcmp(w->widget->sname, swidget->widget->sname)) continue; dai = w->private; + if (dai->type != SOF_DAI_INTEL_ALH) + continue; alh_copier = (struct sof_ipc4_copier *)dai->private; alh_data = &alh_copier->data; node_type = SOF_IPC4_GET_NODE_TYPE(alh_data->gtw_cfg.node_id); @@ -2827,7 +2852,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id); msg->extension &= ~SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK; - msg->extension |= ipc_size >> 2; + msg->extension |= SOF_IPC4_MOD_EXT_PARAM_SIZE(ipc_size >> 2); msg->extension &= ~SOF_IPC4_MOD_EXT_PPL_ID_MASK; msg->extension |= SOF_IPC4_MOD_EXT_PPL_ID(pipe_widget->instance_id); @@ -3401,9 +3426,6 @@ static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai * static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify) { - struct snd_sof_pcm *spcm; - int dir, ret; - /* * This function is called during system suspend, we need to make sure * that all streams have been freed up. @@ -3415,21 +3437,8 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif * * This will also make sure that paused streams handled correctly. */ - list_for_each_entry(spcm, &sdev->pcm_list, list) { - for_each_pcm_streams(dir) { - struct snd_pcm_substream *substream = spcm->stream[dir].substream; - if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored) - continue; - - if (spcm->stream[dir].list) { - ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); - if (ret < 0) - return ret; - } - } - } - return 0; + return sof_pcm_free_all_streams(sdev); } static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link) diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 4386cbae16d4..37e837b22ac8 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -825,8 +825,14 @@ static void sof_ipc4_exit(struct snd_sof_dev *sdev) static int sof_ipc4_post_boot(struct snd_sof_dev *sdev) { - if (sdev->first_boot) + if (sdev->first_boot) { + int ret = sof_ipc4_complete_split_release(sdev); + + if (ret) + return ret; + return sof_ipc4_query_fw_configuration(sdev); + } return sof_ipc4_reload_fw_libraries(sdev); } @@ -845,3 +851,21 @@ const struct sof_ipc_ops ipc4_ops = { .pcm = &ipc4_pcm_ops, .fw_tracing = &ipc4_mtrace_ops, }; + +void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state) +{ + struct sof_ipc4_msg msg; + u32 data = state; + + msg.primary = SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg.primary |= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID); + msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID); + msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_MIC_PRIVACY_STATE_CHANGE); + + msg.data_size = sizeof(data); + msg.data_ptr = &data; + + sof_ipc4_set_get_data(sdev, &msg, msg.data_size, true); +} +EXPORT_SYMBOL(sof_ipc4_mic_privacy_state_change); diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index 9955dfa520ae..7ff080452cbe 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -22,7 +22,6 @@ #include <sound/sof/xtensa.h> #include "../../ops.h" #include "../../sof-of-dev.h" -#include "../../sof-audio.h" #include "../adsp_helper.h" #include "../mtk-adsp-common.h" #include "mt8186.h" @@ -38,53 +37,9 @@ static int mt8186_get_window_offset(struct snd_sof_dev *sdev, u32 id) return MBOX_OFFSET; } -static int mt8186_send_msg(struct snd_sof_dev *sdev, - struct snd_sof_ipc_msg *msg) -{ - struct adsp_priv *priv = sdev->pdata->hw_pdata; - - sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, - msg->msg_size); - - return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ); -} - -static void mt8186_dsp_handle_reply(struct mtk_adsp_ipc *ipc) -{ - struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); - unsigned long flags; - - spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - snd_sof_ipc_process_reply(priv->sdev, 0); - spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); -} - -static void mt8186_dsp_handle_request(struct mtk_adsp_ipc *ipc) -{ - struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); - u32 p; /* panic code */ - int ret; - - /* Read the message from the debug box. */ - sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, - &p, sizeof(p)); - - /* Check to see if the message is a panic code 0x0dead*** */ - if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(priv->sdev, p, true); - } else { - snd_sof_ipc_msgs_rx(priv->sdev); - - /* tell DSP cmd is done */ - ret = mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_RSP, MTK_ADSP_IPC_OP_RSP); - if (ret) - dev_err(priv->dev, "request send ipc failed"); - } -} - static const struct mtk_adsp_ipc_ops dsp_ops = { - .handle_reply = mt8186_dsp_handle_reply, - .handle_request = mt8186_dsp_handle_request, + .handle_reply = mtk_adsp_handle_reply, + .handle_request = mtk_adsp_handle_request, }; static int platform_parse_resource(struct platform_device *pdev, void *data) @@ -238,7 +193,7 @@ static int mt8186_run(struct snd_sof_dev *sdev) static int mt8186_dsp_probe(struct snd_sof_dev *sdev) { - struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(sdev->dev); struct adsp_priv *priv; int ret; @@ -381,54 +336,6 @@ static int mt8186_dsp_resume(struct snd_sof_dev *sdev) return ret; } -/* on mt8186 there is 1 to 1 match between type and BAR idx */ -static int mt8186_get_bar_index(struct snd_sof_dev *sdev, u32 type) -{ - return type; -} - -static int mt8186_pcm_hw_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_sof_platform_stream_params *platform_params) -{ - platform_params->cont_update_posn = 1; - - return 0; -} - -static snd_pcm_uframes_t mt8186_pcm_pointer(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream) -{ - int ret; - snd_pcm_uframes_t pos; - struct snd_sof_pcm *spcm; - struct sof_ipc_stream_posn posn; - struct snd_sof_pcm_stream *stream; - struct snd_soc_component *scomp = sdev->component; - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - - spcm = snd_sof_find_spcm_dai(scomp, rtd); - if (!spcm) { - dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", - rtd->dai_link->id); - return 0; - } - - stream = &spcm->stream[substream->stream]; - ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); - if (ret < 0) { - dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); - return 0; - } - - memcpy(&stream->posn, &posn, sizeof(posn)); - pos = spcm->stream[substream->stream].posn.host_posn; - pos = bytes_to_frames(substream->runtime, pos); - - return pos; -} - static void mt8186_adsp_dump(struct snd_sof_dev *sdev, u32 flags) { u32 dbg_pc, dbg_data, dbg_inst, dbg_ls0stat, dbg_status, faultinfo; @@ -505,19 +412,19 @@ static const struct snd_sof_dsp_ops sof_mt8186_ops = { .read64 = sof_io_read64, /* ipc */ - .send_msg = mt8186_send_msg, + .send_msg = mtk_adsp_send_msg, .get_mailbox_offset = mt8186_get_mailbox_offset, .get_window_offset = mt8186_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, .set_stream_data_offset = sof_set_stream_data_offset, /* misc */ - .get_bar_index = mt8186_get_bar_index, + .get_bar_index = mtk_adsp_get_bar_index, /* stream callbacks */ .pcm_open = sof_stream_pcm_open, - .pcm_hw_params = mt8186_pcm_hw_params, - .pcm_pointer = mt8186_pcm_pointer, + .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, + .pcm_pointer = mtk_adsp_stream_pcm_pointer, .pcm_close = sof_stream_pcm_close, /* firmware loading */ @@ -660,7 +567,7 @@ static struct platform_driver snd_sof_of_mt8186_driver = { .shutdown = sof_of_shutdown, .driver = { .name = "sof-audio-of-mt8186", - .pm = &sof_of_pm, + .pm = pm_ptr(&sof_of_pm), .of_match_table = sof_of_mt8186_ids, }, }; diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c index 7cffcad00f9b..2c2c4cd323fc 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c @@ -8,6 +8,7 @@ #include <linux/clk.h> #include <linux/io.h> +#include <linux/string_choices.h> #include "mt8195.h" #include "mt8195-clk.h" #include "../adsp_helper.h" @@ -114,7 +115,7 @@ static int adsp_default_clk_init(struct snd_sof_dev *sdev, bool enable) struct adsp_priv *priv = sdev->pdata->hw_pdata; int ret; - dev_dbg(dev, "%s: %s\n", __func__, enable ? "on" : "off"); + dev_dbg(dev, "%s: %s\n", __func__, str_on_off(enable)); if (enable) { ret = clk_set_parent(priv->clk[CLK_TOP_ADSP], diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 6032b566c679..3b3582d74510 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -22,7 +22,6 @@ #include <sound/sof/xtensa.h> #include "../../ops.h" #include "../../sof-of-dev.h" -#include "../../sof-audio.h" #include "../adsp_helper.h" #include "../mtk-adsp-common.h" #include "mt8195.h" @@ -38,53 +37,9 @@ static int mt8195_get_window_offset(struct snd_sof_dev *sdev, u32 id) return MBOX_OFFSET; } -static int mt8195_send_msg(struct snd_sof_dev *sdev, - struct snd_sof_ipc_msg *msg) -{ - struct adsp_priv *priv = sdev->pdata->hw_pdata; - - sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, - msg->msg_size); - - return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ); -} - -static void mt8195_dsp_handle_reply(struct mtk_adsp_ipc *ipc) -{ - struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); - unsigned long flags; - - spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - snd_sof_ipc_process_reply(priv->sdev, 0); - spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); -} - -static void mt8195_dsp_handle_request(struct mtk_adsp_ipc *ipc) -{ - struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); - u32 p; /* panic code */ - int ret; - - /* Read the message from the debug box. */ - sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, - &p, sizeof(p)); - - /* Check to see if the message is a panic code 0x0dead*** */ - if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(priv->sdev, p, true); - } else { - snd_sof_ipc_msgs_rx(priv->sdev); - - /* tell DSP cmd is done */ - ret = mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_RSP, MTK_ADSP_IPC_OP_RSP); - if (ret) - dev_err(priv->dev, "request send ipc failed"); - } -} - static const struct mtk_adsp_ipc_ops dsp_ops = { - .handle_reply = mt8195_dsp_handle_reply, - .handle_request = mt8195_dsp_handle_request, + .handle_reply = mtk_adsp_handle_reply, + .handle_request = mtk_adsp_handle_request, }; static int platform_parse_resource(struct platform_device *pdev, void *data) @@ -228,7 +183,7 @@ static int mt8195_run(struct snd_sof_dev *sdev) static int mt8195_dsp_probe(struct snd_sof_dev *sdev) { - struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(sdev->dev); struct adsp_priv *priv; int ret; @@ -341,7 +296,7 @@ static int mt8195_dsp_shutdown(struct snd_sof_dev *sdev) static void mt8195_dsp_remove(struct snd_sof_dev *sdev) { - struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(sdev->dev); struct adsp_priv *priv = sdev->pdata->hw_pdata; platform_device_unregister(priv->ipc_dev); @@ -351,7 +306,7 @@ static void mt8195_dsp_remove(struct snd_sof_dev *sdev) static int mt8195_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) { - struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + struct platform_device *pdev = to_platform_device(sdev->dev); int ret; u32 reset_sw, dbg_pc; @@ -400,54 +355,6 @@ static int mt8195_dsp_resume(struct snd_sof_dev *sdev) return ret; } -/* on mt8195 there is 1 to 1 match between type and BAR idx */ -static int mt8195_get_bar_index(struct snd_sof_dev *sdev, u32 type) -{ - return type; -} - -static int mt8195_pcm_hw_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_sof_platform_stream_params *platform_params) -{ - platform_params->cont_update_posn = 1; - - return 0; -} - -static snd_pcm_uframes_t mt8195_pcm_pointer(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream) -{ - int ret; - snd_pcm_uframes_t pos; - struct snd_sof_pcm *spcm; - struct sof_ipc_stream_posn posn; - struct snd_sof_pcm_stream *stream; - struct snd_soc_component *scomp = sdev->component; - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - - spcm = snd_sof_find_spcm_dai(scomp, rtd); - if (!spcm) { - dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", - rtd->dai_link->id); - return 0; - } - - stream = &spcm->stream[substream->stream]; - ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); - if (ret < 0) { - dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); - return 0; - } - - memcpy(&stream->posn, &posn, sizeof(posn)); - pos = spcm->stream[substream->stream].posn.host_posn; - pos = bytes_to_frames(substream->runtime, pos); - - return pos; -} - static void mt8195_adsp_dump(struct snd_sof_dev *sdev, u32 flags) { u32 dbg_pc, dbg_data, dbg_bus0, dbg_bus1, dbg_inst; @@ -529,19 +436,19 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = { .read64 = sof_io_read64, /* ipc */ - .send_msg = mt8195_send_msg, + .send_msg = mtk_adsp_send_msg, .get_mailbox_offset = mt8195_get_mailbox_offset, .get_window_offset = mt8195_get_window_offset, .ipc_msg_data = sof_ipc_msg_data, .set_stream_data_offset = sof_set_stream_data_offset, /* misc */ - .get_bar_index = mt8195_get_bar_index, + .get_bar_index = mtk_adsp_get_bar_index, /* stream callbacks */ .pcm_open = sof_stream_pcm_open, - .pcm_hw_params = mt8195_pcm_hw_params, - .pcm_pointer = mt8195_pcm_pointer, + .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, + .pcm_pointer = mtk_adsp_stream_pcm_pointer, .pcm_close = sof_stream_pcm_close, /* firmware loading */ @@ -616,7 +523,7 @@ static struct platform_driver snd_sof_of_mt8195_driver = { .shutdown = sof_of_shutdown, .driver = { .name = "sof-audio-of-mt8195", - .pm = &sof_of_pm, + .pm = pm_ptr(&sof_of_pm), .of_match_table = sof_of_mt8195_ids, }, }; diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c index 20bcf5590eb8..01bbadb160ff 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.c +++ b/sound/soc/sof/mediatek/mtk-adsp-common.c @@ -12,8 +12,11 @@ */ #include <linux/module.h> +#include <sound/asound.h> #include <sound/sof/xtensa.h> #include "../ops.h" +#include "../sof-audio.h" +#include "adsp_helper.h" #include "mtk-adsp-common.h" /** @@ -81,5 +84,132 @@ void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags) } EXPORT_SYMBOL(mtk_adsp_dump); +/** + * mtk_adsp_send_msg - Send message to Audio DSP + * @sdev: SOF device + * @msg: SOF IPC Message to send + */ +int mtk_adsp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +{ + struct adsp_priv *priv = sdev->pdata->hw_pdata; + + sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, + msg->msg_size); + + return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ); +} +EXPORT_SYMBOL(mtk_adsp_send_msg); + +/** + * mtk_adsp_handle_reply - Handle reply from the Audio DSP through Mailbox + * @ipc: ADSP IPC handle + */ +void mtk_adsp_handle_reply(struct mtk_adsp_ipc *ipc) +{ + struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); + unsigned long flags; + + spin_lock_irqsave(&priv->sdev->ipc_lock, flags); + snd_sof_ipc_process_reply(priv->sdev, 0); + spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); +} +EXPORT_SYMBOL(mtk_adsp_handle_reply); + +/** + * mtk_adsp_handle_request - Handle request from the Audio DSP through Mailbox + * @ipc: ADSP IPC handle + */ +void mtk_adsp_handle_request(struct mtk_adsp_ipc *ipc) +{ + struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); + u32 panic_code; + int ret; + + /* Read the message from the debug box. */ + sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, + &panic_code, sizeof(panic_code)); + + /* Check to see if the message is a panic code 0x0dead*** */ + if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(priv->sdev, panic_code, true); + } else { + snd_sof_ipc_msgs_rx(priv->sdev); + + /* Tell DSP cmd is done */ + ret = mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_RSP, MTK_ADSP_IPC_OP_RSP); + if (ret) + dev_err(priv->dev, "request send ipc failed"); + } +} +EXPORT_SYMBOL(mtk_adsp_handle_request); + +/** + * mtk_adsp_get_bar_index - Map section type with BAR idx + * @sdev: SOF device + * @type: Section type as described by snd_sof_fw_blk_type + * + * MediaTek Audio DSPs have a 1:1 match between type and BAR idx + */ +int mtk_adsp_get_bar_index(struct snd_sof_dev *sdev, u32 type) +{ + return type; +} +EXPORT_SYMBOL(mtk_adsp_get_bar_index); + +/** + * mtk_adsp_stream_pcm_hw_params - Platform specific host stream hw params + * @sdev: SOF device + * @substream: PCM Substream + * @params: hw params + * @platform_params: Platform specific SOF stream parameters + */ +int mtk_adsp_stream_pcm_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params) +{ + platform_params->cont_update_posn = 1; + return 0; +} +EXPORT_SYMBOL(mtk_adsp_stream_pcm_hw_params); + +/** + * mtk_adsp_stream_pcm_pointer - Get host stream pointer + * @sdev: SOF device + * @substream: PCM substream + */ +snd_pcm_uframes_t mtk_adsp_stream_pcm_pointer(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_component *scomp = sdev->component; + struct snd_sof_pcm_stream *stream; + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + snd_pcm_uframes_t pos; + int ret; + + spcm = snd_sof_find_spcm_dai(scomp, rtd); + if (!spcm) { + dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", + rtd->dai_link->id); + return 0; + } + + stream = &spcm->stream[substream->stream]; + ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); + if (ret < 0) { + dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); + return 0; + } + + memcpy(&stream->posn, &posn, sizeof(posn)); + pos = spcm->stream[substream->stream].posn.host_posn; + pos = bytes_to_frames(substream->runtime, pos); + + return pos; +} +EXPORT_SYMBOL(mtk_adsp_stream_pcm_pointer); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF helpers for MTK ADSP platforms"); diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.h b/sound/soc/sof/mediatek/mtk-adsp-common.h index 612cff1f38f7..dc36b91d6779 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.h +++ b/sound/soc/sof/mediatek/mtk-adsp-common.h @@ -7,4 +7,14 @@ #define MTK_ADSP_STACK_DUMP_SIZE 32 void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags); +int mtk_adsp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); +void mtk_adsp_handle_reply(struct mtk_adsp_ipc *ipc); +void mtk_adsp_handle_request(struct mtk_adsp_ipc *ipc); +int mtk_adsp_get_bar_index(struct snd_sof_dev *sdev, u32 type); +int mtk_adsp_stream_pcm_hw_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params); +snd_pcm_uframes_t mtk_adsp_stream_pcm_pointer(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); #endif diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 35a7462d8b69..d584a72e6f52 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -99,8 +99,8 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list, dpcm_end_walk_at_be); if (ret < 0) { - dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name, - snd_pcm_direction_name(dir)); + spcm_err(spcm, dir, "dai %s has no valid %s path\n", + dai->name, snd_pcm_direction_name(dir)); return ret; } @@ -108,8 +108,7 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); if (ret < 0) { - dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n", - spcm->pcm.pcm_id, dir); + spcm_err(spcm, dir, "Widget list set up failed\n"); spcm->stream[dir].list = NULL; snd_soc_dapm_dai_free_widgets(&list); return ret; @@ -139,6 +138,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, if (!spcm) return -EINVAL; + spcm_dbg(spcm, substream->stream, "Entry: hw_params\n"); + /* * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. @@ -151,12 +152,9 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, spcm->prepared[substream->stream] = false; } - dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", - spcm->pcm.pcm_id, substream->stream); - ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params); if (ret < 0) { - dev_err(component->dev, "platform hw params failed\n"); + spcm_err(spcm, substream->stream, "platform hw params failed\n"); return ret; } @@ -191,6 +189,84 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, return 0; } +static int sof_pcm_stream_free(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir, + bool free_widget_list) +{ + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + int ret; + int err = 0; + + if (spcm->prepared[substream->stream]) { + /* stop DMA first if needed */ + if (pcm_ops && pcm_ops->platform_stop_during_hw_free) + snd_sof_pcm_platform_trigger(sdev, substream, + SNDRV_PCM_TRIGGER_STOP); + + /* free PCM in the DSP */ + if (pcm_ops && pcm_ops->hw_free) { + ret = pcm_ops->hw_free(sdev->component, substream); + if (ret < 0) { + spcm_err(spcm, substream->stream, + "pcm_ops->hw_free failed %d\n", ret); + err = ret; + } + } + + spcm->prepared[substream->stream] = false; + spcm->pending_stop[substream->stream] = false; + } + + /* reset the DMA */ + ret = snd_sof_pcm_platform_hw_free(sdev, substream); + if (ret < 0) { + spcm_err(spcm, substream->stream, + "platform hw free failed %d\n", ret); + if (!err) + err = ret; + } + + /* free widget list */ + if (free_widget_list) { + ret = sof_widget_list_free(sdev, spcm, dir); + if (ret < 0) { + spcm_err(spcm, substream->stream, + "sof_widget_list_free failed %d\n", ret); + if (!err) + err = ret; + } + } + + return err; +} + +int sof_pcm_free_all_streams(struct snd_sof_dev *sdev) +{ + struct snd_pcm_substream *substream; + struct snd_sof_pcm *spcm; + int dir, ret; + + list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_pcm_streams(dir) { + substream = spcm->stream[dir].substream; + + if (!substream || !substream->runtime || + spcm->stream[dir].suspend_ignored) + continue; + + if (spcm->stream[dir].list) { + ret = sof_pcm_stream_free(sdev, substream, spcm, + dir, true); + if (ret < 0) + return ret; + } + } + } + + return 0; +} + static int sof_pcm_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -207,8 +283,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, if (!spcm) return -EINVAL; - dev_dbg(component->dev, "pcm: free stream %d dir %d\n", - spcm->pcm.pcm_id, substream->stream); + spcm_dbg(spcm, substream->stream, "Entry: hw_free\n"); ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); @@ -233,6 +308,8 @@ static int sof_pcm_prepare(struct snd_soc_component *component, if (!spcm) return -EINVAL; + spcm_dbg(spcm, substream->stream, "Entry: prepare\n"); + if (spcm->prepared[substream->stream]) { if (!spcm->pending_stop[substream->stream]) return 0; @@ -246,15 +323,12 @@ static int sof_pcm_prepare(struct snd_soc_component *component, return ret; } - dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n", - spcm->pcm.pcm_id, substream->stream); - /* set hw_params */ ret = sof_pcm_hw_params(component, substream, &spcm->params[substream->stream]); if (ret < 0) { - dev_err(component->dev, - "error: set pcm hw_params after resume\n"); + spcm_err(spcm, substream->stream, + "failed to set hw_params after resume\n"); return ret; } @@ -284,8 +358,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, if (!spcm) return -EINVAL; - dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n", - spcm->pcm.pcm_id, substream->stream, cmd); + spcm_dbg(spcm, substream->stream, "Entry: trigger (cmd: %d)\n", cmd); spcm->pending_stop[substream->stream] = false; @@ -334,7 +407,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, reset_hw_params = true; break; default: - dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd); + spcm_err(spcm, substream->stream, "Unhandled trigger cmd %d\n", cmd); return -EINVAL; } @@ -436,9 +509,7 @@ static int sof_pcm_open(struct snd_soc_component *component, if (!spcm) return -EINVAL; - dev_dbg(component->dev, "pcm: open stream %d dir %d\n", - spcm->pcm.pcm_id, substream->stream); - + spcm_dbg(spcm, substream->stream, "Entry: open\n"); caps = &spcm->pcm.caps[substream->stream]; @@ -458,15 +529,6 @@ static int sof_pcm_open(struct snd_soc_component *component, */ runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max); - dev_dbg(component->dev, "period min %zd max %zd bytes\n", - runtime->hw.period_bytes_min, - runtime->hw.period_bytes_max); - dev_dbg(component->dev, "period count %d max %d\n", - runtime->hw.periods_min, - runtime->hw.periods_max); - dev_dbg(component->dev, "buffer max %zd bytes\n", - runtime->hw.buffer_bytes_max); - /* set wait time - TODO: come from topology */ substream->wait_time = 500; @@ -476,10 +538,19 @@ static int sof_pcm_open(struct snd_soc_component *component, spcm->prepared[substream->stream] = false; ret = snd_sof_pcm_platform_open(sdev, substream); - if (ret < 0) - dev_err(component->dev, "error: pcm open failed %d\n", ret); + if (ret < 0) { + spcm_err(spcm, substream->stream, + "platform pcm open failed %d\n", ret); + return ret; + } - return ret; + spcm_dbg(spcm, substream->stream, "period bytes min %zd, max %zd\n", + runtime->hw.period_bytes_min, runtime->hw.period_bytes_max); + spcm_dbg(spcm, substream->stream, "period count min %d, max %d\n", + runtime->hw.periods_min, runtime->hw.periods_max); + spcm_dbg(spcm, substream->stream, "buffer bytes max %zd\n", runtime->hw.buffer_bytes_max); + + return 0; } static int sof_pcm_close(struct snd_soc_component *component, @@ -498,19 +569,20 @@ static int sof_pcm_close(struct snd_soc_component *component, if (!spcm) return -EINVAL; - dev_dbg(component->dev, "pcm: close stream %d dir %d\n", - spcm->pcm.pcm_id, substream->stream); + spcm_dbg(spcm, substream->stream, "Entry: close\n"); err = snd_sof_pcm_platform_close(sdev, substream); if (err < 0) { - dev_err(component->dev, "error: pcm close failed %d\n", - err); + spcm_err(spcm, substream->stream, + "platform pcm close failed %d\n", err); /* * keep going, no point in preventing the close * from happening */ } + spcm->stream[substream->stream].substream = NULL; + return 0; } @@ -536,7 +608,8 @@ static int sof_pcm_new(struct snd_soc_component *component, return 0; } - dev_dbg(component->dev, "creating new PCM %s\n", spcm->pcm.pcm_name); + dev_dbg(spcm->scomp->dev, "pcm%u (%s): Entry: pcm_construct\n", + spcm->pcm.pcm_id, spcm->pcm.pcm_name); /* do we need to pre-allocate playback audio buffer pages */ if (!spcm->pcm.playback) @@ -544,16 +617,15 @@ static int sof_pcm_new(struct snd_soc_component *component, caps = &spcm->pcm.caps[stream]; - /* pre-allocate playback audio buffer pages */ - dev_dbg(component->dev, - "spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n", - caps->name, caps->buffer_size_min, caps->buffer_size_max); - if (!pcm->streams[stream].substream) { - dev_err(component->dev, "error: NULL playback substream!\n"); + spcm_err(spcm, stream, "NULL playback substream!\n"); return -EINVAL; } + /* pre-allocate playback audio buffer pages */ + spcm_dbg(spcm, stream, "allocate %s playback DMA buffer size 0x%x max 0x%x\n", + caps->name, caps->buffer_size_min, caps->buffer_size_max); + snd_pcm_set_managed_buffer(pcm->streams[stream].substream, SNDRV_DMA_TYPE_DEV_SG, sdev->dev, 0, le32_to_cpu(caps->buffer_size_max)); @@ -566,16 +638,15 @@ capture: caps = &spcm->pcm.caps[stream]; - /* pre-allocate capture audio buffer pages */ - dev_dbg(component->dev, - "spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n", - caps->name, caps->buffer_size_min, caps->buffer_size_max); - if (!pcm->streams[stream].substream) { - dev_err(component->dev, "error: NULL capture substream!\n"); + spcm_err(spcm, stream, "NULL capture substream!\n"); return -EINVAL; } + /* pre-allocate capture audio buffer pages */ + spcm_dbg(spcm, stream, "allocate %s capture DMA buffer size 0x%x max 0x%x\n", + caps->name, caps->buffer_size_min, caps->buffer_size_max); + snd_pcm_set_managed_buffer(pcm->streams[stream].substream, SNDRV_DMA_TYPE_DEV_SG, sdev->dev, 0, le32_to_cpu(caps->buffer_size_max)); diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index 58fd5f7c7905..68bf1b57093e 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -36,12 +36,11 @@ MODULE_PARM_DESC(sof_acpi_debug, "SOF ACPI debug options (0x0 all off)"); #define SOF_ACPI_DISABLE_PM_RUNTIME BIT(0) -const struct dev_pm_ops sof_acpi_pm = { - SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) - SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, - snd_sof_runtime_idle) +EXPORT_NS_DEV_PM_OPS(sof_acpi_pm, SND_SOC_SOF_ACPI_DEV) = { + SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + snd_sof_runtime_idle) }; -EXPORT_SYMBOL_NS(sof_acpi_pm, "SND_SOC_SOF_ACPI_DEV"); static void sof_acpi_probe_complete(struct device *dev) { diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 9a52781bf8d8..a9664b4cf43f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -829,55 +829,6 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) return false; } -int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_sof_pcm *spcm, int dir, bool free_widget_list) -{ - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); - int ret; - int err = 0; - - if (spcm->prepared[substream->stream]) { - /* stop DMA first if needed */ - if (pcm_ops && pcm_ops->platform_stop_during_hw_free) - snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP); - - /* free PCM in the DSP */ - if (pcm_ops && pcm_ops->hw_free) { - ret = pcm_ops->hw_free(sdev->component, substream); - if (ret < 0) { - dev_err(sdev->dev, "%s: pcm_ops hw_free failed %d\n", - __func__, ret); - err = ret; - } - } - - spcm->prepared[substream->stream] = false; - spcm->pending_stop[substream->stream] = false; - } - - /* reset the DMA */ - ret = snd_sof_pcm_platform_hw_free(sdev, substream); - if (ret < 0) { - dev_err(sdev->dev, "%s: platform hw free failed %d\n", - __func__, ret); - if (!err) - err = ret; - } - - /* free widget list */ - if (free_widget_list) { - ret = sof_widget_list_free(sdev, spcm, dir); - if (ret < 0) { - dev_err(sdev->dev, "%s: sof_widget_list_free failed %d\n", - __func__, ret); - if (!err) - err = ret; - } - } - - return err; -} - /* * Generic object lookup APIs. */ diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 01b819dd8498..36ab75e11779 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -332,6 +332,7 @@ struct snd_sof_pcm_stream { struct work_struct period_elapsed_work; struct snd_soc_dapm_widget_list *list; /* list of connected DAPM widgets */ bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */ + bool pause_supported; /* PCM device supports PAUSE operation */ unsigned int dsp_max_burst_size_in_ms; /* The maximum size of the host DMA burst in ms */ /* * flag to indicate that the DSP pipelines should be kept @@ -616,6 +617,20 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream); void snd_sof_pcm_init_elapsed_work(struct work_struct *work); +/* + * snd_sof_pcm specific wrappers for dev_dbg() and dev_err() to provide + * consistent and useful prints. + */ +#define spcm_dbg(__spcm, __dir, __fmt, ...) \ + dev_dbg((__spcm)->scomp->dev, "pcm%u (%s), dir %d: " __fmt, \ + (__spcm)->pcm.pcm_id, (__spcm)->pcm.pcm_name, __dir, \ + ##__VA_ARGS__) + +#define spcm_err(__spcm, __dir, __fmt, ...) \ + dev_err((__spcm)->scomp->dev, "%s: pcm%u (%s), dir %d: " __fmt, \ + __func__, (__spcm)->pcm.pcm_id, (__spcm)->pcm.pcm_name, __dir, \ + ##__VA_ARGS__) + #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream); void snd_sof_compr_init_elapsed_work(struct work_struct *work); @@ -648,8 +663,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); -int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, - struct snd_sof_pcm *spcm, int dir, bool free_widget_list); +int sof_pcm_free_all_streams(struct snd_sof_dev *sdev); int get_token_u32(void *elem, void *object, u32 offset); int get_token_u16(void *elem, void *object, u32 offset); int get_token_comp_format(void *elem, void *object, u32 offset); diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c index b35c98896968..11b6f7da2882 100644 --- a/sound/soc/sof/sof-client-ipc-flood-test.c +++ b/sound/soc/sof/sof-client-ipc-flood-test.c @@ -158,7 +158,6 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf unsigned long ipc_duration_ms = 0; bool flood_duration_test = false; unsigned long ipc_count = 0; - struct dentry *dentry; int err; char *string; int ret; @@ -182,14 +181,7 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf * ipc_duration_ms test floods the DSP for the time specified * in the debugfs entry. */ - dentry = file->f_path.dentry; - if (strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_COUNT) && - strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) { - ret = -EINVAL; - goto out; - } - - if (!strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) + if (debugfs_get_aux_num(file)) flood_duration_test = true; /* test completion criterion */ @@ -252,22 +244,15 @@ static ssize_t sof_ipc_flood_dfs_read(struct file *file, char __user *buffer, struct sof_ipc_flood_priv *priv = cdev->data; size_t size_ret; - struct dentry *dentry; + if (*ppos) + return 0; - dentry = file->f_path.dentry; - if (!strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_COUNT) || - !strcmp(dentry->d_name.name, DEBUGFS_IPC_FLOOD_DURATION)) { - if (*ppos) - return 0; + count = min_t(size_t, count, strlen(priv->buf)); + size_ret = copy_to_user(buffer, priv->buf, count); + if (size_ret) + return -EFAULT; - count = min_t(size_t, count, strlen(priv->buf)); - size_ret = copy_to_user(buffer, priv->buf, count); - if (size_ret) - return -EFAULT; - - *ppos += count; - return count; - } + *ppos += count; return count; } @@ -320,12 +305,12 @@ static int sof_ipc_flood_probe(struct auxiliary_device *auxdev, priv->dfs_root = debugfs_create_dir(dev_name(dev), debugfs_root); if (!IS_ERR_OR_NULL(priv->dfs_root)) { /* create read-write ipc_flood_count debugfs entry */ - debugfs_create_file(DEBUGFS_IPC_FLOOD_COUNT, 0644, priv->dfs_root, - cdev, &sof_ipc_flood_fops); + debugfs_create_file_aux_num(DEBUGFS_IPC_FLOOD_COUNT, 0644, + priv->dfs_root, cdev, 0, &sof_ipc_flood_fops); /* create read-write ipc_flood_duration_ms debugfs entry */ - debugfs_create_file(DEBUGFS_IPC_FLOOD_DURATION, 0644, - priv->dfs_root, cdev, &sof_ipc_flood_fops); + debugfs_create_file_aux_num(DEBUGFS_IPC_FLOOD_DURATION, 0644, + priv->dfs_root, cdev, 1, &sof_ipc_flood_fops); if (auxdev->id == 0) { /* diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index fb8c8a14d885..3ccfcfb142b7 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -30,14 +30,12 @@ static char *tplg_filename; module_param(tplg_filename, charp, 0444); MODULE_PARM_DESC(tplg_filename, "deprecated - moved to snd-sof module."); -const struct dev_pm_ops sof_of_pm = { +EXPORT_DEV_PM_OPS(sof_of_pm) = { .prepare = snd_sof_prepare, .complete = snd_sof_complete, - SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) - SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, NULL) }; -EXPORT_SYMBOL(sof_of_pm); static void sof_of_probe_complete(struct device *dev) { diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 103377e2caaf..2fc14b9a33d4 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -155,14 +155,13 @@ static const struct dmi_system_id community_key_platforms[] = { {}, }; -const struct dev_pm_ops sof_pci_pm = { +EXPORT_NS_DEV_PM_OPS(sof_pci_pm, SND_SOC_SOF_PCI_DEV) = { .prepare = snd_sof_prepare, .complete = snd_sof_complete, - SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) - SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, - snd_sof_runtime_idle) + SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) + RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, + snd_sof_runtime_idle) }; -EXPORT_SYMBOL_NS(sof_pci_pm, "SND_SOC_SOF_PCI_DEV"); static void sof_pci_probe_complete(struct device *dev) { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 843be3b6415d..abbb5ee7e08c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -76,14 +76,6 @@ bool sof_debug_check_flag(int mask); #define SOF_IPC_DSP_REPLY 0 #define SOF_IPC_HOST_REPLY 1 -/* convenience constructor for DAI driver streams */ -#define SOF_DAI_STREAM(sname, scmin, scmax, srates, sfmt) \ - {.stream_name = sname, .channels_min = scmin, .channels_max = scmax, \ - .rates = srates, .formats = sfmt} - -#define SOF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT) - /* So far the primary core on all DSPs has ID 0 */ #define SOF_DSP_PRIMARY_CORE 0 diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c index 794c7bbccbaf..8262443ac89a 100644 --- a/sound/soc/sof/stream-ipc.c +++ b/sound/soc/sof/stream-ipc.c @@ -43,7 +43,7 @@ int sof_ipc_msg_data(struct snd_sof_dev *sdev, return -ESTRPIPE; posn_offset = stream->posn_offset; - } else { + } else if (sps->cstream) { struct sof_compr_stream *sstream = sps->cstream->runtime->private_data; @@ -51,6 +51,10 @@ int sof_ipc_msg_data(struct snd_sof_dev *sdev, return -ESTRPIPE; posn_offset = sstream->posn_offset; + + } else { + dev_err(sdev->dev, "%s: No stream opened\n", __func__); + return -EINVAL; } snd_sof_dsp_mailbox_read(sdev, posn_offset, p, sz); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index b3fca5fd87d6..dc9cb8324067 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -407,6 +407,10 @@ static const struct sof_topology_token stream_tokens[] = { offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible)}, {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible)}, + {SOF_TKN_STREAM_PLAYBACK_PAUSE_SUPPORTED, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct snd_sof_pcm, stream[0].pause_supported)}, + {SOF_TKN_STREAM_CAPTURE_PAUSE_SUPPORTED, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct snd_sof_pcm, stream[1].pause_supported)}, }; /* Leds */ @@ -1269,8 +1273,8 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s struct snd_sof_tuple *new_tuples; num_tuples += token_list[object_token_list[i]].count * (num_sets - 1); - new_tuples = krealloc(swidget->tuples, - sizeof(*new_tuples) * num_tuples, GFP_KERNEL); + new_tuples = krealloc_array(swidget->tuples, + num_tuples, sizeof(*new_tuples), GFP_KERNEL); if (!new_tuples) { ret = -ENOMEM; goto err; diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 19dc61008a75..6037b7a9c97b 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -1352,7 +1352,6 @@ error: MODULE_DEVICE_TABLE(of, stm32_i2s_ids); -#ifdef CONFIG_PM_SLEEP static int stm32_i2s_suspend(struct device *dev) { struct stm32_i2s_data *i2s = dev_get_drvdata(dev); @@ -1370,17 +1369,16 @@ static int stm32_i2s_resume(struct device *dev) regcache_cache_only(i2s->regmap, false); return regcache_sync(i2s->regmap); } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops stm32_i2s_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_i2s_suspend, stm32_i2s_resume) + SYSTEM_SLEEP_PM_OPS(stm32_i2s_suspend, stm32_i2s_resume) }; static struct platform_driver stm32_i2s_driver = { .driver = { .name = "st,stm32-i2s", .of_match_table = stm32_i2s_ids, - .pm = &stm32_i2s_pm_ops, + .pm = pm_ptr(&stm32_i2s_pm_ops), }, .probe = stm32_i2s_probe, .remove = stm32_i2s_remove, diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index bc8180fc8462..504a14584765 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -270,7 +270,6 @@ static int stm32_sai_probe(struct platform_device *pdev) return devm_of_platform_populate(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP /* * When pins are shared by two sai sub instances, pins have to be defined * in sai parent node. In this case, pins state is not managed by alsa fw. @@ -305,10 +304,9 @@ static int stm32_sai_resume(struct device *dev) return pinctrl_pm_select_default_state(dev); } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops stm32_sai_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume) + SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume) }; MODULE_DEVICE_TABLE(of, stm32_sai_ids); @@ -317,7 +315,7 @@ static struct platform_driver stm32_sai_driver = { .driver = { .name = "st,stm32-sai", .of_match_table = stm32_sai_ids, - .pm = &stm32_sai_pm_ops, + .pm = pm_ptr(&stm32_sai_pm_ops), }, .probe = stm32_sai_probe, }; diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 3efbf4aaf965..e8c1abf1ae0a 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1704,7 +1704,6 @@ static void stm32_sai_sub_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static int stm32_sai_sub_suspend(struct device *dev) { struct stm32_sai_sub_data *sai = dev_get_drvdata(dev); @@ -1738,17 +1737,16 @@ static int stm32_sai_sub_resume(struct device *dev) return ret; } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops stm32_sai_sub_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_sub_suspend, stm32_sai_sub_resume) + SYSTEM_SLEEP_PM_OPS(stm32_sai_sub_suspend, stm32_sai_sub_resume) }; static struct platform_driver stm32_sai_sub_driver = { .driver = { .name = "st,stm32-sai-sub", .of_match_table = stm32_sai_sub_ids, - .pm = &stm32_sai_sub_pm_ops, + .pm = pm_ptr(&stm32_sai_sub_pm_ops), }, .probe = stm32_sai_sub_probe, .remove = stm32_sai_sub_remove, diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 9e30852de93c..57b711c44278 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -1040,7 +1040,6 @@ error: MODULE_DEVICE_TABLE(of, stm32_spdifrx_ids); -#ifdef CONFIG_PM_SLEEP static int stm32_spdifrx_suspend(struct device *dev) { struct stm32_spdifrx_data *spdifrx = dev_get_drvdata(dev); @@ -1059,17 +1058,16 @@ static int stm32_spdifrx_resume(struct device *dev) return regcache_sync(spdifrx->regmap); } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops stm32_spdifrx_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(stm32_spdifrx_suspend, stm32_spdifrx_resume) + SYSTEM_SLEEP_PM_OPS(stm32_spdifrx_suspend, stm32_spdifrx_resume) }; static struct platform_driver stm32_spdifrx_driver = { .driver = { .name = "st,stm32-spdifrx", .of_match_table = stm32_spdifrx_ids, - .pm = &stm32_spdifrx_pm_ops, + .pm = pm_ptr(&stm32_spdifrx_pm_ops), }, .probe = stm32_spdifrx_probe, .remove = stm32_spdifrx_remove, diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 933a0913237c..93733ff2e32a 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -5,6 +5,7 @@ * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com> * Copyright 2015 Adam Sampson <ats@offog.org> * Copyright 2016 Chen-Yu Tsai <wens@csie.org> + * Copyright 2018 Mesih Kilinc <mesihkilinc@gmail.com> * * Based on the Allwinner SDK driver, released under the GPL. */ @@ -21,6 +22,7 @@ #include <linux/gpio/consumer.h> #include <sound/core.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -265,6 +267,64 @@ /* TODO H3 DAP (Digital Audio Processing) bits */ +#define SUN4I_DMA_MAX_BURST (8) + +/* suniv specific registers */ + +#define SUNIV_DMA_MAX_BURST (4) + +/* Codec DAC digital controls and FIFO registers */ +#define SUNIV_CODEC_ADC_FIFOC (0x10) +#define SUNIV_CODEC_ADC_FIFOC_EN_AD (28) +#define SUNIV_CODEC_ADC_FIFOS (0x14) +#define SUNIV_CODEC_ADC_RXDATA (0x18) + +/* Output mixer and gain controls */ +#define SUNIV_CODEC_OM_DACA_CTRL (0x20) +#define SUNIV_CODEC_OM_DACA_CTRL_DACAREN (31) +#define SUNIV_CODEC_OM_DACA_CTRL_DACALEN (30) +#define SUNIV_CODEC_OM_DACA_CTRL_RMIXEN (29) +#define SUNIV_CODEC_OM_DACA_CTRL_LMIXEN (28) +#define SUNIV_CODEC_OM_DACA_CTRL_RHPPAMUTE (27) +#define SUNIV_CODEC_OM_DACA_CTRL_LHPPAMUTE (26) +#define SUNIV_CODEC_OM_DACA_CTRL_RHPIS (25) +#define SUNIV_CODEC_OM_DACA_CTRL_LHPIS (24) +#define SUNIV_CODEC_OM_DACA_CTRL_HPCOM_CTL (22) +#define SUNIV_CODEC_OM_DACA_CTRL_COMPTEN (21) +#define SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_MICIN (20) +#define SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_LINEIN (19) +#define SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_FMIN (18) +#define SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_RDAC (17) +#define SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_LDAC (16) +#define SUNIV_CODEC_OM_DACA_CTRL_HPPAEN (15) +#define SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_MICIN (12) +#define SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_LINEIN (11) +#define SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_FMIN (10) +#define SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_LDAC (9) +#define SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_RDAC (8) +#define SUNIV_CODEC_OM_DACA_CTRL_LTLNMUTE (7) +#define SUNIV_CODEC_OM_DACA_CTRL_RTLNMUTE (6) +#define SUNIV_CODEC_OM_DACA_CTRL_HPVOL (0) + +/* Analog Input Mixer controls */ +#define SUNIV_CODEC_ADC_ACTL (0x24) +#define SUNIV_CODEC_ADC_ADCEN (31) +#define SUNIV_CODEC_ADC_MICG (24) +#define SUNIV_CODEC_ADC_LINEINVOL (21) +#define SUNIV_CODEC_ADC_ADCG (16) +#define SUNIV_CODEC_ADC_ADCMIX_MIC (13) +#define SUNIV_CODEC_ADC_ADCMIX_FMINL (12) +#define SUNIV_CODEC_ADC_ADCMIX_FMINR (11) +#define SUNIV_CODEC_ADC_ADCMIX_LINEIN (10) +#define SUNIV_CODEC_ADC_ADCMIX_LOUT (9) +#define SUNIV_CODEC_ADC_ADCMIX_ROUT (8) +#define SUNIV_CODEC_ADC_PASPEEDSELECT (7) +#define SUNIV_CODEC_ADC_FMINVOL (4) +#define SUNIV_CODEC_ADC_MICAMPEN (3) +#define SUNIV_CODEC_ADC_MICBOOST (0) + +#define SUNIV_CODEC_ADC_DBG (0x4c) + struct sun4i_codec { struct device *dev; struct regmap *regmap; @@ -272,6 +332,7 @@ struct sun4i_codec { struct clk *clk_module; struct reset_control *rst; struct gpio_desc *gpio_pa; + struct gpio_desc *gpio_hp; /* ADC_FIFOC register is at different offset on different SoCs */ struct regmap_field *reg_adc_fifoc; @@ -1255,6 +1316,228 @@ static const struct snd_soc_component_driver sun8i_a23_codec_codec = { .endianness = 1, }; +/*suniv F1C100s codec */ + +/* headphone controls */ +static const char * const suniv_codec_hp_src_enum_text[] = { + "DAC", "Mixer", +}; + +static SOC_ENUM_DOUBLE_DECL(suniv_codec_hp_src_enum, + SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_LHPIS, + SUNIV_CODEC_OM_DACA_CTRL_RHPIS, + suniv_codec_hp_src_enum_text); + +static const struct snd_kcontrol_new suniv_codec_hp_src[] = { + SOC_DAPM_ENUM("Headphone Source Playback Route", + suniv_codec_hp_src_enum), +}; + +/* mixer controls */ +static const struct snd_kcontrol_new suniv_codec_adc_mixer_controls[] = { + SOC_DAPM_SINGLE("Right Out Capture Switch", SUNIV_CODEC_ADC_ACTL, + SUNIV_CODEC_ADC_ADCMIX_ROUT, 1, 0), + SOC_DAPM_SINGLE("Left Out Capture Switch", SUNIV_CODEC_ADC_ACTL, + SUNIV_CODEC_ADC_ADCMIX_LOUT, 1, 0), + SOC_DAPM_SINGLE("Line In Capture Switch", SUNIV_CODEC_ADC_ACTL, + SUNIV_CODEC_ADC_ADCMIX_LINEIN, 1, 0), + SOC_DAPM_SINGLE("Right FM In Capture Switch", SUNIV_CODEC_ADC_ACTL, + SUNIV_CODEC_ADC_ADCMIX_FMINR, 1, 0), + SOC_DAPM_SINGLE("Left FM In Capture Switch", SUNIV_CODEC_ADC_ACTL, + SUNIV_CODEC_ADC_ADCMIX_FMINL, 1, 0), + SOC_DAPM_SINGLE("Mic Capture Switch", SUNIV_CODEC_ADC_ACTL, + SUNIV_CODEC_ADC_ADCMIX_MIC, 1, 0), +}; + +static const struct snd_kcontrol_new suniv_codec_dac_lmixer_controls[] = { + SOC_DAPM_SINGLE("Right DAC Playback Switch", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_RDAC, 1, 0), + SOC_DAPM_SINGLE("Left DAC Playback Switch", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_LDAC, 1, 0), + SOC_DAPM_SINGLE("FM In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_FMIN, 1, 0), + SOC_DAPM_SINGLE("Line In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_LINEIN, 1, 0), + SOC_DAPM_SINGLE("Mic In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_LMIXMUTE_MICIN, 1, 0), +}; + +static const struct snd_kcontrol_new suniv_codec_dac_rmixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Playback Switch", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_LDAC, 1, 0), + SOC_DAPM_SINGLE("Right DAC Playback Switch", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_RDAC, 1, 0), + SOC_DAPM_SINGLE("FM In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_FMIN, 1, 0), + SOC_DAPM_SINGLE("Line In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_LINEIN, 1, 0), + SOC_DAPM_SINGLE("Mic In Playback Switch", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_RMIXMUTE_MICIN, 1, 0), +}; + +static const DECLARE_TLV_DB_SCALE(suniv_codec_dvol_scale, -7308, 116, 0); +static const DECLARE_TLV_DB_SCALE(suniv_codec_hp_vol_scale, -6300, 100, 1); +static const DECLARE_TLV_DB_SCALE(suniv_codec_out_mixer_pregain_scale, + -450, 150, 0); + +static const DECLARE_TLV_DB_RANGE(suniv_codec_mic_gain_scale, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0), +); + +static const struct snd_kcontrol_new suniv_codec_codec_widgets[] = { + SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC, + SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1, + suniv_codec_dvol_scale), + SOC_SINGLE_TLV("Headphone Playback Volume", + SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0, + suniv_codec_hp_vol_scale), + SOC_DOUBLE("Headphone Playback Switch", + SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_LHPPAMUTE, + SUNIV_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0), + SOC_SINGLE_TLV("Line In Playback Volume", + SUNIV_CODEC_ADC_ACTL, SUNIV_CODEC_ADC_LINEINVOL, + 0x7, 0, suniv_codec_out_mixer_pregain_scale), + SOC_SINGLE_TLV("FM In Playback Volume", + SUNIV_CODEC_ADC_ACTL, SUNIV_CODEC_ADC_FMINVOL, + 0x7, 0, suniv_codec_out_mixer_pregain_scale), + SOC_SINGLE_TLV("Mic In Playback Volume", + SUNIV_CODEC_ADC_ACTL, SUNIV_CODEC_ADC_MICG, + 0x7, 0, suniv_codec_out_mixer_pregain_scale), + + /* Microphone Amp boost gains */ + SOC_SINGLE_TLV("Mic Boost Volume", SUNIV_CODEC_ADC_ACTL, + SUNIV_CODEC_ADC_MICBOOST, 0x7, 0, + suniv_codec_mic_gain_scale), + SOC_SINGLE_TLV("ADC Capture Volume", + SUNIV_CODEC_ADC_ACTL, SUNIV_CODEC_ADC_ADCG, + 0x7, 0, suniv_codec_out_mixer_pregain_scale), +}; + +static const struct snd_soc_dapm_widget suniv_codec_codec_dapm_widgets[] = { + /* Microphone inputs */ + SND_SOC_DAPM_INPUT("MIC"), + + /* Microphone Bias */ + /* deleted: HBIAS, MBIAS */ + + /* Mic input path */ + SND_SOC_DAPM_PGA("Mic Amplifier", SUNIV_CODEC_ADC_ACTL, + SUNIV_CODEC_ADC_MICAMPEN, 0, NULL, 0), + + /* Line In */ + SND_SOC_DAPM_INPUT("LINEIN"), + + /* FM In */ + SND_SOC_DAPM_INPUT("FMINR"), + SND_SOC_DAPM_INPUT("FMINL"), + + /* Digital parts of the ADCs */ + SND_SOC_DAPM_SUPPLY("ADC Enable", SUNIV_CODEC_ADC_FIFOC, + SUNIV_CODEC_ADC_FIFOC_EN_AD, 0, + NULL, 0), + + /* Analog parts of the ADCs */ + SND_SOC_DAPM_ADC("ADC", "Codec Capture", SUNIV_CODEC_ADC_ACTL, + SUNIV_CODEC_ADC_ADCEN, 0), + + /* ADC Mixers */ + SOC_MIXER_ARRAY("ADC Mixer", SUNIV_CODEC_ADC_ACTL, + SND_SOC_NOPM, 0, + suniv_codec_adc_mixer_controls), + + /* Digital parts of the DACs */ + SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC, + SUN4I_CODEC_DAC_DPC_EN_DA, 0, + NULL, 0), + + /* Analog parts of the DACs */ + SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", + SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_DACALEN, 0), + SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", + SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_DACAREN, 0), + + /* Mixers */ + SOC_MIXER_ARRAY("Left Mixer", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_LMIXEN, 0, + suniv_codec_dac_lmixer_controls), + SOC_MIXER_ARRAY("Right Mixer", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_RMIXEN, 0, + suniv_codec_dac_rmixer_controls), + + /* Headphone output path */ + SND_SOC_DAPM_MUX("Headphone Source Playback Route", + SND_SOC_NOPM, 0, 0, suniv_codec_hp_src), + SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_HPPAEN, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_COMPTEN, 0, NULL, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUNIV_CODEC_OM_DACA_CTRL, + SUNIV_CODEC_OM_DACA_CTRL_HPCOM_CTL, 0x3, 0x3, 0), + SND_SOC_DAPM_OUTPUT("HP"), +}; + +static const struct snd_soc_dapm_route suniv_codec_codec_dapm_routes[] = { + /* DAC Routes */ + { "Left DAC", NULL, "DAC Enable" }, + { "Right DAC", NULL, "DAC Enable" }, + + /* Microphone Routes */ + { "Mic Amplifier", NULL, "MIC"}, + + /* Left Mixer Routes */ + { "Left Mixer", "Right DAC Playback Switch", "Right DAC" }, + { "Left Mixer", "Left DAC Playback Switch", "Left DAC" }, + { "Left Mixer", "FM In Playback Switch", "FMINL" }, + { "Left Mixer", "Line In Playback Switch", "LINEIN" }, + { "Left Mixer", "Mic In Playback Switch", "Mic Amplifier" }, + + /* Right Mixer Routes */ + { "Right Mixer", "Left DAC Playback Switch", "Left DAC" }, + { "Right Mixer", "Right DAC Playback Switch", "Right DAC" }, + { "Right Mixer", "FM In Playback Switch", "FMINR" }, + { "Right Mixer", "Line In Playback Switch", "LINEIN" }, + { "Right Mixer", "Mic In Playback Switch", "Mic Amplifier" }, + + /* ADC Mixer Routes */ + { "ADC Mixer", "Right Out Capture Switch", "Right Mixer" }, + { "ADC Mixer", "Left Out Capture Switch", "Left Mixer" }, + { "ADC Mixer", "Line In Capture Switch", "LINEIN" }, + { "ADC Mixer", "Right FM In Capture Switch", "FMINR" }, + { "ADC Mixer", "Left FM In Capture Switch", "FMINL" }, + { "ADC Mixer", "Mic Capture Switch", "Mic Amplifier" }, + + /* Headphone Routes */ + { "Headphone Source Playback Route", "DAC", "Left DAC" }, + { "Headphone Source Playback Route", "DAC", "Right DAC" }, + { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, + { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, + { "Headphone Amp", NULL, "Headphone Source Playback Route" }, + { "HP", NULL, "Headphone Amp" }, + { "HPCOM", NULL, "HPCOM Protection" }, + + /* ADC Routes */ + { "ADC", NULL, "ADC Mixer" }, + { "ADC", NULL, "ADC Enable" }, +}; + +static const struct snd_soc_component_driver suniv_codec_codec = { + .controls = suniv_codec_codec_widgets, + .num_controls = ARRAY_SIZE(suniv_codec_codec_widgets), + .dapm_widgets = suniv_codec_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(suniv_codec_codec_dapm_widgets), + .dapm_routes = suniv_codec_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(suniv_codec_codec_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + static const struct snd_soc_component_driver sun4i_codec_component = { .name = "sun4i-codec", .legacy_dai_naming = 1, @@ -1302,6 +1585,49 @@ static struct snd_soc_dai_driver dummy_cpu_dai = { .ops = &dummy_dai_ops, }; +static struct snd_soc_jack sun4i_headphone_jack; + +static struct snd_soc_jack_pin sun4i_headphone_jack_pins[] = { + { .pin = "Headphone", .mask = SND_JACK_HEADPHONE }, +}; + +static struct snd_soc_jack_gpio sun4i_headphone_jack_gpio = { + .name = "hp-det", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, +}; + +static int sun4i_codec_machine_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); + int ret; + + if (scodec->gpio_hp) { + ret = snd_soc_card_jack_new_pins(card, "Headphone Jack", + SND_JACK_HEADPHONE, + &sun4i_headphone_jack, + sun4i_headphone_jack_pins, + ARRAY_SIZE(sun4i_headphone_jack_pins)); + if (ret) { + dev_err(rtd->dev, + "Headphone jack creation failed: %d\n", ret); + return ret; + } + + sun4i_headphone_jack_gpio.desc = scodec->gpio_hp; + ret = snd_soc_jack_add_gpios(&sun4i_headphone_jack, 1, + &sun4i_headphone_jack_gpio); + + if (ret) { + dev_err(rtd->dev, "Headphone GPIO not added: %d\n", ret); + return ret; + } + } + + return 0; +} + static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, int *num_links) { @@ -1327,6 +1653,7 @@ static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, link->codecs->name = dev_name(dev); link->platforms->name = dev_name(dev); link->dai_fmt = SND_SOC_DAIFMT_I2S; + link->init = sun4i_codec_machine_init; *num_links = 1; @@ -1635,10 +1962,11 @@ static const struct snd_soc_component_driver sun50i_h616_codec_codec = { }; static const struct snd_kcontrol_new sun50i_h616_card_controls[] = { - SOC_DAPM_PIN_SWITCH("LINEOUT"), + SOC_DAPM_PIN_SWITCH("Speaker"), }; static const struct snd_soc_dapm_widget sun50i_h616_codec_card_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event), }; @@ -1685,6 +2013,7 @@ static struct snd_soc_card *sun50i_h616_codec_create_card(struct device *dev) card->dev = dev; card->owner = THIS_MODULE; card->name = "H616 Audio Codec"; + card->long_name = "h616-audio-codec"; card->driver_name = "sun4i-codec"; card->controls = sun50i_h616_card_controls; card->num_controls = ARRAY_SIZE(sun50i_h616_card_controls); @@ -1701,6 +2030,56 @@ static struct snd_soc_card *sun50i_h616_codec_create_card(struct device *dev) return card; }; +static const struct snd_soc_dapm_widget suniv_codec_card_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), + SND_SOC_DAPM_LINE("Right FM In", NULL), + SND_SOC_DAPM_LINE("Left FM In", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), + SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event), +}; + +/* Connect digital side enables to analog side widgets */ +static const struct snd_soc_dapm_route suniv_codec_card_routes[] = { + /* ADC Routes */ + { "ADC", NULL, "ADC Enable" }, + { "Codec Capture", NULL, "ADC" }, + + /* DAC Routes */ + { "Left DAC", NULL, "DAC Enable" }, + { "Right DAC", NULL, "DAC Enable" }, + { "Left DAC", NULL, "Codec Playback" }, + { "Right DAC", NULL, "Codec Playback" }, +}; + +static struct snd_soc_card *suniv_codec_create_card(struct device *dev) +{ + struct snd_soc_card *card; + int ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return ERR_PTR(-ENOMEM); + + card->dai_link = sun4i_codec_create_link(dev, &card->num_links); + if (!card->dai_link) + return ERR_PTR(-ENOMEM); + + card->dev = dev; + card->name = "F1C100s Audio Codec"; + card->dapm_widgets = suniv_codec_card_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(suniv_codec_card_dapm_widgets); + card->dapm_routes = suniv_codec_card_routes; + card->num_dapm_routes = ARRAY_SIZE(suniv_codec_card_routes); + card->fully_routed = true; + + ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing"); + if (ret) + dev_warn(dev, "failed to parse audio-routing: %d\n", ret); + + return card; +}; + static const struct regmap_config sun4i_codec_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -1751,6 +2130,13 @@ static const struct regmap_config sun50i_h616_codec_regmap_config = { .cache_type = REGCACHE_NONE, }; +static const struct regmap_config suniv_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUNIV_CODEC_ADC_DBG, +}; + struct sun4i_codec_quirks { const struct regmap_config *regmap_config; const struct snd_soc_component_driver *codec; @@ -1761,6 +2147,7 @@ struct sun4i_codec_quirks { unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */ bool has_reset; bool playback_only; + u32 dma_max_burst; }; static const struct sun4i_codec_quirks sun4i_codec_quirks = { @@ -1771,6 +2158,7 @@ static const struct sun4i_codec_quirks sun4i_codec_quirks = { .reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31), .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA, + .dma_max_burst = SUN4I_DMA_MAX_BURST, }; static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = { @@ -1782,6 +2170,7 @@ static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = { .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, .has_reset = true, + .dma_max_burst = SUN4I_DMA_MAX_BURST, }; static const struct sun4i_codec_quirks sun7i_codec_quirks = { @@ -1792,6 +2181,7 @@ static const struct sun4i_codec_quirks sun7i_codec_quirks = { .reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31), .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, .reg_adc_rxdata = SUN4I_CODEC_ADC_RXDATA, + .dma_max_burst = SUN4I_DMA_MAX_BURST, }; static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = { @@ -1803,6 +2193,7 @@ static const struct sun4i_codec_quirks sun8i_a23_codec_quirks = { .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, .has_reset = true, + .dma_max_burst = SUN4I_DMA_MAX_BURST, }; static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = { @@ -1819,6 +2210,7 @@ static const struct sun4i_codec_quirks sun8i_h3_codec_quirks = { .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, .has_reset = true, + .dma_max_burst = SUN4I_DMA_MAX_BURST, }; static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = { @@ -1834,6 +2226,7 @@ static const struct sun4i_codec_quirks sun8i_v3s_codec_quirks = { .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, .reg_adc_rxdata = SUN6I_CODEC_ADC_RXDATA, .has_reset = true, + .dma_max_burst = SUN4I_DMA_MAX_BURST, }; static const struct sun4i_codec_quirks sun50i_h616_codec_quirks = { @@ -1843,6 +2236,19 @@ static const struct sun4i_codec_quirks sun50i_h616_codec_quirks = { .reg_dac_fifoc = REG_FIELD(SUN50I_H616_CODEC_DAC_FIFOC, 0, 31), .reg_dac_txdata = SUN8I_H3_CODEC_DAC_TXDATA, .has_reset = true, + .dma_max_burst = SUN4I_DMA_MAX_BURST, +}; + +static const struct sun4i_codec_quirks suniv_f1c100s_codec_quirks = { + .regmap_config = &suniv_codec_regmap_config, + .codec = &suniv_codec_codec, + .create_card = suniv_codec_create_card, + .reg_adc_fifoc = REG_FIELD(SUNIV_CODEC_ADC_FIFOC, 0, 31), + .reg_dac_fifoc = REG_FIELD(SUN4I_CODEC_DAC_FIFOC, 0, 31), + .reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA, + .reg_adc_rxdata = SUNIV_CODEC_ADC_RXDATA, + .has_reset = true, + .dma_max_burst = SUNIV_DMA_MAX_BURST, }; static const struct of_device_id sun4i_codec_of_match[] = { @@ -1874,6 +2280,10 @@ static const struct of_device_id sun4i_codec_of_match[] = { .compatible = "allwinner,sun50i-h616-codec", .data = &sun50i_h616_codec_quirks, }, + { + .compatible = "allwinner,suniv-f1c100s-codec", + .data = &suniv_f1c100s_codec_quirks, + }, {} }; MODULE_DEVICE_TABLE(of, sun4i_codec_of_match); @@ -1911,7 +2321,7 @@ static int sun4i_codec_probe(struct platform_device *pdev) } /* Get the clocks from the DT */ - scodec->clk_apb = devm_clk_get(&pdev->dev, "apb"); + scodec->clk_apb = devm_clk_get_enabled(&pdev->dev, "apb"); if (IS_ERR(scodec->clk_apb)) { dev_err(&pdev->dev, "Failed to get the APB clock\n"); return PTR_ERR(scodec->clk_apb); @@ -1924,8 +2334,7 @@ static int sun4i_codec_probe(struct platform_device *pdev) } if (quirks->has_reset) { - scodec->rst = devm_reset_control_get_exclusive(&pdev->dev, - NULL); + scodec->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL); if (IS_ERR(scodec->rst)) { dev_err(&pdev->dev, "Failed to get reset control\n"); return PTR_ERR(scodec->rst); @@ -1940,6 +2349,13 @@ static int sun4i_codec_probe(struct platform_device *pdev) return ret; } + scodec->gpio_hp = devm_gpiod_get_optional(&pdev->dev, "hp-det", GPIOD_IN); + if (IS_ERR(scodec->gpio_hp)) { + ret = PTR_ERR(scodec->gpio_hp); + dev_err_probe(&pdev->dev, ret, "Failed to get hp-det gpio\n"); + return ret; + } + /* reg_field setup */ scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev, scodec->regmap, @@ -1961,32 +2377,16 @@ static int sun4i_codec_probe(struct platform_device *pdev) return ret; } - /* Enable the bus clock */ - if (clk_prepare_enable(scodec->clk_apb)) { - dev_err(&pdev->dev, "Failed to enable the APB clock\n"); - return -EINVAL; - } - - /* Deassert the reset control */ - if (scodec->rst) { - ret = reset_control_deassert(scodec->rst); - if (ret) { - dev_err(&pdev->dev, - "Failed to deassert the reset control\n"); - goto err_clk_disable; - } - } - /* DMA configuration for TX FIFO */ scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata; - scodec->playback_dma_data.maxburst = 8; + scodec->playback_dma_data.maxburst = quirks->dma_max_burst; scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; if (!quirks->playback_only) { /* DMA configuration for RX FIFO */ scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata; - scodec->capture_dma_data.maxburst = 8; + scodec->capture_dma_data.maxburst = quirks->dma_max_burst; scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; } @@ -1994,7 +2394,7 @@ static int sun4i_codec_probe(struct platform_device *pdev) &sun4i_codec_dai, 1); if (ret) { dev_err(&pdev->dev, "Failed to register our codec\n"); - goto err_assert_reset; + return ret; } ret = devm_snd_soc_register_component(&pdev->dev, @@ -2002,20 +2402,20 @@ static int sun4i_codec_probe(struct platform_device *pdev) &dummy_cpu_dai, 1); if (ret) { dev_err(&pdev->dev, "Failed to register our DAI\n"); - goto err_assert_reset; + return ret; } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { dev_err(&pdev->dev, "Failed to register against DMAEngine\n"); - goto err_assert_reset; + return ret; } card = quirks->create_card(&pdev->dev); if (IS_ERR(card)) { ret = PTR_ERR(card); dev_err(&pdev->dev, "Failed to create our card\n"); - goto err_assert_reset; + return ret; } snd_soc_card_set_drvdata(card, scodec); @@ -2023,28 +2423,17 @@ static int sun4i_codec_probe(struct platform_device *pdev) ret = snd_soc_register_card(card); if (ret) { dev_err_probe(&pdev->dev, ret, "Failed to register our card\n"); - goto err_assert_reset; + return ret; } return 0; - -err_assert_reset: - if (scodec->rst) - reset_control_assert(scodec->rst); -err_clk_disable: - clk_disable_unprepare(scodec->clk_apb); - return ret; } static void sun4i_codec_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); - struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card); snd_soc_unregister_card(card); - if (scodec->rst) - reset_control_assert(scodec->rst); - clk_disable_unprepare(scodec->clk_apb); } static struct platform_driver sun4i_codec_driver = { @@ -2063,4 +2452,5 @@ MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>"); MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); MODULE_AUTHOR("Ryan Walklin <ryan@testtoast.com"); +MODULE_AUTHOR("Mesih Kilinc <mesikilinc@gmail.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index 0aa416423246..34e5bd94e9af 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -176,6 +176,7 @@ struct sun4i_spdif_quirks { unsigned int reg_dac_txdata; bool has_reset; unsigned int val_fctl_ftx; + unsigned int mclk_multiplier; }; struct sun4i_spdif_dev { @@ -201,6 +202,10 @@ static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, quirks->val_fctl_ftx, quirks->val_fctl_ftx); + /* Valid data at the MSB of TXFIFO Register */ + regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, + SUN4I_SPDIF_FCTL_TXIM, 0); + /* clear TX counter */ regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0); } @@ -282,14 +287,17 @@ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: fmt |= SUN4I_SPDIF_TXCFG_FMT16BIT; + host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; case SNDRV_PCM_FORMAT_S20_3LE: fmt |= SUN4I_SPDIF_TXCFG_FMT20BIT; break; case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: fmt |= SUN4I_SPDIF_TXCFG_FMT24BIT; break; default: @@ -313,6 +321,7 @@ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, default: return -EINVAL; } + mclk *= host->quirks->mclk_multiplier; ret = clk_set_rate(host->spdif_clk, mclk); if (ret < 0) { @@ -321,9 +330,6 @@ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, return ret; } - regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, - SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM); - switch (rate) { case 22050: case 24000: @@ -347,6 +353,7 @@ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, default: return -EINVAL; } + mclk_div *= host->quirks->mclk_multiplier; reg_val = 0; reg_val |= SUN4I_SPDIF_TXCFG_ASS; @@ -522,9 +529,10 @@ static const struct regmap_config sun4i_spdif_regmap_config = { #define SUN4I_RATES SNDRV_PCM_RATE_8000_192000 -#define SUN4I_FORMATS (SNDRV_PCM_FORMAT_S16_LE | \ - SNDRV_PCM_FORMAT_S20_3LE | \ - SNDRV_PCM_FORMAT_S24_LE) +#define SUN4I_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_driver sun4i_spdif_dai = { .playback = { @@ -540,24 +548,28 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = { static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = { .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, + .mclk_multiplier = 1, }; static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = { .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, .has_reset = true, + .mclk_multiplier = 1, }; static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, .has_reset = true, + .mclk_multiplier = 4, }; static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = { .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, .has_reset = true, + .mclk_multiplier = 1, }; static const struct of_device_id sun4i_spdif_of_match[] = { @@ -715,15 +727,15 @@ static void sun4i_spdif_remove(struct platform_device *pdev) } static const struct dev_pm_ops sun4i_spdif_pm = { - SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend, - sun4i_spdif_runtime_resume, NULL) + RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend, + sun4i_spdif_runtime_resume, NULL) }; static struct platform_driver sun4i_spdif_driver = { .driver = { .name = "sun4i-spdif", .of_match_table = sun4i_spdif_of_match, - .pm = &sun4i_spdif_pm, + .pm = pm_ptr(&sun4i_spdif_pm), }, .probe = sun4i_spdif_probe, .remove = sun4i_spdif_remove, diff --git a/sound/soc/sunxi/sun50i-dmic.c b/sound/soc/sunxi/sun50i-dmic.c index 3e751b5694fe..bab1e29c9988 100644 --- a/sound/soc/sunxi/sun50i-dmic.c +++ b/sound/soc/sunxi/sun50i-dmic.c @@ -415,15 +415,15 @@ static void sun50i_dmic_remove(struct platform_device *pdev) } static const struct dev_pm_ops sun50i_dmic_pm = { - SET_RUNTIME_PM_OPS(sun50i_dmic_runtime_suspend, - sun50i_dmic_runtime_resume, NULL) + RUNTIME_PM_OPS(sun50i_dmic_runtime_suspend, + sun50i_dmic_runtime_resume, NULL) }; static struct platform_driver sun50i_dmic_driver = { .driver = { .name = "sun50i-dmic", .of_match_table = sun50i_dmic_of_match, - .pm = &sun50i_dmic_pm, + .pm = pm_ptr(&sun50i_dmic_pm), }, .probe = sun50i_dmic_probe, .remove = sun50i_dmic_remove, diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 8c645e04d571..8b9eb1a202f7 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -1702,15 +1702,15 @@ static const struct of_device_id sun8i_codec_of_match[] = { MODULE_DEVICE_TABLE(of, sun8i_codec_of_match); static const struct dev_pm_ops sun8i_codec_pm_ops = { - SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend, - sun8i_codec_runtime_resume, NULL) + RUNTIME_PM_OPS(sun8i_codec_runtime_suspend, + sun8i_codec_runtime_resume, NULL) }; static struct platform_driver sun8i_codec_driver = { .driver = { .name = "sun8i-codec", .of_match_table = sun8i_codec_of_match, - .pm = &sun8i_codec_pm_ops, + .pm = pm_ptr(&sun8i_codec_pm_ops), }, .probe = sun8i_codec_probe, .remove = sun8i_codec_remove, diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index cea4b0d54378..defea7f53f11 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -13,7 +13,7 @@ snd-soc-tegra210-dmic-y := tegra210_dmic.o snd-soc-tegra210-i2s-y := tegra210_i2s.o snd-soc-tegra186-asrc-y := tegra186_asrc.o snd-soc-tegra186-dspk-y := tegra186_dspk.o -snd-soc-tegra210-admaif-y := tegra210_admaif.o +snd-soc-tegra210-admaif-y := tegra210_admaif.o tegra_isomgr_bw.o snd-soc-tegra210-mvc-y := tegra210_mvc.o snd-soc-tegra210-sfc-y := tegra210_sfc.o snd-soc-tegra210-amx-y := tegra210_amx.o diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c index d914dba56013..5c67e1f01d9b 100644 --- a/sound/soc/tegra/tegra186_asrc.c +++ b/sound/soc/tegra/tegra186_asrc.c @@ -76,7 +76,7 @@ static void tegra186_asrc_lock_stream(struct tegra186_asrc *asrc, 1); } -static int __maybe_unused tegra186_asrc_runtime_suspend(struct device *dev) +static int tegra186_asrc_runtime_suspend(struct device *dev) { struct tegra186_asrc *asrc = dev_get_drvdata(dev); @@ -86,7 +86,7 @@ static int __maybe_unused tegra186_asrc_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra186_asrc_runtime_resume(struct device *dev) +static int tegra186_asrc_runtime_resume(struct device *dev) { struct tegra186_asrc *asrc = dev_get_drvdata(dev); int id; @@ -1021,17 +1021,16 @@ static void tegra186_asrc_platform_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra186_asrc_pm_ops = { - SET_RUNTIME_PM_OPS(tegra186_asrc_runtime_suspend, - tegra186_asrc_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra186_asrc_runtime_suspend, + tegra186_asrc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra186_asrc_driver = { .driver = { .name = "tegra186-asrc", .of_match_table = tegra186_asrc_of_match, - .pm = &tegra186_asrc_pm_ops, + .pm = pm_ptr(&tegra186_asrc_pm_ops), }, .probe = tegra186_asrc_platform_probe, .remove = tegra186_asrc_platform_remove, diff --git a/sound/soc/tegra/tegra186_dspk.c b/sound/soc/tegra/tegra186_dspk.c index 1be6c09cbe1a..21fdab2a1977 100644 --- a/sound/soc/tegra/tegra186_dspk.c +++ b/sound/soc/tegra/tegra186_dspk.c @@ -181,7 +181,7 @@ static int tegra186_dspk_put_stereo_to_mono(struct snd_kcontrol *kcontrol, return 1; } -static int __maybe_unused tegra186_dspk_runtime_suspend(struct device *dev) +static int tegra186_dspk_runtime_suspend(struct device *dev) { struct tegra186_dspk *dspk = dev_get_drvdata(dev); @@ -193,7 +193,7 @@ static int __maybe_unused tegra186_dspk_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra186_dspk_runtime_resume(struct device *dev) +static int tegra186_dspk_runtime_resume(struct device *dev) { struct tegra186_dspk *dspk = dev_get_drvdata(dev); int err; @@ -532,17 +532,16 @@ static void tegra186_dspk_platform_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra186_dspk_pm_ops = { - SET_RUNTIME_PM_OPS(tegra186_dspk_runtime_suspend, - tegra186_dspk_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra186_dspk_runtime_suspend, + tegra186_dspk_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra186_dspk_driver = { .driver = { .name = "tegra186-dspk", .of_match_table = tegra186_dspk_of_match, - .pm = &tegra186_dspk_pm_ops, + .pm = pm_ptr(&tegra186_dspk_pm_ops), }, .probe = tegra186_dspk_platform_probe, .remove = tegra186_dspk_platform_remove, diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index 3b9823d1a87a..51df0835ce3e 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -34,7 +34,7 @@ #define DRV_NAME "tegra20-i2s" -static __maybe_unused int tegra20_i2s_runtime_suspend(struct device *dev) +static int tegra20_i2s_runtime_suspend(struct device *dev) { struct tegra20_i2s *i2s = dev_get_drvdata(dev); @@ -45,7 +45,7 @@ static __maybe_unused int tegra20_i2s_runtime_suspend(struct device *dev) return 0; } -static __maybe_unused int tegra20_i2s_runtime_resume(struct device *dev) +static int tegra20_i2s_runtime_resume(struct device *dev) { struct tegra20_i2s *i2s = dev_get_drvdata(dev); int ret; @@ -487,17 +487,16 @@ static const struct of_device_id tegra20_i2s_of_match[] = { }; static const struct dev_pm_ops tegra20_i2s_pm_ops = { - SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend, - tegra20_i2s_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend, + tegra20_i2s_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra20_i2s_driver = { .driver = { .name = DRV_NAME, .of_match_table = tegra20_i2s_of_match, - .pm = &tegra20_i2s_pm_ops, + .pm = pm_ptr(&tegra20_i2s_pm_ops), }, .probe = tegra20_i2s_platform_probe, .remove = tegra20_i2s_platform_remove, diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 380011233eb1..38661d9b4a7c 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -25,7 +25,7 @@ #include "tegra20_spdif.h" -static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev) +static int tegra20_spdif_runtime_suspend(struct device *dev) { struct tegra20_spdif *spdif = dev_get_drvdata(dev); @@ -36,7 +36,7 @@ static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev) return 0; } -static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev) +static int tegra20_spdif_runtime_resume(struct device *dev) { struct tegra20_spdif *spdif = dev_get_drvdata(dev); int ret; @@ -403,10 +403,9 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) } static const struct dev_pm_ops tegra20_spdif_pm_ops = { - SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend, - tegra20_spdif_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend, + tegra20_spdif_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static const struct of_device_id tegra20_spdif_of_match[] = { @@ -418,7 +417,7 @@ MODULE_DEVICE_TABLE(of, tegra20_spdif_of_match); static struct platform_driver tegra20_spdif_driver = { .driver = { .name = "tegra20-spdif", - .pm = &tegra20_spdif_pm_ops, + .pm = pm_ptr(&tegra20_spdif_pm_ops), .of_match_table = tegra20_spdif_of_match, }, .probe = tegra20_spdif_platform_probe, diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c index 58fdb0e79954..76ff4fe40f65 100644 --- a/sound/soc/tegra/tegra210_admaif.c +++ b/sound/soc/tegra/tegra210_admaif.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -// SPDX-FileCopyrightText: Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. +// SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES. // All rights reserved. // // tegra210_admaif.c - Tegra ADMAIF driver @@ -13,6 +13,7 @@ #include <linux/regmap.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include "tegra_isomgr_bw.h" #include "tegra210_admaif.h" #include "tegra_cif.h" #include "tegra_pcm.h" @@ -219,7 +220,7 @@ static const struct regmap_config tegra186_admaif_regmap_config = { .cache_type = REGCACHE_FLAT, }; -static int __maybe_unused tegra_admaif_runtime_suspend(struct device *dev) +static int tegra_admaif_runtime_suspend(struct device *dev) { struct tegra_admaif *admaif = dev_get_drvdata(dev); @@ -229,7 +230,7 @@ static int __maybe_unused tegra_admaif_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra_admaif_runtime_resume(struct device *dev) +static int tegra_admaif_runtime_resume(struct device *dev) { struct tegra_admaif *admaif = dev_get_drvdata(dev); @@ -262,6 +263,18 @@ static int tegra_admaif_set_pack_mode(struct regmap *map, unsigned int reg, return 0; } +static int tegra_admaif_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return tegra_isomgr_adma_setbw(substream, dai, true); +} + +static void tegra_admaif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + tegra_isomgr_adma_setbw(substream, dai, false); +} + static int tegra_admaif_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -554,6 +567,8 @@ static const struct snd_soc_dai_ops tegra_admaif_dai_ops = { .probe = tegra_admaif_dai_probe, .hw_params = tegra_admaif_hw_params, .trigger = tegra_admaif_trigger, + .shutdown = tegra_admaif_shutdown, + .prepare = tegra_admaif_prepare, }; #define DAI(dai_name) \ @@ -800,6 +815,12 @@ static int tegra_admaif_probe(struct platform_device *pdev) regcache_cache_only(admaif->regmap, true); + err = tegra_isomgr_adma_register(&pdev->dev); + if (err) { + dev_err(&pdev->dev, "Failed to add interconnect path\n"); + return err; + } + regmap_update_bits(admaif->regmap, admaif->soc_data->global_base + TEGRA_ADMAIF_GLOBAL_ENABLE, 1, 1); @@ -851,14 +872,14 @@ static int tegra_admaif_probe(struct platform_device *pdev) static void tegra_admaif_remove(struct platform_device *pdev) { + tegra_isomgr_adma_unregister(&pdev->dev); pm_runtime_disable(&pdev->dev); } static const struct dev_pm_ops tegra_admaif_pm_ops = { - SET_RUNTIME_PM_OPS(tegra_admaif_runtime_suspend, - tegra_admaif_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra_admaif_runtime_suspend, + tegra_admaif_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra_admaif_driver = { @@ -867,7 +888,7 @@ static struct platform_driver tegra_admaif_driver = { .driver = { .name = "tegra210-admaif", .of_match_table = tegra_admaif_of_match, - .pm = &tegra_admaif_pm_ops, + .pm = pm_ptr(&tegra_admaif_pm_ops), }, }; module_platform_driver(tegra_admaif_driver); diff --git a/sound/soc/tegra/tegra210_admaif.h b/sound/soc/tegra/tegra210_admaif.h index 96686dc92081..748f886ee74e 100644 --- a/sound/soc/tegra/tegra210_admaif.h +++ b/sound/soc/tegra/tegra210_admaif.h @@ -1,8 +1,8 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * tegra210_admaif.h - Tegra ADMAIF registers +/* SPDX-License-Identifier: GPL-2.0-only + * SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES. + * All rights reserved. * - * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. + * tegra210_admaif.h - Tegra ADMAIF registers * */ @@ -157,6 +157,7 @@ struct tegra_admaif { unsigned int *mono_to_stereo[ADMAIF_PATHS]; unsigned int *stereo_to_mono[ADMAIF_PATHS]; struct regmap *regmap; + struct tegra_adma_isomgr *adma_isomgr; }; #endif diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c index 3e6e8f51f380..b6c798baedea 100644 --- a/sound/soc/tegra/tegra210_adx.c +++ b/sound/soc/tegra/tegra210_adx.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -// SPDX-FileCopyrightText: Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. +// SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. // All rights reserved. // // tegra210_adx.c - Tegra210 ADX driver @@ -57,8 +57,8 @@ static int tegra210_adx_startup(struct snd_pcm_substream *substream, int err; /* Ensure if ADX status is disabled */ - err = regmap_read_poll_timeout_atomic(adx->regmap, TEGRA210_ADX_STATUS, - val, !(val & 0x1), 10, 10000); + err = regmap_read_poll_timeout(adx->regmap, TEGRA210_ADX_STATUS, + val, !(val & 0x1), 10, 10000); if (err < 0) { dev_err(dai->dev, "failed to stop ADX, err = %d\n", err); return err; @@ -84,7 +84,7 @@ static int tegra210_adx_startup(struct snd_pcm_substream *substream, return 0; } -static int __maybe_unused tegra210_adx_runtime_suspend(struct device *dev) +static int tegra210_adx_runtime_suspend(struct device *dev) { struct tegra210_adx *adx = dev_get_drvdata(dev); @@ -94,7 +94,7 @@ static int __maybe_unused tegra210_adx_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra210_adx_runtime_resume(struct device *dev) +static int tegra210_adx_runtime_resume(struct device *dev) { struct tegra210_adx *adx = dev_get_drvdata(dev); @@ -264,7 +264,7 @@ static const struct snd_soc_dai_ops tegra210_adx_out_dai_ops = { .rates = SNDRV_PCM_RATE_8000_192000, \ .formats = SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE, \ }, \ .capture = { \ @@ -274,7 +274,7 @@ static const struct snd_soc_dai_ops tegra210_adx_out_dai_ops = { .rates = SNDRV_PCM_RATE_8000_192000, \ .formats = SNDRV_PCM_FMTBIT_S8 | \ SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE, \ }, \ .ops = &tegra210_adx_out_dai_ops, \ @@ -524,17 +524,16 @@ static void tegra210_adx_platform_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra210_adx_pm_ops = { - SET_RUNTIME_PM_OPS(tegra210_adx_runtime_suspend, - tegra210_adx_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra210_adx_runtime_suspend, + tegra210_adx_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra210_adx_driver = { .driver = { .name = "tegra210-adx", .of_match_table = tegra210_adx_of_match, - .pm = &tegra210_adx_pm_ops, + .pm = pm_ptr(&tegra210_adx_pm_ops), }, .probe = tegra210_adx_platform_probe, .remove = tegra210_adx_platform_remove, diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index 1920b996e9aa..99683f292b5d 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -1319,7 +1319,7 @@ static const struct of_device_id tegra_ahub_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_ahub_of_match); -static int __maybe_unused tegra_ahub_runtime_suspend(struct device *dev) +static int tegra_ahub_runtime_suspend(struct device *dev) { struct tegra_ahub *ahub = dev_get_drvdata(dev); @@ -1331,7 +1331,7 @@ static int __maybe_unused tegra_ahub_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra_ahub_runtime_resume(struct device *dev) +static int tegra_ahub_runtime_resume(struct device *dev) { struct tegra_ahub *ahub = dev_get_drvdata(dev); int err; @@ -1408,10 +1408,9 @@ static void tegra_ahub_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra_ahub_pm_ops = { - SET_RUNTIME_PM_OPS(tegra_ahub_runtime_suspend, - tegra_ahub_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra_ahub_runtime_suspend, + tegra_ahub_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra_ahub_driver = { @@ -1420,7 +1419,7 @@ static struct platform_driver tegra_ahub_driver = { .driver = { .name = "tegra210-ahub", .of_match_table = tegra_ahub_of_match, - .pm = &tegra_ahub_pm_ops, + .pm = pm_ptr(&tegra_ahub_pm_ops), }, }; module_platform_driver(tegra_ahub_driver); diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c index a9ef22c19e81..1981b94009cf 100644 --- a/sound/soc/tegra/tegra210_amx.c +++ b/sound/soc/tegra/tegra210_amx.c @@ -98,7 +98,7 @@ static int tegra210_amx_startup(struct snd_pcm_substream *substream, return 0; } -static int __maybe_unused tegra210_amx_runtime_suspend(struct device *dev) +static int tegra210_amx_runtime_suspend(struct device *dev) { struct tegra210_amx *amx = dev_get_drvdata(dev); @@ -108,7 +108,7 @@ static int __maybe_unused tegra210_amx_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra210_amx_runtime_resume(struct device *dev) +static int tegra210_amx_runtime_resume(struct device *dev) { struct tegra210_amx *amx = dev_get_drvdata(dev); @@ -581,17 +581,16 @@ static void tegra210_amx_platform_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra210_amx_pm_ops = { - SET_RUNTIME_PM_OPS(tegra210_amx_runtime_suspend, - tegra210_amx_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra210_amx_runtime_suspend, + tegra210_amx_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra210_amx_driver = { .driver = { .name = "tegra210-amx", .of_match_table = tegra210_amx_of_match, - .pm = &tegra210_amx_pm_ops, + .pm = pm_ptr(&tegra210_amx_pm_ops), }, .probe = tegra210_amx_platform_probe, .remove = tegra210_amx_platform_remove, diff --git a/sound/soc/tegra/tegra210_dmic.c b/sound/soc/tegra/tegra210_dmic.c index 7986be71f43d..e4a144571265 100644 --- a/sound/soc/tegra/tegra210_dmic.c +++ b/sound/soc/tegra/tegra210_dmic.c @@ -40,7 +40,7 @@ static const struct reg_default tegra210_dmic_reg_defaults[] = { { TEGRA210_DMIC_LP_BIQUAD_1_COEF_4, 0x0 }, }; -static int __maybe_unused tegra210_dmic_runtime_suspend(struct device *dev) +static int tegra210_dmic_runtime_suspend(struct device *dev) { struct tegra210_dmic *dmic = dev_get_drvdata(dev); @@ -52,7 +52,7 @@ static int __maybe_unused tegra210_dmic_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra210_dmic_runtime_resume(struct device *dev) +static int tegra210_dmic_runtime_resume(struct device *dev) { struct tegra210_dmic *dmic = dev_get_drvdata(dev); int err; @@ -543,10 +543,9 @@ static void tegra210_dmic_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra210_dmic_pm_ops = { - SET_RUNTIME_PM_OPS(tegra210_dmic_runtime_suspend, - tegra210_dmic_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra210_dmic_runtime_suspend, + tegra210_dmic_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static const struct of_device_id tegra210_dmic_of_match[] = { @@ -559,7 +558,7 @@ static struct platform_driver tegra210_dmic_driver = { .driver = { .name = "tegra210-dmic", .of_match_table = tegra210_dmic_of_match, - .pm = &tegra210_dmic_pm_ops, + .pm = pm_ptr(&tegra210_dmic_pm_ops), }, .probe = tegra210_dmic_probe, .remove = tegra210_dmic_remove, diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c index 07ce2dbe6c00..766cddebd5f6 100644 --- a/sound/soc/tegra/tegra210_i2s.c +++ b/sound/soc/tegra/tegra210_i2s.c @@ -166,7 +166,7 @@ static int tegra210_i2s_init(struct snd_soc_dapm_widget *w, return tegra210_i2s_sw_reset(compnt, stream); } -static int __maybe_unused tegra210_i2s_runtime_suspend(struct device *dev) +static int tegra210_i2s_runtime_suspend(struct device *dev) { struct tegra210_i2s *i2s = dev_get_drvdata(dev); @@ -178,7 +178,7 @@ static int __maybe_unused tegra210_i2s_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra210_i2s_runtime_resume(struct device *dev) +static int tegra210_i2s_runtime_resume(struct device *dev) { struct tegra210_i2s *i2s = dev_get_drvdata(dev); int err; @@ -1010,10 +1010,9 @@ static void tegra210_i2s_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra210_i2s_pm_ops = { - SET_RUNTIME_PM_OPS(tegra210_i2s_runtime_suspend, - tegra210_i2s_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra210_i2s_runtime_suspend, + tegra210_i2s_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static const struct of_device_id tegra210_i2s_of_match[] = { @@ -1026,7 +1025,7 @@ static struct platform_driver tegra210_i2s_driver = { .driver = { .name = "tegra210-i2s", .of_match_table = tegra210_i2s_of_match, - .pm = &tegra210_i2s_pm_ops, + .pm = pm_ptr(&tegra210_i2s_pm_ops), }, .probe = tegra210_i2s_probe, .remove = tegra210_i2s_remove, diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c index 410259d98dfb..95d69a7e027f 100644 --- a/sound/soc/tegra/tegra210_mixer.c +++ b/sound/soc/tegra/tegra210_mixer.c @@ -73,7 +73,7 @@ static const struct tegra210_mixer_gain_params gain_params = { { 0, 0, 0x400, 0x8000000 }, }; -static int __maybe_unused tegra210_mixer_runtime_suspend(struct device *dev) +static int tegra210_mixer_runtime_suspend(struct device *dev) { struct tegra210_mixer *mixer = dev_get_drvdata(dev); @@ -83,7 +83,7 @@ static int __maybe_unused tegra210_mixer_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra210_mixer_runtime_resume(struct device *dev) +static int tegra210_mixer_runtime_resume(struct device *dev) { struct tegra210_mixer *mixer = dev_get_drvdata(dev); @@ -666,17 +666,16 @@ static void tegra210_mixer_platform_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra210_mixer_pm_ops = { - SET_RUNTIME_PM_OPS(tegra210_mixer_runtime_suspend, - tegra210_mixer_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra210_mixer_runtime_suspend, + tegra210_mixer_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra210_mixer_driver = { .driver = { .name = "tegra210_mixer", .of_match_table = tegra210_mixer_of_match, - .pm = &tegra210_mixer_pm_ops, + .pm = pm_ptr(&tegra210_mixer_pm_ops), }, .probe = tegra210_mixer_platform_probe, .remove = tegra210_mixer_platform_remove, diff --git a/sound/soc/tegra/tegra210_mvc.c b/sound/soc/tegra/tegra210_mvc.c index 119f17501478..35b14c8396f4 100644 --- a/sound/soc/tegra/tegra210_mvc.c +++ b/sound/soc/tegra/tegra210_mvc.c @@ -47,7 +47,7 @@ static const struct tegra210_mvc_gain_params gain_params = { .duration_inv = 14316558, }; -static int __maybe_unused tegra210_mvc_runtime_suspend(struct device *dev) +static int tegra210_mvc_runtime_suspend(struct device *dev) { struct tegra210_mvc *mvc = dev_get_drvdata(dev); @@ -59,7 +59,7 @@ static int __maybe_unused tegra210_mvc_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra210_mvc_runtime_resume(struct device *dev) +static int tegra210_mvc_runtime_resume(struct device *dev) { struct tegra210_mvc *mvc = dev_get_drvdata(dev); @@ -758,17 +758,16 @@ static void tegra210_mvc_platform_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra210_mvc_pm_ops = { - SET_RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend, - tegra210_mvc_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend, + tegra210_mvc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra210_mvc_driver = { .driver = { .name = "tegra210-mvc", .of_match_table = tegra210_mvc_of_match, - .pm = &tegra210_mvc_pm_ops, + .pm = pm_ptr(&tegra210_mvc_pm_ops), }, .probe = tegra210_mvc_platform_probe, .remove = tegra210_mvc_platform_remove, diff --git a/sound/soc/tegra/tegra210_ope.c b/sound/soc/tegra/tegra210_ope.c index c595cec9baab..5036bcfe0828 100644 --- a/sound/soc/tegra/tegra210_ope.c +++ b/sound/soc/tegra/tegra210_ope.c @@ -356,7 +356,7 @@ static void tegra210_ope_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused tegra210_ope_runtime_suspend(struct device *dev) +static int tegra210_ope_runtime_suspend(struct device *dev) { struct tegra210_ope *ope = dev_get_drvdata(dev); @@ -374,7 +374,7 @@ static int __maybe_unused tegra210_ope_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra210_ope_runtime_resume(struct device *dev) +static int tegra210_ope_runtime_resume(struct device *dev) { struct tegra210_ope *ope = dev_get_drvdata(dev); @@ -393,10 +393,9 @@ static int __maybe_unused tegra210_ope_runtime_resume(struct device *dev) } static const struct dev_pm_ops tegra210_ope_pm_ops = { - SET_RUNTIME_PM_OPS(tegra210_ope_runtime_suspend, - tegra210_ope_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra210_ope_runtime_suspend, + tegra210_ope_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static const struct of_device_id tegra210_ope_of_match[] = { @@ -409,7 +408,7 @@ static struct platform_driver tegra210_ope_driver = { .driver = { .name = "tegra210-ope", .of_match_table = tegra210_ope_of_match, - .pm = &tegra210_ope_pm_ops, + .pm = pm_ptr(&tegra210_ope_pm_ops), }, .probe = tegra210_ope_probe, .remove = tegra210_ope_remove, diff --git a/sound/soc/tegra/tegra210_sfc.c b/sound/soc/tegra/tegra210_sfc.c index df88708c733c..a0bd36f12c68 100644 --- a/sound/soc/tegra/tegra210_sfc.c +++ b/sound/soc/tegra/tegra210_sfc.c @@ -3056,7 +3056,7 @@ static s32 *coef_addr_table[TEGRA210_SFC_NUM_RATES][TEGRA210_SFC_NUM_RATES] = { }, }; -static int __maybe_unused tegra210_sfc_runtime_suspend(struct device *dev) +static int tegra210_sfc_runtime_suspend(struct device *dev) { struct tegra210_sfc *sfc = dev_get_drvdata(dev); @@ -3066,7 +3066,7 @@ static int __maybe_unused tegra210_sfc_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused tegra210_sfc_runtime_resume(struct device *dev) +static int tegra210_sfc_runtime_resume(struct device *dev) { struct tegra210_sfc *sfc = dev_get_drvdata(dev); @@ -3623,17 +3623,16 @@ static void tegra210_sfc_platform_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra210_sfc_pm_ops = { - SET_RUNTIME_PM_OPS(tegra210_sfc_runtime_suspend, - tegra210_sfc_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra210_sfc_runtime_suspend, + tegra210_sfc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra210_sfc_driver = { .driver = { .name = "tegra210-sfc", .of_match_table = tegra210_sfc_of_match, - .pm = &tegra210_sfc_pm_ops, + .pm = pm_ptr(&tegra210_sfc_pm_ops), }, .probe = tegra210_sfc_platform_probe, .remove = tegra210_sfc_platform_remove, diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c index c9b52f54cea8..51e5ab6c276b 100644 --- a/sound/soc/tegra/tegra30_ahub.c +++ b/sound/soc/tegra/tegra30_ahub.c @@ -40,7 +40,7 @@ static inline void tegra30_audio_write(u32 reg, u32 val) regmap_write(ahub->regmap_ahub, reg, val); } -static __maybe_unused int tegra30_ahub_runtime_suspend(struct device *dev) +static int tegra30_ahub_runtime_suspend(struct device *dev) { regcache_cache_only(ahub->regmap_apbif, true); regcache_cache_only(ahub->regmap_ahub, true); @@ -61,7 +61,7 @@ static __maybe_unused int tegra30_ahub_runtime_suspend(struct device *dev) * stopping streams should dynamically adjust the clock as required. However, * this is not yet implemented. */ -static __maybe_unused int tegra30_ahub_runtime_resume(struct device *dev) +static int tegra30_ahub_runtime_resume(struct device *dev) { int ret; @@ -600,10 +600,9 @@ static void tegra30_ahub_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra30_ahub_pm_ops = { - SET_RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend, - tegra30_ahub_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra30_ahub_runtime_suspend, + tegra30_ahub_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra30_ahub_driver = { @@ -612,7 +611,7 @@ static struct platform_driver tegra30_ahub_driver = { .driver = { .name = DRV_NAME, .of_match_table = tegra30_ahub_of_match, - .pm = &tegra30_ahub_pm_ops, + .pm = pm_ptr(&tegra30_ahub_pm_ops), }, }; module_platform_driver(tegra30_ahub_driver); diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 0d3badfbe143..b121af9ef8ed 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -35,7 +35,7 @@ #define DRV_NAME "tegra30-i2s" -static __maybe_unused int tegra30_i2s_runtime_suspend(struct device *dev) +static int tegra30_i2s_runtime_suspend(struct device *dev) { struct tegra30_i2s *i2s = dev_get_drvdata(dev); @@ -46,7 +46,7 @@ static __maybe_unused int tegra30_i2s_runtime_suspend(struct device *dev) return 0; } -static __maybe_unused int tegra30_i2s_runtime_resume(struct device *dev) +static int tegra30_i2s_runtime_resume(struct device *dev) { struct tegra30_i2s *i2s = dev_get_drvdata(dev); int ret; @@ -547,17 +547,16 @@ static void tegra30_i2s_platform_remove(struct platform_device *pdev) } static const struct dev_pm_ops tegra30_i2s_pm_ops = { - SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend, - tegra30_i2s_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend, + tegra30_i2s_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver tegra30_i2s_driver = { .driver = { .name = DRV_NAME, .of_match_table = tegra30_i2s_of_match, - .pm = &tegra30_i2s_pm_ops, + .pm = pm_ptr(&tegra30_i2s_pm_ops), }, .probe = tegra30_i2s_platform_probe, .remove = tegra30_i2s_platform_remove, diff --git a/sound/soc/tegra/tegra_asoc_machine.c b/sound/soc/tegra/tegra_asoc_machine.c index 775ce433fdbf..62f896772731 100644 --- a/sound/soc/tegra/tegra_asoc_machine.c +++ b/sound/soc/tegra/tegra_asoc_machine.c @@ -637,7 +637,7 @@ static struct snd_soc_dai_link tegra_wm8753_dai = { .stream_name = "WM8753 PCM", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(wm8753_hifi), }; @@ -701,7 +701,7 @@ static struct snd_soc_dai_link tegra_max98090_dai = { .init = tegra_asoc_machine_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(max98090_hifi), }; @@ -736,7 +736,7 @@ static struct snd_soc_dai_link tegra_max98088_dai = { .init = tegra_asoc_machine_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(max98088_hifi), }; @@ -769,7 +769,7 @@ static struct snd_soc_dai_link tegra_sgtl5000_dai = { .stream_name = "HiFi", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(sgtl5000_hifi), }; @@ -812,7 +812,7 @@ static struct snd_soc_dai_link tegra_tlv320aic23_dai = { .stream_name = "AIC23", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(tlv320aic23_hifi), }; @@ -861,7 +861,7 @@ static struct snd_soc_dai_link tegra_rt5677_dai = { .init = tegra_rt5677_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(rt5677_aif1), }; @@ -895,7 +895,7 @@ static struct snd_soc_dai_link tegra_rt5640_dai = { .init = tegra_asoc_machine_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(rt5640_aif1), }; @@ -928,7 +928,7 @@ static struct snd_soc_dai_link tegra_rt5632_dai = { .init = tegra_rt5677_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(rt5632_hifi), }; @@ -961,7 +961,7 @@ static struct snd_soc_dai_link tegra_rt5631_dai = { .init = tegra_asoc_machine_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(rt5631_hifi), }; diff --git a/sound/soc/tegra/tegra_isomgr_bw.c b/sound/soc/tegra/tegra_isomgr_bw.c new file mode 100644 index 000000000000..18e802bca6a6 --- /dev/null +++ b/sound/soc/tegra/tegra_isomgr_bw.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +// All rights reserved. +// +// ADMA bandwidth calculation + +#include <linux/interconnect.h> +#include <linux/module.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "tegra_isomgr_bw.h" +#include "tegra210_admaif.h" + +/* Max possible rate is 192KHz x 16channel x 4bytes */ +#define MAX_BW_PER_DEV 12288 + +int tegra_isomgr_adma_setbw(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, bool is_running) +{ + struct device *dev = dai->dev; + struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); + struct tegra_adma_isomgr *adma_isomgr = admaif->adma_isomgr; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm *pcm = substream->pcm; + u32 type = substream->stream, bandwidth = 0; + int sample_bytes; + + if (!adma_isomgr) + return 0; + + if (!runtime || !pcm) + return -EINVAL; + + if (pcm->device >= adma_isomgr->max_pcm_device) { + dev_err(dev, "%s: PCM device number %d is greater than %d\n", __func__, + pcm->device, adma_isomgr->max_pcm_device); + return -EINVAL; + } + + /* + * No action if stream is running and bandwidth is already set or + * stream is not running and bandwidth is already reset + */ + if ((adma_isomgr->bw_per_dev[type][pcm->device] && is_running) || + (!adma_isomgr->bw_per_dev[type][pcm->device] && !is_running)) + return 0; + + if (is_running) { + sample_bytes = snd_pcm_format_width(runtime->format) / 8; + if (sample_bytes < 0) + return sample_bytes; + + /* KB/s kilo bytes per sec */ + bandwidth = runtime->channels * (runtime->rate / 1000) * + sample_bytes; + } + + mutex_lock(&adma_isomgr->mutex); + + if (is_running) { + if (bandwidth + adma_isomgr->current_bandwidth > adma_isomgr->max_bw) + bandwidth = adma_isomgr->max_bw - adma_isomgr->current_bandwidth; + + adma_isomgr->current_bandwidth += bandwidth; + } else { + adma_isomgr->current_bandwidth -= adma_isomgr->bw_per_dev[type][pcm->device]; + } + + mutex_unlock(&adma_isomgr->mutex); + + adma_isomgr->bw_per_dev[type][pcm->device] = bandwidth; + + dev_dbg(dev, "Setting up bandwidth to %d KBps\n", adma_isomgr->current_bandwidth); + + return icc_set_bw(adma_isomgr->icc_path_handle, + adma_isomgr->current_bandwidth, adma_isomgr->max_bw); +} + +int tegra_isomgr_adma_register(struct device *dev) +{ + struct tegra_admaif *admaif = dev_get_drvdata(dev); + struct tegra_adma_isomgr *adma_isomgr; + int i; + + adma_isomgr = devm_kzalloc(dev, sizeof(struct tegra_adma_isomgr), GFP_KERNEL); + if (!adma_isomgr) + return -ENOMEM; + + adma_isomgr->icc_path_handle = devm_of_icc_get(dev, "write"); + if (IS_ERR(adma_isomgr->icc_path_handle)) + return dev_err_probe(dev, PTR_ERR(adma_isomgr->icc_path_handle), + "failed to acquire interconnect path\n"); + + /* Either INTERCONNECT config OR interconnect property is not defined */ + if (!adma_isomgr->icc_path_handle) { + devm_kfree(dev, adma_isomgr); + return 0; + } + + adma_isomgr->max_pcm_device = admaif->soc_data->num_ch; + adma_isomgr->max_bw = STREAM_TYPE * MAX_BW_PER_DEV * adma_isomgr->max_pcm_device; + + for (i = 0; i < STREAM_TYPE; i++) { + adma_isomgr->bw_per_dev[i] = devm_kzalloc(dev, adma_isomgr->max_pcm_device * + sizeof(u32), GFP_KERNEL); + if (!adma_isomgr->bw_per_dev[i]) + return -ENOMEM; + } + + adma_isomgr->current_bandwidth = 0; + mutex_init(&adma_isomgr->mutex); + admaif->adma_isomgr = adma_isomgr; + + return 0; +} + +void tegra_isomgr_adma_unregister(struct device *dev) +{ + struct tegra_admaif *admaif = dev_get_drvdata(dev); + + if (!admaif->adma_isomgr) + return; + + mutex_destroy(&admaif->adma_isomgr->mutex); +} + +MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>"); +MODULE_DESCRIPTION("Tegra ADMA Bandwidth Request driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra_isomgr_bw.h b/sound/soc/tegra/tegra_isomgr_bw.h new file mode 100644 index 000000000000..86db3cfd4e43 --- /dev/null +++ b/sound/soc/tegra/tegra_isomgr_bw.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. + * All rights reserved. + * + * tegra_isomgr_bw.h - Definitions for ADMA bandwidth calculation + * + */ + +#ifndef __TEGRA_ISOMGR_BW_H__ +#define __TEGRA_ISOMGR_BW_H__ + +/* Playback and Capture streams */ +#define STREAM_TYPE 2 + +struct tegra_adma_isomgr { + /* Protect pcm devices bandwidth */ + struct mutex mutex; + /* interconnect path handle */ + struct icc_path *icc_path_handle; + u32 *bw_per_dev[STREAM_TYPE]; + u32 current_bandwidth; + u32 max_pcm_device; + u32 max_bw; +}; + +int tegra_isomgr_adma_register(struct device *dev); +void tegra_isomgr_adma_unregister(struct device *dev); +int tegra_isomgr_adma_setbw(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, bool is_running); + +#endif diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index 6116d2e30fca..78b02c64d95c 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -124,7 +124,7 @@ static struct snd_soc_dai_link tegra_wm8903_dai = { .init = tegra_wm8903_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, SND_SOC_DAILINK_REG(hifi), }; diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c index 94645f275495..8a4423646aea 100644 --- a/sound/soc/ti/ams-delta.c +++ b/sound/soc/ti/ams-delta.c @@ -532,7 +532,7 @@ static struct snd_soc_dai_link ams_delta_dai_link = { .init = ams_delta_cx20442_init, .ops = &ams_delta_ops, .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, SND_SOC_DAILINK_REG(cx20442), }; diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c index 1bf333d2740d..2a2f5bc95576 100644 --- a/sound/soc/ti/davinci-evm.c +++ b/sound/soc/ti/davinci-evm.c @@ -152,7 +152,7 @@ static struct snd_soc_dai_link evm_dai_tlv320aic3x = { .stream_name = "AIC3X", .ops = &evm_ops, .init = evm_aic3x_init, - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBP_CFP | SND_SOC_DAIFMT_IB_NF, SND_SOC_DAILINK_REG(evm), }; diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c index d682b98d6848..059967f0e632 100644 --- a/sound/soc/ti/davinci-i2s.c +++ b/sound/soc/ti/davinci-i2s.c @@ -892,8 +892,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) err_unregister_component: snd_soc_unregister_component(&pdev->dev); err_disable_ext_clk: - if (dev->ext_clk) - clk_disable_unprepare(dev->ext_clk); + clk_disable_unprepare(dev->ext_clk); err_disable_clk: clk_disable_unprepare(dev->clk); @@ -908,8 +907,7 @@ static void davinci_i2s_remove(struct platform_device *pdev) clk_disable_unprepare(dev->clk); - if (dev->ext_clk) - clk_disable_unprepare(dev->ext_clk); + clk_disable_unprepare(dev->ext_clk); } static const struct of_device_id davinci_i2s_match[] __maybe_unused = { diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index d9d1e021f5b2..0e7e4ff950b5 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -37,7 +37,7 @@ enum j721e_audio_domain_id { #define J721E_DAI_FMT (SND_SOC_DAIFMT_RIGHT_J | \ SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBS_CFS) + SND_SOC_DAIFMT_CBC_CFC) enum j721e_board_type { J721E_BOARD_CPB = 1, @@ -182,6 +182,8 @@ static int j721e_configure_refclk(struct j721e_priv *priv, clk_id = J721E_CLK_PARENT_48000; else if (!(rate % 11025) && priv->pll_rates[J721E_CLK_PARENT_44100]) clk_id = J721E_CLK_PARENT_44100; + else if (!(rate % 11025) && priv->pll_rates[J721E_CLK_PARENT_48000]) + clk_id = J721E_CLK_PARENT_48000; else return ret; @@ -913,8 +915,9 @@ static int j721e_soc_probe(struct platform_device *pdev) mutex_init(&priv->mutex); ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) - dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", - ret); + dev_err_probe(&pdev->dev, ret, + "devm_snd_soc_register_card() failed: %d\n", + ret); return ret; } diff --git a/sound/soc/ti/n810.c b/sound/soc/ti/n810.c index 50a8ec97cf20..345c98765380 100644 --- a/sound/soc/ti/n810.c +++ b/sound/soc/ti/n810.c @@ -258,7 +258,7 @@ static struct snd_soc_dai_link n810_dai = { .name = "TLV320AIC33", .stream_name = "AIC33", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .ops = &n810_ops, SND_SOC_DAILINK_REG(aic33), }; diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c index a402d66e4f4d..3548b58002c4 100644 --- a/sound/soc/ti/omap-twl4030.c +++ b/sound/soc/ti/omap-twl4030.c @@ -42,12 +42,12 @@ static int omap_twl4030_hw_params(struct snd_pcm_substream *substream, case 2: /* Stereo I2S mode */ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; + SND_SOC_DAIFMT_CBP_CFP; break; case 4: /* Four channel TDM mode */ fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBM_CFM; + SND_SOC_DAIFMT_CBP_CFP; break; default: return -EINVAL; @@ -218,7 +218,7 @@ static struct snd_soc_dai_link omap_twl4030_dai_links[] = { .name = "TWL4030 Voice", .stream_name = "TWL4030 Voice", .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, SND_SOC_DAILINK_REG(voice), }, }; diff --git a/sound/soc/ti/omap3pandora.c b/sound/soc/ti/omap3pandora.c index be69476e59d6..808fb6765c05 100644 --- a/sound/soc/ti/omap3pandora.c +++ b/sound/soc/ti/omap3pandora.c @@ -189,7 +189,7 @@ static struct snd_soc_dai_link omap3pandora_dai[] = { .name = "PCM1773", .stream_name = "HiFi Out", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ops = &omap3pandora_ops, .init = omap3pandora_out_init, SND_SOC_DAILINK_REG(out), @@ -197,7 +197,7 @@ static struct snd_soc_dai_link omap3pandora_dai[] = { .name = "TWL4030", .stream_name = "Line/Mic In", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ops = &omap3pandora_ops, .init = omap3pandora_in_init, SND_SOC_DAILINK_REG(in), diff --git a/sound/soc/ti/osk5912.c b/sound/soc/ti/osk5912.c index 98714c593496..fa5ef7814dab 100644 --- a/sound/soc/ti/osk5912.c +++ b/sound/soc/ti/osk5912.c @@ -86,7 +86,7 @@ static struct snd_soc_dai_link osk_dai = { .name = "TLV320AIC23", .stream_name = "AIC23", .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .ops = &osk_ops, SND_SOC_DAILINK_REG(aic23), }; diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c index d9900c69e536..e969031657e9 100644 --- a/sound/soc/ti/rx51.c +++ b/sound/soc/ti/rx51.c @@ -307,7 +307,7 @@ static struct snd_soc_dai_link rx51_dai[] = { .name = "TLV320AIC34", .stream_name = "AIC34", .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBM_CFM, + SND_SOC_DAIFMT_CBP_CFP, .init = rx51_aic34_init, .ops = &rx51_ops, SND_SOC_DAILINK_REG(aic34), diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index 470f129166a4..3224c11a527f 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -762,14 +762,10 @@ int uniphier_aio_probe(struct platform_device *pdev) return -ENOMEM; chip->num_plls = chip->chip_spec->num_plls; - chip->plls = devm_kcalloc(dev, - chip->num_plls, - sizeof(struct uniphier_aio_pll), - GFP_KERNEL); + chip->plls = devm_kmemdup_array(dev, chip->chip_spec->plls, chip->num_plls, + sizeof(*chip->chip_spec->plls), GFP_KERNEL); if (!chip->plls) return -ENOMEM; - memcpy(chip->plls, chip->chip_spec->plls, - sizeof(struct uniphier_aio_pll) * chip->num_plls); for (i = 0; i < chip->num_aios; i++) { struct uniphier_aio *aio = &chip->aios[i]; diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 710b6744e013..5246f73ef12e 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -275,12 +275,12 @@ static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream, if (driver_mode == DRIVERMODE_NORMAL) { fmt = SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_CBP_CFP | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CONT; } else { fmt = SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_CBP_CFP | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_GATED; } diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c index 7febb3830dc2..017a64ab9f1e 100644 --- a/sound/soc/xilinx/xlnx_spdif.c +++ b/sound/soc/xilinx/xlnx_spdif.c @@ -248,41 +248,35 @@ static int xlnx_spdif_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; - ctx->axi_clk = devm_clk_get(dev, "s_axi_aclk"); + ctx->axi_clk = devm_clk_get_enabled(dev, "s_axi_aclk"); if (IS_ERR(ctx->axi_clk)) { ret = PTR_ERR(ctx->axi_clk); dev_err(dev, "failed to get s_axi_aclk(%d)\n", ret); return ret; } - ret = clk_prepare_enable(ctx->axi_clk); - if (ret) { - dev_err(dev, "failed to enable s_axi_aclk(%d)\n", ret); - return ret; - } ctx->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(ctx->base)) { - ret = PTR_ERR(ctx->base); - goto clk_err; - } + if (IS_ERR(ctx->base)) + return PTR_ERR(ctx->base); + ret = of_property_read_u32(node, "xlnx,spdif-mode", &ctx->mode); if (ret < 0) { dev_err(dev, "cannot get SPDIF mode\n"); - goto clk_err; + return ret; } if (ctx->mode) { dai_drv = &xlnx_spdif_tx_dai; } else { ret = platform_get_irq(pdev, 0); if (ret < 0) - goto clk_err; + return ret; + ret = devm_request_irq(dev, ret, xlnx_spdifrx_irq_handler, 0, "XLNX_SPDIF_RX", ctx); if (ret) { dev_err(dev, "spdif rx irq request failed\n"); - ret = -ENODEV; - goto clk_err; + return -ENODEV; } init_waitqueue_head(&ctx->chsts_q); @@ -292,7 +286,7 @@ static int xlnx_spdif_probe(struct platform_device *pdev) ret = of_property_read_u32(node, "xlnx,aud_clk_i", &ctx->aclk); if (ret < 0) { dev_err(dev, "cannot get aud_clk_i value\n"); - goto clk_err; + return ret; } dev_set_drvdata(dev, ctx); @@ -301,22 +295,13 @@ static int xlnx_spdif_probe(struct platform_device *pdev) dai_drv, 1); if (ret) { dev_err(dev, "SPDIF component registration failed\n"); - goto clk_err; + return ret; } writel(XSPDIF_SOFT_RESET_VALUE, ctx->base + XSPDIF_SOFT_RESET_REG); dev_info(dev, "%s DAI registered\n", dai_drv->name); -clk_err: - clk_disable_unprepare(ctx->axi_clk); - return ret; -} - -static void xlnx_spdif_remove(struct platform_device *pdev) -{ - struct spdif_dev_data *ctx = dev_get_drvdata(&pdev->dev); - - clk_disable_unprepare(ctx->axi_clk); + return 0; } static struct platform_driver xlnx_spdif_driver = { @@ -325,7 +310,6 @@ static struct platform_driver xlnx_spdif_driver = { .of_match_table = xlnx_spdif_of_match, }, .probe = xlnx_spdif_probe, - .remove = xlnx_spdif_remove, }; module_platform_driver(xlnx_spdif_driver); diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c index 4eaa9011405f..678ded059b95 100644 --- a/sound/soc/xtensa/xtfpga-i2s.c +++ b/sound/soc/xtensa/xtfpga-i2s.c @@ -629,8 +629,8 @@ MODULE_DEVICE_TABLE(of, xtfpga_i2s_of_match); #endif static const struct dev_pm_ops xtfpga_i2s_pm_ops = { - SET_RUNTIME_PM_OPS(xtfpga_i2s_runtime_suspend, - xtfpga_i2s_runtime_resume, NULL) + RUNTIME_PM_OPS(xtfpga_i2s_runtime_suspend, + xtfpga_i2s_runtime_resume, NULL) }; static struct platform_driver xtfpga_i2s_driver = { @@ -639,7 +639,7 @@ static struct platform_driver xtfpga_i2s_driver = { .driver = { .name = "xtfpga-i2s", .of_match_table = of_match_ptr(xtfpga_i2s_of_match), - .pm = &xtfpga_i2s_pm_ops, + .pm = pm_ptr(&xtfpga_i2s_pm_ops), }, }; diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 0532499dbc6d..30bd5348477b 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -6,6 +6,7 @@ snd-usb-audio-y := card.o \ clock.o \ endpoint.o \ + fcp.o \ format.o \ helper.o \ implicit.o \ diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c new file mode 100644 index 000000000000..7df65041ace5 --- /dev/null +++ b/sound/usb/fcp.c @@ -0,0 +1,1134 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Focusrite Control Protocol Driver for ALSA + * + * Copyright (c) 2024-2025 by Geoffrey D. Bennett <g at b4.vu> + */ +/* + * DOC: Theory of Operation + * + * The Focusrite Control Protocol (FCP) driver provides a minimal + * kernel interface that allows a user-space driver (primarily + * fcp-server) to communicate with Focusrite USB audio interfaces + * using their vendor-specific protocol. This protocol is used by + * Scarlett 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, Clarett+, and + * Vocaster series devices. + * + * Unlike the existing scarlett2 driver which implements all controls + * in kernel space, this driver takes a lighter-weight approach by + * moving most functionality to user space. The only control + * implemented in kernel space is the Level Meter, since it requires + * frequent polling of volatile data. + * + * The driver provides an hwdep interface that allows the user-space + * driver to: + * - Initialise the protocol + * - Send arbitrary FCP commands to the device + * - Receive notifications from the device + * - Configure the Level Meter control + * + * Usage Flow + * ---------- + * 1. Open the hwdep device (requires CAP_SYS_RAWIO) + * 2. Get protocol version using FCP_IOCTL_PVERSION + * 3. Initialise protocol using FCP_IOCTL_INIT + * 4. Send commands using FCP_IOCTL_CMD + * 5. Receive notifications using read() + * 6. Optionally set up the Level Meter control using + * FCP_IOCTL_SET_METER_MAP + * 7. Optionally add labels to the Level Meter control using + * FCP_IOCTL_SET_METER_LABELS + * + * Level Meter + * ----------- + * The Level Meter is implemented as an ALSA control that provides + * real-time level monitoring. When the control is read, the driver + * requests the current meter levels from the device, translates the + * levels using the configured mapping, and returns the result to the + * user. The mapping between device meters and the ALSA control's + * channels is configured with FCP_IOCTL_SET_METER_MAP. + * + * Labels for the Level Meter channels can be set using + * FCP_IOCTL_SET_METER_LABELS and read by applications through the + * control's TLV data. The labels are transferred as a sequence of + * null-terminated strings. + */ + +#include <linux/slab.h> +#include <linux/usb.h> + +#include <sound/control.h> +#include <sound/hwdep.h> +#include <sound/tlv.h> + +#include <uapi/sound/fcp.h> + +#include "usbaudio.h" +#include "mixer.h" +#include "helper.h" + +#include "fcp.h" + +/* notify waiting to send to *file */ +struct fcp_notify { + wait_queue_head_t queue; + u32 event; + spinlock_t lock; +}; + +struct fcp_data { + struct usb_mixer_interface *mixer; + + struct mutex mutex; /* serialise access to the device */ + struct completion cmd_done; /* wait for command completion */ + struct file *file; /* hwdep file */ + + struct fcp_notify notify; + + u8 bInterfaceNumber; + u8 bEndpointAddress; + u16 wMaxPacketSize; + u8 bInterval; + + uint16_t step0_resp_size; + uint16_t step2_resp_size; + uint32_t init1_opcode; + uint32_t init2_opcode; + + u8 init; + u16 seq; + + u8 num_meter_slots; + s16 *meter_level_map; + __le32 *meter_levels; + struct snd_kcontrol *meter_ctl; + + unsigned int *meter_labels_tlv; + int meter_labels_tlv_size; +}; + +/*** USB Interactions ***/ + +/* FCP Command ACK notification bit */ +#define FCP_NOTIFY_ACK 1 + +/* Vendor-specific USB control requests */ +#define FCP_USB_REQ_STEP0 0 +#define FCP_USB_REQ_CMD_TX 2 +#define FCP_USB_REQ_CMD_RX 3 + +/* Focusrite Control Protocol opcodes that the kernel side needs to + * know about + */ +#define FCP_USB_REBOOT 0x00000003 +#define FCP_USB_GET_METER 0x00001001 +#define FCP_USB_FLASH_ERASE 0x00004002 +#define FCP_USB_FLASH_WRITE 0x00004004 + +#define FCP_USB_METER_LEVELS_GET_MAGIC 1 + +#define FCP_SEGMENT_APP_GOLD 0 + +/* Forward declarations */ +static int fcp_init(struct usb_mixer_interface *mixer, + void *step0_resp, void *step2_resp); + +/* FCP command request/response format */ +struct fcp_usb_packet { + __le32 opcode; + __le16 size; + __le16 seq; + __le32 error; + __le32 pad; + u8 data[]; +}; + +static void fcp_fill_request_header(struct fcp_data *private, + struct fcp_usb_packet *req, + u32 opcode, u16 req_size) +{ + /* sequence must go up by 1 for each request */ + u16 seq = private->seq++; + + req->opcode = cpu_to_le32(opcode); + req->size = cpu_to_le16(req_size); + req->seq = cpu_to_le16(seq); + req->error = 0; + req->pad = 0; +} + +static int fcp_usb_tx(struct usb_device *dev, int interface, + void *buf, u16 size) +{ + return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), + FCP_USB_REQ_CMD_TX, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + 0, interface, buf, size); +} + +static int fcp_usb_rx(struct usb_device *dev, int interface, + void *buf, u16 size) +{ + return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), + FCP_USB_REQ_CMD_RX, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + 0, interface, buf, size); +} + +/* Send an FCP command and get the response */ +static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode, + const void *req_data, u16 req_size, + void *resp_data, u16 resp_size) +{ + struct fcp_data *private = mixer->private_data; + struct usb_device *dev = mixer->chip->dev; + struct fcp_usb_packet *req __free(kfree) = NULL; + struct fcp_usb_packet *resp __free(kfree) = NULL; + size_t req_buf_size = struct_size(req, data, req_size); + size_t resp_buf_size = struct_size(resp, data, resp_size); + int retries = 0; + const int max_retries = 5; + int err; + + if (!mixer->urb) + return -ENODEV; + + req = kmalloc(req_buf_size, GFP_KERNEL); + if (!req) + return -ENOMEM; + + resp = kmalloc(resp_buf_size, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + /* build request message */ + fcp_fill_request_header(private, req, opcode, req_size); + if (req_size) + memcpy(req->data, req_data, req_size); + + /* send the request and retry on EPROTO */ +retry: + err = fcp_usb_tx(dev, private->bInterfaceNumber, req, req_buf_size); + if (err == -EPROTO && ++retries <= max_retries) { + msleep(1 << (retries - 1)); + goto retry; + } + + if (err != req_buf_size) { + usb_audio_err(mixer->chip, + "FCP request %08x failed: %d\n", opcode, err); + return -EINVAL; + } + + if (!wait_for_completion_timeout(&private->cmd_done, + msecs_to_jiffies(1000))) { + usb_audio_err(mixer->chip, + "FCP request %08x timed out\n", opcode); + + return -ETIMEDOUT; + } + + /* send a second message to get the response */ + err = fcp_usb_rx(dev, private->bInterfaceNumber, resp, resp_buf_size); + + /* validate the response */ + + if (err < 0) { + + /* ESHUTDOWN and EPROTO are valid responses to a + * reboot request + */ + if (opcode == FCP_USB_REBOOT && + (err == -ESHUTDOWN || err == -EPROTO)) + return 0; + + usb_audio_err(mixer->chip, + "FCP read response %08x failed: %d\n", + opcode, err); + return -EINVAL; + } + + if (err < sizeof(*resp)) { + usb_audio_err(mixer->chip, + "FCP response %08x too short: %d\n", + opcode, err); + return -EINVAL; + } + + if (req->seq != resp->seq) { + usb_audio_err(mixer->chip, + "FCP response %08x seq mismatch %d/%d\n", + opcode, + le16_to_cpu(req->seq), le16_to_cpu(resp->seq)); + return -EINVAL; + } + + if (req->opcode != resp->opcode) { + usb_audio_err(mixer->chip, + "FCP response %08x opcode mismatch %08x\n", + opcode, le32_to_cpu(resp->opcode)); + return -EINVAL; + } + + if (resp->error) { + usb_audio_err(mixer->chip, + "FCP response %08x error %d\n", + opcode, le32_to_cpu(resp->error)); + return -EINVAL; + } + + if (err != resp_buf_size) { + usb_audio_err(mixer->chip, + "FCP response %08x buffer size mismatch %d/%zu\n", + opcode, err, resp_buf_size); + return -EINVAL; + } + + if (resp_size != le16_to_cpu(resp->size)) { + usb_audio_err(mixer->chip, + "FCP response %08x size mismatch %d/%d\n", + opcode, resp_size, le16_to_cpu(resp->size)); + return -EINVAL; + } + + if (resp_data && resp_size > 0) + memcpy(resp_data, resp->data, resp_size); + + return 0; +} + +static int fcp_reinit(struct usb_mixer_interface *mixer) +{ + struct fcp_data *private = mixer->private_data; + void *step0_resp __free(kfree) = NULL; + void *step2_resp __free(kfree) = NULL; + + if (mixer->urb) + return 0; + + step0_resp = kmalloc(private->step0_resp_size, GFP_KERNEL); + if (!step0_resp) + return -ENOMEM; + step2_resp = kmalloc(private->step2_resp_size, GFP_KERNEL); + if (!step2_resp) + return -ENOMEM; + + return fcp_init(mixer, step0_resp, step2_resp); +} + +/*** Control Functions ***/ + +/* helper function to create a new control */ +static int fcp_add_new_ctl(struct usb_mixer_interface *mixer, + const struct snd_kcontrol_new *ncontrol, + int index, int channels, const char *name, + struct snd_kcontrol **kctl_return) +{ + struct snd_kcontrol *kctl; + struct usb_mixer_elem_info *elem; + int err; + + elem = kzalloc(sizeof(*elem), GFP_KERNEL); + if (!elem) + return -ENOMEM; + + /* We set USB_MIXER_BESPOKEN type, so that the core USB mixer code + * ignores them for resume and other operations. + * Also, the head.id field is set to 0, as we don't use this field. + */ + elem->head.mixer = mixer; + elem->control = index; + elem->head.id = 0; + elem->channels = channels; + elem->val_type = USB_MIXER_BESPOKEN; + + kctl = snd_ctl_new1(ncontrol, elem); + if (!kctl) { + kfree(elem); + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; + + strscpy(kctl->id.name, name, sizeof(kctl->id.name)); + + err = snd_usb_mixer_add_control(&elem->head, kctl); + if (err < 0) + return err; + + if (kctl_return) + *kctl_return = kctl; + + return 0; +} + +/*** Level Meter Control ***/ + +static int fcp_meter_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = elem->channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 4095; + uinfo->value.integer.step = 1; + return 0; +} + +static int fcp_meter_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct fcp_data *private = mixer->private_data; + int num_meter_slots, resp_size; + __le32 *resp = private->meter_levels; + int i, err = 0; + + struct { + __le16 pad; + __le16 num_meters; + __le32 magic; + } __packed req; + + guard(mutex)(&private->mutex); + + err = fcp_reinit(mixer); + if (err < 0) + return err; + + num_meter_slots = private->num_meter_slots; + resp_size = num_meter_slots * sizeof(u32); + + req.pad = 0; + req.num_meters = cpu_to_le16(num_meter_slots); + req.magic = cpu_to_le32(FCP_USB_METER_LEVELS_GET_MAGIC); + err = fcp_usb(mixer, FCP_USB_GET_METER, + &req, sizeof(req), resp, resp_size); + if (err < 0) + return err; + + /* copy & translate from resp[] using meter_level_map[] */ + for (i = 0; i < elem->channels; i++) { + int idx = private->meter_level_map[i]; + int value = idx < 0 ? 0 : le32_to_cpu(resp[idx]); + + ucontrol->value.integer.value[i] = value; + } + + return 0; +} + +static int fcp_meter_tlv_callback(struct snd_kcontrol *kctl, + int op_flag, unsigned int size, + unsigned int __user *tlv) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct usb_mixer_interface *mixer = elem->head.mixer; + struct fcp_data *private = mixer->private_data; + + guard(mutex)(&private->mutex); + + if (op_flag == SNDRV_CTL_TLV_OP_READ) { + if (private->meter_labels_tlv_size == 0) + return 0; + + if (size > private->meter_labels_tlv_size) + size = private->meter_labels_tlv_size; + + if (copy_to_user(tlv, private->meter_labels_tlv, size)) + return -EFAULT; + + return size; + } + + return -EINVAL; +} + +static const struct snd_kcontrol_new fcp_meter_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fcp_meter_ctl_info, + .get = fcp_meter_ctl_get, + .tlv = { .c = fcp_meter_tlv_callback }, +}; + +/*** hwdep interface ***/ + +/* FCP initialisation */ +static int fcp_ioctl_init(struct usb_mixer_interface *mixer, + struct fcp_init __user *arg) +{ + struct fcp_init init; + struct usb_device *dev = mixer->chip->dev; + struct fcp_data *private = mixer->private_data; + void *resp __free(kfree) = NULL; + void *step2_resp; + int err, buf_size; + + if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0))) + return -EINVAL; + + /* Get initialisation parameters */ + if (copy_from_user(&init, arg, sizeof(init))) + return -EFAULT; + + /* Validate the response sizes */ + if (init.step0_resp_size < 1 || + init.step0_resp_size > 255 || + init.step2_resp_size < 1 || + init.step2_resp_size > 255) + return -EINVAL; + + /* Allocate response buffer */ + buf_size = init.step0_resp_size + init.step2_resp_size; + + resp = kmalloc(buf_size, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + private->step0_resp_size = init.step0_resp_size; + private->step2_resp_size = init.step2_resp_size; + private->init1_opcode = init.init1_opcode; + private->init2_opcode = init.init2_opcode; + + step2_resp = resp + private->step0_resp_size; + + err = fcp_init(mixer, resp, step2_resp); + if (err < 0) + return err; + + if (copy_to_user(arg->resp, resp, buf_size)) + return -EFAULT; + + return 0; +} + +/* Check that the command is allowed + * Don't permit erasing/writing segment 0 (App_Gold) + */ +static int fcp_validate_cmd(u32 opcode, void *data, u16 size) +{ + if (opcode == FCP_USB_FLASH_ERASE) { + struct { + __le32 segment_num; + __le32 pad; + } __packed *req = data; + + if (size != sizeof(*req)) + return -EINVAL; + + if (le32_to_cpu(req->segment_num) == FCP_SEGMENT_APP_GOLD) + return -EPERM; + + if (req->pad != 0) + return -EINVAL; + + } else if (opcode == FCP_USB_FLASH_WRITE) { + struct { + __le32 segment_num; + __le32 offset; + __le32 pad; + u8 data[]; + } __packed *req = data; + + if (size < sizeof(*req)) + return -EINVAL; + + if (le32_to_cpu(req->segment_num) == FCP_SEGMENT_APP_GOLD) + return -EPERM; + + if (req->pad != 0) + return -EINVAL; + } + + return 0; +} + +/* Execute an FCP command specified by the user */ +static int fcp_ioctl_cmd(struct usb_mixer_interface *mixer, + struct fcp_cmd __user *arg) +{ + struct fcp_cmd cmd; + int err, buf_size; + void *data __free(kfree) = NULL; + + /* get opcode and request/response size */ + if (copy_from_user(&cmd, arg, sizeof(cmd))) + return -EFAULT; + + /* validate request and response sizes */ + if (cmd.req_size > 4096 || cmd.resp_size > 4096) + return -EINVAL; + + /* reinit if needed */ + err = fcp_reinit(mixer); + if (err < 0) + return err; + + /* allocate request/response buffer */ + buf_size = max(cmd.req_size, cmd.resp_size); + + if (buf_size > 0) { + data = kmalloc(buf_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + } + + /* copy request from user */ + if (cmd.req_size > 0) + if (copy_from_user(data, arg->data, cmd.req_size)) + return -EFAULT; + + /* check that the command is allowed */ + err = fcp_validate_cmd(cmd.opcode, data, cmd.req_size); + if (err < 0) + return err; + + /* send request, get response */ + err = fcp_usb(mixer, cmd.opcode, + data, cmd.req_size, data, cmd.resp_size); + if (err < 0) + return err; + + /* copy response to user */ + if (cmd.resp_size > 0) + if (copy_to_user(arg->data, data, cmd.resp_size)) + return -EFAULT; + + return 0; +} + +/* Validate the Level Meter map passed by the user */ +static int validate_meter_map(const s16 *map, int map_size, int meter_slots) +{ + int i; + + for (i = 0; i < map_size; i++) + if (map[i] < -1 || map[i] >= meter_slots) + return -EINVAL; + + return 0; +} + +/* Set the Level Meter map and add the control */ +static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer, + struct fcp_meter_map __user *arg) +{ + struct fcp_meter_map map; + struct fcp_data *private = mixer->private_data; + s16 *tmp_map __free(kfree) = NULL; + int err; + + if (copy_from_user(&map, arg, sizeof(map))) + return -EFAULT; + + /* Don't allow changing the map size or meter slots once set */ + if (private->meter_ctl) { + struct usb_mixer_elem_info *elem = + private->meter_ctl->private_data; + + if (map.map_size != elem->channels || + map.meter_slots != private->num_meter_slots) + return -EINVAL; + } + + /* Validate the map size */ + if (map.map_size < 1 || map.map_size > 255 || + map.meter_slots < 1 || map.meter_slots > 255) + return -EINVAL; + + /* Allocate and copy the map data */ + tmp_map = kmalloc_array(map.map_size, sizeof(s16), GFP_KERNEL); + if (!tmp_map) + return -ENOMEM; + + if (copy_from_user(tmp_map, arg->map, map.map_size * sizeof(s16))) + return -EFAULT; + + err = validate_meter_map(tmp_map, map.map_size, map.meter_slots); + if (err < 0) + return err; + + /* If the control doesn't exist, create it */ + if (!private->meter_ctl) { + s16 *new_map __free(kfree) = NULL; + __le32 *meter_levels __free(kfree) = NULL; + + /* Allocate buffer for the map */ + new_map = kmalloc_array(map.map_size, sizeof(s16), GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + /* Allocate buffer for reading meter levels */ + meter_levels = kmalloc_array(map.meter_slots, sizeof(__le32), + GFP_KERNEL); + if (!meter_levels) + return -ENOMEM; + + /* Create the Level Meter control */ + err = fcp_add_new_ctl(mixer, &fcp_meter_ctl, 0, map.map_size, + "Level Meter", &private->meter_ctl); + if (err < 0) + return err; + + /* Success; save the pointers in private and don't free them */ + private->meter_level_map = new_map; + private->meter_levels = meter_levels; + private->num_meter_slots = map.meter_slots; + new_map = NULL; + meter_levels = NULL; + } + + /* Install the new map */ + memcpy(private->meter_level_map, tmp_map, map.map_size * sizeof(s16)); + + return 0; +} + +/* Set the Level Meter labels */ +static int fcp_ioctl_set_meter_labels(struct usb_mixer_interface *mixer, + struct fcp_meter_labels __user *arg) +{ + struct fcp_meter_labels labels; + struct fcp_data *private = mixer->private_data; + unsigned int *tlv_data; + unsigned int tlv_size, data_size; + + if (copy_from_user(&labels, arg, sizeof(labels))) + return -EFAULT; + + /* Remove existing labels if size is zero */ + if (!labels.labels_size) { + + /* Clear TLV read/callback bits if labels were present */ + if (private->meter_labels_tlv) { + private->meter_ctl->vd[0].access &= + ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); + snd_ctl_notify(mixer->chip->card, + SNDRV_CTL_EVENT_MASK_INFO, + &private->meter_ctl->id); + } + + kfree(private->meter_labels_tlv); + private->meter_labels_tlv = NULL; + private->meter_labels_tlv_size = 0; + + return 0; + } + + /* Validate size */ + if (labels.labels_size > 4096) + return -EINVAL; + + /* Calculate padded data size */ + data_size = ALIGN(labels.labels_size, sizeof(unsigned int)); + + /* Calculate total TLV size including header */ + tlv_size = sizeof(unsigned int) * 2 + data_size; + + /* Allocate, set up TLV header, and copy the labels data */ + tlv_data = kzalloc(tlv_size, GFP_KERNEL); + if (!tlv_data) + return -ENOMEM; + tlv_data[0] = SNDRV_CTL_TLVT_FCP_CHANNEL_LABELS; + tlv_data[1] = data_size; + if (copy_from_user(&tlv_data[2], arg->labels, labels.labels_size)) { + kfree(tlv_data); + return -EFAULT; + } + + /* Set TLV read/callback bits if labels weren't present */ + if (!private->meter_labels_tlv) { + private->meter_ctl->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + snd_ctl_notify(mixer->chip->card, + SNDRV_CTL_EVENT_MASK_INFO, + &private->meter_ctl->id); + } + + /* Swap in the new labels */ + kfree(private->meter_labels_tlv); + private->meter_labels_tlv = tlv_data; + private->meter_labels_tlv_size = tlv_size; + + return 0; +} + +static int fcp_hwdep_open(struct snd_hwdep *hw, struct file *file) +{ + struct usb_mixer_interface *mixer = hw->private_data; + struct fcp_data *private = mixer->private_data; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + private->file = file; + + return 0; +} + +static int fcp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct usb_mixer_interface *mixer = hw->private_data; + struct fcp_data *private = mixer->private_data; + void __user *argp = (void __user *)arg; + + guard(mutex)(&private->mutex); + + switch (cmd) { + + case FCP_IOCTL_PVERSION: + return put_user(FCP_HWDEP_VERSION, + (int __user *)argp) ? -EFAULT : 0; + break; + + case FCP_IOCTL_INIT: + return fcp_ioctl_init(mixer, argp); + + case FCP_IOCTL_CMD: + if (!private->init) + return -EINVAL; + return fcp_ioctl_cmd(mixer, argp); + + case FCP_IOCTL_SET_METER_MAP: + if (!private->init) + return -EINVAL; + return fcp_ioctl_set_meter_map(mixer, argp); + + case FCP_IOCTL_SET_METER_LABELS: + if (!private->init) + return -EINVAL; + if (!private->meter_ctl) + return -EINVAL; + return fcp_ioctl_set_meter_labels(mixer, argp); + + default: + return -ENOIOCTLCMD; + } + + /* not reached */ +} + +static long fcp_hwdep_read(struct snd_hwdep *hw, char __user *buf, + long count, loff_t *offset) +{ + struct usb_mixer_interface *mixer = hw->private_data; + struct fcp_data *private = mixer->private_data; + unsigned long flags; + long ret = 0; + u32 event; + + if (count < sizeof(event)) + return -EINVAL; + + ret = wait_event_interruptible(private->notify.queue, + private->notify.event); + if (ret) + return ret; + + spin_lock_irqsave(&private->notify.lock, flags); + event = private->notify.event; + private->notify.event = 0; + spin_unlock_irqrestore(&private->notify.lock, flags); + + if (copy_to_user(buf, &event, sizeof(event))) + return -EFAULT; + + return sizeof(event); +} + +static __poll_t fcp_hwdep_poll(struct snd_hwdep *hw, + struct file *file, + poll_table *wait) +{ + struct usb_mixer_interface *mixer = hw->private_data; + struct fcp_data *private = mixer->private_data; + __poll_t mask = 0; + + poll_wait(file, &private->notify.queue, wait); + + if (private->notify.event) + mask |= EPOLLIN | EPOLLRDNORM; + + return mask; +} + +static int fcp_hwdep_release(struct snd_hwdep *hw, struct file *file) +{ + struct usb_mixer_interface *mixer = hw->private_data; + struct fcp_data *private = mixer->private_data; + + if (!private) + return 0; + + private->file = NULL; + + return 0; +} + +static int fcp_hwdep_init(struct usb_mixer_interface *mixer) +{ + struct snd_hwdep *hw; + int err; + + err = snd_hwdep_new(mixer->chip->card, "Focusrite Control", 0, &hw); + if (err < 0) + return err; + + hw->private_data = mixer; + hw->exclusive = 1; + hw->ops.open = fcp_hwdep_open; + hw->ops.ioctl = fcp_hwdep_ioctl; + hw->ops.ioctl_compat = fcp_hwdep_ioctl; + hw->ops.read = fcp_hwdep_read; + hw->ops.poll = fcp_hwdep_poll; + hw->ops.release = fcp_hwdep_release; + + return 0; +} + +/*** Cleanup ***/ + +static void fcp_cleanup_urb(struct usb_mixer_interface *mixer) +{ + if (!mixer->urb) + return; + + usb_kill_urb(mixer->urb); + kfree(mixer->urb->transfer_buffer); + usb_free_urb(mixer->urb); + mixer->urb = NULL; +} + +static void fcp_private_free(struct usb_mixer_interface *mixer) +{ + struct fcp_data *private = mixer->private_data; + + fcp_cleanup_urb(mixer); + + kfree(private->meter_level_map); + kfree(private->meter_levels); + kfree(private->meter_labels_tlv); + kfree(private); + mixer->private_data = NULL; +} + +static void fcp_private_suspend(struct usb_mixer_interface *mixer) +{ + fcp_cleanup_urb(mixer); +} + +/*** Callbacks ***/ + +static void fcp_notify(struct urb *urb) +{ + struct usb_mixer_interface *mixer = urb->context; + struct fcp_data *private = mixer->private_data; + int len = urb->actual_length; + int ustatus = urb->status; + u32 data; + + if (ustatus != 0 || len != 8) + goto requeue; + + data = le32_to_cpu(*(__le32 *)urb->transfer_buffer); + + /* Handle command acknowledgement */ + if (data & FCP_NOTIFY_ACK) { + complete(&private->cmd_done); + data &= ~FCP_NOTIFY_ACK; + } + + if (data) { + unsigned long flags; + + spin_lock_irqsave(&private->notify.lock, flags); + private->notify.event |= data; + spin_unlock_irqrestore(&private->notify.lock, flags); + + wake_up_interruptible(&private->notify.queue); + } + +requeue: + if (ustatus != -ENOENT && + ustatus != -ECONNRESET && + ustatus != -ESHUTDOWN) { + urb->dev = mixer->chip->dev; + usb_submit_urb(urb, GFP_ATOMIC); + } else { + complete(&private->cmd_done); + } +} + +/* Submit a URB to receive notifications from the device */ +static int fcp_init_notify(struct usb_mixer_interface *mixer) +{ + struct usb_device *dev = mixer->chip->dev; + struct fcp_data *private = mixer->private_data; + unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); + void *transfer_buffer; + int err; + + /* Already set up */ + if (mixer->urb) + return 0; + + if (usb_pipe_type_check(dev, pipe)) + return -EINVAL; + + mixer->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!mixer->urb) + return -ENOMEM; + + transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); + if (!transfer_buffer) { + usb_free_urb(mixer->urb); + mixer->urb = NULL; + return -ENOMEM; + } + + usb_fill_int_urb(mixer->urb, dev, pipe, + transfer_buffer, private->wMaxPacketSize, + fcp_notify, mixer, private->bInterval); + + init_completion(&private->cmd_done); + + err = usb_submit_urb(mixer->urb, GFP_KERNEL); + if (err) { + usb_audio_err(mixer->chip, + "%s: usb_submit_urb failed: %d\n", + __func__, err); + kfree(transfer_buffer); + usb_free_urb(mixer->urb); + mixer->urb = NULL; + } + + return err; +} + +/*** Initialisation ***/ + +static int fcp_init(struct usb_mixer_interface *mixer, + void *step0_resp, void *step2_resp) +{ + struct fcp_data *private = mixer->private_data; + struct usb_device *dev = mixer->chip->dev; + int err; + + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), + FCP_USB_REQ_STEP0, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + 0, private->bInterfaceNumber, + step0_resp, private->step0_resp_size); + if (err < 0) + return err; + + err = fcp_init_notify(mixer); + if (err < 0) + return err; + + private->seq = 0; + private->init = 1; + + err = fcp_usb(mixer, private->init1_opcode, NULL, 0, NULL, 0); + if (err < 0) + return err; + + err = fcp_usb(mixer, private->init2_opcode, + NULL, 0, step2_resp, private->step2_resp_size); + if (err < 0) + return err; + + return 0; +} + +static int fcp_init_private(struct usb_mixer_interface *mixer) +{ + struct fcp_data *private = + kzalloc(sizeof(struct fcp_data), GFP_KERNEL); + + if (!private) + return -ENOMEM; + + mutex_init(&private->mutex); + init_waitqueue_head(&private->notify.queue); + spin_lock_init(&private->notify.lock); + + mixer->private_data = private; + mixer->private_free = fcp_private_free; + mixer->private_suspend = fcp_private_suspend; + + private->mixer = mixer; + + return 0; +} + +/* Look through the interface descriptors for the Focusrite Control + * interface (bInterfaceClass = 255 Vendor Specific Class) and set + * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval + * in private + */ +static int fcp_find_fc_interface(struct usb_mixer_interface *mixer) +{ + struct snd_usb_audio *chip = mixer->chip; + struct fcp_data *private = mixer->private_data; + struct usb_host_config *config = chip->dev->actconfig; + int i; + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + struct usb_interface *intf = config->interface[i]; + struct usb_interface_descriptor *desc = + &intf->altsetting[0].desc; + struct usb_endpoint_descriptor *epd; + + if (desc->bInterfaceClass != 255) + continue; + + epd = get_endpoint(intf->altsetting, 0); + private->bInterfaceNumber = desc->bInterfaceNumber; + private->bEndpointAddress = epd->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize); + private->bInterval = epd->bInterval; + return 0; + } + + usb_audio_err(chip, "Focusrite vendor-specific interface not found\n"); + return -EINVAL; +} + +int snd_fcp_init(struct usb_mixer_interface *mixer) +{ + struct snd_usb_audio *chip = mixer->chip; + int err; + + /* only use UAC_VERSION_2 */ + if (!mixer->protocol) + return 0; + + err = fcp_init_private(mixer); + if (err < 0) + return err; + + err = fcp_find_fc_interface(mixer); + if (err < 0) + return err; + + err = fcp_hwdep_init(mixer); + if (err < 0) + return err; + + usb_audio_info(chip, + "Focusrite Control Protocol Driver ready (pid=0x%04x); " + "report any issues to " + "https://github.com/geoffreybennett/fcp-support/issues", + USB_ID_PRODUCT(chip->usb_id)); + + return err; +} diff --git a/sound/usb/fcp.h b/sound/usb/fcp.h new file mode 100644 index 000000000000..d58cc7db75b1 --- /dev/null +++ b/sound/usb/fcp.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __USBAUDIO_FCP_H +#define __USBAUDIO_FCP_H + +int snd_fcp_init(struct usb_mixer_interface *mixer); + +#endif /* __USBAUDIO_FCP_H */ diff --git a/sound/usb/format.c b/sound/usb/format.c index 6049d957694c..9d32b21a5fbb 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -384,6 +384,10 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, if (chip->usb_id == USB_ID(0x194f, 0x010c) && !s1810c_valid_sample_rate(fp, rate)) goto skip_rate; + /* Filter out invalid rates on Presonus Studio 1824c */ + if (chip->usb_id == USB_ID(0x194f, 0x010d) && + !s1810c_valid_sample_rate(fp, rate)) + goto skip_rate; /* Filter out invalid rates on Focusrite devices */ if (USB_ID_VENDOR(chip->usb_id) == 0x1235 && diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index ca2c6f5de407..c073b38cd673 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -386,7 +386,7 @@ static int toneport_setup(struct usb_line6_toneport *toneport) toneport_update_led(toneport); schedule_delayed_work(&toneport->line6.startup_work, - msecs_to_jiffies(TONEPORT_PCM_DELAY * 1000)); + secs_to_jiffies(TONEPORT_PCM_DELAY)); return 0; } diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 737dd00e97b1..826ac870f246 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -489,16 +489,84 @@ static void ch345_broken_sysex_input(struct snd_usb_midi_in_endpoint *ep, /* * CME protocol: like the standard protocol, but SysEx commands are sent as a - * single USB packet preceded by a 0x0F byte. + * single USB packet preceded by a 0x0F byte, as are system realtime + * messages and MIDI Active Sensing. + * Also, multiple messages can be sent in the same packet. */ static void snd_usbmidi_cme_input(struct snd_usb_midi_in_endpoint *ep, uint8_t *buffer, int buffer_length) { - if (buffer_length < 2 || (buffer[0] & 0x0f) != 0x0f) - snd_usbmidi_standard_input(ep, buffer, buffer_length); - else - snd_usbmidi_input_data(ep, buffer[0] >> 4, - &buffer[1], buffer_length - 1); + int remaining = buffer_length; + + /* + * CME send sysex, song position pointer, system realtime + * and active sensing using CIN 0x0f, which in the standard + * is only intended for single byte unparsed data. + * So we need to interpret these here before sending them on. + * By default, we assume single byte data, which is true + * for system realtime (midi clock, start, stop and continue) + * and active sensing, and handle the other (known) cases + * separately. + * In contrast to the standard, CME does not split sysex + * into multiple 4-byte packets, but lumps everything together + * into one. In addition, CME can string multiple messages + * together in the same packet; pressing the Record button + * on an UF6 sends a sysex message directly followed + * by a song position pointer in the same packet. + * For it to have any reasonable meaning, a sysex message + * needs to be at least 3 bytes in length (0xf0, id, 0xf7), + * corresponding to a packet size of 4 bytes, and the ones sent + * by CME devices are 6 or 7 bytes, making the packet fragments + * 7 or 8 bytes long (six or seven bytes plus preceding CN+CIN byte). + * For the other types, the packet size is always 4 bytes, + * as per the standard, with the data size being 3 for SPP + * and 1 for the others. + * Thus all packet fragments are at least 4 bytes long, so we can + * skip anything that is shorter; this also conveniantly skips + * packets with size 0, which CME devices continuously send when + * they have nothing better to do. + * Another quirk is that sometimes multiple messages are sent + * in the same packet. This has been observed for midi clock + * and active sensing i.e. 0x0f 0xf8 0x00 0x00 0x0f 0xfe 0x00 0x00, + * but also multiple note ons/offs, and control change together + * with MIDI clock. Similarly, some sysex messages are followed by + * the song position pointer in the same packet, and occasionally + * additionally by a midi clock or active sensing. + * We handle this by looping over all data and parsing it along the way. + */ + while (remaining >= 4) { + int source_length = 4; /* default */ + + if ((buffer[0] & 0x0f) == 0x0f) { + int data_length = 1; /* default */ + + if (buffer[1] == 0xf0) { + /* Sysex: Find EOX and send on whole message. */ + /* To kick off the search, skip the first + * two bytes (CN+CIN and SYSEX (0xf0). + */ + uint8_t *tmp_buf = buffer + 2; + int tmp_length = remaining - 2; + + while (tmp_length > 1 && *tmp_buf != 0xf7) { + tmp_buf++; + tmp_length--; + } + data_length = tmp_buf - buffer; + source_length = data_length + 1; + } else if (buffer[1] == 0xf2) { + /* Three byte song position pointer */ + data_length = 3; + } + snd_usbmidi_input_data(ep, buffer[0] >> 4, + &buffer[1], data_length); + } else { + /* normal channel events */ + snd_usbmidi_standard_input(ep, buffer, source_length); + } + buffer += source_length; + remaining -= source_length; + } } /* @@ -1145,7 +1213,7 @@ static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) { struct usbmidi_out_port *port = substream->runtime->private_data; - cancel_work_sync(&port->ep->work); + flush_work(&port->ep->work); return substream_open(substream, 0, 0); } diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 23fcd680167d..a90673d43822 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -38,6 +38,7 @@ #include "mixer_us16x08.h" #include "mixer_s1810c.h" #include "helper.h" +#include "fcp.h" struct std_mono_table { unsigned int unitid, control, cmask; @@ -3687,8 +3688,7 @@ static const char *snd_djm_get_label(u8 device_idx, u16 wvalue, u16 windex) // common DJM capture level option values static const u16 snd_djm_opts_cap_level[] = { - 0x0000, 0x0100, 0x0200, 0x0300, 0x400, 0x500 }; - + 0x0000, 0x0100, 0x0200, 0x0300 }; // DJM-250MK2 static const u16 snd_djm_opts_250mk2_cap1[] = { @@ -3830,6 +3830,8 @@ static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = { // DJM-A9 +static const u16 snd_djm_opts_a9_cap_level[] = { + 0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500 }; static const u16 snd_djm_opts_a9_cap1[] = { 0x0107, 0x0108, 0x0109, 0x010a, 0x010e, 0x111, 0x112, 0x113, 0x114, 0x0131, 0x132, 0x133, 0x134 }; @@ -3843,7 +3845,7 @@ static const u16 snd_djm_opts_a9_cap5[] = { 0x0501, 0x0502, 0x0503, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x050a, 0x050e }; static const struct snd_djm_ctl snd_djm_ctls_a9[] = { - SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL), + SND_DJM_CTL("Capture Level", a9_cap_level, 0, SND_DJM_WINDEX_CAPLVL), SND_DJM_CTL("Master Input", a9_cap1, 3, SND_DJM_WINDEX_CAP), SND_DJM_CTL("Ch1 Input", a9_cap2, 2, SND_DJM_WINDEX_CAP), SND_DJM_CTL("Ch2 Input", a9_cap3, 2, SND_DJM_WINDEX_CAP), @@ -4090,6 +4092,12 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_scarlett2_init(mixer); break; + case USB_ID(0x1235, 0x821b): /* Focusrite Scarlett 16i16 4th Gen */ + case USB_ID(0x1235, 0x821c): /* Focusrite Scarlett 18i16 4th Gen */ + case USB_ID(0x1235, 0x821d): /* Focusrite Scarlett 18i20 4th Gen */ + err = snd_fcp_init(mixer); + break; + case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */ err = snd_soundblaster_e1_switch_create(mixer); break; @@ -4112,6 +4120,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x194f, 0x010c): /* Presonus Studio 1810c */ err = snd_sc1810_init_mixer(mixer); break; + case USB_ID(0x194f, 0x010d): /* Presonus Studio 1824c */ + err = snd_sc1810_init_mixer(mixer); + break; case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */ err = snd_bbfpro_controls_create(mixer); break; @@ -4216,6 +4227,52 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer, } } +/* + * Some Plantronics headsets have control names that don't meet ALSA naming + * standards. This function fixes nonstandard source names. By the time + * this function is called the control name should look like one of these: + * "source names Playback Volume" + * "source names Playback Switch" + * "source names Capture Volume" + * "source names Capture Switch" + * If any of the trigger words are found in the name then the name will + * be changed to: + * "Headset Playback Volume" + * "Headset Playback Switch" + * "Headset Capture Volume" + * "Headset Capture Switch" + * depending on the current suffix. + */ +static void snd_fix_plt_name(struct snd_usb_audio *chip, + struct snd_ctl_elem_id *id) +{ + /* no variant of "Sidetone" should be added to this list */ + static const char * const trigger[] = { + "Earphone", "Microphone", "Receive", "Transmit" + }; + static const char * const suffix[] = { + " Playback Volume", " Playback Switch", + " Capture Volume", " Capture Switch" + }; + int i; + + for (i = 0; i < ARRAY_SIZE(trigger); i++) + if (strstr(id->name, trigger[i])) + goto triggered; + usb_audio_dbg(chip, "no change in %s\n", id->name); + return; + +triggered: + for (i = 0; i < ARRAY_SIZE(suffix); i++) + if (strstr(id->name, suffix[i])) { + usb_audio_dbg(chip, "fixing kctl name %s\n", id->name); + snprintf(id->name, sizeof(id->name), "Headset%s", + suffix[i]); + return; + } + usb_audio_dbg(chip, "something wrong in kctl name %s\n", id->name); +} + void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, struct usb_mixer_elem_info *cval, int unitid, struct snd_kcontrol *kctl) @@ -4233,5 +4290,10 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, cval->min_mute = 1; break; } + + /* ALSA-ify some Plantronics headset control names */ + if (USB_ID_VENDOR(mixer->chip->usb_id) == 0x047f && + (cval->control == UAC_FU_MUTE || cval->control == UAC_FU_VOLUME)) + snd_fix_plt_name(mixer->chip, &kctl->id); } diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 7f595c1752a5..288d22e6a0b2 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -166,6 +166,7 @@ #include "helper.h" #include "mixer_scarlett2.h" +#include "fcp.h" /* device_setup value to allow turning MSD mode back on */ #define SCARLETT2_MSD_ENABLE 0x02 @@ -173,6 +174,9 @@ /* device_setup value to disable this mixer driver */ #define SCARLETT2_DISABLE 0x04 +/* device_setup value to use the FCP driver instead */ +#define SCARLETT2_USE_FCP_DRIVER 0x08 + /* some gui mixers can't handle negative ctl values */ #define SCARLETT2_VOLUME_BIAS 127 @@ -9702,6 +9706,10 @@ int snd_scarlett2_init(struct usb_mixer_interface *mixer) if (!mixer->protocol) return 0; + /* check if the user wants to use the FCP driver instead */ + if (chip->setup & SCARLETT2_USE_FCP_DRIVER) + return snd_fcp_init(mixer); + /* find entry in scarlett2_devices */ entry = get_scarlett2_device_entry(mixer); if (!entry) { diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 8ba0aff8be2e..9112313a9dbc 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1599,7 +1599,9 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, /* presonus studio 1810c: skip altsets incompatible with device_setup */ if (chip->usb_id == USB_ID(0x194f, 0x010c)) return s1810c_skip_setting_quirk(chip, iface, altno); - + /* presonus studio 1824c: skip altsets incompatible with device_setup */ + if (chip->usb_id == USB_ID(0x194f, 0x010d)) + return s1810c_skip_setting_quirk(chip, iface, altno); return 0; } @@ -1868,6 +1870,7 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs, case USB_ID(0x534d, 0x2109): /* MacroSilicon MS2109 */ subs->stream_offset_adj = 2; break; + case USB_ID(0x2b73, 0x000a): /* Pioneer DJM-900NXS2 */ case USB_ID(0x2b73, 0x0013): /* Pioneer DJM-450 */ pioneer_djm_set_format_quirk(subs, 0x0082); break; @@ -2239,6 +2242,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x0c45, 0x6340, /* Sonix HD USB Camera */ QUIRK_FLAG_GET_SAMPLE_RATE), + DEVICE_FLG(0x0d8c, 0x0014, /* USB Audio Device */ + QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */ @@ -2341,6 +2346,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x2d95, 0x8021, /* VIVO USB-C-XE710 HEADSET */ QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x2fc6, 0xf0b7, /* iBasso DC07 Pro */ + QUIRK_FLAG_CTL_MSG_DELAY_1M), DEVICE_FLG(0x30be, 0x0101, /* Schiit Hel */ QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */ diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 5f81c68fd42b..5756ff3528a2 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -151,6 +151,12 @@ static int snd_usx2y_card_used[SNDRV_CARDS]; static void snd_usx2y_card_private_free(struct snd_card *card); static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s); +#ifdef USX2Y_NRPACKS_VARIABLE +int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */ +module_param(nrpacks, int, 0444); +MODULE_PARM_DESC(nrpacks, "Number of packets per URB."); +#endif + /* * pipe 4 is used for switching the lamps, setting samplerate, volumes .... */ @@ -432,6 +438,11 @@ static int snd_usx2y_probe(struct usb_interface *intf, struct snd_card *card; int err; +#ifdef USX2Y_NRPACKS_VARIABLE + if (nrpacks < 0 || nrpacks > USX2Y_NRPACKS_MAX) + return -EINVAL; +#endif + if (le16_to_cpu(device->descriptor.idVendor) != 0x1604 || (le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 && le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 && diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h index 391fd7b4ed5e..6a76d04bf1c7 100644 --- a/sound/usb/usx2y/usbusx2y.h +++ b/sound/usb/usx2y/usbusx2y.h @@ -7,6 +7,32 @@ #define NRURBS 2 +/* Default value used for nr of packs per urb. + * 1 to 4 have been tested ok on uhci. + * To use 3 on ohci, you'd need a patch: + * look for "0000425-linux-2.6.9-rc4-mm1_ohci-hcd.patch.gz" on + * "https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000425" + * + * 1, 2 and 4 work out of the box on ohci, if I recall correctly. + * Bigger is safer operation, smaller gives lower latencies. + */ +#define USX2Y_NRPACKS 4 + +#define USX2Y_NRPACKS_MAX 1024 + +/* If your system works ok with this module's parameter + * nrpacks set to 1, you might as well comment + * this define out, and thereby produce smaller, faster code. + * You'd also set USX2Y_NRPACKS to 1 then. + */ +#define USX2Y_NRPACKS_VARIABLE 1 + +#ifdef USX2Y_NRPACKS_VARIABLE +extern int nrpacks; +#define nr_of_packs() nrpacks +#else +#define nr_of_packs() USX2Y_NRPACKS +#endif #define URBS_ASYNC_SEQ 10 #define URB_DATA_LEN_ASYNC_SEQ 32 diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index f540f46a0b14..acca8bead82e 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -28,33 +28,6 @@ #include "usx2y.h" #include "usbusx2y.h" -/* Default value used for nr of packs per urb. - * 1 to 4 have been tested ok on uhci. - * To use 3 on ohci, you'd need a patch: - * look for "0000425-linux-2.6.9-rc4-mm1_ohci-hcd.patch.gz" on - * "https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000425" - * - * 1, 2 and 4 work out of the box on ohci, if I recall correctly. - * Bigger is safer operation, smaller gives lower latencies. - */ -#define USX2Y_NRPACKS 4 - -/* If your system works ok with this module's parameter - * nrpacks set to 1, you might as well comment - * this define out, and thereby produce smaller, faster code. - * You'd also set USX2Y_NRPACKS to 1 then. - */ -#define USX2Y_NRPACKS_VARIABLE 1 - -#ifdef USX2Y_NRPACKS_VARIABLE -static int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */ -#define nr_of_packs() nrpacks -module_param(nrpacks, int, 0444); -MODULE_PARM_DESC(nrpacks, "Number of packets per URB."); -#else -#define nr_of_packs() USX2Y_NRPACKS -#endif - static int usx2y_urb_capt_retire(struct snd_usx2y_substream *subs) { struct urb *urb = subs->completed_urb; diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index d41ea09ffbe5..7fcc528a0204 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1617,7 +1617,7 @@ static int had_create_jack(struct snd_intelhad *ctx, * PM callbacks */ -static int __maybe_unused hdmi_lpe_audio_suspend(struct device *dev) +static int hdmi_lpe_audio_suspend(struct device *dev) { struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev); @@ -1626,7 +1626,7 @@ static int __maybe_unused hdmi_lpe_audio_suspend(struct device *dev) return 0; } -static int __maybe_unused hdmi_lpe_audio_resume(struct device *dev) +static int hdmi_lpe_audio_resume(struct device *dev) { struct snd_intelhad_card *card_ctx = dev_get_drvdata(dev); @@ -1833,13 +1833,13 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) } static const struct dev_pm_ops hdmi_lpe_audio_pm = { - SET_SYSTEM_SLEEP_PM_OPS(hdmi_lpe_audio_suspend, hdmi_lpe_audio_resume) + SYSTEM_SLEEP_PM_OPS(hdmi_lpe_audio_suspend, hdmi_lpe_audio_resume) }; static struct platform_driver hdmi_lpe_audio_driver = { .driver = { .name = "hdmi-lpe-audio", - .pm = &hdmi_lpe_audio_pm, + .pm = pm_ptr(&hdmi_lpe_audio_pm), }, .probe = hdmi_lpe_audio_probe, }; |