Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
omap-abe-twl6040.c
Go to the documentation of this file.
1 /*
2  * omap-abe-twl6040.c -- SoC audio for TI OMAP based boards with ABE and
3  * twl6040 codec
4  *
5  * Author: Misael Lopez Cruz <[email protected]>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA
20  *
21  */
22 
23 #include <linux/clk.h>
24 #include <linux/platform_device.h>
25 #include <linux/mfd/twl6040.h>
27 #include <linux/module.h>
28 #include <linux/of.h>
29 
30 #include <sound/core.h>
31 #include <sound/pcm.h>
32 #include <sound/soc.h>
33 #include <sound/jack.h>
34 
35 #include "omap-dmic.h"
36 #include "omap-mcpdm.h"
37 #include "omap-pcm.h"
38 #include "../codecs/twl6040.h"
39 
40 struct abe_twl6040 {
41  int jack_detection; /* board can detect jack events */
42  int mclk_freq; /* MCLK frequency speed for twl6040 */
43 
45 };
46 
47 static int omap_abe_hw_params(struct snd_pcm_substream *substream,
48  struct snd_pcm_hw_params *params)
49 {
50  struct snd_soc_pcm_runtime *rtd = substream->private_data;
51  struct snd_soc_dai *codec_dai = rtd->codec_dai;
52  struct snd_soc_codec *codec = rtd->codec;
53  struct snd_soc_card *card = codec->card;
54  struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
55  int clk_id, freq;
56  int ret;
57 
58  clk_id = twl6040_get_clk_id(rtd->codec);
59  if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
60  freq = priv->mclk_freq;
61  else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
62  freq = 32768;
63  else
64  return -EINVAL;
65 
66  /* set the codec mclk */
67  ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq,
69  if (ret) {
70  printk(KERN_ERR "can't set codec system clock\n");
71  return ret;
72  }
73  return ret;
74 }
75 
76 static struct snd_soc_ops omap_abe_ops = {
77  .hw_params = omap_abe_hw_params,
78 };
79 
80 static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream,
81  struct snd_pcm_hw_params *params)
82 {
83  struct snd_soc_pcm_runtime *rtd = substream->private_data;
84  struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
85  int ret = 0;
86 
88  19200000, SND_SOC_CLOCK_IN);
89  if (ret < 0) {
90  printk(KERN_ERR "can't set DMIC cpu system clock\n");
91  return ret;
92  }
93  ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_ABE_DMIC_CLK, 2400000,
95  if (ret < 0) {
96  printk(KERN_ERR "can't set DMIC output clock\n");
97  return ret;
98  }
99  return 0;
100 }
101 
102 static struct snd_soc_ops omap_abe_dmic_ops = {
103  .hw_params = omap_abe_dmic_hw_params,
104 };
105 
106 /* Headset jack */
107 static struct snd_soc_jack hs_jack;
108 
109 /*Headset jack detection DAPM pins */
110 static struct snd_soc_jack_pin hs_jack_pins[] = {
111  {
112  .pin = "Headset Mic",
113  .mask = SND_JACK_MICROPHONE,
114  },
115  {
116  .pin = "Headset Stereophone",
117  .mask = SND_JACK_HEADPHONE,
118  },
119 };
120 
121 /* SDP4430 machine DAPM */
122 static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
123  /* Outputs */
124  SND_SOC_DAPM_HP("Headset Stereophone", NULL),
125  SND_SOC_DAPM_SPK("Earphone Spk", NULL),
126  SND_SOC_DAPM_SPK("Ext Spk", NULL),
127  SND_SOC_DAPM_LINE("Line Out", NULL),
128  SND_SOC_DAPM_SPK("Vibrator", NULL),
129 
130  /* Inputs */
131  SND_SOC_DAPM_MIC("Headset Mic", NULL),
132  SND_SOC_DAPM_MIC("Main Handset Mic", NULL),
133  SND_SOC_DAPM_MIC("Sub Handset Mic", NULL),
134  SND_SOC_DAPM_LINE("Line In", NULL),
135 
136  /* Digital microphones */
137  SND_SOC_DAPM_MIC("Digital Mic", NULL),
138 };
139 
140 static const struct snd_soc_dapm_route audio_map[] = {
141  /* Routings for outputs */
142  {"Headset Stereophone", NULL, "HSOL"},
143  {"Headset Stereophone", NULL, "HSOR"},
144 
145  {"Earphone Spk", NULL, "EP"},
146 
147  {"Ext Spk", NULL, "HFL"},
148  {"Ext Spk", NULL, "HFR"},
149 
150  {"Line Out", NULL, "AUXL"},
151  {"Line Out", NULL, "AUXR"},
152 
153  {"Vibrator", NULL, "VIBRAL"},
154  {"Vibrator", NULL, "VIBRAR"},
155 
156  /* Routings for inputs */
157  {"HSMIC", NULL, "Headset Mic"},
158  {"Headset Mic", NULL, "Headset Mic Bias"},
159 
160  {"MAINMIC", NULL, "Main Handset Mic"},
161  {"Main Handset Mic", NULL, "Main Mic Bias"},
162 
163  {"SUBMIC", NULL, "Sub Handset Mic"},
164  {"Sub Handset Mic", NULL, "Main Mic Bias"},
165 
166  {"AFML", NULL, "Line In"},
167  {"AFMR", NULL, "Line In"},
168 };
169 
170 static inline void twl6040_disconnect_pin(struct snd_soc_dapm_context *dapm,
171  int connected, char *pin)
172 {
173  if (!connected)
174  snd_soc_dapm_disable_pin(dapm, pin);
175 }
176 
177 static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
178 {
179  struct snd_soc_codec *codec = rtd->codec;
180  struct snd_soc_card *card = codec->card;
181  struct snd_soc_dapm_context *dapm = &codec->dapm;
182  struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev);
183  struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
184  int hs_trim;
185  int ret = 0;
186 
187  /*
188  * Configure McPDM offset cancellation based on the HSOTRIM value from
189  * twl6040.
190  */
193  TWL6040_HSF_TRIM_RIGHT(hs_trim));
194 
195  /* Headset jack detection only if it is supported */
196  if (priv->jack_detection) {
197  ret = snd_soc_jack_new(codec, "Headset Jack",
198  SND_JACK_HEADSET, &hs_jack);
199  if (ret)
200  return ret;
201 
202  ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
203  hs_jack_pins);
204  twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET);
205  }
206 
207  /*
208  * NULL pdata means we booted with DT. In this case the routing is
209  * provided and the card is fully routed, no need to mark pins.
210  */
211  if (!pdata)
212  return ret;
213 
214  /* Disable not connected paths if not used */
215  twl6040_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
216  twl6040_disconnect_pin(dapm, pdata->has_hf, "Ext Spk");
217  twl6040_disconnect_pin(dapm, pdata->has_ep, "Earphone Spk");
218  twl6040_disconnect_pin(dapm, pdata->has_aux, "Line Out");
219  twl6040_disconnect_pin(dapm, pdata->has_vibra, "Vibrator");
220  twl6040_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
221  twl6040_disconnect_pin(dapm, pdata->has_mainmic, "Main Handset Mic");
222  twl6040_disconnect_pin(dapm, pdata->has_submic, "Sub Handset Mic");
223  twl6040_disconnect_pin(dapm, pdata->has_afm, "Line In");
224 
225  return ret;
226 }
227 
228 static const struct snd_soc_dapm_route dmic_audio_map[] = {
229  {"DMic", NULL, "Digital Mic"},
230  {"Digital Mic", NULL, "Digital Mic1 Bias"},
231 };
232 
233 static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd)
234 {
235  struct snd_soc_codec *codec = rtd->codec;
236  struct snd_soc_dapm_context *dapm = &codec->dapm;
237 
238  return snd_soc_dapm_add_routes(dapm, dmic_audio_map,
239  ARRAY_SIZE(dmic_audio_map));
240 }
241 
242 /* Digital audio interface glue - connects codec <--> CPU */
243 static struct snd_soc_dai_link abe_twl6040_dai_links[] = {
244  {
245  .name = "TWL6040",
246  .stream_name = "TWL6040",
247  .cpu_dai_name = "omap-mcpdm",
248  .codec_dai_name = "twl6040-legacy",
249  .platform_name = "omap-pcm-audio",
250  .codec_name = "twl6040-codec",
251  .init = omap_abe_twl6040_init,
252  .ops = &omap_abe_ops,
253  },
254  {
255  .name = "DMIC",
256  .stream_name = "DMIC Capture",
257  .cpu_dai_name = "omap-dmic",
258  .codec_dai_name = "dmic-hifi",
259  .platform_name = "omap-pcm-audio",
260  .codec_name = "dmic-codec",
261  .init = omap_abe_dmic_init,
262  .ops = &omap_abe_dmic_ops,
263  },
264 };
265 
266 /* Audio machine driver */
267 static struct snd_soc_card omap_abe_card = {
268  .owner = THIS_MODULE,
269 
270  .dapm_widgets = twl6040_dapm_widgets,
271  .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets),
272  .dapm_routes = audio_map,
273  .num_dapm_routes = ARRAY_SIZE(audio_map),
274 };
275 
276 static __devinit int omap_abe_probe(struct platform_device *pdev)
277 {
278  struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev);
279  struct device_node *node = pdev->dev.of_node;
280  struct snd_soc_card *card = &omap_abe_card;
281  struct abe_twl6040 *priv;
282  int num_links = 0;
283  int ret = 0;
284 
285  card->dev = &pdev->dev;
286 
287  priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
288  if (priv == NULL)
289  return -ENOMEM;
290 
291  priv->dmic_codec_dev = ERR_PTR(-EINVAL);
292 
293  if (node) {
294  struct device_node *dai_node;
295 
296  if (snd_soc_of_parse_card_name(card, "ti,model")) {
297  dev_err(&pdev->dev, "Card name is not provided\n");
298  return -ENODEV;
299  }
300 
302  "ti,audio-routing");
303  if (ret) {
304  dev_err(&pdev->dev,
305  "Error while parsing DAPM routing\n");
306  return ret;
307  }
308 
309  dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
310  if (!dai_node) {
311  dev_err(&pdev->dev, "McPDM node is not provided\n");
312  return -EINVAL;
313  }
314  abe_twl6040_dai_links[0].cpu_dai_name = NULL;
315  abe_twl6040_dai_links[0].cpu_of_node = dai_node;
316 
317  dai_node = of_parse_phandle(node, "ti,dmic", 0);
318  if (dai_node) {
319  num_links = 2;
320  abe_twl6040_dai_links[1].cpu_dai_name = NULL;
321  abe_twl6040_dai_links[1].cpu_of_node = dai_node;
322 
323  priv->dmic_codec_dev = platform_device_register_simple(
324  "dmic-codec", -1, NULL, 0);
325  if (IS_ERR(priv->dmic_codec_dev)) {
326  dev_err(&pdev->dev,
327  "Can't instantiate dmic-codec\n");
328  return PTR_ERR(priv->dmic_codec_dev);
329  }
330  } else {
331  num_links = 1;
332  }
333 
334  of_property_read_u32(node, "ti,jack-detection",
335  &priv->jack_detection);
336  of_property_read_u32(node, "ti,mclk-freq",
337  &priv->mclk_freq);
338  if (!priv->mclk_freq) {
339  dev_err(&pdev->dev, "MCLK frequency not provided\n");
340  ret = -EINVAL;
341  goto err_unregister;
342  }
343 
344  omap_abe_card.fully_routed = 1;
345  } else if (pdata) {
346  if (pdata->card_name) {
347  card->name = pdata->card_name;
348  } else {
349  dev_err(&pdev->dev, "Card name is not provided\n");
350  return -ENODEV;
351  }
352 
353  if (pdata->has_dmic)
354  num_links = 2;
355  else
356  num_links = 1;
357 
358  priv->jack_detection = pdata->jack_detection;
359  priv->mclk_freq = pdata->mclk_freq;
360  } else {
361  dev_err(&pdev->dev, "Missing pdata\n");
362  return -ENODEV;
363  }
364 
365 
366  if (!priv->mclk_freq) {
367  dev_err(&pdev->dev, "MCLK frequency missing\n");
368  ret = -ENODEV;
369  goto err_unregister;
370  }
371 
372  card->dai_link = abe_twl6040_dai_links;
373  card->num_links = num_links;
374 
375  snd_soc_card_set_drvdata(card, priv);
376 
377  ret = snd_soc_register_card(card);
378  if (ret) {
379  dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
380  ret);
381  goto err_unregister;
382  }
383 
384  return 0;
385 
386 err_unregister:
387  if (!IS_ERR(priv->dmic_codec_dev))
389 
390  return ret;
391 }
392 
393 static int __devexit omap_abe_remove(struct platform_device *pdev)
394 {
395  struct snd_soc_card *card = platform_get_drvdata(pdev);
396  struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
397 
399 
400  if (!IS_ERR(priv->dmic_codec_dev))
402 
403  return 0;
404 }
405 
406 static const struct of_device_id omap_abe_of_match[] = {
407  {.compatible = "ti,abe-twl6040", },
408  { },
409 };
410 MODULE_DEVICE_TABLE(of, omap_abe_of_match);
411 
412 static struct platform_driver omap_abe_driver = {
413  .driver = {
414  .name = "omap-abe-twl6040",
415  .owner = THIS_MODULE,
416  .pm = &snd_soc_pm_ops,
417  .of_match_table = omap_abe_of_match,
418  },
419  .probe = omap_abe_probe,
420  .remove = __devexit_p(omap_abe_remove),
421 };
422 
423 module_platform_driver(omap_abe_driver);
424 
425 MODULE_AUTHOR("Misael Lopez Cruz <[email protected]>");
426 MODULE_DESCRIPTION("ALSA SoC for OMAP boards with ABE and twl6040 codec");
427 MODULE_LICENSE("GPL");
428 MODULE_ALIAS("platform:omap-abe-twl6040");