Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tegra_alc5632.c
Go to the documentation of this file.
1 /*
2 * tegra_alc5632.c -- Toshiba AC100(PAZ00) machine ASoC driver
3  *
4  * Copyright (C) 2011 The AC100 Kernel Team <[email protected]>
5  * Copyright (C) 2012 - NVIDIA, Inc.
6  *
7  * Authors: Leon Romanovsky <[email protected]>
8  * Andrey Danin <[email protected]>
9  * Marc Dietrich <[email protected]>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  */
15 
16 #include <asm/mach-types.h>
17 
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/slab.h>
21 #include <linux/gpio.h>
22 #include <linux/of_gpio.h>
23 
24 #include <sound/core.h>
25 #include <sound/jack.h>
26 #include <sound/pcm.h>
27 #include <sound/pcm_params.h>
28 #include <sound/soc.h>
29 
30 #include "../codecs/alc5632.h"
31 
32 #include "tegra_asoc_utils.h"
33 
34 #define DRV_NAME "tegra-alc5632"
35 
36 struct tegra_alc5632 {
39 };
40 
41 static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream,
42  struct snd_pcm_hw_params *params)
43 {
44  struct snd_soc_pcm_runtime *rtd = substream->private_data;
45  struct snd_soc_dai *codec_dai = rtd->codec_dai;
46  struct snd_soc_codec *codec = codec_dai->codec;
47  struct snd_soc_card *card = codec->card;
48  struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card);
49  int srate, mclk;
50  int err;
51 
52  srate = params_rate(params);
53  mclk = 512 * srate;
54 
55  err = tegra_asoc_utils_set_rate(&alc5632->util_data, srate, mclk);
56  if (err < 0) {
57  dev_err(card->dev, "Can't configure clocks\n");
58  return err;
59  }
60 
61  err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
63  if (err < 0) {
64  dev_err(card->dev, "codec_dai clock not set\n");
65  return err;
66  }
67 
68  return 0;
69 }
70 
71 static struct snd_soc_ops tegra_alc5632_asoc_ops = {
72  .hw_params = tegra_alc5632_asoc_hw_params,
73 };
74 
75 static struct snd_soc_jack tegra_alc5632_hs_jack;
76 
77 static struct snd_soc_jack_pin tegra_alc5632_hs_jack_pins[] = {
78  {
79  .pin = "Headset Mic",
80  .mask = SND_JACK_MICROPHONE,
81  },
82  {
83  .pin = "Headset Stereophone",
84  .mask = SND_JACK_HEADPHONE,
85  },
86 };
87 
88 static struct snd_soc_jack_gpio tegra_alc5632_hp_jack_gpio = {
89  .name = "Headset detection",
90  .report = SND_JACK_HEADSET,
91  .debounce_time = 150,
92 };
93 
94 static const struct snd_soc_dapm_widget tegra_alc5632_dapm_widgets[] = {
95  SND_SOC_DAPM_SPK("Int Spk", NULL),
96  SND_SOC_DAPM_HP("Headset Stereophone", NULL),
97  SND_SOC_DAPM_MIC("Headset Mic", NULL),
98  SND_SOC_DAPM_MIC("Digital Mic", NULL),
99 };
100 
101 static const struct snd_kcontrol_new tegra_alc5632_controls[] = {
102  SOC_DAPM_PIN_SWITCH("Int Spk"),
103 };
104 
105 static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd)
106 {
107  struct snd_soc_dai *codec_dai = rtd->codec_dai;
108  struct snd_soc_codec *codec = codec_dai->codec;
109  struct snd_soc_dapm_context *dapm = &codec->dapm;
110  struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(codec->card);
111 
112  snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
113  &tegra_alc5632_hs_jack);
114  snd_soc_jack_add_pins(&tegra_alc5632_hs_jack,
115  ARRAY_SIZE(tegra_alc5632_hs_jack_pins),
116  tegra_alc5632_hs_jack_pins);
117 
118  if (gpio_is_valid(machine->gpio_hp_det)) {
119  tegra_alc5632_hp_jack_gpio.gpio = machine->gpio_hp_det;
120  snd_soc_jack_add_gpios(&tegra_alc5632_hs_jack,
121  1,
122  &tegra_alc5632_hp_jack_gpio);
123  }
124 
125  snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
126 
127  return 0;
128 }
129 
130 static struct snd_soc_dai_link tegra_alc5632_dai = {
131  .name = "ALC5632",
132  .stream_name = "ALC5632 PCM",
133  .codec_dai_name = "alc5632-hifi",
134  .init = tegra_alc5632_asoc_init,
135  .ops = &tegra_alc5632_asoc_ops,
136  .dai_fmt = SND_SOC_DAIFMT_I2S
139 };
140 
141 static struct snd_soc_card snd_soc_tegra_alc5632 = {
142  .name = "tegra-alc5632",
143  .owner = THIS_MODULE,
144  .dai_link = &tegra_alc5632_dai,
145  .num_links = 1,
146  .controls = tegra_alc5632_controls,
147  .num_controls = ARRAY_SIZE(tegra_alc5632_controls),
148  .dapm_widgets = tegra_alc5632_dapm_widgets,
149  .num_dapm_widgets = ARRAY_SIZE(tegra_alc5632_dapm_widgets),
150  .fully_routed = true,
151 };
152 
153 static __devinit int tegra_alc5632_probe(struct platform_device *pdev)
154 {
155  struct device_node *np = pdev->dev.of_node;
156  struct snd_soc_card *card = &snd_soc_tegra_alc5632;
157  struct tegra_alc5632 *alc5632;
158  int ret;
159 
160  alc5632 = devm_kzalloc(&pdev->dev,
161  sizeof(struct tegra_alc5632), GFP_KERNEL);
162  if (!alc5632) {
163  dev_err(&pdev->dev, "Can't allocate tegra_alc5632\n");
164  ret = -ENOMEM;
165  goto err;
166  }
167 
168  card->dev = &pdev->dev;
169  platform_set_drvdata(pdev, card);
170  snd_soc_card_set_drvdata(card, alc5632);
171 
172  if (!(pdev->dev.of_node)) {
173  dev_err(&pdev->dev, "Must be instantiated using device tree\n");
174  ret = -EINVAL;
175  goto err;
176  }
177 
178  alc5632->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
179  if (alc5632->gpio_hp_det == -EPROBE_DEFER)
180  return -EPROBE_DEFER;
181 
182  ret = snd_soc_of_parse_card_name(card, "nvidia,model");
183  if (ret)
184  goto err;
185 
186  ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
187  if (ret)
188  goto err;
189 
190  tegra_alc5632_dai.codec_of_node = of_parse_phandle(
191  pdev->dev.of_node, "nvidia,audio-codec", 0);
192 
193  if (!tegra_alc5632_dai.codec_of_node) {
194  dev_err(&pdev->dev,
195  "Property 'nvidia,audio-codec' missing or invalid\n");
196  ret = -EINVAL;
197  goto err;
198  }
199 
200  tegra_alc5632_dai.cpu_of_node = of_parse_phandle(
201  pdev->dev.of_node, "nvidia,i2s-controller", 0);
202  if (!tegra_alc5632_dai.cpu_of_node) {
203  dev_err(&pdev->dev,
204  "Property 'nvidia,i2s-controller' missing or invalid\n");
205  ret = -EINVAL;
206  goto err;
207  }
208 
209  tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_of_node;
210 
211  ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev);
212  if (ret)
213  goto err;
214 
215  ret = snd_soc_register_card(card);
216  if (ret) {
217  dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
218  ret);
219  goto err_fini_utils;
220  }
221 
222  return 0;
223 
224 err_fini_utils:
225  tegra_asoc_utils_fini(&alc5632->util_data);
226 err:
227  return ret;
228 }
229 
230 static int __devexit tegra_alc5632_remove(struct platform_device *pdev)
231 {
232  struct snd_soc_card *card = platform_get_drvdata(pdev);
233  struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(card);
234 
235  snd_soc_jack_free_gpios(&tegra_alc5632_hs_jack, 1,
236  &tegra_alc5632_hp_jack_gpio);
237 
239 
240  tegra_asoc_utils_fini(&machine->util_data);
241 
242  return 0;
243 }
244 
245 static const struct of_device_id tegra_alc5632_of_match[] __devinitconst = {
246  { .compatible = "nvidia,tegra-audio-alc5632", },
247  {},
248 };
249 
250 static struct platform_driver tegra_alc5632_driver = {
251  .driver = {
252  .name = DRV_NAME,
253  .owner = THIS_MODULE,
254  .pm = &snd_soc_pm_ops,
255  .of_match_table = tegra_alc5632_of_match,
256  },
257  .probe = tegra_alc5632_probe,
258  .remove = __devexit_p(tegra_alc5632_remove),
259 };
260 module_platform_driver(tegra_alc5632_driver);
261 
262 MODULE_AUTHOR("Leon Romanovsky <[email protected]>");
263 MODULE_DESCRIPTION("Tegra+ALC5632 machine ASoC driver");
264 MODULE_LICENSE("GPL");
265 MODULE_ALIAS("platform:" DRV_NAME);
266 MODULE_DEVICE_TABLE(of, tegra_alc5632_of_match);