xref: /OK3568_Linux_fs/kernel/sound/soc/intel/boards/sof_sdw_rt711.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun // Copyright (c) 2020 Intel Corporation
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun /*
5*4882a593Smuzhiyun  *  sof_sdw_rt711 - Helpers to handle RT711 from generic machine driver
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/device.h>
9*4882a593Smuzhiyun #include <linux/errno.h>
10*4882a593Smuzhiyun #include <linux/input.h>
11*4882a593Smuzhiyun #include <linux/soundwire/sdw.h>
12*4882a593Smuzhiyun #include <linux/soundwire/sdw_type.h>
13*4882a593Smuzhiyun #include <sound/control.h>
14*4882a593Smuzhiyun #include <sound/soc.h>
15*4882a593Smuzhiyun #include <sound/soc-acpi.h>
16*4882a593Smuzhiyun #include <sound/soc-dapm.h>
17*4882a593Smuzhiyun #include <sound/jack.h>
18*4882a593Smuzhiyun #include "sof_sdw_common.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun /*
21*4882a593Smuzhiyun  * Note this MUST be called before snd_soc_register_card(), so that the props
22*4882a593Smuzhiyun  * are in place before the codec component driver's probe function parses them.
23*4882a593Smuzhiyun  */
rt711_add_codec_device_props(const char * sdw_dev_name)24*4882a593Smuzhiyun static int rt711_add_codec_device_props(const char *sdw_dev_name)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	struct property_entry props[MAX_NO_PROPS] = {};
27*4882a593Smuzhiyun 	struct device *sdw_dev;
28*4882a593Smuzhiyun 	int ret;
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun 	sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_dev_name);
31*4882a593Smuzhiyun 	if (!sdw_dev)
32*4882a593Smuzhiyun 		return -EPROBE_DEFER;
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun 	if (SOF_RT711_JDSRC(sof_sdw_quirk)) {
35*4882a593Smuzhiyun 		props[0] = PROPERTY_ENTRY_U32("realtek,jd-src",
36*4882a593Smuzhiyun 					      SOF_RT711_JDSRC(sof_sdw_quirk));
37*4882a593Smuzhiyun 	}
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 	ret = device_add_properties(sdw_dev, props);
40*4882a593Smuzhiyun 	put_device(sdw_dev);
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 	return ret;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun static const struct snd_soc_dapm_widget rt711_widgets[] = {
46*4882a593Smuzhiyun 	SND_SOC_DAPM_HP("Headphone", NULL),
47*4882a593Smuzhiyun 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
48*4882a593Smuzhiyun };
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun static const struct snd_soc_dapm_route rt711_map[] = {
51*4882a593Smuzhiyun 	/* Headphones */
52*4882a593Smuzhiyun 	{ "Headphone", NULL, "rt711 HP" },
53*4882a593Smuzhiyun 	{ "rt711 MIC2", NULL, "Headset Mic" },
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun static const struct snd_kcontrol_new rt711_controls[] = {
57*4882a593Smuzhiyun 	SOC_DAPM_PIN_SWITCH("Headphone"),
58*4882a593Smuzhiyun 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
59*4882a593Smuzhiyun };
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun static struct snd_soc_jack_pin rt711_jack_pins[] = {
62*4882a593Smuzhiyun 	{
63*4882a593Smuzhiyun 		.pin    = "Headphone",
64*4882a593Smuzhiyun 		.mask   = SND_JACK_HEADPHONE,
65*4882a593Smuzhiyun 	},
66*4882a593Smuzhiyun 	{
67*4882a593Smuzhiyun 		.pin    = "Headset Mic",
68*4882a593Smuzhiyun 		.mask   = SND_JACK_MICROPHONE,
69*4882a593Smuzhiyun 	},
70*4882a593Smuzhiyun };
71*4882a593Smuzhiyun 
rt711_rtd_init(struct snd_soc_pcm_runtime * rtd)72*4882a593Smuzhiyun static int rt711_rtd_init(struct snd_soc_pcm_runtime *rtd)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun 	struct snd_soc_card *card = rtd->card;
75*4882a593Smuzhiyun 	struct mc_private *ctx = snd_soc_card_get_drvdata(card);
76*4882a593Smuzhiyun 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
77*4882a593Smuzhiyun 	struct snd_soc_component *component = codec_dai->component;
78*4882a593Smuzhiyun 	struct snd_soc_jack *jack;
79*4882a593Smuzhiyun 	int ret;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	card->components = devm_kasprintf(card->dev, GFP_KERNEL,
82*4882a593Smuzhiyun 					  "%s hs:rt711",
83*4882a593Smuzhiyun 					  card->components);
84*4882a593Smuzhiyun 	if (!card->components)
85*4882a593Smuzhiyun 		return -ENOMEM;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	ret = snd_soc_add_card_controls(card, rt711_controls,
88*4882a593Smuzhiyun 					ARRAY_SIZE(rt711_controls));
89*4882a593Smuzhiyun 	if (ret) {
90*4882a593Smuzhiyun 		dev_err(card->dev, "rt711 controls addition failed: %d\n", ret);
91*4882a593Smuzhiyun 		return ret;
92*4882a593Smuzhiyun 	}
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	ret = snd_soc_dapm_new_controls(&card->dapm, rt711_widgets,
95*4882a593Smuzhiyun 					ARRAY_SIZE(rt711_widgets));
96*4882a593Smuzhiyun 	if (ret) {
97*4882a593Smuzhiyun 		dev_err(card->dev, "rt711 widgets addition failed: %d\n", ret);
98*4882a593Smuzhiyun 		return ret;
99*4882a593Smuzhiyun 	}
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	ret = snd_soc_dapm_add_routes(&card->dapm, rt711_map,
102*4882a593Smuzhiyun 				      ARRAY_SIZE(rt711_map));
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	if (ret) {
105*4882a593Smuzhiyun 		dev_err(card->dev, "rt711 map addition failed: %d\n", ret);
106*4882a593Smuzhiyun 		return ret;
107*4882a593Smuzhiyun 	}
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
110*4882a593Smuzhiyun 				    SND_JACK_HEADSET | SND_JACK_BTN_0 |
111*4882a593Smuzhiyun 				    SND_JACK_BTN_1 | SND_JACK_BTN_2 |
112*4882a593Smuzhiyun 				    SND_JACK_BTN_3,
113*4882a593Smuzhiyun 				    &ctx->sdw_headset,
114*4882a593Smuzhiyun 				    rt711_jack_pins,
115*4882a593Smuzhiyun 				    ARRAY_SIZE(rt711_jack_pins));
116*4882a593Smuzhiyun 	if (ret) {
117*4882a593Smuzhiyun 		dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
118*4882a593Smuzhiyun 			ret);
119*4882a593Smuzhiyun 		return ret;
120*4882a593Smuzhiyun 	}
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	jack = &ctx->sdw_headset;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
125*4882a593Smuzhiyun 	snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
126*4882a593Smuzhiyun 	snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
127*4882a593Smuzhiyun 	snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	ret = snd_soc_component_set_jack(component, jack, NULL);
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	if (ret)
132*4882a593Smuzhiyun 		dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
133*4882a593Smuzhiyun 			ret);
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	return ret;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
sof_sdw_rt711_exit(struct device * dev,struct snd_soc_dai_link * dai_link)138*4882a593Smuzhiyun int sof_sdw_rt711_exit(struct device *dev, struct snd_soc_dai_link *dai_link)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	struct device *sdw_dev;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL,
143*4882a593Smuzhiyun 					  dai_link->codecs[0].name);
144*4882a593Smuzhiyun 	if (!sdw_dev)
145*4882a593Smuzhiyun 		return -EINVAL;
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	device_remove_properties(sdw_dev);
148*4882a593Smuzhiyun 	put_device(sdw_dev);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	return 0;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun 
sof_sdw_rt711_init(const struct snd_soc_acpi_link_adr * link,struct snd_soc_dai_link * dai_links,struct sof_sdw_codec_info * info,bool playback)153*4882a593Smuzhiyun int sof_sdw_rt711_init(const struct snd_soc_acpi_link_adr *link,
154*4882a593Smuzhiyun 		       struct snd_soc_dai_link *dai_links,
155*4882a593Smuzhiyun 		       struct sof_sdw_codec_info *info,
156*4882a593Smuzhiyun 		       bool playback)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	int ret;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	/*
161*4882a593Smuzhiyun 	 * headset should be initialized once.
162*4882a593Smuzhiyun 	 * Do it with dai link for playback.
163*4882a593Smuzhiyun 	 */
164*4882a593Smuzhiyun 	if (!playback)
165*4882a593Smuzhiyun 		return 0;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	ret = rt711_add_codec_device_props(dai_links->codecs[0].name);
168*4882a593Smuzhiyun 	if (ret < 0)
169*4882a593Smuzhiyun 		return ret;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	dai_links->init = rt711_rtd_init;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	return 0;
174*4882a593Smuzhiyun }
175