]> git.karo-electronics.de Git - karo-tx-linux.git/blob - sound/soc/imx/imx-sgtl5000.c
ENGR00160862 unique id for each soc-audio device
[karo-tx-linux.git] / sound / soc / imx / imx-sgtl5000.c
1 /*
2  * sound/soc/imx/3ds-sgtl5000.c --  SoC audio for i.MX 3ds boards with
3  *                                  sgtl5000 codec
4  *
5  * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
6  * Copyright (C) 2011 Freescale Semiconductor, Inc.
7  *
8  * This program is free software; you can redistribute  it and/or modify it
9  * under  the terms of  the GNU General  Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  */
13
14 #include <linux/module.h>
15 #include <linux/moduleparam.h>
16 #include <linux/device.h>
17 #include <linux/i2c.h>
18 #include <linux/fsl_devices.h>
19 #include <linux/gpio.h>
20 #include <sound/core.h>
21 #include <sound/pcm.h>
22 #include <sound/soc.h>
23 #include <sound/jack.h>
24 #include <sound/soc-dapm.h>
25 #include <asm/mach-types.h>
26 #include <mach/audmux.h>
27
28 #include "../codecs/sgtl5000.h"
29 #include "imx-ssi.h"
30
31 static struct imx_sgtl5000_priv {
32         int sysclk;
33         int hw;
34         struct platform_device *pdev;
35 } card_priv;
36
37 static struct snd_soc_jack hs_jack;
38 static struct snd_soc_card imx_sgtl5000;
39
40 /* Headphones jack detection DAPM pins */
41 static struct snd_soc_jack_pin hs_jack_pins[] = {
42         {
43                 .pin = "Headphone Jack",
44                 .mask = SND_JACK_HEADPHONE,
45         },
46 };
47
48 /* Headphones jack detection gpios */
49 static struct snd_soc_jack_gpio hs_jack_gpios[] = {
50         [0] = {
51                 /* gpio is set on per-platform basis */
52                 .name           = "hp-gpio",
53                 .report         = SND_JACK_HEADPHONE,
54                 .debounce_time  = 200,
55         },
56 };
57
58 static int sgtl5000_params(struct snd_pcm_substream *substream,
59         struct snd_pcm_hw_params *params)
60 {
61         struct snd_soc_pcm_runtime *rtd = substream->private_data;
62         struct snd_soc_dai *codec_dai = rtd->codec_dai;
63         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
64         u32 dai_format;
65         int ret;
66         unsigned int channels = params_channels(params);
67
68         snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, card_priv.sysclk, 0);
69
70         snd_soc_dai_set_sysclk(codec_dai, SGTL5000_LRCLK, params_rate(params), 0);
71
72         dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
73                 SND_SOC_DAIFMT_CBM_CFM;
74
75         /* set codec DAI configuration */
76         ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
77         if (ret < 0)
78                 return ret;
79
80
81         /* TODO: The SSI driver should figure this out for us */
82         switch (channels) {
83         case 2:
84                 snd_soc_dai_set_tdm_slot(cpu_dai, 0xfffffffc, 0xfffffffc, 2, 0);
85                 break;
86         case 1:
87                 snd_soc_dai_set_tdm_slot(cpu_dai, 0xfffffffe, 0xfffffffe, 1, 0);
88                 break;
89         default:
90                 return -EINVAL;
91         }
92
93         /* set cpu DAI configuration */
94         dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
95                 SND_SOC_DAIFMT_CBM_CFM;
96         ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
97         if (ret < 0)
98                 return ret;
99
100         return 0;
101 }
102
103 static struct snd_soc_ops imx_sgtl5000_hifi_ops = {
104         .hw_params = sgtl5000_params,
105 };
106
107 static int sgtl5000_jack_func;
108 static int sgtl5000_spk_func;
109 static int sgtl5000_line_in_func;
110
111 static const char *jack_function[] = { "off", "on"};
112
113 static const char *spk_function[] = { "off", "on" };
114
115 static const char *line_in_function[] = { "off", "on" };
116
117 static const struct soc_enum sgtl5000_enum[] = {
118         SOC_ENUM_SINGLE_EXT(2, jack_function),
119         SOC_ENUM_SINGLE_EXT(2, spk_function),
120         SOC_ENUM_SINGLE_EXT(2, line_in_function),
121 };
122
123 static int sgtl5000_get_jack(struct snd_kcontrol *kcontrol,
124                              struct snd_ctl_elem_value *ucontrol)
125 {
126         ucontrol->value.enumerated.item[0] = sgtl5000_jack_func;
127         return 0;
128 }
129
130 static int sgtl5000_set_jack(struct snd_kcontrol *kcontrol,
131                              struct snd_ctl_elem_value *ucontrol)
132 {
133         struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
134
135         if (sgtl5000_jack_func == ucontrol->value.enumerated.item[0])
136                 return 0;
137
138         sgtl5000_jack_func = ucontrol->value.enumerated.item[0];
139         if (sgtl5000_jack_func)
140                 snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack");
141         else
142                 snd_soc_dapm_disable_pin(&codec->dapm, "Headphone Jack");
143
144         snd_soc_dapm_sync(&codec->dapm);
145         return 1;
146 }
147
148 static int sgtl5000_get_spk(struct snd_kcontrol *kcontrol,
149                             struct snd_ctl_elem_value *ucontrol)
150 {
151         ucontrol->value.enumerated.item[0] = sgtl5000_spk_func;
152         return 0;
153 }
154
155 static int sgtl5000_set_spk(struct snd_kcontrol *kcontrol,
156                             struct snd_ctl_elem_value *ucontrol)
157 {
158         struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
159
160         if (sgtl5000_spk_func == ucontrol->value.enumerated.item[0])
161                 return 0;
162
163         sgtl5000_spk_func = ucontrol->value.enumerated.item[0];
164         if (sgtl5000_spk_func)
165                 snd_soc_dapm_enable_pin(&codec->dapm, "Ext Spk");
166         else
167                 snd_soc_dapm_disable_pin(&codec->dapm, "Ext Spk");
168
169         snd_soc_dapm_sync(&codec->dapm);
170         return 1;
171 }
172
173 static int sgtl5000_get_line_in(struct snd_kcontrol *kcontrol,
174                              struct snd_ctl_elem_value *ucontrol)
175 {
176         ucontrol->value.enumerated.item[0] = sgtl5000_line_in_func;
177         return 0;
178 }
179
180 static int sgtl5000_set_line_in(struct snd_kcontrol *kcontrol,
181                              struct snd_ctl_elem_value *ucontrol)
182 {
183         struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
184
185         if (sgtl5000_line_in_func == ucontrol->value.enumerated.item[0])
186                 return 0;
187
188         sgtl5000_line_in_func = ucontrol->value.enumerated.item[0];
189         if (sgtl5000_line_in_func)
190                 snd_soc_dapm_enable_pin(&codec->dapm, "Line In Jack");
191         else
192                 snd_soc_dapm_disable_pin(&codec->dapm, "Line In Jack");
193
194         snd_soc_dapm_sync(&codec->dapm);
195         return 1;
196 }
197
198 /* imx_3stack card dapm widgets */
199 static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
200         SND_SOC_DAPM_MIC("Mic Jack", NULL),
201         SND_SOC_DAPM_LINE("Line In Jack", NULL),
202         SND_SOC_DAPM_SPK("Ext Spk", NULL),
203         SND_SOC_DAPM_HP("Headphone Jack", NULL),
204 };
205
206 static const struct snd_kcontrol_new sgtl5000_machine_controls[] = {
207         SOC_ENUM_EXT("Jack Function", sgtl5000_enum[0], sgtl5000_get_jack,
208                      sgtl5000_set_jack),
209         SOC_ENUM_EXT("Speaker Function", sgtl5000_enum[1], sgtl5000_get_spk,
210                      sgtl5000_set_spk),
211         SOC_ENUM_EXT("Line In Function", sgtl5000_enum[1], sgtl5000_get_line_in,
212                      sgtl5000_set_line_in),
213 };
214
215 /* imx_3stack machine connections to the codec pins */
216 static const struct snd_soc_dapm_route audio_map[] = {
217
218         /* Mic Jack --> MIC_IN (with automatic bias) */
219         {"MIC_IN", NULL, "Mic Jack"},
220
221         /* Line in Jack --> LINE_IN */
222         {"LINE_IN", NULL, "Line In Jack"},
223
224         /* HP_OUT --> Headphone Jack */
225         {"Headphone Jack", NULL, "HP_OUT"},
226
227         /* LINE_OUT --> Ext Speaker */
228         {"Ext Spk", NULL, "LINE_OUT"},
229 };
230
231 static int imx_3stack_sgtl5000_init(struct snd_soc_pcm_runtime *rtd)
232 {
233         struct snd_soc_codec *codec = rtd->codec;
234         int ret;
235
236         ret = snd_soc_add_controls(codec, sgtl5000_machine_controls,
237                         ARRAY_SIZE(sgtl5000_machine_controls));
238         if (ret)
239                 return ret;
240
241         /* Add imx_3stack specific widgets */
242         snd_soc_dapm_new_controls(&codec->dapm, imx_3stack_dapm_widgets,
243                                   ARRAY_SIZE(imx_3stack_dapm_widgets));
244
245         /* Set up imx_3stack specific audio path audio_map */
246         snd_soc_dapm_add_routes(&codec->dapm, audio_map, ARRAY_SIZE(audio_map));
247
248         snd_soc_dapm_disable_pin(&codec->dapm, "Line In Jack");
249         snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack");
250         snd_soc_dapm_sync(&codec->dapm);
251
252         if (hs_jack_gpios[0].gpio != -1) {
253                 /* Jack detection API stuff */
254                 ret = snd_soc_jack_new(codec, "Headphone Jack",
255                                        SND_JACK_HEADPHONE, &hs_jack);
256                 if (ret)
257                         return ret;
258
259                 ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
260                                         hs_jack_pins);
261                 if (ret) {
262                         printk(KERN_ERR "failed to call  snd_soc_jack_add_pins\n");
263                         return ret;
264                 }
265
266                 ret = snd_soc_jack_add_gpios(&hs_jack,
267                                         ARRAY_SIZE(hs_jack_gpios), hs_jack_gpios);
268                 if (ret)
269                         printk(KERN_WARNING "failed to call snd_soc_jack_add_gpios\n");
270         }
271
272         return 0;
273 }
274
275 static struct snd_soc_dai_link imx_sgtl5000_dai[] = {
276         {
277                 .name           = "HiFi",
278                 .stream_name    = "HiFi",
279                 .codec_dai_name = "sgtl5000",
280                 .codec_name     = "sgtl5000.1-000a",
281                 .cpu_dai_name   = "imx-ssi.1",
282                 .platform_name  = "imx-pcm-audio.1",
283                 .init           = imx_3stack_sgtl5000_init,
284                 .ops            = &imx_sgtl5000_hifi_ops,
285         },
286 };
287
288 static struct snd_soc_card imx_sgtl5000 = {
289         .name           = "sgtl5000-audio",
290         .dai_link       = imx_sgtl5000_dai,
291         .num_links      = ARRAY_SIZE(imx_sgtl5000_dai),
292 };
293
294 static struct platform_device *imx_sgtl5000_snd_device;
295
296 static int imx_audmux_config(int slave, int master)
297 {
298         unsigned int ptcr, pdcr;
299         slave = slave - 1;
300         master = master - 1;
301
302         /* SSI0 mastered by port 5 */
303         ptcr = MXC_AUDMUX_V2_PTCR_SYN |
304                 MXC_AUDMUX_V2_PTCR_TFSDIR |
305                 MXC_AUDMUX_V2_PTCR_TFSEL(master) |
306                 MXC_AUDMUX_V2_PTCR_TCLKDIR |
307                 MXC_AUDMUX_V2_PTCR_TCSEL(master);
308         pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master);
309         mxc_audmux_v2_configure_port(slave, ptcr, pdcr);
310
311         ptcr = MXC_AUDMUX_V2_PTCR_SYN;
312         pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(slave);
313         mxc_audmux_v2_configure_port(master, ptcr, pdcr);
314
315         return 0;
316 }
317
318 static int __devinit imx_sgtl5000_probe(struct platform_device *pdev)
319 {
320         struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
321
322         int ret = 0;
323
324         card_priv.pdev = pdev;
325
326         imx_audmux_config(plat->src_port, plat->ext_port);
327
328         ret = -EINVAL;
329         if (plat->init && plat->init())
330                 return ret;
331
332         card_priv.sysclk = plat->sysclk;
333
334         hs_jack_gpios[0].gpio = plat->hp_gpio;
335         hs_jack_gpios[0].invert = plat->hp_active_low;
336
337         return 0;
338 }
339
340 static int imx_sgtl5000_remove(struct platform_device *pdev)
341 {
342         struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
343
344         if (plat->finit)
345                 plat->finit();
346
347         return 0;
348 }
349
350 static struct platform_driver imx_sgtl5000_audio_driver = {
351         .probe = imx_sgtl5000_probe,
352         .remove = imx_sgtl5000_remove,
353         .driver = {
354                    .name = "imx-sgtl5000",
355                    },
356 };
357
358 static int __init imx_sgtl5000_init(void)
359 {
360         int ret;
361
362         ret = platform_driver_register(&imx_sgtl5000_audio_driver);
363         if (ret)
364                 return -ENOMEM;
365
366         if (machine_is_mx35_3ds())
367                 imx_sgtl5000_dai[0].codec_name = "sgtl5000.0-000a";
368         else
369                 imx_sgtl5000_dai[0].codec_name = "sgtl5000.1-000a";
370
371         imx_sgtl5000_snd_device = platform_device_alloc("soc-audio", 1);
372         if (!imx_sgtl5000_snd_device)
373                 return -ENOMEM;
374
375         platform_set_drvdata(imx_sgtl5000_snd_device, &imx_sgtl5000);
376
377         ret = platform_device_add(imx_sgtl5000_snd_device);
378
379         if (ret) {
380                 printk(KERN_ERR "ASoC: Platform device allocation failed\n");
381                 platform_device_put(imx_sgtl5000_snd_device);
382         }
383
384         return ret;
385 }
386
387 static void __exit imx_sgtl5000_exit(void)
388 {
389         platform_driver_unregister(&imx_sgtl5000_audio_driver);
390         platform_device_unregister(imx_sgtl5000_snd_device);
391 }
392
393 module_init(imx_sgtl5000_init);
394 module_exit(imx_sgtl5000_exit);
395
396 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
397 MODULE_DESCRIPTION("PhyCORE ALSA SoC driver");
398 MODULE_LICENSE("GPL");