215 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// This file incorporates work covered by the following copyright notice:3// Copyright (c) 2020 Intel Corporation4// Copyright (c) 2024 Advanced Micro Devices, Inc.5 6/*7 * soc_sdw_rt711_sdca - Helpers to handle RT711-SDCA from generic machine driver8 */9 10#include <linux/device.h>11#include <linux/errno.h>12#include <linux/input.h>13#include <linux/soundwire/sdw.h>14#include <linux/soundwire/sdw_type.h>15#include <sound/control.h>16#include <sound/soc.h>17#include <sound/soc-acpi.h>18#include <sound/soc-dapm.h>19#include <sound/jack.h>20#include <sound/soc_sdw_utils.h>21 22/*23 * Note this MUST be called before snd_soc_register_card(), so that the props24 * are in place before the codec component driver's probe function parses them.25 */26static int rt_sdca_jack_add_codec_device_props(struct device *sdw_dev, unsigned long quirk)27{28 struct property_entry props[SOC_SDW_MAX_NO_PROPS] = {};29 struct fwnode_handle *fwnode;30 int ret;31 32 if (!SOC_SDW_JACK_JDSRC(quirk))33 return 0;34 35 props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOC_SDW_JACK_JDSRC(quirk));36 37 fwnode = fwnode_create_software_node(props, NULL);38 if (IS_ERR(fwnode))39 return PTR_ERR(fwnode);40 41 ret = device_add_software_node(sdw_dev, to_software_node(fwnode));42 43 fwnode_handle_put(fwnode);44 45 return ret;46}47 48static const struct snd_soc_dapm_route rt711_sdca_map[] = {49 { "Headphone", NULL, "rt711 HP" },50 { "rt711 MIC2", NULL, "Headset Mic" },51};52 53static const struct snd_soc_dapm_route rt712_sdca_map[] = {54 { "Headphone", NULL, "rt712 HP" },55 { "rt712 MIC2", NULL, "Headset Mic" },56};57 58static const struct snd_soc_dapm_route rt713_sdca_map[] = {59 { "Headphone", NULL, "rt713 HP" },60 { "rt713 MIC2", NULL, "Headset Mic" },61};62 63static const struct snd_soc_dapm_route rt722_sdca_map[] = {64 { "Headphone", NULL, "rt722 HP" },65 { "rt722 MIC2", NULL, "Headset Mic" },66};67 68static struct snd_soc_jack_pin rt_sdca_jack_pins[] = {69 {70 .pin = "Headphone",71 .mask = SND_JACK_HEADPHONE,72 },73 {74 .pin = "Headset Mic",75 .mask = SND_JACK_MICROPHONE,76 },77};78 79/*80 * The sdca suffix is required for rt711 since there are two generations of the same chip.81 * RT713 is an SDCA device but the sdca suffix is required for backwards-compatibility with82 * previous UCM definitions.83 */84static const char * const need_sdca_suffix[] = {85 "rt711", "rt713"86};87 88int asoc_sdw_rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)89{90 struct snd_soc_card *card = rtd->card;91 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);92 struct snd_soc_component *component;93 struct snd_soc_jack *jack;94 int ret;95 int i;96 97 component = dai->component;98 card->components = devm_kasprintf(card->dev, GFP_KERNEL,99 "%s hs:%s",100 card->components, component->name_prefix);101 if (!card->components)102 return -ENOMEM;103 104 for (i = 0; i < ARRAY_SIZE(need_sdca_suffix); i++) {105 if (strstr(component->name_prefix, need_sdca_suffix[i])) {106 /* Add -sdca suffix for existing UCMs */107 card->components = devm_kasprintf(card->dev, GFP_KERNEL,108 "%s-sdca", card->components);109 if (!card->components)110 return -ENOMEM;111 break;112 }113 }114 115 if (strstr(component->name_prefix, "rt711")) {116 ret = snd_soc_dapm_add_routes(&card->dapm, rt711_sdca_map,117 ARRAY_SIZE(rt711_sdca_map));118 } else if (strstr(component->name_prefix, "rt712")) {119 ret = snd_soc_dapm_add_routes(&card->dapm, rt712_sdca_map,120 ARRAY_SIZE(rt712_sdca_map));121 } else if (strstr(component->name_prefix, "rt713")) {122 ret = snd_soc_dapm_add_routes(&card->dapm, rt713_sdca_map,123 ARRAY_SIZE(rt713_sdca_map));124 } else if (strstr(component->name_prefix, "rt722")) {125 ret = snd_soc_dapm_add_routes(&card->dapm, rt722_sdca_map,126 ARRAY_SIZE(rt722_sdca_map));127 } else {128 dev_err(card->dev, "%s is not supported\n", component->name_prefix);129 return -EINVAL;130 }131 132 if (ret) {133 dev_err(card->dev, "rt sdca jack map addition failed: %d\n", ret);134 return ret;135 }136 137 ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",138 SND_JACK_HEADSET | SND_JACK_BTN_0 |139 SND_JACK_BTN_1 | SND_JACK_BTN_2 |140 SND_JACK_BTN_3,141 &ctx->sdw_headset,142 rt_sdca_jack_pins,143 ARRAY_SIZE(rt_sdca_jack_pins));144 if (ret) {145 dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",146 ret);147 return ret;148 }149 150 jack = &ctx->sdw_headset;151 152 snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);153 snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);154 snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);155 snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);156 157 ret = snd_soc_component_set_jack(component, jack, NULL);158 159 if (ret)160 dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",161 ret);162 163 return ret;164}165EXPORT_SYMBOL_NS(asoc_sdw_rt_sdca_jack_rtd_init, SND_SOC_SDW_UTILS);166 167int asoc_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)168{169 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);170 171 if (!ctx->headset_codec_dev)172 return 0;173 174 if (!SOC_SDW_JACK_JDSRC(ctx->mc_quirk))175 return 0;176 177 device_remove_software_node(ctx->headset_codec_dev);178 put_device(ctx->headset_codec_dev);179 ctx->headset_codec_dev = NULL;180 181 return 0;182}183EXPORT_SYMBOL_NS(asoc_sdw_rt_sdca_jack_exit, SND_SOC_SDW_UTILS);184 185int asoc_sdw_rt_sdca_jack_init(struct snd_soc_card *card,186 struct snd_soc_dai_link *dai_links,187 struct asoc_sdw_codec_info *info,188 bool playback)189{190 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);191 struct device *sdw_dev;192 int ret;193 194 /*195 * Jack detection should be only initialized once for headsets since196 * the playback/capture is sharing the same jack197 */198 if (ctx->headset_codec_dev)199 return 0;200 201 sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);202 if (!sdw_dev)203 return -EPROBE_DEFER;204 205 ret = rt_sdca_jack_add_codec_device_props(sdw_dev, ctx->mc_quirk);206 if (ret < 0) {207 put_device(sdw_dev);208 return ret;209 }210 ctx->headset_codec_dev = sdw_dev;211 212 return 0;213}214EXPORT_SYMBOL_NS(asoc_sdw_rt_sdca_jack_init, SND_SOC_SDW_UTILS);215