Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/tegra/tegra_wm8903.c
10817 views
1
/*
2
* tegra_wm8903.c - Tegra machine ASoC driver for boards using WM8903 codec.
3
*
4
* Author: Stephen Warren <[email protected]>
5
* Copyright (C) 2010-2011 - NVIDIA, Inc.
6
*
7
* Based on code copyright/by:
8
*
9
* (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
10
*
11
* Copyright 2007 Wolfson Microelectronics PLC.
12
* Author: Graeme Gregory
13
* [email protected] or [email protected]
14
*
15
* This program is free software; you can redistribute it and/or
16
* modify it under the terms of the GNU General Public License
17
* version 2 as published by the Free Software Foundation.
18
*
19
* This program is distributed in the hope that it will be useful, but
20
* WITHOUT ANY WARRANTY; without even the implied warranty of
21
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
* General Public License for more details.
23
*
24
* You should have received a copy of the GNU General Public License
25
* along with this program; if not, write to the Free Software
26
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27
* 02110-1301 USA
28
*
29
*/
30
31
#include <asm/mach-types.h>
32
33
#include <linux/module.h>
34
#include <linux/platform_device.h>
35
#include <linux/slab.h>
36
#include <linux/gpio.h>
37
38
#include <mach/tegra_wm8903_pdata.h>
39
40
#include <sound/core.h>
41
#include <sound/jack.h>
42
#include <sound/pcm.h>
43
#include <sound/pcm_params.h>
44
#include <sound/soc.h>
45
46
#include "../codecs/wm8903.h"
47
48
#include "tegra_das.h"
49
#include "tegra_i2s.h"
50
#include "tegra_pcm.h"
51
#include "tegra_asoc_utils.h"
52
53
#define DRV_NAME "tegra-snd-wm8903"
54
55
#define GPIO_SPKR_EN BIT(0)
56
#define GPIO_HP_MUTE BIT(1)
57
#define GPIO_INT_MIC_EN BIT(2)
58
#define GPIO_EXT_MIC_EN BIT(3)
59
60
struct tegra_wm8903 {
61
struct tegra_asoc_utils_data util_data;
62
struct tegra_wm8903_platform_data *pdata;
63
int gpio_requested;
64
};
65
66
static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
67
struct snd_pcm_hw_params *params)
68
{
69
struct snd_soc_pcm_runtime *rtd = substream->private_data;
70
struct snd_soc_dai *codec_dai = rtd->codec_dai;
71
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
72
struct snd_soc_codec *codec = rtd->codec;
73
struct snd_soc_card *card = codec->card;
74
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
75
int srate, mclk;
76
int err;
77
78
srate = params_rate(params);
79
switch (srate) {
80
case 64000:
81
case 88200:
82
case 96000:
83
mclk = 128 * srate;
84
break;
85
default:
86
mclk = 256 * srate;
87
break;
88
}
89
/* FIXME: Codec only requires >= 3MHz if OSR==0 */
90
while (mclk < 6000000)
91
mclk *= 2;
92
93
err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
94
if (err < 0) {
95
dev_err(card->dev, "Can't configure clocks\n");
96
return err;
97
}
98
99
err = snd_soc_dai_set_fmt(codec_dai,
100
SND_SOC_DAIFMT_I2S |
101
SND_SOC_DAIFMT_NB_NF |
102
SND_SOC_DAIFMT_CBS_CFS);
103
if (err < 0) {
104
dev_err(card->dev, "codec_dai fmt not set\n");
105
return err;
106
}
107
108
err = snd_soc_dai_set_fmt(cpu_dai,
109
SND_SOC_DAIFMT_I2S |
110
SND_SOC_DAIFMT_NB_NF |
111
SND_SOC_DAIFMT_CBS_CFS);
112
if (err < 0) {
113
dev_err(card->dev, "cpu_dai fmt not set\n");
114
return err;
115
}
116
117
err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
118
SND_SOC_CLOCK_IN);
119
if (err < 0) {
120
dev_err(card->dev, "codec_dai clock not set\n");
121
return err;
122
}
123
124
return 0;
125
}
126
127
static struct snd_soc_ops tegra_wm8903_ops = {
128
.hw_params = tegra_wm8903_hw_params,
129
};
130
131
static struct snd_soc_jack tegra_wm8903_hp_jack;
132
133
static struct snd_soc_jack_pin tegra_wm8903_hp_jack_pins[] = {
134
{
135
.pin = "Headphone Jack",
136
.mask = SND_JACK_HEADPHONE,
137
},
138
};
139
140
static struct snd_soc_jack_gpio tegra_wm8903_hp_jack_gpio = {
141
.name = "headphone detect",
142
.report = SND_JACK_HEADPHONE,
143
.debounce_time = 150,
144
.invert = 1,
145
};
146
147
static struct snd_soc_jack tegra_wm8903_mic_jack;
148
149
static struct snd_soc_jack_pin tegra_wm8903_mic_jack_pins[] = {
150
{
151
.pin = "Mic Jack",
152
.mask = SND_JACK_MICROPHONE,
153
},
154
};
155
156
static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w,
157
struct snd_kcontrol *k, int event)
158
{
159
struct snd_soc_dapm_context *dapm = w->dapm;
160
struct snd_soc_card *card = dapm->card;
161
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
162
struct tegra_wm8903_platform_data *pdata = machine->pdata;
163
164
if (!(machine->gpio_requested & GPIO_SPKR_EN))
165
return 0;
166
167
gpio_set_value_cansleep(pdata->gpio_spkr_en,
168
SND_SOC_DAPM_EVENT_ON(event));
169
170
return 0;
171
}
172
173
static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w,
174
struct snd_kcontrol *k, int event)
175
{
176
struct snd_soc_dapm_context *dapm = w->dapm;
177
struct snd_soc_card *card = dapm->card;
178
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
179
struct tegra_wm8903_platform_data *pdata = machine->pdata;
180
181
if (!(machine->gpio_requested & GPIO_HP_MUTE))
182
return 0;
183
184
gpio_set_value_cansleep(pdata->gpio_hp_mute,
185
!SND_SOC_DAPM_EVENT_ON(event));
186
187
return 0;
188
}
189
190
static const struct snd_soc_dapm_widget tegra_wm8903_dapm_widgets[] = {
191
SND_SOC_DAPM_SPK("Int Spk", tegra_wm8903_event_int_spk),
192
SND_SOC_DAPM_HP("Headphone Jack", tegra_wm8903_event_hp),
193
SND_SOC_DAPM_MIC("Mic Jack", NULL),
194
};
195
196
static const struct snd_soc_dapm_route harmony_audio_map[] = {
197
{"Headphone Jack", NULL, "HPOUTR"},
198
{"Headphone Jack", NULL, "HPOUTL"},
199
{"Int Spk", NULL, "ROP"},
200
{"Int Spk", NULL, "RON"},
201
{"Int Spk", NULL, "LOP"},
202
{"Int Spk", NULL, "LON"},
203
{"Mic Bias", NULL, "Mic Jack"},
204
{"IN1L", NULL, "Mic Bias"},
205
};
206
207
static const struct snd_soc_dapm_route seaboard_audio_map[] = {
208
{"Headphone Jack", NULL, "HPOUTR"},
209
{"Headphone Jack", NULL, "HPOUTL"},
210
{"Int Spk", NULL, "ROP"},
211
{"Int Spk", NULL, "RON"},
212
{"Int Spk", NULL, "LOP"},
213
{"Int Spk", NULL, "LON"},
214
{"Mic Bias", NULL, "Mic Jack"},
215
{"IN1R", NULL, "Mic Bias"},
216
};
217
218
static const struct snd_soc_dapm_route kaen_audio_map[] = {
219
{"Headphone Jack", NULL, "HPOUTR"},
220
{"Headphone Jack", NULL, "HPOUTL"},
221
{"Int Spk", NULL, "ROP"},
222
{"Int Spk", NULL, "RON"},
223
{"Int Spk", NULL, "LOP"},
224
{"Int Spk", NULL, "LON"},
225
{"Mic Bias", NULL, "Mic Jack"},
226
{"IN2R", NULL, "Mic Bias"},
227
};
228
229
static const struct snd_soc_dapm_route aebl_audio_map[] = {
230
{"Headphone Jack", NULL, "HPOUTR"},
231
{"Headphone Jack", NULL, "HPOUTL"},
232
{"Int Spk", NULL, "LINEOUTR"},
233
{"Int Spk", NULL, "LINEOUTL"},
234
{"Mic Bias", NULL, "Mic Jack"},
235
{"IN1R", NULL, "Mic Bias"},
236
};
237
238
static const struct snd_kcontrol_new tegra_wm8903_controls[] = {
239
SOC_DAPM_PIN_SWITCH("Int Spk"),
240
};
241
242
static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
243
{
244
struct snd_soc_codec *codec = rtd->codec;
245
struct snd_soc_dapm_context *dapm = &codec->dapm;
246
struct snd_soc_card *card = codec->card;
247
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
248
struct tegra_wm8903_platform_data *pdata = machine->pdata;
249
int ret;
250
251
if (gpio_is_valid(pdata->gpio_spkr_en)) {
252
ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
253
if (ret) {
254
dev_err(card->dev, "cannot get spkr_en gpio\n");
255
return ret;
256
}
257
machine->gpio_requested |= GPIO_SPKR_EN;
258
259
gpio_direction_output(pdata->gpio_spkr_en, 0);
260
}
261
262
if (gpio_is_valid(pdata->gpio_hp_mute)) {
263
ret = gpio_request(pdata->gpio_hp_mute, "hp_mute");
264
if (ret) {
265
dev_err(card->dev, "cannot get hp_mute gpio\n");
266
return ret;
267
}
268
machine->gpio_requested |= GPIO_HP_MUTE;
269
270
gpio_direction_output(pdata->gpio_hp_mute, 0);
271
}
272
273
if (gpio_is_valid(pdata->gpio_int_mic_en)) {
274
ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
275
if (ret) {
276
dev_err(card->dev, "cannot get int_mic_en gpio\n");
277
return ret;
278
}
279
machine->gpio_requested |= GPIO_INT_MIC_EN;
280
281
/* Disable int mic; enable signal is active-high */
282
gpio_direction_output(pdata->gpio_int_mic_en, 0);
283
}
284
285
if (gpio_is_valid(pdata->gpio_ext_mic_en)) {
286
ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
287
if (ret) {
288
dev_err(card->dev, "cannot get ext_mic_en gpio\n");
289
return ret;
290
}
291
machine->gpio_requested |= GPIO_EXT_MIC_EN;
292
293
/* Enable ext mic; enable signal is active-low */
294
gpio_direction_output(pdata->gpio_ext_mic_en, 0);
295
}
296
297
if (gpio_is_valid(pdata->gpio_hp_det)) {
298
tegra_wm8903_hp_jack_gpio.gpio = pdata->gpio_hp_det;
299
snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
300
&tegra_wm8903_hp_jack);
301
snd_soc_jack_add_pins(&tegra_wm8903_hp_jack,
302
ARRAY_SIZE(tegra_wm8903_hp_jack_pins),
303
tegra_wm8903_hp_jack_pins);
304
snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack,
305
1,
306
&tegra_wm8903_hp_jack_gpio);
307
}
308
309
snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
310
&tegra_wm8903_mic_jack);
311
snd_soc_jack_add_pins(&tegra_wm8903_mic_jack,
312
ARRAY_SIZE(tegra_wm8903_mic_jack_pins),
313
tegra_wm8903_mic_jack_pins);
314
wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
315
0);
316
317
snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
318
319
/* FIXME: Calculate automatically based on DAPM routes? */
320
if (!machine_is_harmony() && !machine_is_ventana())
321
snd_soc_dapm_nc_pin(dapm, "IN1L");
322
if (!machine_is_seaboard() && !machine_is_aebl())
323
snd_soc_dapm_nc_pin(dapm, "IN1R");
324
snd_soc_dapm_nc_pin(dapm, "IN2L");
325
if (!machine_is_kaen())
326
snd_soc_dapm_nc_pin(dapm, "IN2R");
327
snd_soc_dapm_nc_pin(dapm, "IN3L");
328
snd_soc_dapm_nc_pin(dapm, "IN3R");
329
330
if (machine_is_aebl()) {
331
snd_soc_dapm_nc_pin(dapm, "LON");
332
snd_soc_dapm_nc_pin(dapm, "RON");
333
snd_soc_dapm_nc_pin(dapm, "ROP");
334
snd_soc_dapm_nc_pin(dapm, "LOP");
335
} else {
336
snd_soc_dapm_nc_pin(dapm, "LINEOUTR");
337
snd_soc_dapm_nc_pin(dapm, "LINEOUTL");
338
}
339
340
snd_soc_dapm_sync(dapm);
341
342
return 0;
343
}
344
345
static struct snd_soc_dai_link tegra_wm8903_dai = {
346
.name = "WM8903",
347
.stream_name = "WM8903 PCM",
348
.codec_name = "wm8903.0-001a",
349
.platform_name = "tegra-pcm-audio",
350
.cpu_dai_name = "tegra-i2s.0",
351
.codec_dai_name = "wm8903-hifi",
352
.init = tegra_wm8903_init,
353
.ops = &tegra_wm8903_ops,
354
};
355
356
static struct snd_soc_card snd_soc_tegra_wm8903 = {
357
.name = "tegra-wm8903",
358
.dai_link = &tegra_wm8903_dai,
359
.num_links = 1,
360
361
.controls = tegra_wm8903_controls,
362
.num_controls = ARRAY_SIZE(tegra_wm8903_controls),
363
.dapm_widgets = tegra_wm8903_dapm_widgets,
364
.num_dapm_widgets = ARRAY_SIZE(tegra_wm8903_dapm_widgets),
365
};
366
367
static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
368
{
369
struct snd_soc_card *card = &snd_soc_tegra_wm8903;
370
struct tegra_wm8903 *machine;
371
struct tegra_wm8903_platform_data *pdata;
372
int ret;
373
374
pdata = pdev->dev.platform_data;
375
if (!pdata) {
376
dev_err(&pdev->dev, "No platform data supplied\n");
377
return -EINVAL;
378
}
379
380
machine = kzalloc(sizeof(struct tegra_wm8903), GFP_KERNEL);
381
if (!machine) {
382
dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
383
return -ENOMEM;
384
}
385
386
machine->pdata = pdata;
387
388
ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
389
if (ret)
390
goto err_free_machine;
391
392
card->dev = &pdev->dev;
393
platform_set_drvdata(pdev, card);
394
snd_soc_card_set_drvdata(card, machine);
395
396
if (machine_is_harmony() || machine_is_ventana()) {
397
card->dapm_routes = harmony_audio_map;
398
card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map);
399
} else if (machine_is_seaboard()) {
400
card->dapm_routes = seaboard_audio_map;
401
card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map);
402
} else if (machine_is_kaen()) {
403
card->dapm_routes = kaen_audio_map;
404
card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map);
405
} else {
406
card->dapm_routes = aebl_audio_map;
407
card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map);
408
}
409
410
ret = snd_soc_register_card(card);
411
if (ret) {
412
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
413
ret);
414
goto err_fini_utils;
415
}
416
417
return 0;
418
419
err_fini_utils:
420
tegra_asoc_utils_fini(&machine->util_data);
421
err_free_machine:
422
kfree(machine);
423
return ret;
424
}
425
426
static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev)
427
{
428
struct snd_soc_card *card = platform_get_drvdata(pdev);
429
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
430
struct tegra_wm8903_platform_data *pdata = machine->pdata;
431
432
snd_soc_unregister_card(card);
433
434
tegra_asoc_utils_fini(&machine->util_data);
435
436
if (machine->gpio_requested & GPIO_EXT_MIC_EN)
437
gpio_free(pdata->gpio_ext_mic_en);
438
if (machine->gpio_requested & GPIO_INT_MIC_EN)
439
gpio_free(pdata->gpio_int_mic_en);
440
if (machine->gpio_requested & GPIO_HP_MUTE)
441
gpio_free(pdata->gpio_hp_mute);
442
if (machine->gpio_requested & GPIO_SPKR_EN)
443
gpio_free(pdata->gpio_spkr_en);
444
445
kfree(machine);
446
447
return 0;
448
}
449
450
static struct platform_driver tegra_wm8903_driver = {
451
.driver = {
452
.name = DRV_NAME,
453
.owner = THIS_MODULE,
454
.pm = &snd_soc_pm_ops,
455
},
456
.probe = tegra_wm8903_driver_probe,
457
.remove = __devexit_p(tegra_wm8903_driver_remove),
458
};
459
460
static int __init tegra_wm8903_modinit(void)
461
{
462
return platform_driver_register(&tegra_wm8903_driver);
463
}
464
module_init(tegra_wm8903_modinit);
465
466
static void __exit tegra_wm8903_modexit(void)
467
{
468
platform_driver_unregister(&tegra_wm8903_driver);
469
}
470
module_exit(tegra_wm8903_modexit);
471
472
MODULE_AUTHOR("Stephen Warren <[email protected]>");
473
MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver");
474
MODULE_LICENSE("GPL");
475
MODULE_ALIAS("platform:" DRV_NAME);
476
477