treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 336
[linux-2.6-microblaze.git] / sound / soc / ti / omap3pandora.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * omap3pandora.c  --  SoC audio for Pandora Handheld Console
4  *
5  * Author: GraÅžvydas Ignotas <notasas@gmail.com>
6  */
7
8 #include <linux/clk.h>
9 #include <linux/platform_device.h>
10 #include <linux/gpio.h>
11 #include <linux/delay.h>
12 #include <linux/regulator/consumer.h>
13 #include <linux/module.h>
14
15 #include <sound/core.h>
16 #include <sound/pcm.h>
17 #include <sound/soc.h>
18
19 #include <asm/mach-types.h>
20 #include <linux/platform_data/asoc-ti-mcbsp.h>
21
22 #include "omap-mcbsp.h"
23
24 #define OMAP3_PANDORA_DAC_POWER_GPIO    118
25 #define OMAP3_PANDORA_AMP_POWER_GPIO    14
26
27 #define PREFIX "ASoC omap3pandora: "
28
29 static struct regulator *omap3pandora_dac_reg;
30
31 static int omap3pandora_hw_params(struct snd_pcm_substream *substream,
32         struct snd_pcm_hw_params *params)
33 {
34         struct snd_soc_pcm_runtime *rtd = substream->private_data;
35         struct snd_soc_dai *codec_dai = rtd->codec_dai;
36         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
37         int ret;
38
39         /* Set the codec system clock for DAC and ADC */
40         ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
41                                             SND_SOC_CLOCK_IN);
42         if (ret < 0) {
43                 pr_err(PREFIX "can't set codec system clock\n");
44                 return ret;
45         }
46
47         /* Set McBSP clock to external */
48         ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT,
49                                      256 * params_rate(params),
50                                      SND_SOC_CLOCK_IN);
51         if (ret < 0) {
52                 pr_err(PREFIX "can't set cpu system clock\n");
53                 return ret;
54         }
55
56         ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8);
57         if (ret < 0) {
58                 pr_err(PREFIX "can't set SRG clock divider\n");
59                 return ret;
60         }
61
62         return 0;
63 }
64
65 static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w,
66         struct snd_kcontrol *k, int event)
67 {
68         int ret;
69
70         /*
71          * The PCM1773 DAC datasheet requires 1ms delay between switching
72          * VCC power on/off and /PD pin high/low
73          */
74         if (SND_SOC_DAPM_EVENT_ON(event)) {
75                 ret = regulator_enable(omap3pandora_dac_reg);
76                 if (ret) {
77                         dev_err(w->dapm->dev, "Failed to power DAC: %d\n", ret);
78                         return ret;
79                 }
80                 mdelay(1);
81                 gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1);
82         } else {
83                 gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
84                 mdelay(1);
85                 regulator_disable(omap3pandora_dac_reg);
86         }
87
88         return 0;
89 }
90
91 static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
92         struct snd_kcontrol *k, int event)
93 {
94         if (SND_SOC_DAPM_EVENT_ON(event))
95                 gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1);
96         else
97                 gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
98
99         return 0;
100 }
101
102 /*
103  * Audio paths on Pandora board:
104  *
105  *  |O| ---> PCM DAC +-> AMP -> Headphone Jack
106  *  |M|         A    +--------> Line Out
107  *  |A| <~~clk~~+
108  *  |P| <--- TWL4030 <--------- Line In and MICs
109  */
110 static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = {
111         SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM,
112                            0, 0, omap3pandora_dac_event,
113                            SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
114         SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,
115                            0, 0, NULL, 0, omap3pandora_hp_event,
116                            SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
117         SND_SOC_DAPM_HP("Headphone Jack", NULL),
118         SND_SOC_DAPM_LINE("Line Out", NULL),
119
120         SND_SOC_DAPM_MIC("Mic (internal)", NULL),
121         SND_SOC_DAPM_MIC("Mic (external)", NULL),
122         SND_SOC_DAPM_LINE("Line In", NULL),
123 };
124
125 static const struct snd_soc_dapm_route omap3pandora_map[] = {
126         {"PCM DAC", NULL, "APLL Enable"},
127         {"Headphone Amplifier", NULL, "PCM DAC"},
128         {"Line Out", NULL, "PCM DAC"},
129         {"Headphone Jack", NULL, "Headphone Amplifier"},
130
131         {"AUXL", NULL, "Line In"},
132         {"AUXR", NULL, "Line In"},
133
134         {"MAINMIC", NULL, "Mic (internal)"},
135         {"Mic (internal)", NULL, "Mic Bias 1"},
136
137         {"SUBMIC", NULL, "Mic (external)"},
138         {"Mic (external)", NULL, "Mic Bias 2"},
139 };
140
141 static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
142 {
143         struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
144
145         /* All TWL4030 output pins are floating */
146         snd_soc_dapm_nc_pin(dapm, "EARPIECE");
147         snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");
148         snd_soc_dapm_nc_pin(dapm, "PREDRIVER");
149         snd_soc_dapm_nc_pin(dapm, "HSOL");
150         snd_soc_dapm_nc_pin(dapm, "HSOR");
151         snd_soc_dapm_nc_pin(dapm, "CARKITL");
152         snd_soc_dapm_nc_pin(dapm, "CARKITR");
153         snd_soc_dapm_nc_pin(dapm, "HFL");
154         snd_soc_dapm_nc_pin(dapm, "HFR");
155         snd_soc_dapm_nc_pin(dapm, "VIBRA");
156
157         return 0;
158 }
159
160 static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
161 {
162         struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
163
164         /* Not comnnected */
165         snd_soc_dapm_nc_pin(dapm, "HSMIC");
166         snd_soc_dapm_nc_pin(dapm, "CARKITMIC");
167         snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
168         snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
169
170         return 0;
171 }
172
173 static const struct snd_soc_ops omap3pandora_ops = {
174         .hw_params = omap3pandora_hw_params,
175 };
176
177 /* Digital audio interface glue - connects codec <--> CPU */
178 static struct snd_soc_dai_link omap3pandora_dai[] = {
179         {
180                 .name = "PCM1773",
181                 .stream_name = "HiFi Out",
182                 .cpu_dai_name = "omap-mcbsp.2",
183                 .codec_dai_name = "twl4030-hifi",
184                 .platform_name = "omap-mcbsp.2",
185                 .codec_name = "twl4030-codec",
186                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
187                            SND_SOC_DAIFMT_CBS_CFS,
188                 .ops = &omap3pandora_ops,
189                 .init = omap3pandora_out_init,
190         }, {
191                 .name = "TWL4030",
192                 .stream_name = "Line/Mic In",
193                 .cpu_dai_name = "omap-mcbsp.4",
194                 .codec_dai_name = "twl4030-hifi",
195                 .platform_name = "omap-mcbsp.4",
196                 .codec_name = "twl4030-codec",
197                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
198                            SND_SOC_DAIFMT_CBS_CFS,
199                 .ops = &omap3pandora_ops,
200                 .init = omap3pandora_in_init,
201         }
202 };
203
204 /* SoC card */
205 static struct snd_soc_card snd_soc_card_omap3pandora = {
206         .name = "omap3pandora",
207         .owner = THIS_MODULE,
208         .dai_link = omap3pandora_dai,
209         .num_links = ARRAY_SIZE(omap3pandora_dai),
210
211         .dapm_widgets = omap3pandora_dapm_widgets,
212         .num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets),
213         .dapm_routes = omap3pandora_map,
214         .num_dapm_routes = ARRAY_SIZE(omap3pandora_map),
215 };
216
217 static struct platform_device *omap3pandora_snd_device;
218
219 static int __init omap3pandora_soc_init(void)
220 {
221         int ret;
222
223         if (!machine_is_omap3_pandora())
224                 return -ENODEV;
225
226         pr_info("OMAP3 Pandora SoC init\n");
227
228         ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power");
229         if (ret) {
230                 pr_err(PREFIX "Failed to get DAC power GPIO\n");
231                 return ret;
232         }
233
234         ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
235         if (ret) {
236                 pr_err(PREFIX "Failed to set DAC power GPIO direction\n");
237                 goto fail0;
238         }
239
240         ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power");
241         if (ret) {
242                 pr_err(PREFIX "Failed to get amp power GPIO\n");
243                 goto fail0;
244         }
245
246         ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
247         if (ret) {
248                 pr_err(PREFIX "Failed to set amp power GPIO direction\n");
249                 goto fail1;
250         }
251
252         omap3pandora_snd_device = platform_device_alloc("soc-audio", -1);
253         if (omap3pandora_snd_device == NULL) {
254                 pr_err(PREFIX "Platform device allocation failed\n");
255                 ret = -ENOMEM;
256                 goto fail1;
257         }
258
259         platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora);
260
261         ret = platform_device_add(omap3pandora_snd_device);
262         if (ret) {
263                 pr_err(PREFIX "Unable to add platform device\n");
264                 goto fail2;
265         }
266
267         omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc");
268         if (IS_ERR(omap3pandora_dac_reg)) {
269                 pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n",
270                         dev_name(&omap3pandora_snd_device->dev),
271                         PTR_ERR(omap3pandora_dac_reg));
272                 ret = PTR_ERR(omap3pandora_dac_reg);
273                 goto fail3;
274         }
275
276         return 0;
277
278 fail3:
279         platform_device_del(omap3pandora_snd_device);
280 fail2:
281         platform_device_put(omap3pandora_snd_device);
282 fail1:
283         gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
284 fail0:
285         gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
286         return ret;
287 }
288 module_init(omap3pandora_soc_init);
289
290 static void __exit omap3pandora_soc_exit(void)
291 {
292         regulator_put(omap3pandora_dac_reg);
293         platform_device_unregister(omap3pandora_snd_device);
294         gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
295         gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
296 }
297 module_exit(omap3pandora_soc_exit);
298
299 MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
300 MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
301 MODULE_LICENSE("GPL");