]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - sound/soc/bcm/audioinjector-octo-soundcard.c
Add support for the AudioInjector.net Octo sound card
[mirror_ubuntu-artful-kernel.git] / sound / soc / bcm / audioinjector-octo-soundcard.c
1 /*
2 * ASoC Driver for AudioInjector Pi octo channel soundcard (hat)
3 *
4 * Created on: 27-October-2016
5 * Author: flatmax@flatmax.org
6 * based on audioinjector-pi-soundcard.c
7 *
8 * Copyright (C) 2016 Flatmax Pty. Ltd.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * version 2 as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 */
19
20 #include <linux/module.h>
21 #include <linux/types.h>
22 #include <linux/gpio/consumer.h>
23
24 #include <sound/core.h>
25 #include <sound/soc.h>
26 #include <sound/pcm_params.h>
27 #include <sound/control.h>
28
29 static struct gpio_descs *mult_gpios;
30 static unsigned int audioinjector_octo_rate;
31
32 static int audioinjector_octo_dai_init(struct snd_soc_pcm_runtime *rtd)
33 {
34 return snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, 64);
35 }
36
37 static int audioinjector_octo_startup(struct snd_pcm_substream *substream)
38 {
39 struct snd_soc_pcm_runtime *rtd = substream->private_data;
40 rtd->cpu_dai->driver->playback.channels_min = 8;
41 rtd->cpu_dai->driver->playback.channels_max = 8;
42 rtd->cpu_dai->driver->capture.channels_min = 8;
43 rtd->cpu_dai->driver->capture.channels_max = 8;
44 rtd->codec_dai->driver->capture.channels_max = 8;
45 return 0;
46 }
47
48 static void audioinjector_octo_shutdown(struct snd_pcm_substream *substream)
49 {
50 struct snd_soc_pcm_runtime *rtd = substream->private_data;
51 rtd->cpu_dai->driver->playback.channels_min = 2;
52 rtd->cpu_dai->driver->playback.channels_max = 2;
53 rtd->cpu_dai->driver->capture.channels_min = 2;
54 rtd->cpu_dai->driver->capture.channels_max = 2;
55 rtd->codec_dai->driver->capture.channels_max = 6;
56 }
57
58 static int audioinjector_octo_hw_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
63 // set codec DAI configuration
64 int ret = snd_soc_dai_set_fmt(rtd->codec_dai,
65 SND_SOC_DAIFMT_CBS_CFS|SND_SOC_DAIFMT_DSP_A|
66 SND_SOC_DAIFMT_NB_NF);
67 if (ret < 0)
68 return ret;
69
70 // set cpu DAI configuration
71 ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
72 SND_SOC_DAIFMT_CBM_CFM|SND_SOC_DAIFMT_I2S|
73 SND_SOC_DAIFMT_NB_NF);
74 if (ret < 0)
75 return ret;
76
77 audioinjector_octo_rate = params_rate(params);
78
79 return 0;
80 }
81
82 static int audioinjector_octo_trigger(struct snd_pcm_substream *substream,
83 int cmd){
84 int mult[4];
85 mult[0] = 0;
86 mult[1] = 0;
87 mult[2] = 0;
88 mult[3] = 0;
89
90 switch (cmd) {
91 case SNDRV_PCM_TRIGGER_START:
92 case SNDRV_PCM_TRIGGER_RESUME:
93 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
94 switch (audioinjector_octo_rate) {
95 case 192000:
96 mult[3] = 1;
97 case 176640:
98 mult[0] = 1;
99 mult[1] = 1;
100 mult[2] = 1;
101 break;
102 case 96000:
103 mult[3] = 1;
104 case 88200:
105 mult[1] = 1;
106 mult[2] = 1;
107 break;
108 case 64000:
109 mult[3] = 1;
110 case 58800:
111 mult[0] = 1;
112 mult[2] = 1;
113 break;
114 case 48000:
115 mult[3] = 1;
116 case 44100:
117 mult[2] = 1;
118 break;
119 case 32000:
120 mult[3] = 1;
121 case 29400:
122 mult[0] = 1;
123 mult[1] = 1;
124 break;
125 case 24000:
126 mult[3] = 1;
127 case 22050:
128 mult[1] = 1;
129 break;
130 case 16000:
131 mult[3] = 1;
132 case 14700:
133 mult[0] = 1;
134 break;
135 case 8000:
136 mult[3] = 1;
137 break;
138 default:
139 return -EINVAL;
140 }
141 break;
142 case SNDRV_PCM_TRIGGER_STOP:
143 case SNDRV_PCM_TRIGGER_SUSPEND:
144 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
145 break;
146 default:
147 return -EINVAL;
148 }
149 gpiod_set_array_value_cansleep(mult_gpios->ndescs, mult_gpios->desc,
150 mult);
151
152 return 0;
153 }
154
155 static struct snd_soc_ops audioinjector_octo_ops = {
156 .startup = audioinjector_octo_startup,
157 .shutdown = audioinjector_octo_shutdown,
158 .hw_params = audioinjector_octo_hw_params,
159 .trigger = audioinjector_octo_trigger,
160 };
161
162 static struct snd_soc_dai_link audioinjector_octo_dai[] = {
163 {
164 .name = "AudioInjector Octo",
165 .stream_name = "AudioInject-HIFI",
166 .codec_dai_name = "cs42448",
167 .ops = &audioinjector_octo_ops,
168 .init = audioinjector_octo_dai_init,
169 },
170 };
171
172 static const struct snd_soc_dapm_widget audioinjector_octo_widgets[] = {
173 SND_SOC_DAPM_OUTPUT("OUTPUTS0"),
174 SND_SOC_DAPM_OUTPUT("OUTPUTS1"),
175 SND_SOC_DAPM_OUTPUT("OUTPUTS2"),
176 SND_SOC_DAPM_OUTPUT("OUTPUTS3"),
177 SND_SOC_DAPM_INPUT("INPUTS0"),
178 SND_SOC_DAPM_INPUT("INPUTS1"),
179 SND_SOC_DAPM_INPUT("INPUTS2"),
180 };
181
182 static const struct snd_soc_dapm_route audioinjector_octo_route[] = {
183 /* Balanced outputs */
184 {"OUTPUTS0", NULL, "AOUT1L"},
185 {"OUTPUTS0", NULL, "AOUT1R"},
186 {"OUTPUTS1", NULL, "AOUT2L"},
187 {"OUTPUTS1", NULL, "AOUT2R"},
188 {"OUTPUTS2", NULL, "AOUT3L"},
189 {"OUTPUTS2", NULL, "AOUT3R"},
190 {"OUTPUTS3", NULL, "AOUT4L"},
191 {"OUTPUTS3", NULL, "AOUT4R"},
192
193 /* Balanced inputs */
194 {"AIN1L", NULL, "INPUTS0"},
195 {"AIN1R", NULL, "INPUTS0"},
196 {"AIN2L", NULL, "INPUTS1"},
197 {"AIN2R", NULL, "INPUTS1"},
198 {"AIN3L", NULL, "INPUTS2"},
199 {"AIN3R", NULL, "INPUTS2"},
200 };
201
202 static struct snd_soc_card snd_soc_audioinjector_octo = {
203 .name = "audioinjector-octo-soundcard",
204 .dai_link = audioinjector_octo_dai,
205 .num_links = ARRAY_SIZE(audioinjector_octo_dai),
206
207 .dapm_widgets = audioinjector_octo_widgets,
208 .num_dapm_widgets = ARRAY_SIZE(audioinjector_octo_widgets),
209 .dapm_routes = audioinjector_octo_route,
210 .num_dapm_routes = ARRAY_SIZE(audioinjector_octo_route),
211 };
212
213 static int audioinjector_octo_probe(struct platform_device *pdev)
214 {
215 struct snd_soc_card *card = &snd_soc_audioinjector_octo;
216 int ret;
217
218 card->dev = &pdev->dev;
219
220 if (pdev->dev.of_node) {
221 struct snd_soc_dai_link *dai = &audioinjector_octo_dai[0];
222 struct device_node *i2s_node =
223 of_parse_phandle(pdev->dev.of_node,
224 "i2s-controller", 0);
225 struct device_node *codec_node =
226 of_parse_phandle(pdev->dev.of_node,
227 "codec", 0);
228
229 mult_gpios = devm_gpiod_get_array_optional(&pdev->dev, "mult",
230 GPIOD_OUT_LOW);
231 if (IS_ERR(mult_gpios))
232 return PTR_ERR(mult_gpios);
233
234 if (i2s_node && codec_node) {
235 dai->cpu_dai_name = NULL;
236 dai->cpu_of_node = i2s_node;
237 dai->platform_name = NULL;
238 dai->platform_of_node = i2s_node;
239 dai->codec_name = NULL;
240 dai->codec_of_node = codec_node;
241 } else
242 if (!dai->cpu_of_node) {
243 dev_err(&pdev->dev,
244 "i2s-controller missing or invalid in DT\n");
245 return -EINVAL;
246 } else {
247 dev_err(&pdev->dev,
248 "Property 'codec' missing or invalid\n");
249 return -EINVAL;
250 }
251 }
252
253 ret = snd_soc_register_card(card);
254 if (ret != 0)
255 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
256 return ret;
257 }
258
259 static int audioinjector_octo_remove(struct platform_device *pdev)
260 {
261 struct snd_soc_card *card = platform_get_drvdata(pdev);
262
263 return snd_soc_unregister_card(card);
264 }
265
266 static const struct of_device_id audioinjector_octo_of_match[] = {
267 { .compatible = "ai,audioinjector-octo-soundcard", },
268 {},
269 };
270 MODULE_DEVICE_TABLE(of, audioinjector_octo_of_match);
271
272 static struct platform_driver audioinjector_octo_driver = {
273 .driver = {
274 .name = "audioinjector-audio",
275 .owner = THIS_MODULE,
276 .of_match_table = audioinjector_octo_of_match,
277 },
278 .probe = audioinjector_octo_probe,
279 .remove = audioinjector_octo_remove,
280 };
281
282 module_platform_driver(audioinjector_octo_driver);
283 MODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>");
284 MODULE_DESCRIPTION("AudioInjector.net octo Soundcard");
285 MODULE_LICENSE("GPL v2");
286 MODULE_ALIAS("platform:audioinjector-octo-soundcard");