Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/pxa/spitz.c
26388 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita
4
*
5
* Copyright 2005 Wolfson Microelectronics PLC.
6
* Copyright 2005 Openedhand Ltd.
7
*
8
* Authors: Liam Girdwood <[email protected]>
9
* Richard Purdie <[email protected]>
10
*/
11
12
#include <linux/module.h>
13
#include <linux/moduleparam.h>
14
#include <linux/timer.h>
15
#include <linux/interrupt.h>
16
#include <linux/platform_device.h>
17
#include <linux/gpio/consumer.h>
18
#include <sound/core.h>
19
#include <sound/pcm.h>
20
#include <sound/soc.h>
21
22
#include <asm/mach-types.h>
23
#include "../codecs/wm8750.h"
24
#include "pxa2xx-i2s.h"
25
26
#define SPITZ_HP 0
27
#define SPITZ_MIC 1
28
#define SPITZ_LINE 2
29
#define SPITZ_HEADSET 3
30
#define SPITZ_HP_OFF 4
31
#define SPITZ_SPK_ON 0
32
#define SPITZ_SPK_OFF 1
33
34
/* audio clock in Hz - rounded from 12.235MHz */
35
#define SPITZ_AUDIO_CLOCK 12288000
36
37
static int spitz_jack_func;
38
static int spitz_spk_func;
39
static struct gpio_desc *gpiod_mic, *gpiod_mute_l, *gpiod_mute_r;
40
41
static void spitz_ext_control(struct snd_soc_dapm_context *dapm)
42
{
43
snd_soc_dapm_mutex_lock(dapm);
44
45
if (spitz_spk_func == SPITZ_SPK_ON)
46
snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
47
else
48
snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
49
50
/* set up jack connection */
51
switch (spitz_jack_func) {
52
case SPITZ_HP:
53
/* enable and unmute hp jack, disable mic bias */
54
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
55
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
56
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
57
snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
58
gpiod_set_value(gpiod_mute_l, 1);
59
gpiod_set_value(gpiod_mute_r, 1);
60
break;
61
case SPITZ_MIC:
62
/* enable mic jack and bias, mute hp */
63
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
64
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
65
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
66
snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
67
gpiod_set_value(gpiod_mute_l, 0);
68
gpiod_set_value(gpiod_mute_r, 0);
69
break;
70
case SPITZ_LINE:
71
/* enable line jack, disable mic bias and mute hp */
72
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
73
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
74
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
75
snd_soc_dapm_enable_pin_unlocked(dapm, "Line Jack");
76
gpiod_set_value(gpiod_mute_l, 0);
77
gpiod_set_value(gpiod_mute_r, 0);
78
break;
79
case SPITZ_HEADSET:
80
/* enable and unmute headset jack enable mic bias, mute L hp */
81
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
82
snd_soc_dapm_enable_pin_unlocked(dapm, "Mic Jack");
83
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
84
snd_soc_dapm_enable_pin_unlocked(dapm, "Headset Jack");
85
gpiod_set_value(gpiod_mute_l, 0);
86
gpiod_set_value(gpiod_mute_r, 1);
87
break;
88
case SPITZ_HP_OFF:
89
90
/* jack removed, everything off */
91
snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
92
snd_soc_dapm_disable_pin_unlocked(dapm, "Headset Jack");
93
snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Jack");
94
snd_soc_dapm_disable_pin_unlocked(dapm, "Line Jack");
95
gpiod_set_value(gpiod_mute_l, 0);
96
gpiod_set_value(gpiod_mute_r, 0);
97
break;
98
}
99
100
snd_soc_dapm_sync_unlocked(dapm);
101
102
snd_soc_dapm_mutex_unlock(dapm);
103
}
104
105
static int spitz_startup(struct snd_pcm_substream *substream)
106
{
107
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
108
109
/* check the jack status at stream startup */
110
spitz_ext_control(&rtd->card->dapm);
111
112
return 0;
113
}
114
115
static int spitz_hw_params(struct snd_pcm_substream *substream,
116
struct snd_pcm_hw_params *params)
117
{
118
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
119
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
120
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
121
unsigned int clk = 0;
122
int ret = 0;
123
124
switch (params_rate(params)) {
125
case 8000:
126
case 16000:
127
case 48000:
128
case 96000:
129
clk = 12288000;
130
break;
131
case 11025:
132
case 22050:
133
case 44100:
134
clk = 11289600;
135
break;
136
}
137
138
/* set the codec system clock for DAC and ADC */
139
ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
140
SND_SOC_CLOCK_IN);
141
if (ret < 0)
142
return ret;
143
144
/* set the I2S system clock as input (unused) */
145
ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
146
SND_SOC_CLOCK_IN);
147
if (ret < 0)
148
return ret;
149
150
return 0;
151
}
152
153
static const struct snd_soc_ops spitz_ops = {
154
.startup = spitz_startup,
155
.hw_params = spitz_hw_params,
156
};
157
158
static int spitz_get_jack(struct snd_kcontrol *kcontrol,
159
struct snd_ctl_elem_value *ucontrol)
160
{
161
ucontrol->value.enumerated.item[0] = spitz_jack_func;
162
return 0;
163
}
164
165
static int spitz_set_jack(struct snd_kcontrol *kcontrol,
166
struct snd_ctl_elem_value *ucontrol)
167
{
168
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
169
170
if (spitz_jack_func == ucontrol->value.enumerated.item[0])
171
return 0;
172
173
spitz_jack_func = ucontrol->value.enumerated.item[0];
174
spitz_ext_control(&card->dapm);
175
return 1;
176
}
177
178
static int spitz_get_spk(struct snd_kcontrol *kcontrol,
179
struct snd_ctl_elem_value *ucontrol)
180
{
181
ucontrol->value.enumerated.item[0] = spitz_spk_func;
182
return 0;
183
}
184
185
static int spitz_set_spk(struct snd_kcontrol *kcontrol,
186
struct snd_ctl_elem_value *ucontrol)
187
{
188
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
189
190
if (spitz_spk_func == ucontrol->value.enumerated.item[0])
191
return 0;
192
193
spitz_spk_func = ucontrol->value.enumerated.item[0];
194
spitz_ext_control(&card->dapm);
195
return 1;
196
}
197
198
static int spitz_mic_bias(struct snd_soc_dapm_widget *w,
199
struct snd_kcontrol *k, int event)
200
{
201
gpiod_set_value_cansleep(gpiod_mic, SND_SOC_DAPM_EVENT_ON(event));
202
return 0;
203
}
204
205
/* spitz machine dapm widgets */
206
static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
207
SND_SOC_DAPM_HP("Headphone Jack", NULL),
208
SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
209
SND_SOC_DAPM_SPK("Ext Spk", NULL),
210
SND_SOC_DAPM_LINE("Line Jack", NULL),
211
212
/* headset is a mic and mono headphone */
213
SND_SOC_DAPM_HP("Headset Jack", NULL),
214
};
215
216
/* Spitz machine audio_map */
217
static const struct snd_soc_dapm_route spitz_audio_map[] = {
218
219
/* headphone connected to LOUT1, ROUT1 */
220
{"Headphone Jack", NULL, "LOUT1"},
221
{"Headphone Jack", NULL, "ROUT1"},
222
223
/* headset connected to ROUT1 and LINPUT1 with bias (def below) */
224
{"Headset Jack", NULL, "ROUT1"},
225
226
/* ext speaker connected to LOUT2, ROUT2 */
227
{"Ext Spk", NULL, "ROUT2"},
228
{"Ext Spk", NULL, "LOUT2"},
229
230
/* mic is connected to input 1 - with bias */
231
{"LINPUT1", NULL, "Mic Bias"},
232
{"Mic Bias", NULL, "Mic Jack"},
233
234
/* line is connected to input 1 - no bias */
235
{"LINPUT1", NULL, "Line Jack"},
236
};
237
238
static const char * const jack_function[] = {"Headphone", "Mic", "Line",
239
"Headset", "Off"};
240
static const char * const spk_function[] = {"On", "Off"};
241
static const struct soc_enum spitz_enum[] = {
242
SOC_ENUM_SINGLE_EXT(5, jack_function),
243
SOC_ENUM_SINGLE_EXT(2, spk_function),
244
};
245
246
static const struct snd_kcontrol_new wm8750_spitz_controls[] = {
247
SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack,
248
spitz_set_jack),
249
SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk,
250
spitz_set_spk),
251
};
252
253
/* spitz digital audio interface glue - connects codec <--> CPU */
254
SND_SOC_DAILINK_DEFS(wm8750,
255
DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
256
DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001b", "wm8750-hifi")),
257
DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
258
259
static struct snd_soc_dai_link spitz_dai = {
260
.name = "wm8750",
261
.stream_name = "WM8750",
262
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
263
SND_SOC_DAIFMT_CBC_CFC,
264
.ops = &spitz_ops,
265
SND_SOC_DAILINK_REG(wm8750),
266
};
267
268
/* spitz audio machine driver */
269
static struct snd_soc_card snd_soc_spitz = {
270
.name = "Spitz",
271
.owner = THIS_MODULE,
272
.dai_link = &spitz_dai,
273
.num_links = 1,
274
275
.controls = wm8750_spitz_controls,
276
.num_controls = ARRAY_SIZE(wm8750_spitz_controls),
277
.dapm_widgets = wm8750_dapm_widgets,
278
.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
279
.dapm_routes = spitz_audio_map,
280
.num_dapm_routes = ARRAY_SIZE(spitz_audio_map),
281
.fully_routed = true,
282
};
283
284
static int spitz_probe(struct platform_device *pdev)
285
{
286
struct snd_soc_card *card = &snd_soc_spitz;
287
int ret;
288
289
gpiod_mic = devm_gpiod_get(&pdev->dev, "mic", GPIOD_OUT_LOW);
290
if (IS_ERR(gpiod_mic))
291
return PTR_ERR(gpiod_mic);
292
gpiod_mute_l = devm_gpiod_get(&pdev->dev, "mute-l", GPIOD_OUT_LOW);
293
if (IS_ERR(gpiod_mute_l))
294
return PTR_ERR(gpiod_mute_l);
295
gpiod_mute_r = devm_gpiod_get(&pdev->dev, "mute-r", GPIOD_OUT_LOW);
296
if (IS_ERR(gpiod_mute_r))
297
return PTR_ERR(gpiod_mute_r);
298
299
card->dev = &pdev->dev;
300
301
ret = devm_snd_soc_register_card(&pdev->dev, card);
302
if (ret)
303
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
304
ret);
305
306
return ret;
307
}
308
309
static struct platform_driver spitz_driver = {
310
.driver = {
311
.name = "spitz-audio",
312
.pm = &snd_soc_pm_ops,
313
},
314
.probe = spitz_probe,
315
};
316
317
module_platform_driver(spitz_driver);
318
319
MODULE_AUTHOR("Richard Purdie");
320
MODULE_DESCRIPTION("ALSA SoC Spitz");
321
MODULE_LICENSE("GPL");
322
MODULE_ALIAS("platform:spitz-audio");
323
324