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
;
58 switch (params_channels(params
)) {
59 case 2: /* Stereo I2S mode */
60 fmt
= SND_SOC_DAIFMT_I2S
|
61 SND_SOC_DAIFMT_NB_NF
|
62 SND_SOC_DAIFMT_CBM_CFM
;
64 case 4: /* Four channel TDM mode */
65 fmt
= SND_SOC_DAIFMT_DSP_A
|
66 SND_SOC_DAIFMT_IB_NF
|
67 SND_SOC_DAIFMT_CBM_CFM
;
73 return snd_soc_runtime_set_dai_fmt(rtd
, fmt
);
76 static const struct snd_soc_ops omap_twl4030_ops
= {
77 .hw_params
= omap_twl4030_hw_params
,
80 static const struct snd_soc_dapm_widget dapm_widgets
[] = {
81 SND_SOC_DAPM_SPK("Earpiece Spk", NULL
),
82 SND_SOC_DAPM_SPK("Handsfree Spk", NULL
),
83 SND_SOC_DAPM_HP("Headset Stereophone", NULL
),
84 SND_SOC_DAPM_SPK("Ext Spk", NULL
),
85 SND_SOC_DAPM_SPK("Carkit Spk", NULL
),
87 SND_SOC_DAPM_MIC("Main Mic", NULL
),
88 SND_SOC_DAPM_MIC("Sub Mic", NULL
),
89 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
90 SND_SOC_DAPM_MIC("Carkit Mic", NULL
),
91 SND_SOC_DAPM_MIC("Digital0 Mic", NULL
),
92 SND_SOC_DAPM_MIC("Digital1 Mic", NULL
),
93 SND_SOC_DAPM_LINE("Line In", NULL
),
96 static const struct snd_soc_dapm_route audio_map
[] = {
97 /* Headset Stereophone: HSOL, HSOR */
98 {"Headset Stereophone", NULL
, "HSOL"},
99 {"Headset Stereophone", NULL
, "HSOR"},
100 /* External Speakers: HFL, HFR */
101 {"Handsfree Spk", NULL
, "HFL"},
102 {"Handsfree Spk", NULL
, "HFR"},
103 /* External Speakers: PredrivL, PredrivR */
104 {"Ext Spk", NULL
, "PREDRIVEL"},
105 {"Ext Spk", NULL
, "PREDRIVER"},
106 /* Carkit speakers: CARKITL, CARKITR */
107 {"Carkit Spk", NULL
, "CARKITL"},
108 {"Carkit Spk", NULL
, "CARKITR"},
110 {"Earpiece Spk", NULL
, "EARPIECE"},
112 /* External Mics: MAINMIC, SUBMIC with bias */
113 {"MAINMIC", NULL
, "Main Mic"},
114 {"Main Mic", NULL
, "Mic Bias 1"},
115 {"SUBMIC", NULL
, "Sub Mic"},
116 {"Sub Mic", NULL
, "Mic Bias 2"},
117 /* Headset Mic: HSMIC with bias */
118 {"HSMIC", NULL
, "Headset Mic"},
119 {"Headset Mic", NULL
, "Headset Mic Bias"},
120 /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */
121 {"DIGIMIC0", NULL
, "Digital0 Mic"},
122 {"Digital0 Mic", NULL
, "Mic Bias 1"},
123 {"DIGIMIC1", NULL
, "Digital1 Mic"},
124 {"Digital1 Mic", NULL
, "Mic Bias 2"},
125 /* Carkit In: CARKITMIC */
126 {"CARKITMIC", NULL
, "Carkit Mic"},
127 /* Aux In: AUXL, AUXR */
128 {"AUXL", NULL
, "Line In"},
129 {"AUXR", NULL
, "Line In"},
132 /* Headset jack detection DAPM pins */
133 static struct snd_soc_jack_pin hs_jack_pins
[] = {
135 .pin
= "Headset Mic",
136 .mask
= SND_JACK_MICROPHONE
,
139 .pin
= "Headset Stereophone",
140 .mask
= SND_JACK_HEADPHONE
,
144 /* Headset jack detection gpios */
145 static struct snd_soc_jack_gpio hs_jack_gpios
[] = {
147 .name
= "hsdet-gpio",
148 .report
= SND_JACK_HEADSET
,
149 .debounce_time
= 200,
153 static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context
*dapm
,
154 int connected
, char *pin
)
157 snd_soc_dapm_disable_pin(dapm
, pin
);
160 static int omap_twl4030_init(struct snd_soc_pcm_runtime
*rtd
)
162 struct snd_soc_card
*card
= rtd
->card
;
163 struct snd_soc_dapm_context
*dapm
= &card
->dapm
;
164 struct omap_tw4030_pdata
*pdata
= dev_get_platdata(card
->dev
);
165 struct omap_twl4030
*priv
= snd_soc_card_get_drvdata(card
);
168 /* Headset jack detection only if it is supported */
169 if (priv
->jack_detect
> 0) {
170 hs_jack_gpios
[0].gpio
= priv
->jack_detect
;
172 ret
= snd_soc_card_jack_new(rtd
->card
, "Headset Jack",
173 SND_JACK_HEADSET
, &priv
->hs_jack
,
175 ARRAY_SIZE(hs_jack_pins
));
179 ret
= snd_soc_jack_add_gpios(&priv
->hs_jack
,
180 ARRAY_SIZE(hs_jack_gpios
),
187 * NULL pdata means we booted with DT. In this case the routing is
188 * provided and the card is fully routed, no need to mark pins.
190 if (!pdata
|| !pdata
->custom_routing
)
193 /* Disable not connected paths if not used */
194 twl4030_disconnect_pin(dapm
, pdata
->has_ear
, "Earpiece Spk");
195 twl4030_disconnect_pin(dapm
, pdata
->has_hf
, "Handsfree Spk");
196 twl4030_disconnect_pin(dapm
, pdata
->has_hs
, "Headset Stereophone");
197 twl4030_disconnect_pin(dapm
, pdata
->has_predriv
, "Ext Spk");
198 twl4030_disconnect_pin(dapm
, pdata
->has_carkit
, "Carkit Spk");
200 twl4030_disconnect_pin(dapm
, pdata
->has_mainmic
, "Main Mic");
201 twl4030_disconnect_pin(dapm
, pdata
->has_submic
, "Sub Mic");
202 twl4030_disconnect_pin(dapm
, pdata
->has_hsmic
, "Headset Mic");
203 twl4030_disconnect_pin(dapm
, pdata
->has_carkitmic
, "Carkit Mic");
204 twl4030_disconnect_pin(dapm
, pdata
->has_digimic0
, "Digital0 Mic");
205 twl4030_disconnect_pin(dapm
, pdata
->has_digimic1
, "Digital1 Mic");
206 twl4030_disconnect_pin(dapm
, pdata
->has_linein
, "Line In");
211 /* Digital audio interface glue - connects codec <--> CPU */
212 static struct snd_soc_dai_link omap_twl4030_dai_links
[] = {
214 .name
= "TWL4030 HiFi",
215 .stream_name
= "TWL4030 HiFi",
216 .cpu_dai_name
= "omap-mcbsp.2",
217 .codec_dai_name
= "twl4030-hifi",
218 .platform_name
= "omap-mcbsp.2",
219 .codec_name
= "twl4030-codec",
220 .init
= omap_twl4030_init
,
221 .ops
= &omap_twl4030_ops
,
224 .name
= "TWL4030 Voice",
225 .stream_name
= "TWL4030 Voice",
226 .cpu_dai_name
= "omap-mcbsp.3",
227 .codec_dai_name
= "twl4030-voice",
228 .platform_name
= "omap-mcbsp.3",
229 .codec_name
= "twl4030-codec",
230 .dai_fmt
= SND_SOC_DAIFMT_DSP_A
| SND_SOC_DAIFMT_IB_NF
|
231 SND_SOC_DAIFMT_CBM_CFM
,
235 /* Audio machine driver */
236 static struct snd_soc_card omap_twl4030_card
= {
237 .owner
= THIS_MODULE
,
238 .dai_link
= omap_twl4030_dai_links
,
239 .num_links
= ARRAY_SIZE(omap_twl4030_dai_links
),
241 .dapm_widgets
= dapm_widgets
,
242 .num_dapm_widgets
= ARRAY_SIZE(dapm_widgets
),
243 .dapm_routes
= audio_map
,
244 .num_dapm_routes
= ARRAY_SIZE(audio_map
),
247 static int omap_twl4030_probe(struct platform_device
*pdev
)
249 struct omap_tw4030_pdata
*pdata
= dev_get_platdata(&pdev
->dev
);
250 struct device_node
*node
= pdev
->dev
.of_node
;
251 struct snd_soc_card
*card
= &omap_twl4030_card
;
252 struct omap_twl4030
*priv
;
255 card
->dev
= &pdev
->dev
;
257 priv
= devm_kzalloc(&pdev
->dev
, sizeof(struct omap_twl4030
), GFP_KERNEL
);
262 struct device_node
*dai_node
;
263 struct property
*prop
;
265 if (snd_soc_of_parse_card_name(card
, "ti,model")) {
266 dev_err(&pdev
->dev
, "Card name is not provided\n");
270 dai_node
= of_parse_phandle(node
, "ti,mcbsp", 0);
272 dev_err(&pdev
->dev
, "McBSP node is not provided\n");
275 omap_twl4030_dai_links
[0].cpu_dai_name
= NULL
;
276 omap_twl4030_dai_links
[0].cpu_of_node
= dai_node
;
278 omap_twl4030_dai_links
[0].platform_name
= NULL
;
279 omap_twl4030_dai_links
[0].platform_of_node
= dai_node
;
281 dai_node
= of_parse_phandle(node
, "ti,mcbsp-voice", 0);
285 omap_twl4030_dai_links
[1].cpu_dai_name
= NULL
;
286 omap_twl4030_dai_links
[1].cpu_of_node
= dai_node
;
288 omap_twl4030_dai_links
[1].platform_name
= NULL
;
289 omap_twl4030_dai_links
[1].platform_of_node
= dai_node
;
292 priv
->jack_detect
= of_get_named_gpio(node
,
293 "ti,jack-det-gpio", 0);
295 /* Optional: audio routing can be provided */
296 prop
= of_find_property(node
, "ti,audio-routing", NULL
);
298 ret
= snd_soc_of_parse_audio_routing(card
,
303 card
->fully_routed
= 1;
306 if (pdata
->card_name
) {
307 card
->name
= pdata
->card_name
;
309 dev_err(&pdev
->dev
, "Card name is not provided\n");
313 if (!pdata
->voice_connected
)
316 priv
->jack_detect
= pdata
->jack_detect
;
318 dev_err(&pdev
->dev
, "Missing pdata\n");
322 snd_soc_card_set_drvdata(card
, priv
);
323 ret
= devm_snd_soc_register_card(&pdev
->dev
, card
);
325 dev_err(&pdev
->dev
, "devm_snd_soc_register_card() failed: %d\n",
333 static const struct of_device_id omap_twl4030_of_match
[] = {
334 {.compatible
= "ti,omap-twl4030", },
337 MODULE_DEVICE_TABLE(of
, omap_twl4030_of_match
);
339 static struct platform_driver omap_twl4030_driver
= {
341 .name
= "omap-twl4030",
342 .pm
= &snd_soc_pm_ops
,
343 .of_match_table
= omap_twl4030_of_match
,
345 .probe
= omap_twl4030_probe
,
348 module_platform_driver(omap_twl4030_driver
);
350 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
351 MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
352 MODULE_LICENSE("GPL");
353 MODULE_ALIAS("platform:omap-twl4030");