xref: /openbmc/linux/sound/soc/ti/omap-twl4030.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f2055e14SPeter Ujfalusi /*
3f2055e14SPeter Ujfalusi  * omap-twl4030.c  --  SoC audio for TI SoC based boards with twl4030 codec
4f2055e14SPeter Ujfalusi  *
53323a148SAlexander A. Klimov  * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com
6f2055e14SPeter Ujfalusi  * All rights reserved.
7f2055e14SPeter Ujfalusi  *
8f2055e14SPeter Ujfalusi  * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
9f2055e14SPeter Ujfalusi  *
10f2055e14SPeter Ujfalusi  * This driver replaces the following machine drivers:
11f2055e14SPeter Ujfalusi  * omap3beagle (Author: Steve Sakoman <steve@sakoman.com>)
12f2055e14SPeter Ujfalusi  * omap3evm (Author: Anuj Aggarwal <anuj.aggarwal@ti.com>)
13f2055e14SPeter Ujfalusi  * overo (Author: Steve Sakoman <steve@sakoman.com>)
14f2055e14SPeter Ujfalusi  * igep0020 (Author: Enric Balletbo i Serra <eballetbo@iseebcn.com>)
15f2055e14SPeter Ujfalusi  * zoom2 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
16f2055e14SPeter Ujfalusi  * sdp3430 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
17f2055e14SPeter Ujfalusi  */
18f2055e14SPeter Ujfalusi 
19f2055e14SPeter Ujfalusi #include <linux/platform_device.h>
20f2055e14SPeter Ujfalusi #include <linux/platform_data/omap-twl4030.h>
21f2055e14SPeter Ujfalusi #include <linux/module.h>
22f2055e14SPeter Ujfalusi #include <linux/of.h>
23f2055e14SPeter Ujfalusi #include <linux/gpio.h>
24f2055e14SPeter Ujfalusi #include <linux/of_gpio.h>
25f2055e14SPeter Ujfalusi 
26f2055e14SPeter Ujfalusi #include <sound/core.h>
27f2055e14SPeter Ujfalusi #include <sound/pcm.h>
28f2055e14SPeter Ujfalusi #include <sound/soc.h>
29f2055e14SPeter Ujfalusi #include <sound/jack.h>
30f2055e14SPeter Ujfalusi 
31f2055e14SPeter Ujfalusi #include "omap-mcbsp.h"
32f2055e14SPeter Ujfalusi 
33f2055e14SPeter Ujfalusi struct omap_twl4030 {
34f2055e14SPeter Ujfalusi 	int jack_detect;	/* board can detect jack events */
35f2055e14SPeter Ujfalusi 	struct snd_soc_jack hs_jack;
36f2055e14SPeter Ujfalusi };
37f2055e14SPeter Ujfalusi 
omap_twl4030_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)38f2055e14SPeter Ujfalusi static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
39f2055e14SPeter Ujfalusi 	struct snd_pcm_hw_params *params)
40f2055e14SPeter Ujfalusi {
4102cde14aSKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
42f2055e14SPeter Ujfalusi 	unsigned int fmt;
43f2055e14SPeter Ujfalusi 
44f2055e14SPeter Ujfalusi 	switch (params_channels(params)) {
45f2055e14SPeter Ujfalusi 	case 2: /* Stereo I2S mode */
46f2055e14SPeter Ujfalusi 		fmt =	SND_SOC_DAIFMT_I2S |
47f2055e14SPeter Ujfalusi 			SND_SOC_DAIFMT_NB_NF |
48f2055e14SPeter Ujfalusi 			SND_SOC_DAIFMT_CBM_CFM;
49f2055e14SPeter Ujfalusi 		break;
50f2055e14SPeter Ujfalusi 	case 4: /* Four channel TDM mode */
51f2055e14SPeter Ujfalusi 		fmt =	SND_SOC_DAIFMT_DSP_A |
52f2055e14SPeter Ujfalusi 			SND_SOC_DAIFMT_IB_NF |
53f2055e14SPeter Ujfalusi 			SND_SOC_DAIFMT_CBM_CFM;
54f2055e14SPeter Ujfalusi 		break;
55f2055e14SPeter Ujfalusi 	default:
56f2055e14SPeter Ujfalusi 		return -EINVAL;
57f2055e14SPeter Ujfalusi 	}
58f2055e14SPeter Ujfalusi 
59f2055e14SPeter Ujfalusi 	return snd_soc_runtime_set_dai_fmt(rtd, fmt);
60f2055e14SPeter Ujfalusi }
61f2055e14SPeter Ujfalusi 
62f2055e14SPeter Ujfalusi static const struct snd_soc_ops omap_twl4030_ops = {
63f2055e14SPeter Ujfalusi 	.hw_params = omap_twl4030_hw_params,
64f2055e14SPeter Ujfalusi };
65f2055e14SPeter Ujfalusi 
66f2055e14SPeter Ujfalusi static const struct snd_soc_dapm_widget dapm_widgets[] = {
67f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_SPK("Earpiece Spk", NULL),
68f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_SPK("Handsfree Spk", NULL),
69f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_HP("Headset Stereophone", NULL),
70f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_SPK("Ext Spk", NULL),
71f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_SPK("Carkit Spk", NULL),
72f2055e14SPeter Ujfalusi 
73f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_MIC("Main Mic", NULL),
74f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_MIC("Sub Mic", NULL),
75f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
76f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_MIC("Carkit Mic", NULL),
77f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_MIC("Digital0 Mic", NULL),
78f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_MIC("Digital1 Mic", NULL),
79f2055e14SPeter Ujfalusi 	SND_SOC_DAPM_LINE("Line In", NULL),
80f2055e14SPeter Ujfalusi };
81f2055e14SPeter Ujfalusi 
82f2055e14SPeter Ujfalusi static const struct snd_soc_dapm_route audio_map[] = {
83f2055e14SPeter Ujfalusi 	/* Headset Stereophone:  HSOL, HSOR */
84f2055e14SPeter Ujfalusi 	{"Headset Stereophone", NULL, "HSOL"},
85f2055e14SPeter Ujfalusi 	{"Headset Stereophone", NULL, "HSOR"},
86f2055e14SPeter Ujfalusi 	/* External Speakers: HFL, HFR */
87f2055e14SPeter Ujfalusi 	{"Handsfree Spk", NULL, "HFL"},
88f2055e14SPeter Ujfalusi 	{"Handsfree Spk", NULL, "HFR"},
89f2055e14SPeter Ujfalusi 	/* External Speakers: PredrivL, PredrivR */
90f2055e14SPeter Ujfalusi 	{"Ext Spk", NULL, "PREDRIVEL"},
91f2055e14SPeter Ujfalusi 	{"Ext Spk", NULL, "PREDRIVER"},
92f2055e14SPeter Ujfalusi 	/* Carkit speakers:  CARKITL, CARKITR */
93f2055e14SPeter Ujfalusi 	{"Carkit Spk", NULL, "CARKITL"},
94f2055e14SPeter Ujfalusi 	{"Carkit Spk", NULL, "CARKITR"},
95f2055e14SPeter Ujfalusi 	/* Earpiece */
96f2055e14SPeter Ujfalusi 	{"Earpiece Spk", NULL, "EARPIECE"},
97f2055e14SPeter Ujfalusi 
98f2055e14SPeter Ujfalusi 	/* External Mics: MAINMIC, SUBMIC with bias */
99f2055e14SPeter Ujfalusi 	{"MAINMIC", NULL, "Main Mic"},
100f2055e14SPeter Ujfalusi 	{"Main Mic", NULL, "Mic Bias 1"},
101f2055e14SPeter Ujfalusi 	{"SUBMIC", NULL, "Sub Mic"},
102f2055e14SPeter Ujfalusi 	{"Sub Mic", NULL, "Mic Bias 2"},
103f2055e14SPeter Ujfalusi 	/* Headset Mic: HSMIC with bias */
104f2055e14SPeter Ujfalusi 	{"HSMIC", NULL, "Headset Mic"},
105f2055e14SPeter Ujfalusi 	{"Headset Mic", NULL, "Headset Mic Bias"},
106f2055e14SPeter Ujfalusi 	/* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */
107f2055e14SPeter Ujfalusi 	{"DIGIMIC0", NULL, "Digital0 Mic"},
108f2055e14SPeter Ujfalusi 	{"Digital0 Mic", NULL, "Mic Bias 1"},
109f2055e14SPeter Ujfalusi 	{"DIGIMIC1", NULL, "Digital1 Mic"},
110f2055e14SPeter Ujfalusi 	{"Digital1 Mic", NULL, "Mic Bias 2"},
111f2055e14SPeter Ujfalusi 	/* Carkit In: CARKITMIC */
112f2055e14SPeter Ujfalusi 	{"CARKITMIC", NULL, "Carkit Mic"},
113f2055e14SPeter Ujfalusi 	/* Aux In: AUXL, AUXR */
114f2055e14SPeter Ujfalusi 	{"AUXL", NULL, "Line In"},
115f2055e14SPeter Ujfalusi 	{"AUXR", NULL, "Line In"},
116f2055e14SPeter Ujfalusi };
117f2055e14SPeter Ujfalusi 
118f2055e14SPeter Ujfalusi /* Headset jack detection DAPM pins */
119f2055e14SPeter Ujfalusi static struct snd_soc_jack_pin hs_jack_pins[] = {
120f2055e14SPeter Ujfalusi 	{
121f2055e14SPeter Ujfalusi 		.pin = "Headset Mic",
122f2055e14SPeter Ujfalusi 		.mask = SND_JACK_MICROPHONE,
123f2055e14SPeter Ujfalusi 	},
124f2055e14SPeter Ujfalusi 	{
125f2055e14SPeter Ujfalusi 		.pin = "Headset Stereophone",
126f2055e14SPeter Ujfalusi 		.mask = SND_JACK_HEADPHONE,
127f2055e14SPeter Ujfalusi 	},
128f2055e14SPeter Ujfalusi };
129f2055e14SPeter Ujfalusi 
130f2055e14SPeter Ujfalusi /* Headset jack detection gpios */
131f2055e14SPeter Ujfalusi static struct snd_soc_jack_gpio hs_jack_gpios[] = {
132f2055e14SPeter Ujfalusi 	{
133f2055e14SPeter Ujfalusi 		.name = "hsdet-gpio",
134f2055e14SPeter Ujfalusi 		.report = SND_JACK_HEADSET,
135f2055e14SPeter Ujfalusi 		.debounce_time = 200,
136f2055e14SPeter Ujfalusi 	},
137f2055e14SPeter Ujfalusi };
138f2055e14SPeter Ujfalusi 
twl4030_disconnect_pin(struct snd_soc_dapm_context * dapm,int connected,char * pin)139f2055e14SPeter Ujfalusi static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
140f2055e14SPeter Ujfalusi 					  int connected, char *pin)
141f2055e14SPeter Ujfalusi {
142f2055e14SPeter Ujfalusi 	if (!connected)
143f2055e14SPeter Ujfalusi 		snd_soc_dapm_disable_pin(dapm, pin);
144f2055e14SPeter Ujfalusi }
145f2055e14SPeter Ujfalusi 
omap_twl4030_init(struct snd_soc_pcm_runtime * rtd)146f2055e14SPeter Ujfalusi static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
147f2055e14SPeter Ujfalusi {
148f2055e14SPeter Ujfalusi 	struct snd_soc_card *card = rtd->card;
149f2055e14SPeter Ujfalusi 	struct snd_soc_dapm_context *dapm = &card->dapm;
150f2055e14SPeter Ujfalusi 	struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
151f2055e14SPeter Ujfalusi 	struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
152f2055e14SPeter Ujfalusi 	int ret = 0;
153f2055e14SPeter Ujfalusi 
154f2055e14SPeter Ujfalusi 	/* Headset jack detection only if it is supported */
155f2055e14SPeter Ujfalusi 	if (priv->jack_detect > 0) {
156f2055e14SPeter Ujfalusi 		hs_jack_gpios[0].gpio = priv->jack_detect;
157f2055e14SPeter Ujfalusi 
158*19aed2d6SAkihiko Odaki 		ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
159*19aed2d6SAkihiko Odaki 						 SND_JACK_HEADSET,
160*19aed2d6SAkihiko Odaki 						 &priv->hs_jack, hs_jack_pins,
161f2055e14SPeter Ujfalusi 						 ARRAY_SIZE(hs_jack_pins));
162f2055e14SPeter Ujfalusi 		if (ret)
163f2055e14SPeter Ujfalusi 			return ret;
164f2055e14SPeter Ujfalusi 
165f2055e14SPeter Ujfalusi 		ret = snd_soc_jack_add_gpios(&priv->hs_jack,
166f2055e14SPeter Ujfalusi 					     ARRAY_SIZE(hs_jack_gpios),
167f2055e14SPeter Ujfalusi 					     hs_jack_gpios);
168f2055e14SPeter Ujfalusi 		if (ret)
169f2055e14SPeter Ujfalusi 			return ret;
170f2055e14SPeter Ujfalusi 	}
171f2055e14SPeter Ujfalusi 
172f2055e14SPeter Ujfalusi 	/*
173f2055e14SPeter Ujfalusi 	 * NULL pdata means we booted with DT. In this case the routing is
174f2055e14SPeter Ujfalusi 	 * provided and the card is fully routed, no need to mark pins.
175f2055e14SPeter Ujfalusi 	 */
176f2055e14SPeter Ujfalusi 	if (!pdata || !pdata->custom_routing)
177f2055e14SPeter Ujfalusi 		return ret;
178f2055e14SPeter Ujfalusi 
179f2055e14SPeter Ujfalusi 	/* Disable not connected paths if not used */
180f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk");
181f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk");
182f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
183f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk");
184f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk");
185f2055e14SPeter Ujfalusi 
186f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic");
187f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic");
188f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
189f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic");
190f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic");
191f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic");
192f2055e14SPeter Ujfalusi 	twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In");
193f2055e14SPeter Ujfalusi 
194f2055e14SPeter Ujfalusi 	return ret;
195f2055e14SPeter Ujfalusi }
196f2055e14SPeter Ujfalusi 
197f2055e14SPeter Ujfalusi /* Digital audio interface glue - connects codec <--> CPU */
198d6db366aSKuninori Morimoto SND_SOC_DAILINK_DEFS(hifi,
199d6db366aSKuninori Morimoto 	DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.2")),
200eb313e23SKuninori Morimoto 	DAILINK_COMP_ARRAY(COMP_CODEC("twl4030-codec", "twl4030-hifi")),
201eb313e23SKuninori Morimoto 	DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.2")));
202d6db366aSKuninori Morimoto 
203d6db366aSKuninori Morimoto SND_SOC_DAILINK_DEFS(voice,
204d6db366aSKuninori Morimoto 	DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.3")),
205eb313e23SKuninori Morimoto 	DAILINK_COMP_ARRAY(COMP_CODEC("twl4030-codec", "twl4030-voice")),
206eb313e23SKuninori Morimoto 	DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.3")));
207d6db366aSKuninori Morimoto 
208f2055e14SPeter Ujfalusi static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
209f2055e14SPeter Ujfalusi 	{
210f2055e14SPeter Ujfalusi 		.name = "TWL4030 HiFi",
211f2055e14SPeter Ujfalusi 		.stream_name = "TWL4030 HiFi",
212f2055e14SPeter Ujfalusi 		.init = omap_twl4030_init,
213f2055e14SPeter Ujfalusi 		.ops = &omap_twl4030_ops,
214d6db366aSKuninori Morimoto 		SND_SOC_DAILINK_REG(hifi),
215f2055e14SPeter Ujfalusi 	},
216f2055e14SPeter Ujfalusi 	{
217f2055e14SPeter Ujfalusi 		.name = "TWL4030 Voice",
218f2055e14SPeter Ujfalusi 		.stream_name = "TWL4030 Voice",
219f2055e14SPeter Ujfalusi 		.dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
220f2055e14SPeter Ujfalusi 			   SND_SOC_DAIFMT_CBM_CFM,
221d6db366aSKuninori Morimoto 		SND_SOC_DAILINK_REG(voice),
222f2055e14SPeter Ujfalusi 	},
223f2055e14SPeter Ujfalusi };
224f2055e14SPeter Ujfalusi 
225f2055e14SPeter Ujfalusi /* Audio machine driver */
226f2055e14SPeter Ujfalusi static struct snd_soc_card omap_twl4030_card = {
227f2055e14SPeter Ujfalusi 	.owner = THIS_MODULE,
228f2055e14SPeter Ujfalusi 	.dai_link = omap_twl4030_dai_links,
229f2055e14SPeter Ujfalusi 	.num_links = ARRAY_SIZE(omap_twl4030_dai_links),
230f2055e14SPeter Ujfalusi 
231f2055e14SPeter Ujfalusi 	.dapm_widgets = dapm_widgets,
232f2055e14SPeter Ujfalusi 	.num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
233f2055e14SPeter Ujfalusi 	.dapm_routes = audio_map,
234f2055e14SPeter Ujfalusi 	.num_dapm_routes = ARRAY_SIZE(audio_map),
235f2055e14SPeter Ujfalusi };
236f2055e14SPeter Ujfalusi 
omap_twl4030_probe(struct platform_device * pdev)237f2055e14SPeter Ujfalusi static int omap_twl4030_probe(struct platform_device *pdev)
238f2055e14SPeter Ujfalusi {
239f2055e14SPeter Ujfalusi 	struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev);
240f2055e14SPeter Ujfalusi 	struct device_node *node = pdev->dev.of_node;
241f2055e14SPeter Ujfalusi 	struct snd_soc_card *card = &omap_twl4030_card;
242f2055e14SPeter Ujfalusi 	struct omap_twl4030 *priv;
243f2055e14SPeter Ujfalusi 	int ret = 0;
244f2055e14SPeter Ujfalusi 
245f2055e14SPeter Ujfalusi 	card->dev = &pdev->dev;
246f2055e14SPeter Ujfalusi 
247f2055e14SPeter Ujfalusi 	priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL);
248f2055e14SPeter Ujfalusi 	if (priv == NULL)
249f2055e14SPeter Ujfalusi 		return -ENOMEM;
250f2055e14SPeter Ujfalusi 
251f2055e14SPeter Ujfalusi 	if (node) {
252f2055e14SPeter Ujfalusi 		struct device_node *dai_node;
253f2055e14SPeter Ujfalusi 		struct property *prop;
254f2055e14SPeter Ujfalusi 
255f2055e14SPeter Ujfalusi 		if (snd_soc_of_parse_card_name(card, "ti,model")) {
256f2055e14SPeter Ujfalusi 			dev_err(&pdev->dev, "Card name is not provided\n");
257f2055e14SPeter Ujfalusi 			return -ENODEV;
258f2055e14SPeter Ujfalusi 		}
259f2055e14SPeter Ujfalusi 
260f2055e14SPeter Ujfalusi 		dai_node = of_parse_phandle(node, "ti,mcbsp", 0);
261f2055e14SPeter Ujfalusi 		if (!dai_node) {
262f2055e14SPeter Ujfalusi 			dev_err(&pdev->dev, "McBSP node is not provided\n");
263f2055e14SPeter Ujfalusi 			return -EINVAL;
264f2055e14SPeter Ujfalusi 		}
265d6db366aSKuninori Morimoto 		omap_twl4030_dai_links[0].cpus->dai_name  = NULL;
266d6db366aSKuninori Morimoto 		omap_twl4030_dai_links[0].cpus->of_node = dai_node;
267f2055e14SPeter Ujfalusi 
268eb313e23SKuninori Morimoto 		omap_twl4030_dai_links[0].platforms->name  = NULL;
269eb313e23SKuninori Morimoto 		omap_twl4030_dai_links[0].platforms->of_node = dai_node;
270eb313e23SKuninori Morimoto 
271f2055e14SPeter Ujfalusi 		dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0);
272f2055e14SPeter Ujfalusi 		if (!dai_node) {
273f2055e14SPeter Ujfalusi 			card->num_links = 1;
274f2055e14SPeter Ujfalusi 		} else {
275d6db366aSKuninori Morimoto 			omap_twl4030_dai_links[1].cpus->dai_name  = NULL;
276d6db366aSKuninori Morimoto 			omap_twl4030_dai_links[1].cpus->of_node = dai_node;
277eb313e23SKuninori Morimoto 
278eb313e23SKuninori Morimoto 			omap_twl4030_dai_links[1].platforms->name  = NULL;
279eb313e23SKuninori Morimoto 			omap_twl4030_dai_links[1].platforms->of_node = dai_node;
280f2055e14SPeter Ujfalusi 		}
281f2055e14SPeter Ujfalusi 
282f2055e14SPeter Ujfalusi 		priv->jack_detect = of_get_named_gpio(node,
283f2055e14SPeter Ujfalusi 						      "ti,jack-det-gpio", 0);
284f2055e14SPeter Ujfalusi 
285f2055e14SPeter Ujfalusi 		/* Optional: audio routing can be provided */
286f2055e14SPeter Ujfalusi 		prop = of_find_property(node, "ti,audio-routing", NULL);
287f2055e14SPeter Ujfalusi 		if (prop) {
288f2055e14SPeter Ujfalusi 			ret = snd_soc_of_parse_audio_routing(card,
289f2055e14SPeter Ujfalusi 							    "ti,audio-routing");
290f2055e14SPeter Ujfalusi 			if (ret)
291f2055e14SPeter Ujfalusi 				return ret;
292f2055e14SPeter Ujfalusi 
293f2055e14SPeter Ujfalusi 			card->fully_routed = 1;
294f2055e14SPeter Ujfalusi 		}
295f2055e14SPeter Ujfalusi 	} else if (pdata) {
296f2055e14SPeter Ujfalusi 		if (pdata->card_name) {
297f2055e14SPeter Ujfalusi 			card->name = pdata->card_name;
298f2055e14SPeter Ujfalusi 		} else {
299f2055e14SPeter Ujfalusi 			dev_err(&pdev->dev, "Card name is not provided\n");
300f2055e14SPeter Ujfalusi 			return -ENODEV;
301f2055e14SPeter Ujfalusi 		}
302f2055e14SPeter Ujfalusi 
303f2055e14SPeter Ujfalusi 		if (!pdata->voice_connected)
304f2055e14SPeter Ujfalusi 			card->num_links = 1;
305f2055e14SPeter Ujfalusi 
306f2055e14SPeter Ujfalusi 		priv->jack_detect = pdata->jack_detect;
307f2055e14SPeter Ujfalusi 	} else {
308f2055e14SPeter Ujfalusi 		dev_err(&pdev->dev, "Missing pdata\n");
309f2055e14SPeter Ujfalusi 		return -ENODEV;
310f2055e14SPeter Ujfalusi 	}
311f2055e14SPeter Ujfalusi 
312f2055e14SPeter Ujfalusi 	snd_soc_card_set_drvdata(card, priv);
313f2055e14SPeter Ujfalusi 	ret = devm_snd_soc_register_card(&pdev->dev, card);
314f2055e14SPeter Ujfalusi 	if (ret) {
315f2055e14SPeter Ujfalusi 		dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
316f2055e14SPeter Ujfalusi 			ret);
317f2055e14SPeter Ujfalusi 		return ret;
318f2055e14SPeter Ujfalusi 	}
319f2055e14SPeter Ujfalusi 
320f2055e14SPeter Ujfalusi 	return 0;
321f2055e14SPeter Ujfalusi }
322f2055e14SPeter Ujfalusi 
323f2055e14SPeter Ujfalusi static const struct of_device_id omap_twl4030_of_match[] = {
324f2055e14SPeter Ujfalusi 	{.compatible = "ti,omap-twl4030", },
325f2055e14SPeter Ujfalusi 	{ },
326f2055e14SPeter Ujfalusi };
327f2055e14SPeter Ujfalusi MODULE_DEVICE_TABLE(of, omap_twl4030_of_match);
328f2055e14SPeter Ujfalusi 
329f2055e14SPeter Ujfalusi static struct platform_driver omap_twl4030_driver = {
330f2055e14SPeter Ujfalusi 	.driver = {
331f2055e14SPeter Ujfalusi 		.name = "omap-twl4030",
332f2055e14SPeter Ujfalusi 		.pm = &snd_soc_pm_ops,
333f2055e14SPeter Ujfalusi 		.of_match_table = omap_twl4030_of_match,
334f2055e14SPeter Ujfalusi 	},
335f2055e14SPeter Ujfalusi 	.probe = omap_twl4030_probe,
336f2055e14SPeter Ujfalusi };
337f2055e14SPeter Ujfalusi 
338f2055e14SPeter Ujfalusi module_platform_driver(omap_twl4030_driver);
339f2055e14SPeter Ujfalusi 
340f2055e14SPeter Ujfalusi MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
341f2055e14SPeter Ujfalusi MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
342f2055e14SPeter Ujfalusi MODULE_LICENSE("GPL");
343f2055e14SPeter Ujfalusi MODULE_ALIAS("platform:omap-twl4030");
344