1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * SoC audio for HTC Magician
4  *
5  * Copyright (c) 2006 Philipp Zabel <philipp.zabel@gmail.com>
6  *
7  * based on spitz.c,
8  * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
9  *          Richard Purdie <richard@openedhand.com>
10  */
11 
12 #include <linux/module.h>
13 #include <linux/timer.h>
14 #include <linux/interrupt.h>
15 #include <linux/platform_device.h>
16 #include <linux/delay.h>
17 #include <linux/gpio/consumer.h>
18 #include <linux/i2c.h>
19 
20 #include <sound/core.h>
21 #include <sound/pcm.h>
22 #include <sound/pcm_params.h>
23 #include <sound/soc.h>
24 
25 #include <asm/mach-types.h>
26 #include "../codecs/uda1380.h"
27 #include "pxa2xx-i2s.h"
28 #include "pxa-ssp.h"
29 
30 #define MAGICIAN_MIC       0
31 #define MAGICIAN_MIC_EXT   1
32 
33 static int magician_hp_switch;
34 static int magician_spk_switch = 1;
35 static int magician_in_sel = MAGICIAN_MIC;
36 
37 static struct gpio_desc *gpiod_spk_power, *gpiod_ep_power, *gpiod_mic_power;
38 static struct gpio_desc *gpiod_in_sel0, *gpiod_in_sel1;
39 
magician_ext_control(struct snd_soc_dapm_context * dapm)40 static void magician_ext_control(struct snd_soc_dapm_context *dapm)
41 {
42 
43 	snd_soc_dapm_mutex_lock(dapm);
44 
45 	if (magician_spk_switch)
46 		snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
47 	else
48 		snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
49 	if (magician_hp_switch)
50 		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
51 	else
52 		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
53 
54 	switch (magician_in_sel) {
55 	case MAGICIAN_MIC:
56 		snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Mic");
57 		snd_soc_dapm_enable_pin_unlocked(dapm, "Call Mic");
58 		break;
59 	case MAGICIAN_MIC_EXT:
60 		snd_soc_dapm_disable_pin_unlocked(dapm, "Call Mic");
61 		snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Mic");
62 		break;
63 	}
64 
65 	snd_soc_dapm_sync_unlocked(dapm);
66 
67 	snd_soc_dapm_mutex_unlock(dapm);
68 }
69 
magician_startup(struct snd_pcm_substream * substream)70 static int magician_startup(struct snd_pcm_substream *substream)
71 {
72 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
73 
74 	/* check the jack status at stream startup */
75 	magician_ext_control(&rtd->card->dapm);
76 
77 	return 0;
78 }
79 
80 /*
81  * Magician uses SSP port for playback.
82  */
magician_playback_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)83 static int magician_playback_hw_params(struct snd_pcm_substream *substream,
84 				       struct snd_pcm_hw_params *params)
85 {
86 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
87 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
88 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
89 	unsigned int width;
90 	int ret = 0;
91 
92 	/* set codec DAI configuration */
93 	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
94 			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_BC_FC);
95 	if (ret < 0)
96 		return ret;
97 
98 	/* set cpu DAI configuration */
99 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
100 			SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_BP_FP);
101 	if (ret < 0)
102 		return ret;
103 
104 	width = snd_pcm_format_physical_width(params_format(params));
105 	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width);
106 	if (ret < 0)
107 		return ret;
108 
109 	/* set audio clock as clock source */
110 	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0,
111 			SND_SOC_CLOCK_OUT);
112 	if (ret < 0)
113 		return ret;
114 
115 	return 0;
116 }
117 
118 /*
119  * Magician uses I2S for capture.
120  */
magician_capture_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)121 static int magician_capture_hw_params(struct snd_pcm_substream *substream,
122 				      struct snd_pcm_hw_params *params)
123 {
124 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
125 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
126 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
127 	int ret = 0;
128 
129 	/* set codec DAI configuration */
130 	ret = snd_soc_dai_set_fmt(codec_dai,
131 			SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
132 			SND_SOC_DAIFMT_BC_FC);
133 	if (ret < 0)
134 		return ret;
135 
136 	/* set cpu DAI configuration */
137 	ret = snd_soc_dai_set_fmt(cpu_dai,
138 			SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
139 			SND_SOC_DAIFMT_BP_FP);
140 	if (ret < 0)
141 		return ret;
142 
143 	/* set the I2S system clock as output */
144 	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
145 			SND_SOC_CLOCK_OUT);
146 	if (ret < 0)
147 		return ret;
148 
149 	return 0;
150 }
151 
152 static const struct snd_soc_ops magician_capture_ops = {
153 	.startup = magician_startup,
154 	.hw_params = magician_capture_hw_params,
155 };
156 
157 static const struct snd_soc_ops magician_playback_ops = {
158 	.startup = magician_startup,
159 	.hw_params = magician_playback_hw_params,
160 };
161 
magician_get_hp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)162 static int magician_get_hp(struct snd_kcontrol *kcontrol,
163 			     struct snd_ctl_elem_value *ucontrol)
164 {
165 	ucontrol->value.integer.value[0] = magician_hp_switch;
166 	return 0;
167 }
168 
magician_set_hp(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)169 static int magician_set_hp(struct snd_kcontrol *kcontrol,
170 			     struct snd_ctl_elem_value *ucontrol)
171 {
172 	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
173 
174 	if (magician_hp_switch == ucontrol->value.integer.value[0])
175 		return 0;
176 
177 	magician_hp_switch = ucontrol->value.integer.value[0];
178 	magician_ext_control(&card->dapm);
179 	return 1;
180 }
181 
magician_get_spk(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)182 static int magician_get_spk(struct snd_kcontrol *kcontrol,
183 			    struct snd_ctl_elem_value *ucontrol)
184 {
185 	ucontrol->value.integer.value[0] = magician_spk_switch;
186 	return 0;
187 }
188 
magician_set_spk(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)189 static int magician_set_spk(struct snd_kcontrol *kcontrol,
190 			    struct snd_ctl_elem_value *ucontrol)
191 {
192 	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
193 
194 	if (magician_spk_switch == ucontrol->value.integer.value[0])
195 		return 0;
196 
197 	magician_spk_switch = ucontrol->value.integer.value[0];
198 	magician_ext_control(&card->dapm);
199 	return 1;
200 }
201 
magician_get_input(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)202 static int magician_get_input(struct snd_kcontrol *kcontrol,
203 			      struct snd_ctl_elem_value *ucontrol)
204 {
205 	ucontrol->value.enumerated.item[0] = magician_in_sel;
206 	return 0;
207 }
208 
magician_set_input(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)209 static int magician_set_input(struct snd_kcontrol *kcontrol,
210 			      struct snd_ctl_elem_value *ucontrol)
211 {
212 	if (magician_in_sel == ucontrol->value.enumerated.item[0])
213 		return 0;
214 
215 	magician_in_sel = ucontrol->value.enumerated.item[0];
216 
217 	switch (magician_in_sel) {
218 	case MAGICIAN_MIC:
219 		gpiod_set_value(gpiod_in_sel1, 1);
220 		break;
221 	case MAGICIAN_MIC_EXT:
222 		gpiod_set_value(gpiod_in_sel1, 0);
223 	}
224 
225 	return 1;
226 }
227 
magician_spk_power(struct snd_soc_dapm_widget * w,struct snd_kcontrol * k,int event)228 static int magician_spk_power(struct snd_soc_dapm_widget *w,
229 				struct snd_kcontrol *k, int event)
230 {
231 	gpiod_set_value(gpiod_spk_power, SND_SOC_DAPM_EVENT_ON(event));
232 	return 0;
233 }
234 
magician_hp_power(struct snd_soc_dapm_widget * w,struct snd_kcontrol * k,int event)235 static int magician_hp_power(struct snd_soc_dapm_widget *w,
236 				struct snd_kcontrol *k, int event)
237 {
238 	gpiod_set_value(gpiod_ep_power, SND_SOC_DAPM_EVENT_ON(event));
239 	return 0;
240 }
241 
magician_mic_bias(struct snd_soc_dapm_widget * w,struct snd_kcontrol * k,int event)242 static int magician_mic_bias(struct snd_soc_dapm_widget *w,
243 				struct snd_kcontrol *k, int event)
244 {
245 	gpiod_set_value(gpiod_mic_power, SND_SOC_DAPM_EVENT_ON(event));
246 	return 0;
247 }
248 
249 /* magician machine dapm widgets */
250 static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
251 	SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power),
252 	SND_SOC_DAPM_SPK("Speaker", magician_spk_power),
253 	SND_SOC_DAPM_MIC("Call Mic", magician_mic_bias),
254 	SND_SOC_DAPM_MIC("Headset Mic", magician_mic_bias),
255 };
256 
257 /* magician machine audio_map */
258 static const struct snd_soc_dapm_route audio_map[] = {
259 
260 	/* Headphone connected to VOUTL, VOUTR */
261 	{"Headphone Jack", NULL, "VOUTL"},
262 	{"Headphone Jack", NULL, "VOUTR"},
263 
264 	/* Speaker connected to VOUTL, VOUTR */
265 	{"Speaker", NULL, "VOUTL"},
266 	{"Speaker", NULL, "VOUTR"},
267 
268 	/* Mics are connected to VINM */
269 	{"VINM", NULL, "Headset Mic"},
270 	{"VINM", NULL, "Call Mic"},
271 };
272 
273 static const char * const input_select[] = {"Call Mic", "Headset Mic"};
274 static const struct soc_enum magician_in_sel_enum =
275 	SOC_ENUM_SINGLE_EXT(2, input_select);
276 
277 static const struct snd_kcontrol_new uda1380_magician_controls[] = {
278 	SOC_SINGLE_BOOL_EXT("Headphone Switch",
279 			(unsigned long)&magician_hp_switch,
280 			magician_get_hp, magician_set_hp),
281 	SOC_SINGLE_BOOL_EXT("Speaker Switch",
282 			(unsigned long)&magician_spk_switch,
283 			magician_get_spk, magician_set_spk),
284 	SOC_ENUM_EXT("Input Select", magician_in_sel_enum,
285 			magician_get_input, magician_set_input),
286 };
287 
288 /* magician digital audio interface glue - connects codec <--> CPU */
289 SND_SOC_DAILINK_DEFS(playback,
290 	DAILINK_COMP_ARRAY(COMP_CPU("pxa-ssp-dai.0")),
291 	DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018",
292 				      "uda1380-hifi-playback")),
293 	DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
294 
295 SND_SOC_DAILINK_DEFS(capture,
296 	DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
297 	DAILINK_COMP_ARRAY(COMP_CODEC("uda1380-codec.0-0018",
298 				      "uda1380-hifi-capture")),
299 	DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
300 
301 static struct snd_soc_dai_link magician_dai[] = {
302 {
303 	.name = "uda1380",
304 	.stream_name = "UDA1380 Playback",
305 	.ops = &magician_playback_ops,
306 	SND_SOC_DAILINK_REG(playback),
307 },
308 {
309 	.name = "uda1380",
310 	.stream_name = "UDA1380 Capture",
311 	.ops = &magician_capture_ops,
312 	SND_SOC_DAILINK_REG(capture),
313 }
314 };
315 
316 /* magician audio machine driver */
317 static struct snd_soc_card snd_soc_card_magician = {
318 	.name = "Magician",
319 	.owner = THIS_MODULE,
320 	.dai_link = magician_dai,
321 	.num_links = ARRAY_SIZE(magician_dai),
322 
323 	.controls = uda1380_magician_controls,
324 	.num_controls = ARRAY_SIZE(uda1380_magician_controls),
325 	.dapm_widgets = uda1380_dapm_widgets,
326 	.num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
327 	.dapm_routes = audio_map,
328 	.num_dapm_routes = ARRAY_SIZE(audio_map),
329 	.fully_routed = true,
330 };
331 
magician_audio_probe(struct platform_device * pdev)332 static int magician_audio_probe(struct platform_device *pdev)
333 {
334 	struct device *dev = &pdev->dev;
335 
336 	gpiod_spk_power = devm_gpiod_get(dev, "SPK_POWER", GPIOD_OUT_LOW);
337 	if (IS_ERR(gpiod_spk_power))
338 		return PTR_ERR(gpiod_spk_power);
339 	gpiod_ep_power = devm_gpiod_get(dev, "EP_POWER", GPIOD_OUT_LOW);
340 	if (IS_ERR(gpiod_ep_power))
341 		return PTR_ERR(gpiod_ep_power);
342 	gpiod_mic_power = devm_gpiod_get(dev, "MIC_POWER", GPIOD_OUT_LOW);
343 	if (IS_ERR(gpiod_mic_power))
344 		return PTR_ERR(gpiod_mic_power);
345 	gpiod_in_sel0 = devm_gpiod_get(dev, "IN_SEL0", GPIOD_OUT_HIGH);
346 	if (IS_ERR(gpiod_in_sel0))
347 		return PTR_ERR(gpiod_in_sel0);
348 	gpiod_in_sel1 = devm_gpiod_get(dev, "IN_SEL1", GPIOD_OUT_LOW);
349 	if (IS_ERR(gpiod_in_sel1))
350 		return PTR_ERR(gpiod_in_sel1);
351 
352 	snd_soc_card_magician.dev = &pdev->dev;
353 	return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_magician);
354 }
355 
356 static struct platform_driver magician_audio_driver = {
357 	.driver.name = "magician-audio",
358 	.driver.pm = &snd_soc_pm_ops,
359 	.probe = magician_audio_probe,
360 };
361 module_platform_driver(magician_audio_driver);
362 
363 MODULE_AUTHOR("Philipp Zabel");
364 MODULE_DESCRIPTION("ALSA SoC Magician");
365 MODULE_LICENSE("GPL");
366 MODULE_ALIAS("platform:magician-audio");
367