161 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 - Helpers to handle RT711 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 rt711_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 props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", SOC_SDW_JACK_JDSRC(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_route rt711_map[] = {48 /* Headphones */49 { "Headphone", NULL, "rt711 HP" },50 { "rt711 MIC2", NULL, "Headset Mic" },51};52 53static struct snd_soc_jack_pin rt711_jack_pins[] = {54 {55 .pin = "Headphone",56 .mask = SND_JACK_HEADPHONE,57 },58 {59 .pin = "Headset Mic",60 .mask = SND_JACK_MICROPHONE,61 },62};63 64int asoc_sdw_rt711_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)65{66 struct snd_soc_card *card = rtd->card;67 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);68 struct snd_soc_component *component;69 struct snd_soc_jack *jack;70 int ret;71 72 component = dai->component;73 card->components = devm_kasprintf(card->dev, GFP_KERNEL,74 "%s hs:rt711",75 card->components);76 if (!card->components)77 return -ENOMEM;78 79 ret = snd_soc_dapm_add_routes(&card->dapm, rt711_map,80 ARRAY_SIZE(rt711_map));81 82 if (ret) {83 dev_err(card->dev, "rt711 map addition failed: %d\n", ret);84 return ret;85 }86 87 ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",88 SND_JACK_HEADSET | SND_JACK_BTN_0 |89 SND_JACK_BTN_1 | SND_JACK_BTN_2 |90 SND_JACK_BTN_3,91 &ctx->sdw_headset,92 rt711_jack_pins,93 ARRAY_SIZE(rt711_jack_pins));94 if (ret) {95 dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",96 ret);97 return ret;98 }99 100 jack = &ctx->sdw_headset;101 102 snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);103 snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);104 snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);105 snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);106 107 ret = snd_soc_component_set_jack(component, jack, NULL);108 109 if (ret)110 dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",111 ret);112 113 return ret;114}115EXPORT_SYMBOL_NS(asoc_sdw_rt711_rtd_init, SND_SOC_SDW_UTILS);116 117int asoc_sdw_rt711_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)118{119 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);120 121 if (!ctx->headset_codec_dev)122 return 0;123 124 device_remove_software_node(ctx->headset_codec_dev);125 put_device(ctx->headset_codec_dev);126 127 return 0;128}129EXPORT_SYMBOL_NS(asoc_sdw_rt711_exit, SND_SOC_SDW_UTILS);130 131int asoc_sdw_rt711_init(struct snd_soc_card *card,132 struct snd_soc_dai_link *dai_links,133 struct asoc_sdw_codec_info *info,134 bool playback)135{136 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);137 struct device *sdw_dev;138 int ret;139 140 /*141 * headset should be initialized once.142 * Do it with dai link for playback.143 */144 if (!playback)145 return 0;146 147 sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);148 if (!sdw_dev)149 return -EPROBE_DEFER;150 151 ret = rt711_add_codec_device_props(sdw_dev, ctx->mc_quirk);152 if (ret < 0) {153 put_device(sdw_dev);154 return ret;155 }156 ctx->headset_codec_dev = sdw_dev;157 158 return 0;159}160EXPORT_SYMBOL_NS(asoc_sdw_rt711_init, SND_SOC_SDW_UTILS);161