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