Linux Audio

Check our new training course

Loading...
v4.17
  1/*
  2 * omap-twl4030.c  --  SoC audio for TI SoC based boards with twl4030 codec
  3 *
  4 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
  5 * All rights reserved.
  6 *
  7 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
  8 *
  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>)
 16 *
 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.
 20 *
 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.
 25 *
 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
 29 * 02110-1301 USA
 30 *
 31 */
 32
 33#include <linux/platform_device.h>
 34#include <linux/platform_data/omap-twl4030.h>
 35#include <linux/module.h>
 36#include <linux/of.h>
 37#include <linux/gpio.h>
 38#include <linux/of_gpio.h>
 39
 40#include <sound/core.h>
 41#include <sound/pcm.h>
 42#include <sound/soc.h>
 43#include <sound/jack.h>
 44
 45#include "omap-mcbsp.h"
 46
 47struct omap_twl4030 {
 48	int jack_detect;	/* board can detect jack events */
 49	struct snd_soc_jack hs_jack;
 50};
 51
 52static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
 53	struct snd_pcm_hw_params *params)
 54{
 55	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 56	unsigned int fmt;
 57
 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;
 63		break;
 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;
 68		break;
 69	default:
 70		return -EINVAL;
 71	}
 72
 73	return snd_soc_runtime_set_dai_fmt(rtd, fmt);
 74}
 75
 76static const struct snd_soc_ops omap_twl4030_ops = {
 77	.hw_params = omap_twl4030_hw_params,
 78};
 79
 80static 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),
 86
 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),
 94};
 95
 96static 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"},
109	/* Earpiece */
110	{"Earpiece Spk", NULL, "EARPIECE"},
111
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"},
130};
131
132/* Headset jack detection DAPM pins */
133static struct snd_soc_jack_pin hs_jack_pins[] = {
134	{
135		.pin = "Headset Mic",
136		.mask = SND_JACK_MICROPHONE,
137	},
138	{
139		.pin = "Headset Stereophone",
140		.mask = SND_JACK_HEADPHONE,
141	},
142};
143
144/* Headset jack detection gpios */
145static struct snd_soc_jack_gpio hs_jack_gpios[] = {
146	{
147		.name = "hsdet-gpio",
148		.report = SND_JACK_HEADSET,
149		.debounce_time = 200,
150	},
151};
152
153static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
154					  int connected, char *pin)
155{
156	if (!connected)
157		snd_soc_dapm_disable_pin(dapm, pin);
158}
159
160static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
161{
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);
166	int ret = 0;
167
168	/* Headset jack detection only if it is supported */
169	if (priv->jack_detect > 0) {
170		hs_jack_gpios[0].gpio = priv->jack_detect;
171
172		ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
173					    SND_JACK_HEADSET, &priv->hs_jack,
174					    hs_jack_pins,
175					    ARRAY_SIZE(hs_jack_pins));
176		if (ret)
177			return ret;
178
179		ret = snd_soc_jack_add_gpios(&priv->hs_jack,
180					     ARRAY_SIZE(hs_jack_gpios),
181					     hs_jack_gpios);
182		if (ret)
183			return ret;
184	}
185
186	/*
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.
189	 */
190	if (!pdata || !pdata->custom_routing)
191		return ret;
192
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");
199
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");
207
208	return ret;
209}
210
 
 
 
 
 
 
 
 
 
 
 
 
211/* Digital audio interface glue - connects codec <--> CPU */
212static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
213	{
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,
222	},
223	{
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,
232	},
233};
234
235/* Audio machine driver */
236static 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),
240
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),
245};
246
247static int omap_twl4030_probe(struct platform_device *pdev)
248{
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;
253	int ret = 0;
254
255	card->dev = &pdev->dev;
256
257	priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL);
258	if (priv == NULL)
259		return -ENOMEM;
260
261	if (node) {
262		struct device_node *dai_node;
263		struct property *prop;
264
265		if (snd_soc_of_parse_card_name(card, "ti,model")) {
266			dev_err(&pdev->dev, "Card name is not provided\n");
267			return -ENODEV;
268		}
269
270		dai_node = of_parse_phandle(node, "ti,mcbsp", 0);
271		if (!dai_node) {
272			dev_err(&pdev->dev, "McBSP node is not provided\n");
273			return -EINVAL;
274		}
275		omap_twl4030_dai_links[0].cpu_dai_name  = NULL;
276		omap_twl4030_dai_links[0].cpu_of_node = dai_node;
277
278		omap_twl4030_dai_links[0].platform_name  = NULL;
279		omap_twl4030_dai_links[0].platform_of_node = dai_node;
280
281		dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0);
282		if (!dai_node) {
283			card->num_links = 1;
284		} else {
285			omap_twl4030_dai_links[1].cpu_dai_name  = NULL;
286			omap_twl4030_dai_links[1].cpu_of_node = dai_node;
287
288			omap_twl4030_dai_links[1].platform_name  = NULL;
289			omap_twl4030_dai_links[1].platform_of_node = dai_node;
290		}
291
292		priv->jack_detect = of_get_named_gpio(node,
293						      "ti,jack-det-gpio", 0);
294
295		/* Optional: audio routing can be provided */
296		prop = of_find_property(node, "ti,audio-routing", NULL);
297		if (prop) {
298			ret = snd_soc_of_parse_audio_routing(card,
299							    "ti,audio-routing");
300			if (ret)
301				return ret;
302
303			card->fully_routed = 1;
304		}
305	} else if (pdata) {
306		if (pdata->card_name) {
307			card->name = pdata->card_name;
308		} else {
309			dev_err(&pdev->dev, "Card name is not provided\n");
310			return -ENODEV;
311		}
312
313		if (!pdata->voice_connected)
314			card->num_links = 1;
315
316		priv->jack_detect = pdata->jack_detect;
317	} else {
318		dev_err(&pdev->dev, "Missing pdata\n");
319		return -ENODEV;
320	}
321
322	snd_soc_card_set_drvdata(card, priv);
323	ret = devm_snd_soc_register_card(&pdev->dev, card);
324	if (ret) {
325		dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
326			ret);
327		return ret;
328	}
329
330	return 0;
331}
332
333static const struct of_device_id omap_twl4030_of_match[] = {
334	{.compatible = "ti,omap-twl4030", },
335	{ },
336};
337MODULE_DEVICE_TABLE(of, omap_twl4030_of_match);
338
339static struct platform_driver omap_twl4030_driver = {
340	.driver = {
341		.name = "omap-twl4030",
342		.pm = &snd_soc_pm_ops,
343		.of_match_table = omap_twl4030_of_match,
344	},
345	.probe = omap_twl4030_probe,
346};
347
348module_platform_driver(omap_twl4030_driver);
349
350MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
351MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
352MODULE_LICENSE("GPL");
353MODULE_ALIAS("platform:omap-twl4030");
v4.6
  1/*
  2 * omap-twl4030.c  --  SoC audio for TI SoC based boards with twl4030 codec
  3 *
  4 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
  5 * All rights reserved.
  6 *
  7 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
  8 *
  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>)
 16 *
 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.
 20 *
 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.
 25 *
 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
 29 * 02110-1301 USA
 30 *
 31 */
 32
 33#include <linux/platform_device.h>
 34#include <linux/platform_data/omap-twl4030.h>
 35#include <linux/module.h>
 36#include <linux/of.h>
 37#include <linux/gpio.h>
 38#include <linux/of_gpio.h>
 39
 40#include <sound/core.h>
 41#include <sound/pcm.h>
 42#include <sound/soc.h>
 43#include <sound/jack.h>
 44
 45#include "omap-mcbsp.h"
 46
 47struct omap_twl4030 {
 48	int jack_detect;	/* board can detect jack events */
 49	struct snd_soc_jack hs_jack;
 50};
 51
 52static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
 53	struct snd_pcm_hw_params *params)
 54{
 55	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 56	unsigned int fmt;
 57
 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;
 63		break;
 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;
 68		break;
 69	default:
 70		return -EINVAL;
 71	}
 72
 73	return snd_soc_runtime_set_dai_fmt(rtd, fmt);
 74}
 75
 76static struct snd_soc_ops omap_twl4030_ops = {
 77	.hw_params = omap_twl4030_hw_params,
 78};
 79
 80static 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),
 86
 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),
 94};
 95
 96static 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"},
