aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorAnatol Pomozov <anatol.pomozov@gmail.com>2014-12-30 14:12:35 -0500
committerMark Brown <broonie@kernel.org>2015-01-07 13:54:23 -0500
commita37f1b8fdc912600c24f9d0d45d7046e50a031e4 (patch)
tree9dc750a6fdefa969f0b272cad1368c76ea7991a8 /sound
parent97bf6af1f928216fd6c5a66e8a57bfa95a659672 (diff)
ASoC: tegra: Add platform driver for rt5677 audio codec
The driver supports NVIDIA Tegra Ryu board Sponsored: Google ChromeOS Signed-off-by: Anatol Pomozov <anatol.pomozov@gmail.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/tegra/Kconfig10
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra_rt5677.c347
3 files changed, 359 insertions, 0 deletions
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 31198cf7f88d..a6768f832c6f 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -128,3 +128,13 @@ config SND_SOC_TEGRA_MAX98090
128 help 128 help
129 Say Y or M here if you want to add support for SoC audio on Tegra 129 Say Y or M here if you want to add support for SoC audio on Tegra
130 boards using the MAX98090 codec, such as Venice2. 130 boards using the MAX98090 codec, such as Venice2.
131
132config SND_SOC_TEGRA_RT5677
133 tristate "SoC Audio support for Tegra boards using a RT5677 codec"
134 depends on SND_SOC_TEGRA && I2C && GPIOLIB
135 select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
136 select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
137 select SND_SOC_RT5677
138 help
139 Say Y or M here if you want to add support for SoC audio on Tegra
140 boards using the RT5677 codec, such as Ryu.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 5ae588cd96c4..9171655ad843 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
19 19
20# Tegra machine Support 20# Tegra machine Support
21snd-soc-tegra-rt5640-objs := tegra_rt5640.o 21snd-soc-tegra-rt5640-objs := tegra_rt5640.o
22snd-soc-tegra-rt5677-objs := tegra_rt5677.o
22snd-soc-tegra-wm8753-objs := tegra_wm8753.o 23snd-soc-tegra-wm8753-objs := tegra_wm8753.o
23snd-soc-tegra-wm8903-objs := tegra_wm8903.o 24snd-soc-tegra-wm8903-objs := tegra_wm8903.o
24snd-soc-tegra-wm9712-objs := tegra_wm9712.o 25snd-soc-tegra-wm9712-objs := tegra_wm9712.o
@@ -27,6 +28,7 @@ snd-soc-tegra-alc5632-objs := tegra_alc5632.o
27snd-soc-tegra-max98090-objs := tegra_max98090.o 28snd-soc-tegra-max98090-objs := tegra_max98090.o
28 29
29obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o 30obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o
31obj-$(CONFIG_SND_SOC_TEGRA_RT5677) += snd-soc-tegra-rt5677.o
30obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o 32obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o
31obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o 33obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
32obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o 34obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o
diff --git a/sound/soc/tegra/tegra_rt5677.c b/sound/soc/tegra/tegra_rt5677.c
new file mode 100644
index 000000000000..e4cf978a6e3a
--- /dev/null
+++ b/sound/soc/tegra/tegra_rt5677.c
@@ -0,0 +1,347 @@
1/*
2* tegra_rt5677.c - Tegra machine ASoC driver for boards using RT5677 codec.
3 *
4 * Copyright (c) 2014, The Chromium OS Authors. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Based on code copyright/by:
19 *
20 * Copyright (C) 2010-2012 - NVIDIA, Inc.
21 * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
22 * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
23 * Copyright 2007 Wolfson Microelectronics PLC.
24 */
25
26#include <linux/module.h>
27#include <linux/platform_device.h>
28#include <linux/slab.h>
29#include <linux/gpio.h>
30#include <linux/of_gpio.h>
31
32#include <sound/core.h>
33#include <sound/jack.h>
34#include <sound/pcm.h>
35#include <sound/pcm_params.h>
36#include <sound/soc.h>
37
38#include "../codecs/rt5677.h"
39
40#include "tegra_asoc_utils.h"
41
42#define DRV_NAME "tegra-snd-rt5677"
43
44struct tegra_rt5677 {
45 struct tegra_asoc_utils_data util_data;
46 int gpio_hp_det;
47 int gpio_hp_en;
48 int gpio_mic_present;
49 int gpio_dmic_clk_en;
50};
51
52static int tegra_rt5677_asoc_hw_params(struct snd_pcm_substream *substream,
53 struct snd_pcm_hw_params *params)
54{
55 struct snd_soc_pcm_runtime *rtd = substream->private_data;
56 struct snd_soc_dai *codec_dai = rtd->codec_dai;
57 struct snd_soc_card *card = rtd->card;
58 struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card);
59 int srate, mclk, err;
60
61 srate = params_rate(params);
62 mclk = 256 * srate;
63
64 err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
65 if (err < 0) {
66 dev_err(card->dev, "Can't configure clocks\n");
67 return err;
68 }
69
70 err = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, mclk,
71 SND_SOC_CLOCK_IN);
72 if (err < 0) {
73 dev_err(card->dev, "codec_dai clock not set\n");
74 return err;
75 }
76
77 return 0;
78}
79
80static int tegra_rt5677_event_hp(struct snd_soc_dapm_widget *w,
81 struct snd_kcontrol *k, int event)
82{
83 struct snd_soc_dapm_context *dapm = w->dapm;
84 struct snd_soc_card *card = dapm->card;
85 struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card);
86
87 if (!gpio_is_valid(machine->gpio_hp_en))
88 return 0;
89
90 gpio_set_value_cansleep(machine->gpio_hp_en,
91 SND_SOC_DAPM_EVENT_ON(event));
92
93 return 0;
94}
95
96static struct snd_soc_ops tegra_rt5677_ops = {
97 .hw_params = tegra_rt5677_asoc_hw_params,
98};
99
100static struct snd_soc_jack tegra_rt5677_hp_jack;
101
102static struct snd_soc_jack_pin tegra_rt5677_hp_jack_pins = {
103 .pin = "Headphone",
104 .mask = SND_JACK_HEADPHONE,
105};
106static struct snd_soc_jack_gpio tegra_rt5677_hp_jack_gpio = {
107 .name = "Headphone detection",
108 .report = SND_JACK_HEADPHONE,
109 .debounce_time = 150,
110};
111
112static struct snd_soc_jack tegra_rt5677_mic_jack;
113
114static struct snd_soc_jack_pin tegra_rt5677_mic_jack_pins = {
115 .pin = "Headset Mic",
116 .mask = SND_JACK_MICROPHONE,
117};
118
119static struct snd_soc_jack_gpio tegra_rt5677_mic_jack_gpio = {
120 .name = "Headset Mic detection",
121 .report = SND_JACK_MICROPHONE,
122 .debounce_time = 150,
123 .invert = 1
124};
125
126static const struct snd_soc_dapm_widget tegra_rt5677_dapm_widgets[] = {
127 SND_SOC_DAPM_SPK("Speaker", NULL),
128 SND_SOC_DAPM_HP("Headphone", tegra_rt5677_event_hp),
129 SND_SOC_DAPM_MIC("Headset Mic", NULL),
130 SND_SOC_DAPM_MIC("Internal Mic 1", NULL),
131 SND_SOC_DAPM_MIC("Internal Mic 2", NULL),
132};
133
134static const struct snd_kcontrol_new tegra_rt5677_controls[] = {
135 SOC_DAPM_PIN_SWITCH("Speaker"),
136 SOC_DAPM_PIN_SWITCH("Headphone"),
137 SOC_DAPM_PIN_SWITCH("Headset Mic"),
138 SOC_DAPM_PIN_SWITCH("Internal Mic 1"),
139 SOC_DAPM_PIN_SWITCH("Internal Mic 2"),
140};
141
142static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd)
143{
144 struct snd_soc_dai *codec_dai = rtd->codec_dai;
145 struct snd_soc_codec *codec = codec_dai->codec;
146 struct snd_soc_dapm_context *dapm = &codec->dapm;
147 struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(rtd->card);
148
149 snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
150 &tegra_rt5677_hp_jack);
151 snd_soc_jack_add_pins(&tegra_rt5677_hp_jack, 1,
152 &tegra_rt5677_hp_jack_pins);
153
154 if (gpio_is_valid(machine->gpio_hp_det)) {
155 tegra_rt5677_hp_jack_gpio.gpio = machine->gpio_hp_det;
156 snd_soc_jack_add_gpios(&tegra_rt5677_hp_jack, 1,
157 &tegra_rt5677_hp_jack_gpio);
158 }
159
160
161 snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
162 &tegra_rt5677_mic_jack);
163 snd_soc_jack_add_pins(&tegra_rt5677_mic_jack, 1,
164 &tegra_rt5677_mic_jack_pins);
165
166 if (gpio_is_valid(machine->gpio_mic_present)) {
167 tegra_rt5677_mic_jack_gpio.gpio = machine->gpio_mic_present;
168 snd_soc_jack_add_gpios(&tegra_rt5677_mic_jack, 1,
169 &tegra_rt5677_mic_jack_gpio);
170 }
171
172 snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
173
174 return 0;
175}
176
177static int tegra_rt5677_card_remove(struct snd_soc_card *card)
178{
179 struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card);
180
181 if (gpio_is_valid(machine->gpio_hp_det)) {
182 snd_soc_jack_free_gpios(&tegra_rt5677_hp_jack, 1,
183 &tegra_rt5677_hp_jack_gpio);
184 }
185
186 if (gpio_is_valid(machine->gpio_mic_present)) {
187 snd_soc_jack_free_gpios(&tegra_rt5677_mic_jack, 1,
188 &tegra_rt5677_mic_jack_gpio);
189 }
190
191 return 0;
192}
193
194static struct snd_soc_dai_link tegra_rt5677_dai = {
195 .name = "RT5677",
196 .stream_name = "RT5677 PCM",
197 .codec_dai_name = "rt5677-aif1",
198 .init = tegra_rt5677_asoc_init,
199 .ops = &tegra_rt5677_ops,
200 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
201 SND_SOC_DAIFMT_CBS_CFS,
202};
203
204static struct snd_soc_card snd_soc_tegra_rt5677 = {
205 .name = "tegra-rt5677",
206 .owner = THIS_MODULE,
207 .remove = tegra_rt5677_card_remove,
208 .dai_link = &tegra_rt5677_dai,
209 .num_links = 1,
210 .controls = tegra_rt5677_controls,
211 .num_controls = ARRAY_SIZE(tegra_rt5677_controls),
212 .dapm_widgets = tegra_rt5677_dapm_widgets,
213 .num_dapm_widgets = ARRAY_SIZE(tegra_rt5677_dapm_widgets),
214 .fully_routed = true,
215};
216
217static int tegra_rt5677_probe(struct platform_device *pdev)
218{
219 struct device_node *np = pdev->dev.of_node;
220 struct snd_soc_card *card = &snd_soc_tegra_rt5677;
221 struct tegra_rt5677 *machine;
222 int ret;
223
224 machine = devm_kzalloc(&pdev->dev,
225 sizeof(struct tegra_rt5677), GFP_KERNEL);
226 if (!machine)
227 return -ENOMEM;
228
229 card->dev = &pdev->dev;
230 platform_set_drvdata(pdev, card);
231 snd_soc_card_set_drvdata(card, machine);
232
233 machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
234 if (machine->gpio_hp_det == -EPROBE_DEFER)
235 return -EPROBE_DEFER;
236
237 machine->gpio_mic_present = of_get_named_gpio(np,
238 "nvidia,mic-present-gpios", 0);
239 if (machine->gpio_mic_present == -EPROBE_DEFER)
240 return -EPROBE_DEFER;
241
242 machine->gpio_hp_en = of_get_named_gpio(np, "nvidia,hp-en-gpios", 0);
243 if (machine->gpio_hp_en == -EPROBE_DEFER)
244 return -EPROBE_DEFER;
245 if (gpio_is_valid(machine->gpio_hp_en)) {
246 ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_en,
247 GPIOF_OUT_INIT_LOW, "hp_en");
248 if (ret) {
249 dev_err(card->dev, "cannot get hp_en gpio\n");
250 return ret;
251 }
252 }
253
254 machine->gpio_dmic_clk_en = of_get_named_gpio(np,
255 "nvidia,dmic-clk-en-gpios", 0);
256 if (machine->gpio_dmic_clk_en == -EPROBE_DEFER)
257 return -EPROBE_DEFER;
258 if (gpio_is_valid(machine->gpio_dmic_clk_en)) {
259 ret = devm_gpio_request_one(&pdev->dev,
260 machine->gpio_dmic_clk_en,
261 GPIOF_OUT_INIT_HIGH, "dmic_clk_en");
262 if (ret) {
263 dev_err(card->dev, "cannot get dmic_clk_en gpio\n");
264 return ret;
265 }
266 }
267
268 ret = snd_soc_of_parse_card_name(card, "nvidia,model");
269 if (ret)
270 goto err;
271
272 ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
273 if (ret)
274 goto err;
275
276 tegra_rt5677_dai.codec_of_node = of_parse_phandle(np,
277 "nvidia,audio-codec", 0);
278 if (!tegra_rt5677_dai.codec_of_node) {
279 dev_err(&pdev->dev,
280 "Property 'nvidia,audio-codec' missing or invalid\n");
281 ret = -EINVAL;
282 goto err;
283 }
284
285 tegra_rt5677_dai.cpu_of_node = of_parse_phandle(np,
286 "nvidia,i2s-controller", 0);
287 if (!tegra_rt5677_dai.cpu_of_node) {
288 dev_err(&pdev->dev,
289 "Property 'nvidia,i2s-controller' missing or invalid\n");
290 ret = -EINVAL;
291 goto err;
292 }
293 tegra_rt5677_dai.platform_of_node = tegra_rt5677_dai.cpu_of_node;
294
295 ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
296 if (ret)
297 goto err;
298
299 ret = snd_soc_register_card(card);
300 if (ret) {
301 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
302 ret);
303 goto err_fini_utils;
304 }
305
306 return 0;
307
308err_fini_utils:
309 tegra_asoc_utils_fini(&machine->util_data);
310err:
311 return ret;
312}
313
314static int tegra_rt5677_remove(struct platform_device *pdev)
315{
316 struct snd_soc_card *card = platform_get_drvdata(pdev);
317 struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card);
318
319 snd_soc_unregister_card(card);
320
321 tegra_asoc_utils_fini(&machine->util_data);
322
323 return 0;
324}
325
326static const struct of_device_id tegra_rt5677_of_match[] = {
327 { .compatible = "nvidia,tegra-audio-rt5677", },
328 {},
329};
330
331static struct platform_driver tegra_rt5677_driver = {
332 .driver = {
333 .name = DRV_NAME,
334 .owner = THIS_MODULE,
335 .pm = &snd_soc_pm_ops,
336 .of_match_table = tegra_rt5677_of_match,
337 },
338 .probe = tegra_rt5677_probe,
339 .remove = tegra_rt5677_remove,
340};
341module_platform_driver(tegra_rt5677_driver);
342
343MODULE_AUTHOR("Anatol Pomozov <anatol@google.com>");
344MODULE_DESCRIPTION("Tegra+RT5677 machine ASoC driver");
345MODULE_LICENSE("GPL v2");
346MODULE_ALIAS("platform:" DRV_NAME);
347MODULE_DEVICE_TABLE(of, tegra_rt5677_of_match);