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_card
*card
= rtd
->card
;
62 switch (params_channels(params
)) {
63 case 2: /* Stereo I2S mode */
64 fmt
= SND_SOC_DAIFMT_I2S
|
65 SND_SOC_DAIFMT_NB_NF
|
66 SND_SOC_DAIFMT_CBM_CFM
;
68 case 4: /* Four channel TDM mode */
69 fmt
= SND_SOC_DAIFMT_DSP_A
|
70 SND_SOC_DAIFMT_IB_NF
|
71 SND_SOC_DAIFMT_CBM_CFM
;
77 /* Set codec DAI configuration */
78 ret
= snd_soc_dai_set_fmt(codec_dai
, fmt
);
80 dev_err(card
->dev
, "can't set codec DAI configuration\n");
84 /* Set cpu DAI configuration */
85 ret
= snd_soc_dai_set_fmt(cpu_dai
, fmt
);
87 dev_err(card
->dev
, "can't set cpu DAI configuration\n");
94 static struct snd_soc_ops omap_twl4030_ops
= {
95 .hw_params
= omap_twl4030_hw_params
,
98 static const struct snd_soc_dapm_widget dapm_widgets
[] = {
99 SND_SOC_DAPM_SPK("Earpiece Spk", NULL
),
100 SND_SOC_DAPM_SPK("Handsfree Spk", NULL
),
101 SND_SOC_DAPM_HP("Headset Stereophone", NULL
),
102 SND_SOC_DAPM_SPK("Ext Spk", NULL
),
103 SND_SOC_DAPM_SPK("Carkit Spk", NULL
),
105 SND_SOC_DAPM_MIC("Main Mic", NULL
),
106 SND_SOC_DAPM_MIC("Sub Mic", NULL
),
107 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
108 SND_SOC_DAPM_MIC("Carkit Mic", NULL
),
109 SND_SOC_DAPM_MIC("Digital0 Mic", NULL
),
110 SND_SOC_DAPM_MIC("Digital1 Mic", NULL
),
111 SND_SOC_DAPM_LINE("Line In", NULL
),
114 static const struct snd_soc_dapm_route audio_map
[] = {
115 /* Headset Stereophone: HSOL, HSOR */
116 {"Headset Stereophone", NULL
, "HSOL"},
117 {"Headset Stereophone", NULL
, "HSOR"},
118 /* External Speakers: HFL, HFR */
119 {"Handsfree Spk", NULL
, "HFL"},
120 {"Handsfree Spk", NULL
, "HFR"},
121 /* External Speakers: PredrivL, PredrivR */
122 {"Ext Spk", NULL
, "PREDRIVEL"},
123 {"Ext Spk", NULL
, "PREDRIVER"},
124 /* Carkit speakers: CARKITL, CARKITR */
125 {"Carkit Spk", NULL
, "CARKITL"},
126 {"Carkit Spk", NULL
, "CARKITR"},
128 {"Earpiece Spk", NULL
, "EARPIECE"},
130 /* External Mics: MAINMIC, SUBMIC with bias */
131 {"MAINMIC", NULL
, "Main Mic"},
132 {"Main Mic", NULL
, "Mic Bias 1"},
133 {"SUBMIC", NULL
, "Sub Mic"},
134 {"Sub Mic", NULL
, "Mic Bias 2"},
135 /* Headset Mic: HSMIC with bias */
136 {"HSMIC", NULL
, "Headset Mic"},
137 {"Headset Mic", NULL
, "Headset Mic Bias"},
138 /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */
139 {"DIGIMIC0", NULL
, "Digital0 Mic"},
140 {"Digital0 Mic", NULL
, "Mic Bias 1"},
141 {"DIGIMIC1", NULL
, "Digital1 Mic"},
142 {"Digital1 Mic", NULL
, "Mic Bias 2"},
143 /* Carkit In: CARKITMIC */
144 {"CARKITMIC", NULL
, "Carkit Mic"},
145 /* Aux In: AUXL, AUXR */
146 {"AUXL", NULL
, "Line In"},
147 {"AUXR", NULL
, "Line In"},
150 /* Headset jack detection DAPM pins */
151 static struct snd_soc_jack_pin hs_jack_pins
[] = {
153 .pin
= "Headset Mic",
154 .mask
= SND_JACK_MICROPHONE
,
157 .pin
= "Headset Stereophone",
158 .mask
= SND_JACK_HEADPHONE
,
162 /* Headset jack detection gpios */
163 static struct snd_soc_jack_gpio hs_jack_gpios
[] = {
165 .name
= "hsdet-gpio",
166 .report
= SND_JACK_HEADSET
,
167 .debounce_time
= 200,
171 static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context
*dapm
,
172 int connected
, char *pin
)
175 snd_soc_dapm_disable_pin(dapm
, pin
);
178 static int omap_twl4030_init(struct snd_soc_pcm_runtime
*rtd
)
180 struct snd_soc_codec
*codec
= rtd
->codec
;
181 struct snd_soc_card
*card
= rtd
->card
;
182 struct snd_soc_dapm_context
*dapm
= &codec
->dapm
;
183 struct omap_tw4030_pdata
*pdata
= dev_get_platdata(card
->dev
);
184 struct omap_twl4030
*priv
= snd_soc_card_get_drvdata(card
);
187 /* Headset jack detection only if it is supported */
188 if (priv
->jack_detect
> 0) {
189 hs_jack_gpios
[0].gpio
= priv
->jack_detect
;
191 ret
= snd_soc_jack_new(codec
, "Headset Jack", SND_JACK_HEADSET
,
196 ret
= snd_soc_jack_add_pins(&priv
->hs_jack
,
197 ARRAY_SIZE(hs_jack_pins
),
202 ret
= snd_soc_jack_add_gpios(&priv
->hs_jack
,
203 ARRAY_SIZE(hs_jack_gpios
),
210 * NULL pdata means we booted with DT. In this case the routing is
211 * provided and the card is fully routed, no need to mark pins.
213 if (!pdata
|| !pdata
->custom_routing
)
216 /* Disable not connected paths if not used */
217 twl4030_disconnect_pin(dapm
, pdata
->has_ear
, "Earpiece Spk");
218 twl4030_disconnect_pin(dapm
, pdata
->has_hf
, "Handsfree Spk");
219 twl4030_disconnect_pin(dapm
, pdata
->has_hs
, "Headset Stereophone");
220 twl4030_disconnect_pin(dapm
, pdata
->has_predriv
, "Ext Spk");
221 twl4030_disconnect_pin(dapm
, pdata
->has_carkit
, "Carkit Spk");
223 twl4030_disconnect_pin(dapm
, pdata
->has_mainmic
, "Main Mic");
224 twl4030_disconnect_pin(dapm
, pdata
->has_submic
, "Sub Mic");
225 twl4030_disconnect_pin(dapm
, pdata
->has_hsmic
, "Headset Mic");
226 twl4030_disconnect_pin(dapm
, pdata
->has_carkitmic
, "Carkit Mic");
227 twl4030_disconnect_pin(dapm
, pdata
->has_digimic0
, "Digital0 Mic");
228 twl4030_disconnect_pin(dapm
, pdata
->has_digimic1
, "Digital1 Mic");
229 twl4030_disconnect_pin(dapm
, pdata
->has_linein
, "Line In");
234 static int omap_twl4030_card_remove(struct snd_soc_card
*card
)
236 struct omap_twl4030
*priv
= snd_soc_card_get_drvdata(card
);
238 if (priv
->jack_detect
> 0)
239 snd_soc_jack_free_gpios(&priv
->hs_jack
,
240 ARRAY_SIZE(hs_jack_gpios
),
246 /* Digital audio interface glue - connects codec <--> CPU */
247 static struct snd_soc_dai_link omap_twl4030_dai_links
[] = {
249 .name
= "TWL4030 HiFi",
250 .stream_name
= "TWL4030 HiFi",
251 .cpu_dai_name
= "omap-mcbsp.2",
252 .codec_dai_name
= "twl4030-hifi",
253 .platform_name
= "omap-mcbsp.2",
254 .codec_name
= "twl4030-codec",
255 .init
= omap_twl4030_init
,
256 .ops
= &omap_twl4030_ops
,
259 .name
= "TWL4030 Voice",
260 .stream_name
= "TWL4030 Voice",
261 .cpu_dai_name
= "omap-mcbsp.3",
262 .codec_dai_name
= "twl4030-voice",
263 .platform_name
= "omap-mcbsp.3",
264 .codec_name
= "twl4030-codec",
265 .dai_fmt
= SND_SOC_DAIFMT_DSP_A
| SND_SOC_DAIFMT_IB_NF
|
266 SND_SOC_DAIFMT_CBM_CFM
,
270 /* Audio machine driver */
271 static struct snd_soc_card omap_twl4030_card
= {
272 .owner
= THIS_MODULE
,
273 .remove
= omap_twl4030_card_remove
,
274 .dai_link
= omap_twl4030_dai_links
,
275 .num_links
= ARRAY_SIZE(omap_twl4030_dai_links
),
277 .dapm_widgets
= dapm_widgets
,
278 .num_dapm_widgets
= ARRAY_SIZE(dapm_widgets
),
279 .dapm_routes
= audio_map
,
280 .num_dapm_routes
= ARRAY_SIZE(audio_map
),
283 static int omap_twl4030_probe(struct platform_device
*pdev
)
285 struct omap_tw4030_pdata
*pdata
= dev_get_platdata(&pdev
->dev
);
286 struct device_node
*node
= pdev
->dev
.of_node
;
287 struct snd_soc_card
*card
= &omap_twl4030_card
;
288 struct omap_twl4030
*priv
;
291 card
->dev
= &pdev
->dev
;
293 priv
= devm_kzalloc(&pdev
->dev
, sizeof(struct omap_twl4030
), GFP_KERNEL
);
298 struct device_node
*dai_node
;
299 struct property
*prop
;
301 if (snd_soc_of_parse_card_name(card
, "ti,model")) {
302 dev_err(&pdev
->dev
, "Card name is not provided\n");
306 dai_node
= of_parse_phandle(node
, "ti,mcbsp", 0);
308 dev_err(&pdev
->dev
, "McBSP node is not provided\n");
311 omap_twl4030_dai_links
[0].cpu_dai_name
= NULL
;
312 omap_twl4030_dai_links
[0].cpu_of_node
= dai_node
;
314 omap_twl4030_dai_links
[0].platform_name
= NULL
;
315 omap_twl4030_dai_links
[0].platform_of_node
= dai_node
;
317 dai_node
= of_parse_phandle(node
, "ti,mcbsp-voice", 0);
321 omap_twl4030_dai_links
[1].cpu_dai_name
= NULL
;
322 omap_twl4030_dai_links
[1].cpu_of_node
= dai_node
;
324 omap_twl4030_dai_links
[1].platform_name
= NULL
;
325 omap_twl4030_dai_links
[1].platform_of_node
= dai_node
;
328 priv
->jack_detect
= of_get_named_gpio(node
,
329 "ti,jack-det-gpio", 0);
331 /* Optional: audio routing can be provided */
332 prop
= of_find_property(node
, "ti,audio-routing", NULL
);
334 ret
= snd_soc_of_parse_audio_routing(card
,
339 card
->fully_routed
= 1;
342 if (pdata
->card_name
) {
343 card
->name
= pdata
->card_name
;
345 dev_err(&pdev
->dev
, "Card name is not provided\n");
349 if (!pdata
->voice_connected
)
352 priv
->jack_detect
= pdata
->jack_detect
;
354 dev_err(&pdev
->dev
, "Missing pdata\n");
358 snd_soc_card_set_drvdata(card
, priv
);
359 ret
= devm_snd_soc_register_card(&pdev
->dev
, card
);
361 dev_err(&pdev
->dev
, "devm_snd_soc_register_card() failed: %d\n",
369 static const struct of_device_id omap_twl4030_of_match
[] = {
370 {.compatible
= "ti,omap-twl4030", },
373 MODULE_DEVICE_TABLE(of
, omap_twl4030_of_match
);
375 static struct platform_driver omap_twl4030_driver
= {
377 .name
= "omap-twl4030",
378 .pm
= &snd_soc_pm_ops
,
379 .of_match_table
= omap_twl4030_of_match
,
381 .probe
= omap_twl4030_probe
,
384 module_platform_driver(omap_twl4030_driver
);
386 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
387 MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
388 MODULE_LICENSE("GPL");
389 MODULE_ALIAS("platform:omap-twl4030");