2 * omap-twl4030.c -- SoC audio for TI SoC based boards with twl4030 codec
4 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
7 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
9 * This driver replaces the following machine drivers:
10 * omap3beagle (Author: Steve Sakoman <steve@sakoman.com>)
11 * omap3evm (Author: Anuj Aggarwal <anuj.aggarwal@ti.com>)
12 * overo (Author: Steve Sakoman <steve@sakoman.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>)
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * version 2 as published by the Free Software Foundation.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
33 #include <linux/platform_device.h>
34 #include <linux/platform_data/omap-twl4030.h>
35 #include <linux/module.h>
37 #include <linux/gpio.h>
38 #include <linux/of_gpio.h>
40 #include <sound/core.h>
41 #include <sound/pcm.h>
42 #include <sound/soc.h>
43 #include <sound/jack.h>
45 #include "omap-mcbsp.h"
48 int jack_detect
; /* board can detect jack events */
49 struct snd_soc_jack hs_jack
;
52 static int omap_twl4030_hw_params(struct snd_pcm_substream
*substream
,
53 struct snd_pcm_hw_params
*params
)
55 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
56 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
57 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
58 struct snd_soc_codec
*codec
= rtd
->codec
;
59 struct snd_soc_card
*card
= codec
->card
;
63 switch (params_channels(params
)) {
64 case 2: /* Stereo I2S mode */
65 fmt
= SND_SOC_DAIFMT_I2S
|
66 SND_SOC_DAIFMT_NB_NF
|
67 SND_SOC_DAIFMT_CBM_CFM
;
69 case 4: /* Four channel TDM mode */
70 fmt
= SND_SOC_DAIFMT_DSP_A
|
71 SND_SOC_DAIFMT_IB_NF
|
72 SND_SOC_DAIFMT_CBM_CFM
;
78 /* Set codec DAI configuration */
79 ret
= snd_soc_dai_set_fmt(codec_dai
, fmt
);
81 dev_err(card
->dev
, "can't set codec DAI configuration\n");
85 /* Set cpu DAI configuration */
86 ret
= snd_soc_dai_set_fmt(cpu_dai
, fmt
);
88 dev_err(card
->dev
, "can't set cpu DAI configuration\n");
95 static struct snd_soc_ops omap_twl4030_ops
= {
96 .hw_params
= omap_twl4030_hw_params
,
99 static const struct snd_soc_dapm_widget dapm_widgets
[] = {
100 SND_SOC_DAPM_SPK("Earpiece Spk", NULL
),
101 SND_SOC_DAPM_SPK("Handsfree Spk", NULL
),
102 SND_SOC_DAPM_HP("Headset Stereophone", NULL
),
103 SND_SOC_DAPM_SPK("Ext Spk", NULL
),
104 SND_SOC_DAPM_SPK("Carkit Spk", NULL
),
106 SND_SOC_DAPM_MIC("Main Mic", NULL
),
107 SND_SOC_DAPM_MIC("Sub Mic", NULL
),
108 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
109 SND_SOC_DAPM_MIC("Carkit Mic", NULL
),
110 SND_SOC_DAPM_MIC("Digital0 Mic", NULL
),
111 SND_SOC_DAPM_MIC("Digital1 Mic", NULL
),
112 SND_SOC_DAPM_LINE("Line In", NULL
),
115 static const struct snd_soc_dapm_route audio_map
[] = {
116 /* Headset Stereophone: HSOL, HSOR */
117 {"Headset Stereophone", NULL
, "HSOL"},
118 {"Headset Stereophone", NULL
, "HSOR"},
119 /* External Speakers: HFL, HFR */
120 {"Handsfree Spk", NULL
, "HFL"},
121 {"Handsfree Spk", NULL
, "HFR"},
122 /* External Speakers: PredrivL, PredrivR */
123 {"Ext Spk", NULL
, "PREDRIVEL"},
124 {"Ext Spk", NULL
, "PREDRIVER"},
125 /* Carkit speakers: CARKITL, CARKITR */
126 {"Carkit Spk", NULL
, "CARKITL"},
127 {"Carkit Spk", NULL
, "CARKITR"},
129 {"Earpiece Spk", NULL
, "EARPIECE"},
131 /* External Mics: MAINMIC, SUBMIC with bias */
132 {"MAINMIC", NULL
, "Main Mic"},
133 {"Main Mic", NULL
, "Mic Bias 1"},
134 {"SUBMIC", NULL
, "Sub Mic"},
135 {"Sub Mic", NULL
, "Mic Bias 2"},
136 /* Headset Mic: HSMIC with bias */
137 {"HSMIC", NULL
, "Headset Mic"},
138 {"Headset Mic", NULL
, "Headset Mic Bias"},
139 /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */
140 {"DIGIMIC0", NULL
, "Digital0 Mic"},
141 {"Digital0 Mic", NULL
, "Mic Bias 1"},
142 {"DIGIMIC1", NULL
, "Digital1 Mic"},
143 {"Digital1 Mic", NULL
, "Mic Bias 2"},
144 /* Carkit In: CARKITMIC */
145 {"CARKITMIC", NULL
, "Carkit Mic"},
146 /* Aux In: AUXL, AUXR */
147 {"AUXL", NULL
, "Line In"},
148 {"AUXR", NULL
, "Line In"},
151 /* Headset jack detection DAPM pins */
152 static struct snd_soc_jack_pin hs_jack_pins
[] = {
154 .pin
= "Headset Mic",
155 .mask
= SND_JACK_MICROPHONE
,
158 .pin
= "Headset Stereophone",
159 .mask
= SND_JACK_HEADPHONE
,
163 /* Headset jack detection gpios */
164 static struct snd_soc_jack_gpio hs_jack_gpios
[] = {
166 .name
= "hsdet-gpio",
167 .report
= SND_JACK_HEADSET
,
168 .debounce_time
= 200,
172 static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context
*dapm
,
173 int connected
, char *pin
)
176 snd_soc_dapm_disable_pin(dapm
, pin
);
179 static int omap_twl4030_init(struct snd_soc_pcm_runtime
*rtd
)
181 struct snd_soc_codec
*codec
= rtd
->codec
;
182 struct snd_soc_card
*card
= codec
->card
;
183 struct snd_soc_dapm_context
*dapm
= &codec
->dapm
;
184 struct omap_tw4030_pdata
*pdata
= dev_get_platdata(card
->dev
);
185 struct omap_twl4030
*priv
= snd_soc_card_get_drvdata(card
);
188 /* Headset jack detection only if it is supported */
189 if (priv
->jack_detect
> 0) {
190 hs_jack_gpios
[0].gpio
= priv
->jack_detect
;
192 ret
= snd_soc_jack_new(codec
, "Headset Jack", SND_JACK_HEADSET
,
197 ret
= snd_soc_jack_add_pins(&priv
->hs_jack
,
198 ARRAY_SIZE(hs_jack_pins
),
203 ret
= snd_soc_jack_add_gpios(&priv
->hs_jack
,
204 ARRAY_SIZE(hs_jack_gpios
),
211 * NULL pdata means we booted with DT. In this case the routing is
212 * provided and the card is fully routed, no need to mark pins.
214 if (!pdata
|| !pdata
->custom_routing
)
217 /* Disable not connected paths if not used */
218 twl4030_disconnect_pin(dapm
, pdata
->has_ear
, "Earpiece Spk");
219 twl4030_disconnect_pin(dapm
, pdata
->has_hf
, "Handsfree Spk");
220 twl4030_disconnect_pin(dapm
, pdata
->has_hs
, "Headset Stereophone");
221 twl4030_disconnect_pin(dapm
, pdata
->has_predriv
, "Ext Spk");
222 twl4030_disconnect_pin(dapm
, pdata
->has_carkit
, "Carkit Spk");
224 twl4030_disconnect_pin(dapm
, pdata
->has_mainmic
, "Main Mic");
225 twl4030_disconnect_pin(dapm
, pdata
->has_submic
, "Sub Mic");
226 twl4030_disconnect_pin(dapm
, pdata
->has_hsmic
, "Headset Mic");
227 twl4030_disconnect_pin(dapm
, pdata
->has_carkitmic
, "Carkit Mic");
228 twl4030_disconnect_pin(dapm
, pdata
->has_digimic0
, "Digital0 Mic");
229 twl4030_disconnect_pin(dapm
, pdata
->has_digimic1
, "Digital1 Mic");
230 twl4030_disconnect_pin(dapm
, pdata
->has_linein
, "Line In");
235 /* Digital audio interface glue - connects codec <--> CPU */
236 static struct snd_soc_dai_link omap_twl4030_dai_links
[] = {
238 .name
= "TWL4030 HiFi",
239 .stream_name
= "TWL4030 HiFi",
240 .cpu_dai_name
= "omap-mcbsp.2",
241 .codec_dai_name
= "twl4030-hifi",
242 .platform_name
= "omap-pcm-audio",
243 .codec_name
= "twl4030-codec",
244 .init
= omap_twl4030_init
,
245 .ops
= &omap_twl4030_ops
,
248 .name
= "TWL4030 Voice",
249 .stream_name
= "TWL4030 Voice",
250 .cpu_dai_name
= "omap-mcbsp.3",
251 .codec_dai_name
= "twl4030-voice",
252 .platform_name
= "omap-pcm-audio",
253 .codec_name
= "twl4030-codec",
254 .dai_fmt
= SND_SOC_DAIFMT_DSP_A
| SND_SOC_DAIFMT_IB_NF
|
255 SND_SOC_DAIFMT_CBM_CFM
,
259 /* Audio machine driver */
260 static struct snd_soc_card omap_twl4030_card
= {
261 .owner
= THIS_MODULE
,
262 .dai_link
= omap_twl4030_dai_links
,
263 .num_links
= ARRAY_SIZE(omap_twl4030_dai_links
),
265 .dapm_widgets
= dapm_widgets
,
266 .num_dapm_widgets
= ARRAY_SIZE(dapm_widgets
),
267 .dapm_routes
= audio_map
,
268 .num_dapm_routes
= ARRAY_SIZE(audio_map
),
271 static int omap_twl4030_probe(struct platform_device
*pdev
)
273 struct omap_tw4030_pdata
*pdata
= dev_get_platdata(&pdev
->dev
);
274 struct device_node
*node
= pdev
->dev
.of_node
;
275 struct snd_soc_card
*card
= &omap_twl4030_card
;
276 struct omap_twl4030
*priv
;
279 card
->dev
= &pdev
->dev
;
281 priv
= devm_kzalloc(&pdev
->dev
, sizeof(struct omap_twl4030
), GFP_KERNEL
);
286 struct device_node
*dai_node
;
287 struct property
*prop
;
289 if (snd_soc_of_parse_card_name(card
, "ti,model")) {
290 dev_err(&pdev
->dev
, "Card name is not provided\n");
294 dai_node
= of_parse_phandle(node
, "ti,mcbsp", 0);
296 dev_err(&pdev
->dev
, "McBSP node is not provided\n");
299 omap_twl4030_dai_links
[0].cpu_dai_name
= NULL
;
300 omap_twl4030_dai_links
[0].cpu_of_node
= dai_node
;
302 dai_node
= of_parse_phandle(node
, "ti,mcbsp-voice", 0);
306 omap_twl4030_dai_links
[1].cpu_dai_name
= NULL
;
307 omap_twl4030_dai_links
[1].cpu_of_node
= dai_node
;
310 priv
->jack_detect
= of_get_named_gpio(node
,
311 "ti,jack-det-gpio", 0);
313 /* Optional: audio routing can be provided */
314 prop
= of_find_property(node
, "ti,audio-routing", NULL
);
316 ret
= snd_soc_of_parse_audio_routing(card
,
321 card
->fully_routed
= 1;
324 if (pdata
->card_name
) {
325 card
->name
= pdata
->card_name
;
327 dev_err(&pdev
->dev
, "Card name is not provided\n");
331 if (!pdata
->voice_connected
)
334 priv
->jack_detect
= pdata
->jack_detect
;
336 dev_err(&pdev
->dev
, "Missing pdata\n");
340 snd_soc_card_set_drvdata(card
, priv
);
341 ret
= snd_soc_register_card(card
);
343 dev_err(&pdev
->dev
, "snd_soc_register_card() failed: %d\n",
351 static int omap_twl4030_remove(struct platform_device
*pdev
)
353 struct snd_soc_card
*card
= platform_get_drvdata(pdev
);
354 struct omap_twl4030
*priv
= snd_soc_card_get_drvdata(card
);
356 if (priv
->jack_detect
> 0)
357 snd_soc_jack_free_gpios(&priv
->hs_jack
,
358 ARRAY_SIZE(hs_jack_gpios
),
360 snd_soc_unregister_card(card
);
365 static const struct of_device_id omap_twl4030_of_match
[] = {
366 {.compatible
= "ti,omap-twl4030", },
369 MODULE_DEVICE_TABLE(of
, omap_twl4030_of_match
);
371 static struct platform_driver omap_twl4030_driver
= {
373 .name
= "omap-twl4030",
374 .owner
= THIS_MODULE
,
375 .pm
= &snd_soc_pm_ops
,
376 .of_match_table
= omap_twl4030_of_match
,
378 .probe
= omap_twl4030_probe
,
379 .remove
= omap_twl4030_remove
,
382 module_platform_driver(omap_twl4030_driver
);
384 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
385 MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
386 MODULE_LICENSE("GPL");
387 MODULE_ALIAS("platform:omap-twl4030");