109	/* Earpiece */
110	{"Earpiece Spk", NULL, "EARPIECE"},
111
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"},
130};
131
132/* Headset jack detection DAPM pins */
133static struct snd_soc_jack_pin hs_jack_pins[] = {
134	{
135		.pin = "Headset Mic",
136		.mask = SND_JACK_MICROPHONE,
137	},
138	{
139		.pin = "Headset Stereophone",
140		.mask = SND_JACK_HEADPHONE,
141	},
142};
143
144/* Headset jack detection gpios */
145static struct snd_soc_jack_gpio hs_jack_gpios[] = {
146	{
147		.name = "hsdet-gpio",
148		.report = SND_JACK_HEADSET,
149		.debounce_time = 200,
150	},
151};
152
153static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
154					  int connected, char *pin)
155{
156	if (!connected)
157		snd_soc_dapm_disable_pin(dapm, pin);
158}
159
160static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
161{
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);
166	int ret = 0;
167
168	/* Headset jack detection only if it is supported */
169	if (priv->jack_detect > 0) {
170		hs_jack_gpios[0].gpio = priv->jack_detect;
171
172		ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
173					    SND_JACK_HEADSET, &priv->hs_jack,
174					    hs_jack_pins,
175					    ARRAY_SIZE(hs_jack_pins));
176		if (ret)
177			return ret;
178
179		ret = snd_soc_jack_add_gpios(&priv->hs_jack,
180					     ARRAY_SIZE(hs_jack_gpios),
181					     hs_jack_gpios);
182		if (ret)
183			return ret;
184	}
185
186	/*
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.
189	 */
190	if (!pdata || !pdata->custom_routing)
191		return ret;
192
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");
199
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");
207
208	return ret;
209}
210
211static int omap_twl4030_card_remove(struct snd_soc_card *card)
212{
213	struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
214
215	if (priv->jack_detect > 0)
216		snd_soc_jack_free_gpios(&priv->hs_jack,
217					ARRAY_SIZE(hs_jack_gpios),
218					hs_jack_gpios);
219
220	return 0;
221}
222
223/* Digital audio interface glue - connects codec <--> CPU */
224static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
225	{
226		.name = "TWL4030 HiFi",
227		.stream_name = "TWL4030 HiFi",
228		.cpu_dai_name = "omap-mcbsp.2",
229		.codec_dai_name = "twl4030-hifi",
230		.platform_name = "omap-mcbsp.2",
231		.codec_name = "twl4030-codec",
232		.init = omap_twl4030_init,
233		.ops = &omap_twl4030_ops,
234	},
235	{
236		.name = "TWL4030 Voice",
237		.stream_name = "TWL4030 Voice",
238		.cpu_dai_name = "omap-mcbsp.3",
239		.codec_dai_name = "twl4030-voice",
240		.platform_name = "omap-mcbsp.3",
241		.codec_name = "twl4030-codec",
242		.dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
243			   SND_SOC_DAIFMT_CBM_CFM,
244	},
245};
246
247/* Audio machine driver */
248static struct snd_soc_card omap_twl4030_card = {
249	.owner = THIS_MODULE,
250	.remove = omap_twl4030_card_remove,
251	.dai_link = omap_twl4030_dai_links,
252	.num_links = ARRAY_SIZE(omap_twl4030_dai_links),
253
254	.dapm_widgets = dapm_widgets,
255	.num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
256	.dapm_routes = audio_map,
257	.num_dapm_routes = ARRAY_SIZE(audio_map),
258};
259
260static int omap_twl4030_probe(struct platform_device *pdev)
261{
262	struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev);
263	struct device_node *node = pdev->dev.of_node;
264	struct snd_soc_card *card = &omap_twl4030_card;
265	struct omap_twl4030 *priv;
266	int ret = 0;
267
268	card->dev = &pdev->dev;
269
270	priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL);
271	if (priv == NULL)
272		return -ENOMEM;
273
274	if (node) {
275		struct device_node *dai_node;
276		struct property *prop;
277
278		if (snd_soc_of_parse_card_name(card, "ti,model")) {
279			dev_err(&pdev->dev, "Card name is not provided\n");
280			return -ENODEV;
281		}
282
283		dai_node = of_parse_phandle(node, "ti,mcbsp", 0);
284		if (!dai_node) {
285			dev_err(&pdev->dev, "McBSP node is not provided\n");
286			return -EINVAL;
287		}
288		omap_twl4030_dai_links[0].cpu_dai_name  = NULL;
289		omap_twl4030_dai_links[0].cpu_of_node = dai_node;
290
291		omap_twl4030_dai_links[0].platform_name  = NULL;
292		omap_twl4030_dai_links[0].platform_of_node = dai_node;
293
294		dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0);
295		if (!dai_node) {
296			card->num_links = 1;
297		} else {
298			omap_twl4030_dai_links[1].cpu_dai_name  = NULL;
299			omap_twl4030_dai_links[1].cpu_of_node = dai_node;
300
301			omap_twl4030_dai_links[1].platform_name  = NULL;
302			omap_twl4030_dai_links[1].platform_of_node = dai_node;
303		}
304
305		priv->jack_detect = of_get_named_gpio(node,
306						      "ti,jack-det-gpio", 0);
307
308		/* Optional: audio routing can be provided */
309		prop = of_find_property(node, "ti,audio-routing", NULL);
310		if (prop) {
311			ret = snd_soc_of_parse_audio_routing(card,
312							    "ti,audio-routing");
313			if (ret)
314				return ret;
315
316			card->fully_routed = 1;
317		}
318	} else if (pdata) {
319		if (pdata->card_name) {
320			card->name = pdata->card_name;
321		} else {
322			dev_err(&pdev->dev, "Card name is not provided\n");
323			return -ENODEV;
324		}
325
326		if (!pdata->voice_connected)
327			card->num_links = 1;
328
329		priv->jack_detect = pdata->jack_detect;
330	} else {
331		dev_err(&pdev->dev, "Missing pdata\n");
332		return -ENODEV;
333	}
334
335	snd_soc_card_set_drvdata(card, priv);
336	ret = devm_snd_soc_register_card(&pdev->dev, card);
337	if (ret) {
338		dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
339			ret);
340		return ret;
341	}
342
343	return 0;
344}
345
346static const struct of_device_id omap_twl4030_of_match[] = {
347	{.compatible = "ti,omap-twl4030", },
348	{ },
349};
350MODULE_DEVICE_TABLE(of, omap_twl4030_of_match);
351
352static struct platform_driver omap_twl4030_driver = {
353	.driver = {
354		.name = "omap-twl4030",
355		.pm = &snd_soc_pm_ops,
356		.of_match_table = omap_twl4030_of_match,
357	},
358	.probe = omap_twl4030_probe,
359};
360
361module_platform_driver(omap_twl4030_driver);
362
363MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
364MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
365MODULE_LICENSE("GPL");
366MODULE_ALIAS("platform:omap-twl4030");