]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - sound/soc/bcm/audioinjector-octo-soundcard.c
AudioInjector Octo: sample rates, regulators, reset
[mirror_ubuntu-zesty-kernel.git] / sound / soc / bcm / audioinjector-octo-soundcard.c
CommitLineData
13b6aff5
MF
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
29static struct gpio_descs *mult_gpios;
b4f175f4 30static struct gpio_desc *codec_rst_gpio;
13b6aff5
MF
31static unsigned int audioinjector_octo_rate;
32
b4f175f4
MF
33static const unsigned int audioinjector_octo_rates[] = {
34 96000, 48000, 32000, 24000, 16000, 8000, 88200, 44100, 29400, 22050, 14700,
35};
36
37static struct snd_pcm_hw_constraint_list audioinjector_octo_constraints = {
38 .list = audioinjector_octo_rates,
39 .count = ARRAY_SIZE(audioinjector_octo_rates),
40};
41
13b6aff5
MF
42static int audioinjector_octo_dai_init(struct snd_soc_pcm_runtime *rtd)
43{
44 return snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, 64);
45}
46
47static int audioinjector_octo_startup(struct snd_pcm_substream *substream)
48{
49 struct snd_soc_pcm_runtime *rtd = substream->private_data;
50 rtd->cpu_dai->driver->playback.channels_min = 8;
51 rtd->cpu_dai->driver->playback.channels_max = 8;
52 rtd->cpu_dai->driver->capture.channels_min = 8;
53 rtd->cpu_dai->driver->capture.channels_max = 8;
54 rtd->codec_dai->driver->capture.channels_max = 8;
b4f175f4
MF
55
56 snd_pcm_hw_constraint_list(substream->runtime, 0,
57 SNDRV_PCM_HW_PARAM_RATE,
58 &audioinjector_octo_constraints);
59
13b6aff5
MF
60 return 0;
61}
62
63static void audioinjector_octo_shutdown(struct snd_pcm_substream *substream)
64{
65 struct snd_soc_pcm_runtime *rtd = substream->private_data;
66 rtd->cpu_dai->driver->playback.channels_min = 2;
67 rtd->cpu_dai->driver->playback.channels_max = 2;
68 rtd->cpu_dai->driver->capture.channels_min = 2;
69 rtd->cpu_dai->driver->capture.channels_max = 2;
70 rtd->codec_dai->driver->capture.channels_max = 6;
71}
72
73static int audioinjector_octo_hw_params(struct snd_pcm_substream *substream,
74 struct snd_pcm_hw_params *params)
75{
76 struct snd_soc_pcm_runtime *rtd = substream->private_data;
77
78 // set codec DAI configuration
79 int ret = snd_soc_dai_set_fmt(rtd->codec_dai,
80 SND_SOC_DAIFMT_CBS_CFS|SND_SOC_DAIFMT_DSP_A|
81 SND_SOC_DAIFMT_NB_NF);
82 if (ret < 0)
83 return ret;
84
85 // set cpu DAI configuration
86 ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
87 SND_SOC_DAIFMT_CBM_CFM|SND_SOC_DAIFMT_I2S|
88 SND_SOC_DAIFMT_NB_NF);
89 if (ret < 0)
90 return ret;
91
92 audioinjector_octo_rate = params_rate(params);
93
b4f175f4
MF
94 // Set the correct sysclock for the codec
95 switch (audioinjector_octo_rate) {
96 case 96000:
97 case 48000:
98 return snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 49152000,
99 0);
100 break;
101 case 24000:
102 return snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 49152000/2,
103 0);
104 break;
105 case 32000:
106 case 16000:
107 return snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 49152000/3,
108 0);
109 break;
110 case 8000:
111 return snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 49152000/6,
112 0);
113 break;
114 case 88200:
115 case 44100:
116 return snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 45185400,
117 0);
118 break;
119 case 22050:
120 return snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 45185400/2,
121 0);
122 break;
123 case 29400:
124 case 14700:
125 return snd_soc_dai_set_sysclk(rtd->codec_dai, 0, 45185400/3,
126 0);
127 break;
128 default:
129 return -EINVAL;
130 }
13b6aff5
MF
131}
132
133static int audioinjector_octo_trigger(struct snd_pcm_substream *substream,
134 int cmd){
135 int mult[4];
136 mult[0] = 0;
137 mult[1] = 0;
138 mult[2] = 0;
139 mult[3] = 0;
140
141 switch (cmd) {
142 case SNDRV_PCM_TRIGGER_START:
143 case SNDRV_PCM_TRIGGER_RESUME:
144 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
145 switch (audioinjector_octo_rate) {
13b6aff5
MF
146 case 96000:
147 mult[3] = 1;
148 case 88200:
149 mult[1] = 1;
150 mult[2] = 1;
151 break;
13b6aff5
MF
152 case 48000:
153 mult[3] = 1;
154 case 44100:
155 mult[2] = 1;
156 break;
157 case 32000:
158 mult[3] = 1;
159 case 29400:
160 mult[0] = 1;
161 mult[1] = 1;
162 break;
163 case 24000:
164 mult[3] = 1;
165 case 22050:
166 mult[1] = 1;
167 break;
168 case 16000:
169 mult[3] = 1;
170 case 14700:
171 mult[0] = 1;
172 break;
173 case 8000:
174 mult[3] = 1;
175 break;
176 default:
177 return -EINVAL;
178 }
179 break;
180 case SNDRV_PCM_TRIGGER_STOP:
181 case SNDRV_PCM_TRIGGER_SUSPEND:
182 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
183 break;
184 default:
185 return -EINVAL;
186 }
187 gpiod_set_array_value_cansleep(mult_gpios->ndescs, mult_gpios->desc,
188 mult);
189
190 return 0;
191}
192
193static struct snd_soc_ops audioinjector_octo_ops = {
194 .startup = audioinjector_octo_startup,
195 .shutdown = audioinjector_octo_shutdown,
196 .hw_params = audioinjector_octo_hw_params,
197 .trigger = audioinjector_octo_trigger,
198};
199
200static struct snd_soc_dai_link audioinjector_octo_dai[] = {
201 {
202 .name = "AudioInjector Octo",
203 .stream_name = "AudioInject-HIFI",
204 .codec_dai_name = "cs42448",
205 .ops = &audioinjector_octo_ops,
206 .init = audioinjector_octo_dai_init,
207 },
208};
209
210static const struct snd_soc_dapm_widget audioinjector_octo_widgets[] = {
211 SND_SOC_DAPM_OUTPUT("OUTPUTS0"),
212 SND_SOC_DAPM_OUTPUT("OUTPUTS1"),
213 SND_SOC_DAPM_OUTPUT("OUTPUTS2"),
214 SND_SOC_DAPM_OUTPUT("OUTPUTS3"),
215 SND_SOC_DAPM_INPUT("INPUTS0"),
216 SND_SOC_DAPM_INPUT("INPUTS1"),
217 SND_SOC_DAPM_INPUT("INPUTS2"),
218};
219
220static const struct snd_soc_dapm_route audioinjector_octo_route[] = {
221 /* Balanced outputs */
222 {"OUTPUTS0", NULL, "AOUT1L"},
223 {"OUTPUTS0", NULL, "AOUT1R"},
224 {"OUTPUTS1", NULL, "AOUT2L"},
225 {"OUTPUTS1", NULL, "AOUT2R"},
226 {"OUTPUTS2", NULL, "AOUT3L"},
227 {"OUTPUTS2", NULL, "AOUT3R"},
228 {"OUTPUTS3", NULL, "AOUT4L"},
229 {"OUTPUTS3", NULL, "AOUT4R"},
230
231 /* Balanced inputs */
232 {"AIN1L", NULL, "INPUTS0"},
233 {"AIN1R", NULL, "INPUTS0"},
234 {"AIN2L", NULL, "INPUTS1"},
235 {"AIN2R", NULL, "INPUTS1"},
236 {"AIN3L", NULL, "INPUTS2"},
237 {"AIN3R", NULL, "INPUTS2"},
238};
239
240static struct snd_soc_card snd_soc_audioinjector_octo = {
241 .name = "audioinjector-octo-soundcard",
242 .dai_link = audioinjector_octo_dai,
243 .num_links = ARRAY_SIZE(audioinjector_octo_dai),
244
245 .dapm_widgets = audioinjector_octo_widgets,
246 .num_dapm_widgets = ARRAY_SIZE(audioinjector_octo_widgets),
247 .dapm_routes = audioinjector_octo_route,
248 .num_dapm_routes = ARRAY_SIZE(audioinjector_octo_route),
249};
250
251static int audioinjector_octo_probe(struct platform_device *pdev)
252{
253 struct snd_soc_card *card = &snd_soc_audioinjector_octo;
254 int ret;
255
256 card->dev = &pdev->dev;
257
258 if (pdev->dev.of_node) {
259 struct snd_soc_dai_link *dai = &audioinjector_octo_dai[0];
260 struct device_node *i2s_node =
261 of_parse_phandle(pdev->dev.of_node,
262 "i2s-controller", 0);
263 struct device_node *codec_node =
264 of_parse_phandle(pdev->dev.of_node,
265 "codec", 0);
266
267 mult_gpios = devm_gpiod_get_array_optional(&pdev->dev, "mult",
268 GPIOD_OUT_LOW);
269 if (IS_ERR(mult_gpios))
270 return PTR_ERR(mult_gpios);
271
b4f175f4
MF
272 codec_rst_gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
273 GPIOD_OUT_LOW);
274 if (IS_ERR(codec_rst_gpio))
275 return PTR_ERR(codec_rst_gpio);
276
277 if (codec_rst_gpio)
278 gpiod_set_value(codec_rst_gpio, 1);
279 msleep(500);
280 if (codec_rst_gpio)
281 gpiod_set_value(codec_rst_gpio, 0);
282 msleep(500);
283 if (codec_rst_gpio)
284 gpiod_set_value(codec_rst_gpio, 1);
285 msleep(500);
286
13b6aff5
MF
287 if (i2s_node && codec_node) {
288 dai->cpu_dai_name = NULL;
289 dai->cpu_of_node = i2s_node;
290 dai->platform_name = NULL;
291 dai->platform_of_node = i2s_node;
292 dai->codec_name = NULL;
293 dai->codec_of_node = codec_node;
294 } else
295 if (!dai->cpu_of_node) {
296 dev_err(&pdev->dev,
297 "i2s-controller missing or invalid in DT\n");
298 return -EINVAL;
299 } else {
300 dev_err(&pdev->dev,
301 "Property 'codec' missing or invalid\n");
302 return -EINVAL;
303 }
304 }
305
306 ret = snd_soc_register_card(card);
307 if (ret != 0)
308 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
309 return ret;
310}
311
312static int audioinjector_octo_remove(struct platform_device *pdev)
313{
314 struct snd_soc_card *card = platform_get_drvdata(pdev);
315
316 return snd_soc_unregister_card(card);
317}
318
319static const struct of_device_id audioinjector_octo_of_match[] = {
320 { .compatible = "ai,audioinjector-octo-soundcard", },
321 {},
322};
323MODULE_DEVICE_TABLE(of, audioinjector_octo_of_match);
324
325static struct platform_driver audioinjector_octo_driver = {
326 .driver = {
327 .name = "audioinjector-audio",
328 .owner = THIS_MODULE,
329 .of_match_table = audioinjector_octo_of_match,
330 },
331 .probe = audioinjector_octo_probe,
332 .remove = audioinjector_octo_remove,
333};
334
335module_platform_driver(audioinjector_octo_driver);
336MODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>");
337MODULE_DESCRIPTION("AudioInjector.net octo Soundcard");
338MODULE_LICENSE("GPL v2");
339MODULE_ALIAS("platform:audioinjector-octo-soundcard");