aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/tegra
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2013-06-12 13:35:34 -0400
committerMark Brown <broonie@linaro.org>2013-06-12 14:28:16 -0400
commit040a62cf1c040362fb11587fb9f02e1881f4c237 (patch)
tree96186aef46d471b927b06e095d1667d0925b1ab5 /sound/soc/tegra
parentdcad9f031240d59e9e1475a8e5b2cb427da94f6e (diff)
ASoC: tegra: add tegra+RT5640 machine driver
Initially, this binding and driver only describe/support playback to headphones and speakers. This driver will support Beaver and Dalmore. Signed-off-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'sound/soc/tegra')
-rw-r--r--sound/soc/tegra/Kconfig10
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra_rt5640.c257
3 files changed, 269 insertions, 0 deletions
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index b1c9d573da05..995b120c2cd0 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -59,6 +59,16 @@ config SND_SOC_TEGRA30_I2S
59 Tegra30 I2S interface. You will also need to select the individual 59 Tegra30 I2S interface. You will also need to select the individual
60 machine drivers to support below. 60 machine drivers to support below.
61 61
62config SND_SOC_TEGRA_RT5640
63 tristate "SoC Audio support for Tegra boards using an RT5640 codec"
64 depends on SND_SOC_TEGRA && I2C
65 select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
66 select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
67 select SND_SOC_RT5640
68 help
69 Say Y or M here if you want to add support for SoC audio on Tegra
70 boards using the RT5640 codec, such as Dalmore.
71
62config SND_SOC_TEGRA_WM8753 72config SND_SOC_TEGRA_WM8753
63 tristate "SoC Audio support for Tegra boards using a WM8753 codec" 73 tristate "SoC Audio support for Tegra boards using a WM8753 codec"
64 depends on SND_SOC_TEGRA && I2C 74 depends on SND_SOC_TEGRA && I2C
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 416a14bde41b..21d2550a08a4 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -18,12 +18,14 @@ obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o
18obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o 18obj-$(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-wm8753-objs := tegra_wm8753.o 22snd-soc-tegra-wm8753-objs := tegra_wm8753.o
22snd-soc-tegra-wm8903-objs := tegra_wm8903.o 23snd-soc-tegra-wm8903-objs := tegra_wm8903.o
23snd-soc-tegra-wm9712-objs := tegra_wm9712.o 24snd-soc-tegra-wm9712-objs := tegra_wm9712.o
24snd-soc-tegra-trimslice-objs := trimslice.o 25snd-soc-tegra-trimslice-objs := trimslice.o
25snd-soc-tegra-alc5632-objs := tegra_alc5632.o 26snd-soc-tegra-alc5632-objs := tegra_alc5632.o
26 27
28obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o
27obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o 29obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o
28obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o 30obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
29obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o 31obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
new file mode 100644
index 000000000000..08794f915a94
--- /dev/null
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -0,0 +1,257 @@
1/*
2* tegra_rt5640.c - Tegra machine ASoC driver for boards using WM8903 codec.
3 *
4 * Copyright (c) 2013, NVIDIA CORPORATION. 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/rt5640.h"
39
40#include "tegra_asoc_utils.h"
41
42#define DRV_NAME "tegra-snd-rt5640"
43
44struct tegra_rt5640 {
45 struct tegra_asoc_utils_data util_data;
46 int gpio_hp_det;
47};
48
49static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream,
50 struct snd_pcm_hw_params *params)
51{
52 struct snd_soc_pcm_runtime *rtd = substream->private_data;
53 struct snd_soc_dai *codec_dai = rtd->codec_dai;
54 struct snd_soc_codec *codec = codec_dai->codec;
55 struct snd_soc_card *card = codec->card;
56 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
57 int srate, mclk;
58 int err;
59
60 srate = params_rate(params);
61 mclk = 256 * srate;
62
63 err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
64 if (err < 0) {
65 dev_err(card->dev, "Can't configure clocks\n");
66 return err;
67 }
68
69 err = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, mclk,
70 SND_SOC_CLOCK_IN);
71 if (err < 0) {
72 dev_err(card->dev, "codec_dai clock not set\n");
73 return err;
74 }
75
76 return 0;
77}
78
79static struct snd_soc_ops tegra_rt5640_ops = {
80 .hw_params = tegra_rt5640_asoc_hw_params,
81};
82
83static struct snd_soc_jack tegra_rt5640_hp_jack;
84
85static struct snd_soc_jack_pin tegra_rt5640_hp_jack_pins[] = {
86 {
87 .pin = "Headphones",
88 .mask = SND_JACK_HEADPHONE,
89 },
90};
91
92static struct snd_soc_jack_gpio tegra_rt5640_hp_jack_gpio = {
93 .name = "Headphone detection",
94 .report = SND_JACK_HEADPHONE,
95 .debounce_time = 150,
96 .invert = 1,
97};
98
99static const struct snd_soc_dapm_widget tegra_rt5640_dapm_widgets[] = {
100 SND_SOC_DAPM_HP("Headphones", NULL),
101 SND_SOC_DAPM_SPK("Speakers", NULL),
102};
103
104static const struct snd_kcontrol_new tegra_rt5640_controls[] = {
105 SOC_DAPM_PIN_SWITCH("Speakers"),
106};
107
108static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
109{
110 struct snd_soc_dai *codec_dai = rtd->codec_dai;
111 struct snd_soc_codec *codec = codec_dai->codec;
112 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(codec->card);
113
114 snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
115 &tegra_rt5640_hp_jack);
116 snd_soc_jack_add_pins(&tegra_rt5640_hp_jack,
117 ARRAY_SIZE(tegra_rt5640_hp_jack_pins),
118 tegra_rt5640_hp_jack_pins);
119
120 if (gpio_is_valid(machine->gpio_hp_det)) {
121 tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det;
122 snd_soc_jack_add_gpios(&tegra_rt5640_hp_jack,
123 1,
124 &tegra_rt5640_hp_jack_gpio);
125 }
126
127 return 0;
128}
129
130static struct snd_soc_dai_link tegra_rt5640_dai = {
131 .name = "RT5640",
132 .stream_name = "RT5640 PCM",
133 .codec_dai_name = "rt5640-aif1",
134 .init = tegra_rt5640_asoc_init,
135 .ops = &tegra_rt5640_ops,
136 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
137 SND_SOC_DAIFMT_CBS_CFS,
138};
139
140static struct snd_soc_card snd_soc_tegra_rt5640 = {
141 .name = "tegra-rt5640",
142 .owner = THIS_MODULE,
143 .dai_link = &tegra_rt5640_dai,
144 .num_links = 1,
145 .controls = tegra_rt5640_controls,
146 .num_controls = ARRAY_SIZE(tegra_rt5640_controls),
147 .dapm_widgets = tegra_rt5640_dapm_widgets,
148 .num_dapm_widgets = ARRAY_SIZE(tegra_rt5640_dapm_widgets),
149 .fully_routed = true,
150};
151
152static int tegra_rt5640_probe(struct platform_device *pdev)
153{
154 struct device_node *np = pdev->dev.of_node;
155 struct snd_soc_card *card = &snd_soc_tegra_rt5640;
156 struct tegra_rt5640 *machine;
157 int ret;
158
159 machine = devm_kzalloc(&pdev->dev,
160 sizeof(struct tegra_rt5640), GFP_KERNEL);
161 if (!machine) {
162 dev_err(&pdev->dev, "Can't allocate tegra_rt5640\n");
163 return -ENOMEM;
164 }
165
166 card->dev = &pdev->dev;
167 platform_set_drvdata(pdev, card);
168 snd_soc_card_set_drvdata(card, machine);
169
170 machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
171 if (machine->gpio_hp_det == -EPROBE_DEFER)
172 return -EPROBE_DEFER;
173
174 ret = snd_soc_of_parse_card_name(card, "nvidia,model");
175 if (ret)
176 goto err;
177
178 ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
179 if (ret)
180 goto err;
181
182 tegra_rt5640_dai.codec_of_node = of_parse_phandle(np,
183 "nvidia,audio-codec", 0);
184 if (!tegra_rt5640_dai.codec_of_node) {
185 dev_err(&pdev->dev,
186 "Property 'nvidia,audio-codec' missing or invalid\n");
187 ret = -EINVAL;
188 goto err;
189 }
190
191 tegra_rt5640_dai.cpu_of_node = of_parse_phandle(np,
192 "nvidia,i2s-controller", 0);
193 if (!tegra_rt5640_dai.cpu_of_node) {
194 dev_err(&pdev->dev,
195 "Property 'nvidia,i2s-controller' missing or invalid\n");
196 ret = -EINVAL;
197 goto err;
198 }
199
200 tegra_rt5640_dai.platform_of_node = tegra_rt5640_dai.cpu_of_node;
201
202 ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
203 if (ret)
204 goto err;
205
206 ret = snd_soc_register_card(card);
207 if (ret) {
208 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
209 ret);
210 goto err_fini_utils;
211 }
212
213 return 0;
214
215err_fini_utils:
216 tegra_asoc_utils_fini(&machine->util_data);
217err:
218 return ret;
219}
220
221static int tegra_rt5640_remove(struct platform_device *pdev)
222{
223 struct snd_soc_card *card = platform_get_drvdata(pdev);
224 struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
225
226 snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack, 1,
227 &tegra_rt5640_hp_jack_gpio);
228
229 snd_soc_unregister_card(card);
230
231 tegra_asoc_utils_fini(&machine->util_data);
232
233 return 0;
234}
235
236static const struct of_device_id tegra_rt5640_of_match[] = {
237 { .compatible = "nvidia,tegra-audio-rt5640", },
238 {},
239};
240
241static struct platform_driver tegra_rt5640_driver = {
242 .driver = {
243 .name = DRV_NAME,
244 .owner = THIS_MODULE,
245 .pm = &snd_soc_pm_ops,
246 .of_match_table = tegra_rt5640_of_match,
247 },
248 .probe = tegra_rt5640_probe,
249 .remove = tegra_rt5640_remove,
250};
251module_platform_driver(tegra_rt5640_driver);
252
253MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
254MODULE_DESCRIPTION("Tegra+RT5640 machine ASoC driver");
255MODULE_LICENSE("GPL v2");
256MODULE_ALIAS("platform:" DRV_NAME);
257MODULE_DEVICE_TABLE(of, tegra_rt5640_of_match);