1 // SPDX-License-Identifier: GPL-2.0-only 2 // This file incorporates work covered by the following copyright notice: 3 // Copyright (c) 2020 Intel Corporation 4 // Copyright (c) 2024 Advanced Micro Devices, Inc. 5 // 6 // soc_sdw_maxim - Helpers to handle maxim codecs 7 // codec devices from generic machine driver 8 9 #include <linux/device.h> 10 #include <linux/errno.h> 11 #include <sound/control.h> 12 #include <sound/soc.h> 13 #include <sound/soc-acpi.h> 14 #include <sound/soc-dapm.h> 15 #include <sound/soc_sdw_utils.h> 16 17 static int maxim_part_id; 18 #define SOC_SDW_PART_ID_MAX98363 0x8363 19 #define SOC_SDW_PART_ID_MAX98373 0x8373 20 21 static const struct snd_soc_dapm_route max_98373_dapm_routes[] = { 22 { "Left Spk", NULL, "Left BE_OUT" }, 23 { "Right Spk", NULL, "Right BE_OUT" }, 24 }; 25 26 int asoc_sdw_maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) 27 { 28 struct snd_soc_card *card = rtd->card; 29 int ret; 30 31 ret = snd_soc_dapm_add_routes(&card->dapm, max_98373_dapm_routes, 2); 32 if (ret) 33 dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); 34 35 return ret; 36 } 37 EXPORT_SYMBOL_NS(asoc_sdw_maxim_spk_rtd_init, "SND_SOC_SDW_UTILS"); 38 39 static int asoc_sdw_mx8373_enable_spk_pin(struct snd_pcm_substream *substream, bool enable) 40 { 41 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 42 struct snd_soc_dai *codec_dai; 43 struct snd_soc_dai *cpu_dai; 44 int ret; 45 int j; 46 47 /* set spk pin by playback only */ 48 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 49 return 0; 50 51 cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 52 for_each_rtd_codec_dais(rtd, j, codec_dai) { 53 struct snd_soc_dapm_context *dapm = 54 snd_soc_component_get_dapm(cpu_dai->component); 55 char pin_name[16]; 56 57 snprintf(pin_name, ARRAY_SIZE(pin_name), "%s Spk", 58 codec_dai->component->name_prefix); 59 60 if (enable) 61 ret = snd_soc_dapm_enable_pin(dapm, pin_name); 62 else 63 ret = snd_soc_dapm_disable_pin(dapm, pin_name); 64 65 if (!ret) 66 snd_soc_dapm_sync(dapm); 67 } 68 69 return 0; 70 } 71 72 static int asoc_sdw_mx8373_prepare(struct snd_pcm_substream *substream) 73 { 74 int ret; 75 76 /* according to soc_pcm_prepare dai link prepare is called first */ 77 ret = asoc_sdw_prepare(substream); 78 if (ret < 0) 79 return ret; 80 81 return asoc_sdw_mx8373_enable_spk_pin(substream, true); 82 } 83 84 static int asoc_sdw_mx8373_hw_free(struct snd_pcm_substream *substream) 85 { 86 int ret; 87 88 /* according to soc_pcm_hw_free dai link free is called first */ 89 ret = asoc_sdw_hw_free(substream); 90 if (ret < 0) 91 return ret; 92 93 return asoc_sdw_mx8373_enable_spk_pin(substream, false); 94 } 95 96 static const struct snd_soc_ops max_98373_sdw_ops = { 97 .startup = asoc_sdw_startup, 98 .prepare = asoc_sdw_mx8373_prepare, 99 .trigger = asoc_sdw_trigger, 100 .hw_params = asoc_sdw_hw_params, 101 .hw_free = asoc_sdw_mx8373_hw_free, 102 .shutdown = asoc_sdw_shutdown, 103 }; 104 105 static int asoc_sdw_mx8373_sdw_late_probe(struct snd_soc_card *card) 106 { 107 struct snd_soc_dapm_context *dapm = &card->dapm; 108 109 /* Disable Left and Right Spk pin after boot */ 110 snd_soc_dapm_disable_pin(dapm, "Left Spk"); 111 snd_soc_dapm_disable_pin(dapm, "Right Spk"); 112 return snd_soc_dapm_sync(dapm); 113 } 114 115 int asoc_sdw_maxim_init(struct snd_soc_card *card, 116 struct snd_soc_dai_link *dai_links, 117 struct asoc_sdw_codec_info *info, 118 bool playback) 119 { 120 info->amp_num++; 121 122 maxim_part_id = info->part_id; 123 switch (maxim_part_id) { 124 case SOC_SDW_PART_ID_MAX98363: 125 /* Default ops are set in function init_dai_link. 126 * called as part of function create_sdw_dailink 127 */ 128 break; 129 case SOC_SDW_PART_ID_MAX98373: 130 info->codec_card_late_probe = asoc_sdw_mx8373_sdw_late_probe; 131 dai_links->ops = &max_98373_sdw_ops; 132 break; 133 default: 134 dev_err(card->dev, "Invalid maxim_part_id %#x\n", maxim_part_id); 135 return -EINVAL; 136 } 137 return 0; 138 } 139 EXPORT_SYMBOL_NS(asoc_sdw_maxim_init, "SND_SOC_SDW_UTILS"); 140