1// SPDX-License-Identifier: GPL-2.0-only 2// Copyright (c) 2020 Intel Corporation 3 4/* 5 * sof_sdw_rt711_sdca - Helpers to handle RT711-SDCA from generic machine driver 6 */ 7 8#include <linux/device.h> 9#include <linux/errno.h> 10#include <linux/input.h> 11#include <linux/soundwire/sdw.h> 12#include <linux/soundwire/sdw_type.h> 13#include <sound/control.h> 14#include <sound/soc.h> 15#include <sound/soc-acpi.h> 16#include <sound/soc-dapm.h> 17#include <sound/jack.h> 18#include "sof_board_helpers.h" 19#include "sof_sdw_common.h" 20 21/* 22 * Note this MUST be called before snd_soc_register_card(), so that the props 23 * are in place before the codec component driver's probe function parses them. 24 */ 25static int rt_sdca_jack_add_codec_device_props(struct device *sdw_dev) 26{ 27 struct property_entry props[MAX_NO_PROPS] = {}; 28 struct fwnode_handle *fwnode; 29 int ret; 30 31 if (!SOF_JACK_JDSRC(sof_sdw_quirk)) 32 return 0; 33 34 props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOF_JACK_JDSRC(sof_sdw_quirk)); 35 36 fwnode = fwnode_create_software_node(props, NULL); 37 if (IS_ERR(fwnode)) 38 return PTR_ERR(fwnode); 39 40 ret = device_add_software_node(sdw_dev, to_software_node(fwnode)); 41 42 fwnode_handle_put(fwnode); 43 44 return ret; 45} 46 47static const struct snd_soc_dapm_widget rt_sdca_jack_widgets[] = { 48 SND_SOC_DAPM_HP("Headphone", NULL), 49 SND_SOC_DAPM_MIC("Headset Mic", NULL), 50}; 51 52static const struct snd_soc_dapm_route rt711_sdca_map[] = { 53 { "Headphone", NULL, "rt711 HP" }, 54 { "rt711 MIC2", NULL, "Headset Mic" }, 55}; 56 57static const struct snd_soc_dapm_route rt712_sdca_map[] = { 58 { "Headphone", NULL, "rt712 HP" }, 59 { "rt712 MIC2", NULL, "Headset Mic" }, 60}; 61 62static const struct snd_soc_dapm_route rt713_sdca_map[] = { 63 { "Headphone", NULL, "rt713 HP" }, 64 { "rt713 MIC2", NULL, "Headset Mic" }, 65}; 66 67static const struct snd_soc_dapm_route rt722_sdca_map[] = { 68 { "Headphone", NULL, "rt722 HP" }, 69 { "rt722 MIC2", NULL, "Headset Mic" }, 70}; 71 72static const struct snd_kcontrol_new rt_sdca_jack_controls[] = { 73 SOC_DAPM_PIN_SWITCH("Headphone"), 74 SOC_DAPM_PIN_SWITCH("Headset Mic"), 75}; 76 77static struct snd_soc_jack_pin rt_sdca_jack_pins[] = { 78 { 79 .pin = "Headphone", 80 .mask = SND_JACK_HEADPHONE, 81 }, 82 { 83 .pin = "Headset Mic", 84 .mask = SND_JACK_MICROPHONE, 85 }, 86}; 87 88static const char * const jack_codecs[] = { 89 "rt711", "rt712", "rt713" 90}; 91 92int rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd) 93{ 94 struct snd_soc_card *card = rtd->card; 95 struct mc_private *ctx = snd_soc_card_get_drvdata(card); 96 struct snd_soc_dai *codec_dai; 97 struct snd_soc_component *component; 98 struct snd_soc_jack *jack; 99 int ret; 100 101 codec_dai = get_codec_dai_by_name(rtd, jack_codecs, ARRAY_SIZE(jack_codecs)); 102 if (!codec_dai) 103 return -EINVAL; 104 105 component = codec_dai->component; 106 card->components = devm_kasprintf(card->dev, GFP_KERNEL, 107 "%s hs:%s-sdca", 108 card->components, component->name_prefix); 109 if (!card->components) 110 return -ENOMEM; 111 112 ret = snd_soc_add_card_controls(card, rt_sdca_jack_controls, 113 ARRAY_SIZE(rt_sdca_jack_controls)); 114 if (ret) { 115 dev_err(card->dev, "rt sdca jack controls addition failed: %d\n", ret); 116 return ret; 117 } 118 119 ret = snd_soc_dapm_new_controls(&card->dapm, rt_sdca_jack_widgets, 120 ARRAY_SIZE(rt_sdca_jack_widgets)); 121 if (ret) { 122 dev_err(card->dev, "rt sdca jack widgets addition failed: %d\n", ret); 123 return ret; 124 } 125 126 if (strstr(component->name_prefix, "rt711")) { 127 ret = snd_soc_dapm_add_routes(&card->dapm, rt711_sdca_map, 128 ARRAY_SIZE(rt711_sdca_map)); 129 } else if (strstr(component->name_prefix, "rt712")) { 130 ret = snd_soc_dapm_add_routes(&card->dapm, rt712_sdca_map, 131 ARRAY_SIZE(rt712_sdca_map)); 132 } else if (strstr(component->name_prefix, "rt713")) { 133 ret = snd_soc_dapm_add_routes(&card->dapm, rt713_sdca_map, 134 ARRAY_SIZE(rt713_sdca_map)); 135 } else if (strstr(component->name_prefix, "rt722")) { 136 ret = snd_soc_dapm_add_routes(&card->dapm, rt722_sdca_map, 137 ARRAY_SIZE(rt722_sdca_map)); 138 } else { 139 dev_err(card->dev, "%s is not supported\n", component->name_prefix); 140 return -EINVAL; 141 } 142 143 if (ret) { 144 dev_err(card->dev, "rt sdca jack map addition failed: %d\n", ret); 145 return ret; 146 } 147 148 ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack", 149 SND_JACK_HEADSET | SND_JACK_BTN_0 | 150 SND_JACK_BTN_1 | SND_JACK_BTN_2 | 151 SND_JACK_BTN_3, 152 &ctx->sdw_headset, 153 rt_sdca_jack_pins, 154 ARRAY_SIZE(rt_sdca_jack_pins)); 155 if (ret) { 156 dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", 157 ret); 158 return ret; 159 } 160 161 jack = &ctx->sdw_headset; 162 163 snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 164 snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); 165 snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); 166 snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); 167 168 ret = snd_soc_component_set_jack(component, jack, NULL); 169 170 if (ret) 171 dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", 172 ret); 173 174 return ret; 175} 176 177int sof_sdw_rt_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) 178{ 179 struct mc_private *ctx = snd_soc_card_get_drvdata(card); 180 181 if (!ctx->headset_codec_dev) 182 return 0; 183 184 if (!SOF_JACK_JDSRC(sof_sdw_quirk)) 185 return 0; 186 187 device_remove_software_node(ctx->headset_codec_dev); 188 put_device(ctx->headset_codec_dev); 189 ctx->headset_codec_dev = NULL; 190 191 return 0; 192} 193 194int sof_sdw_rt_sdca_jack_init(struct snd_soc_card *card, 195 const struct snd_soc_acpi_link_adr *link, 196 struct snd_soc_dai_link *dai_links, 197 struct sof_sdw_codec_info *info, 198 bool playback) 199{ 200 struct mc_private *ctx = snd_soc_card_get_drvdata(card); 201 struct device *sdw_dev; 202 int ret; 203 204 /* 205 * Jack detection should be only initialized once for headsets since 206 * the playback/capture is sharing the same jack 207 */ 208 if (ctx->headset_codec_dev) 209 return 0; 210 211 sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name); 212 if (!sdw_dev) 213 return -EPROBE_DEFER; 214 215 ret = rt_sdca_jack_add_codec_device_props(sdw_dev); 216 if (ret < 0) { 217 put_device(sdw_dev); 218 return ret; 219 } 220 ctx->headset_codec_dev = sdw_dev; 221 222 return 0; 223} 224MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_BOARD_HELPERS); 225