Loading...
Note: File does not exist in v6.13.7.
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
33static int magician_hp_switch;
34static int magician_spk_switch = 1;
35static int magician_in_sel = MAGICIAN_MIC;
36
37static struct gpio_desc *gpiod_spk_power, *gpiod_ep_power, *gpiod_mic_power;
38static struct gpio_desc *gpiod_in_sel0, *gpiod_in_sel1;
39
40static 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
70static 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 */
83static 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 */
121static 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
152static const struct snd_soc_ops magician_capture_ops = {
153 .startup = magician_startup,
154 .hw_params = magician_capture_hw_params,
155};
156
157static const struct snd_soc_ops magician_playback_ops = {
158 .startup = magician_startup,
159 .hw_params = magician_playback_hw_params,
160};
161
162static 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
169static 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
182static 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
189static 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
202static 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
209static 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
228static 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
235static 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
242static 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 */
250static 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 */
258static 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
273static const char * const input_select[] = {"Call Mic", "Headset Mic"};
274static const struct soc_enum magician_in_sel_enum =
275 SOC_ENUM_SINGLE_EXT(2, input_select);
276
277static 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 */
289SND_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
295SND_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
301static 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 */
317static 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
332static 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
356static struct platform_driver magician_audio_driver = {
357 .driver.name = "magician-audio",
358 .driver.pm = &snd_soc_pm_ops,
359 .probe = magician_audio_probe,
360};
361module_platform_driver(magician_audio_driver);
362
363MODULE_AUTHOR("Philipp Zabel");
364MODULE_DESCRIPTION("ALSA SoC Magician");
365MODULE_LICENSE("GPL");
366MODULE_ALIAS("platform:magician-audio");