aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@ti.com>2012-12-31 05:51:48 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2013-01-02 08:04:25 -0500
commitbd0b286e838ef1ca19bbe1cb55f0ec7e0135de1f (patch)
tree9f4c889fd906306c0109a237ce415ce0f9706825
parentfff3dd40132d6106d4d2a61e70e782f82394fd17 (diff)
ASoC: omap-twl4030: Add support for routing, voice port and jack detect
Update the common machine driver to support more boards including Zoom2 and SDP3430. - Support for voice port of twl4030 - HS jack plug detection support - The audio routing can be fine tuned via pdata or via provided routing table from DT. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--Documentation/devicetree/bindings/sound/omap-twl4030.txt46
-rw-r--r--sound/soc/omap/Kconfig2
-rw-r--r--sound/soc/omap/omap-twl4030.c204
3 files changed, 250 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/sound/omap-twl4030.txt b/Documentation/devicetree/bindings/sound/omap-twl4030.txt
index 6fae51c7f766..1ab6bc8404d5 100644
--- a/Documentation/devicetree/bindings/sound/omap-twl4030.txt
+++ b/Documentation/devicetree/bindings/sound/omap-twl4030.txt
@@ -6,6 +6,52 @@ Required properties:
6- ti,mcbsp: phandle for the McBSP node 6- ti,mcbsp: phandle for the McBSP node
7- ti,codec: phandle for the twl4030 audio node 7- ti,codec: phandle for the twl4030 audio node
8 8
9Optional properties:
10- ti,mcbsp-voice: phandle for the McBSP node connected to the voice port of twl
11- ti, jack-det-gpio: Jack detect GPIO
12- ti,audio-routing: List of connections between audio components.
13 Each entry is a pair of strings, the first being the connection's sink,
14 the second being the connection's source.
15 If the routing is not provided all possible connection will be available
16
17Available audio endpoints for the audio-routing table:
18
19Board connectors:
20 * Headset Stereophone
21 * Earpiece Spk
22 * Handsfree Spk
23 * Ext Spk
24 * Main Mic
25 * Sub Mic
26 * Headset Mic
27 * Carkit Mic
28 * Digital0 Mic
29 * Digital1 Mic
30 * Line In
31
32twl4030 pins:
33 * HSOL
34 * HSOR
35 * EARPIECE
36 * HFL
37 * HFR
38 * PREDRIVEL
39 * PREDRIVER
40 * CARKITL
41 * CARKITR
42 * MAINMIC
43 * SUBMIC
44 * HSMIC
45 * DIGIMIC0
46 * DIGIMIC1
47 * CARKITMIC
48 * AUXL
49 * AUXR
50
51 * Headset Mic Bias
52 * Mic Bias 1 /* Used for Main Mic or Digimic0 */
53 * Mic Bias 2 /* Used for Sub Mic or Digimic1 */
54
9Example: 55Example:
10 56
11sound { 57sound {
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 7048137f9a33..e8d2a2f983b5 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -91,6 +91,8 @@ config SND_OMAP_SOC_OMAP_TWL4030
91 - Gumstix Overo or CompuLab CM-T35/CM-T3730 91 - Gumstix Overo or CompuLab CM-T35/CM-T3730
92 - IGEP v2 92 - IGEP v2
93 - OMAP3EVM 93 - OMAP3EVM
94 - SDP3430
95 - Zoom2
94 96
95config SND_OMAP_SOC_OMAP_ABE_TWL6040 97config SND_OMAP_SOC_OMAP_ABE_TWL6040
96 tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" 98 tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c
index 4541d28b5314..fd98509d0f49 100644
--- a/sound/soc/omap/omap-twl4030.c
+++ b/sound/soc/omap/omap-twl4030.c
@@ -11,6 +11,8 @@
11 * omap3evm (Author: Anuj Aggarwal <anuj.aggarwal@ti.com>) 11 * omap3evm (Author: Anuj Aggarwal <anuj.aggarwal@ti.com>)
12 * overo (Author: Steve Sakoman <steve@sakoman.com>) 12 * overo (Author: Steve Sakoman <steve@sakoman.com>)
13 * igep0020 (Author: Enric Balletbo i Serra <eballetbo@iseebcn.com>) 13 * igep0020 (Author: Enric Balletbo i Serra <eballetbo@iseebcn.com>)
14 * zoom2 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
15 * sdp3430 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
14 * 16 *
15 * This program is free software; you can redistribute it and/or 17 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License 18 * modify it under the terms of the GNU General Public License
@@ -32,14 +34,22 @@
32#include <linux/platform_data/omap-twl4030.h> 34#include <linux/platform_data/omap-twl4030.h>
33#include <linux/module.h> 35#include <linux/module.h>
34#include <linux/of.h> 36#include <linux/of.h>
37#include <linux/gpio.h>
38#include <linux/of_gpio.h>
35 39
36#include <sound/core.h> 40#include <sound/core.h>
37#include <sound/pcm.h> 41#include <sound/pcm.h>
38#include <sound/soc.h> 42#include <sound/soc.h>
43#include <sound/jack.h>
39 44
40#include "omap-mcbsp.h" 45#include "omap-mcbsp.h"
41#include "omap-pcm.h" 46#include "omap-pcm.h"
42 47
48struct omap_twl4030 {
49 int jack_detect; /* board can detect jack events */
50 struct snd_soc_jack hs_jack;
51};
52
43static int omap_twl4030_hw_params(struct snd_pcm_substream *substream, 53static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
44 struct snd_pcm_hw_params *params) 54 struct snd_pcm_hw_params *params)
45{ 55{
@@ -87,17 +97,164 @@ static struct snd_soc_ops omap_twl4030_ops = {
87 .hw_params = omap_twl4030_hw_params, 97 .hw_params = omap_twl4030_hw_params,
88}; 98};
89 99
100static const struct snd_soc_dapm_widget dapm_widgets[] = {
101 SND_SOC_DAPM_SPK("Earpiece Spk", NULL),
102 SND_SOC_DAPM_SPK("Handsfree Spk", NULL),
103 SND_SOC_DAPM_HP("Headset Stereophone", NULL),
104 SND_SOC_DAPM_SPK("Ext Spk", NULL),
105 SND_SOC_DAPM_SPK("Carkit Spk", NULL),
106
107 SND_SOC_DAPM_MIC("Main Mic", NULL),
108 SND_SOC_DAPM_MIC("Sub Mic", NULL),
109 SND_SOC_DAPM_MIC("Headset Mic", NULL),
110 SND_SOC_DAPM_MIC("Carkit Mic", NULL),
111 SND_SOC_DAPM_MIC("Digital0 Mic", NULL),
112 SND_SOC_DAPM_MIC("Digital1 Mic", NULL),
113 SND_SOC_DAPM_LINE("Line In", NULL),
114};
115
116static const struct snd_soc_dapm_route audio_map[] = {
117 /* Headset Stereophone: HSOL, HSOR */
118 {"Headset Stereophone", NULL, "HSOL"},
119 {"Headset Stereophone", NULL, "HSOR"},
120 /* External Speakers: HFL, HFR */
121 {"Handsfree Spk", NULL, "HFL"},
122 {"Handsfree Spk", NULL, "HFR"},
123 /* External Speakers: PredrivL, PredrivR */
124 {"Ext Spk", NULL, "PREDRIVEL"},
125 {"Ext Spk", NULL, "PREDRIVER"},
126 /* Carkit speakers: CARKITL, CARKITR */
127 {"Carkit Spk", NULL, "CARKITL"},
128 {"Carkit Spk", NULL, "CARKITR"},
129 /* Earpiece */
130 {"Earpiece Spk", NULL, "EARPIECE"},
131
132 /* External Mics: MAINMIC, SUBMIC with bias */
133 {"MAINMIC", NULL, "Main Mic"},
134 {"Main Mic", NULL, "Mic Bias 1"},
135 {"SUBMIC", NULL, "Sub Mic"},
136 {"Sub Mic", NULL, "Mic Bias 2"},
137 /* Headset Mic: HSMIC with bias */
138 {"HSMIC", NULL, "Headset Mic"},
139 {"Headset Mic", NULL, "Headset Mic Bias"},
140 /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */
141 {"DIGIMIC0", NULL, "Digital0 Mic"},
142 {"Digital0 Mic", NULL, "Mic Bias 1"},
143 {"DIGIMIC1", NULL, "Digital1 Mic"},
144 {"Digital1 Mic", NULL, "Mic Bias 2"},
145 /* Carkit In: CARKITMIC */
146 {"CARKITMIC", NULL, "Carkit Mic"},
147 /* Aux In: AUXL, AUXR */
148 {"AUXL", NULL, "Line In"},
149 {"AUXR", NULL, "Line In"},
150};
151
152/* Headset jack detection DAPM pins */
153static struct snd_soc_jack_pin hs_jack_pins[] = {
154 {
155 .pin = "Headset Mic",
156 .mask = SND_JACK_MICROPHONE,
157 },
158 {
159 .pin = "Headset Stereophone",
160 .mask = SND_JACK_HEADPHONE,
161 },
162};
163
164/* Headset jack detection gpios */
165static struct snd_soc_jack_gpio hs_jack_gpios[] = {
166 {
167 .name = "hsdet-gpio",
168 .report = SND_JACK_HEADSET,
169 .debounce_time = 200,
170 },
171};
172
173static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
174 int connected, char *pin)
175{
176 if (!connected)
177 snd_soc_dapm_disable_pin(dapm, pin);
178}
179
180static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
181{
182 struct snd_soc_codec *codec = rtd->codec;
183 struct snd_soc_card *card = codec->card;
184 struct snd_soc_dapm_context *dapm = &codec->dapm;
185 struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
186 struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
187 int ret = 0;
188
189 /* Headset jack detection only if it is supported */
190 if (priv->jack_detect > 0) {
191 hs_jack_gpios[0].gpio = priv->jack_detect;
192
193 ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
194 &priv->hs_jack);
195 if (ret)
196 return ret;
197
198 ret = snd_soc_jack_add_pins(&priv->hs_jack,
199 ARRAY_SIZE(hs_jack_pins),
200 hs_jack_pins);
201 if (ret)
202 return ret;
203
204 ret = snd_soc_jack_add_gpios(&priv->hs_jack,
205 ARRAY_SIZE(hs_jack_gpios),
206 hs_jack_gpios);
207 if (ret)
208 return ret;
209 }
210
211 /*
212 * NULL pdata means we booted with DT. In this case the routing is
213 * provided and the card is fully routed, no need to mark pins.
214 */
215 if (!pdata || !pdata->custom_routing)
216 return ret;
217
218 /* Disable not connected paths if not used */
219 twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk");
220 twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk");
221 twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
222 twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk");
223 twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk");
224
225 twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic");
226 twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic");
227 twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
228 twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic");
229 twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic");
230 twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic");
231 twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In");
232
233 return ret;
234}
235
90/* Digital audio interface glue - connects codec <--> CPU */ 236/* Digital audio interface glue - connects codec <--> CPU */
91static struct snd_soc_dai_link omap_twl4030_dai_links[] = { 237static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
92 { 238 {
93 .name = "TWL4030", 239 .name = "TWL4030 HiFi",
94 .stream_name = "TWL4030", 240 .stream_name = "TWL4030 HiFi",
95 .cpu_dai_name = "omap-mcbsp.2", 241 .cpu_dai_name = "omap-mcbsp.2",
96 .codec_dai_name = "twl4030-hifi", 242 .codec_dai_name = "twl4030-hifi",
97 .platform_name = "omap-pcm-audio", 243 .platform_name = "omap-pcm-audio",
98 .codec_name = "twl4030-codec", 244 .codec_name = "twl4030-codec",
245 .init = omap_twl4030_init,
99 .ops = &omap_twl4030_ops, 246 .ops = &omap_twl4030_ops,
100 }, 247 },
248 {
249 .name = "TWL4030 Voice",
250 .stream_name = "TWL4030 Voice",
251 .cpu_dai_name = "omap-mcbsp.3",
252 .codec_dai_name = "twl4030-voice",
253 .platform_name = "omap-pcm-audio",
254 .codec_name = "twl4030-codec",
255 .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
256 SND_SOC_DAIFMT_CBM_CFM,
257 },
101}; 258};
102 259
103/* Audio machine driver */ 260/* Audio machine driver */
@@ -105,6 +262,11 @@ static struct snd_soc_card omap_twl4030_card = {
105 .owner = THIS_MODULE, 262 .owner = THIS_MODULE,
106 .dai_link = omap_twl4030_dai_links, 263 .dai_link = omap_twl4030_dai_links,
107 .num_links = ARRAY_SIZE(omap_twl4030_dai_links), 264 .num_links = ARRAY_SIZE(omap_twl4030_dai_links),
265
266 .dapm_widgets = dapm_widgets,
267 .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
268 .dapm_routes = audio_map,
269 .num_dapm_routes = ARRAY_SIZE(audio_map),
108}; 270};
109 271
110static int omap_twl4030_probe(struct platform_device *pdev) 272static int omap_twl4030_probe(struct platform_device *pdev)
@@ -112,12 +274,18 @@ static int omap_twl4030_probe(struct platform_device *pdev)
112 struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); 274 struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev);
113 struct device_node *node = pdev->dev.of_node; 275 struct device_node *node = pdev->dev.of_node;
114 struct snd_soc_card *card = &omap_twl4030_card; 276 struct snd_soc_card *card = &omap_twl4030_card;
277 struct omap_twl4030 *priv;
115 int ret = 0; 278 int ret = 0;
116 279
117 card->dev = &pdev->dev; 280 card->dev = &pdev->dev;
118 281
282 priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL);
283 if (priv == NULL)
284 return -ENOMEM;
285
119 if (node) { 286 if (node) {
120 struct device_node *dai_node; 287 struct device_node *dai_node;
288 struct property *prop;
121 289
122 if (snd_soc_of_parse_card_name(card, "ti,model")) { 290 if (snd_soc_of_parse_card_name(card, "ti,model")) {
123 dev_err(&pdev->dev, "Card name is not provided\n"); 291 dev_err(&pdev->dev, "Card name is not provided\n");
@@ -132,6 +300,27 @@ static int omap_twl4030_probe(struct platform_device *pdev)
132 omap_twl4030_dai_links[0].cpu_dai_name = NULL; 300 omap_twl4030_dai_links[0].cpu_dai_name = NULL;
133 omap_twl4030_dai_links[0].cpu_of_node = dai_node; 301 omap_twl4030_dai_links[0].cpu_of_node = dai_node;
134 302
303 dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0);
304 if (!dai_node) {
305 card->num_links = 1;
306 } else {
307 omap_twl4030_dai_links[1].cpu_dai_name = NULL;
308 omap_twl4030_dai_links[1].cpu_of_node = dai_node;
309 }
310
311 priv->jack_detect = of_get_named_gpio(node,
312 "ti,jack-det-gpio", 0);
313
314 /* Optional: audio routing can be provided */
315 prop = of_find_property(node, "ti,audio-routing", NULL);
316 if (prop) {
317 ret = snd_soc_of_parse_audio_routing(card,
318 "ti,audio-routing");
319 if (ret)
320 return ret;
321
322 card->fully_routed = 1;
323 }
135 } else if (pdata) { 324 } else if (pdata) {
136 if (pdata->card_name) { 325 if (pdata->card_name) {
137 card->name = pdata->card_name; 326 card->name = pdata->card_name;
@@ -139,11 +328,17 @@ static int omap_twl4030_probe(struct platform_device *pdev)
139 dev_err(&pdev->dev, "Card name is not provided\n"); 328 dev_err(&pdev->dev, "Card name is not provided\n");
140 return -ENODEV; 329 return -ENODEV;
141 } 330 }
331
332 if (!pdata->voice_connected)
333 card->num_links = 1;
334
335 priv->jack_detect = pdata->jack_detect;
142 } else { 336 } else {
143 dev_err(&pdev->dev, "Missing pdata\n"); 337 dev_err(&pdev->dev, "Missing pdata\n");
144 return -ENODEV; 338 return -ENODEV;
145 } 339 }
146 340
341 snd_soc_card_set_drvdata(card, priv);
147 ret = snd_soc_register_card(card); 342 ret = snd_soc_register_card(card);
148 if (ret) { 343 if (ret) {
149 dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", 344 dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
@@ -157,7 +352,12 @@ static int omap_twl4030_probe(struct platform_device *pdev)
157static int omap_twl4030_remove(struct platform_device *pdev) 352static int omap_twl4030_remove(struct platform_device *pdev)
158{ 353{
159 struct snd_soc_card *card = platform_get_drvdata(pdev); 354 struct snd_soc_card *card = platform_get_drvdata(pdev);
355 struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
160 356
357 if (priv->jack_detect > 0)
358 snd_soc_jack_free_gpios(&priv->hs_jack,
359 ARRAY_SIZE(hs_jack_gpios),
360 hs_jack_gpios);
161 snd_soc_unregister_card(card); 361 snd_soc_unregister_card(card);
162 362
163 return 0; 363 return 0;