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"
49 int jack_detect
; /* board can detect jack events */
50 struct snd_soc_jack hs_jack
;
53 static int omap_twl4030_hw_params(struct snd_pcm_substream
*substream
,
54 struct snd_pcm_hw_params
*params
)
56 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
57 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
58 struct snd_soc_dai
*cpu_dai
= rtd
->cpu_dai
;
59 struct snd_soc_codec
*codec
= rtd
->codec
;
60 struct snd_soc_card
*card
= codec
->card
;
64 switch (params_channels(params
)) {
65 case 2: /* Stereo I2S mode */
66 fmt
= SND_SOC_DAIFMT_I2S
|
67 SND_SOC_DAIFMT_NB_NF
|
68 SND_SOC_DAIFMT_CBM_CFM
;
70 case 4: /* Four channel TDM mode */
71 fmt
= SND_SOC_DAIFMT_DSP_A
|
72 SND_SOC_DAIFMT_IB_NF
|
73 SND_SOC_DAIFMT_CBM_CFM
;
79 /* Set codec DAI configuration */
80 ret
= snd_soc_dai_set_fmt(codec_dai
, fmt
);
82 dev_err(card
->dev
, "can't set codec DAI configuration\n");
86 /* Set cpu DAI configuration */
87 ret
= snd_soc_dai_set_fmt(cpu_dai
, fmt
);
89 dev_err(card
->dev
, "can't set cpu DAI configuration\n");
96 static struct snd_soc_ops omap_twl4030_ops
= {
97 .hw_params
= omap_twl4030_hw_params
,
100 static 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
),
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
),
116 static 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"},
130 {"Earpiece Spk", NULL
, "EARPIECE"},
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"},
152 /* Headset jack detection DAPM pins */
153 static struct snd_soc_jack_pin hs_jack_pins
[] = {
155 .pin
= "Headset Mic",
156 .mask
= SND_JACK_MICROPHONE
,
159 .pin
= "Headset Stereophone",
160 .mask
= SND_JACK_HEADPHONE
,
164 /* Headset jack detection gpios */
165 static struct snd_soc_jack_gpio hs_jack_gpios
[] = {
167 .name
= "hsdet-gpio",
168 .report
= SND_JACK_HEADSET
,
169 .debounce_time
= 200,
173 static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context
*dapm
,
174 int connected
, char *pin
)
177 snd_soc_dapm_disable_pin(dapm
, pin
);
180 static int omap_twl4030_init(struct snd_soc_pcm_runtime
*rtd
)
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
);
189 /* Headset jack detection only if it is supported */
190 if (priv
->jack_detect
> 0) {
191 hs_jack_gpios
[0].gpio
= priv
->jack_detect
;
193 ret
= snd_soc_jack_new(codec
, "Headset Jack", SND_JACK_HEADSET
,
198 ret
= snd_soc_jack_add_pins(&priv
->hs_jack
,
199 ARRAY_SIZE(hs_jack_pins
),
204 ret
= snd_soc_jack_add_gpios(&priv
->hs_jack
,
205 ARRAY_SIZE(hs_jack_gpios
),
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.
215 if (!pdata
|| !pdata
->custom_routing
)
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");
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");
236 /* Digital audio interface glue - connects codec <--> CPU */
237 static struct snd_soc_dai_link omap_twl4030_dai_links
[] = {
239 .name
= "TWL4030 HiFi",
240 .stream_name
= "TWL4030 HiFi",
241 .cpu_dai_name
= "omap-mcbsp.2",
242 .codec_dai_name
= "twl4030-hifi",
243 .platform_name
= "omap-pcm-audio",
244 .codec_name
= "twl4030-codec",
245 .init
= omap_twl4030_init
,
246 .ops
= &omap_twl4030_ops
,
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
,
260 /* Audio machine driver */
261 static struct snd_soc_card omap_twl4030_card
= {
262 .owner
= THIS_MODULE
,
263 .dai_link
= omap_twl4030_dai_links
,
264 .num_links
= ARRAY_SIZE(omap_twl4030_dai_links
),
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
),
272 static int omap_twl4030_probe(struct platform_device
*pdev
)
274 struct omap_tw4030_pdata
*pdata
= dev_get_platdata(&pdev
->dev
);
275 struct device_node
*node
= pdev
->dev
.of_node
;
276 struct snd_soc_card
*card
= &omap_twl4030_card
;
277 struct omap_twl4030
*priv
;
280 card
->dev
= &pdev
->dev
;
282 priv
= devm_kzalloc(&pdev
->dev
, sizeof(struct omap_twl4030
), GFP_KERNEL
);
287 struct device_node
*dai_node
;
288 struct property
*prop
;
290 if (snd_soc_of_parse_card_name(card
, "ti,model")) {
291 dev_err(&pdev
->dev
, "Card name is not provided\n");
295 dai_node
= of_parse_phandle(node
, "ti,mcbsp", 0);
297 dev_err(&pdev
->dev
, "McBSP node is not provided\n");
300 omap_twl4030_dai_links
[0].cpu_dai_name
= NULL
;
301 omap_twl4030_dai_links
[0].cpu_of_node
= dai_node
;
303 dai_node
= of_parse_phandle(node
, "ti,mcbsp-voice", 0);
307 omap_twl4030_dai_links
[1].cpu_dai_name
= NULL
;
308 omap_twl4030_dai_links
[1].cpu_of_node
= dai_node
;
311 priv
->jack_detect
= of_get_named_gpio(node
,
312 "ti,jack-det-gpio", 0);
314 /* Optional: audio routing can be provided */
315 prop
= of_find_property(node
, "ti,audio-routing", NULL
);
317 ret
= snd_soc_of_parse_audio_routing(card
,
322 card
->fully_routed
= 1;
325 if (pdata
->card_name
) {
326 card
->name
= pdata
->card_name
;
328 dev_err(&pdev
->dev
, "Card name is not provided\n");
332 if (!pdata
->voice_connected
)
335 priv
->jack_detect
= pdata
->jack_detect
;
337 dev_err(&pdev
->dev
, "Missing pdata\n");
341 snd_soc_card_set_drvdata(card
, priv
);
342 ret
= snd_soc_register_card(card
);
344 dev_err(&pdev
->dev
, "snd_soc_register_card() failed: %d\n",
352 static int omap_twl4030_remove(struct platform_device
*pdev
)
354 struct snd_soc_card
*card
= platform_get_drvdata(pdev
);
355 struct omap_twl4030
*priv
= snd_soc_card_get_drvdata(card
);
357 if (priv
->jack_detect
> 0)
358 snd_soc_jack_free_gpios(&priv
->hs_jack
,
359 ARRAY_SIZE(hs_jack_gpios
),
361 snd_soc_unregister_card(card
);
366 static const struct of_device_id omap_twl4030_of_match
[] = {
367 {.compatible
= "ti,omap-twl4030", },
370 MODULE_DEVICE_TABLE(of
, omap_twl4030_of_match
);
372 static struct platform_driver omap_twl4030_driver
= {
374 .name
= "omap-twl4030",
375 .owner
= THIS_MODULE
,
376 .pm
= &snd_soc_pm_ops
,
377 .of_match_table
= omap_twl4030_of_match
,
379 .probe
= omap_twl4030_probe
,
380 .remove
= omap_twl4030_remove
,
383 module_platform_driver(omap_twl4030_driver
);
385 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
386 MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
387 MODULE_LICENSE("GPL");
388 MODULE_ALIAS("platform:omap-twl4030");