aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarkko Nikula <jarkko.nikula@linux.intel.com>2014-02-24 10:26:59 -0500
committerMark Brown <broonie@linaro.org>2014-02-24 23:44:36 -0500
commitaef1311a7d7e77408611cdf143d32bb1709527b6 (patch)
tree9b46c523c78f072413b57a1ca7a7b134573ccff4
parentf7d01fd6754c1a257af46ec465d946132c7d004d (diff)
ASoC: Intel: Add Intel Baytrail SST PCM platform driver
This adds the Baytrail SST DSP PCM platform driver. It registers itself with the ALSA SoC layer and uses Intel Baytrail SST DSP IPC for DSP control. Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Acked-by: Liam Girdwood <liam.r.girdwood@intel.com> Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r--sound/soc/intel/sst-baytrail-pcm.c422
1 files changed, 422 insertions, 0 deletions
diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c
new file mode 100644
index 000000000000..6d101f3813b4
--- /dev/null
+++ b/sound/soc/intel/sst-baytrail-pcm.c
@@ -0,0 +1,422 @@
1/*
2 * Intel Baytrail SST PCM Support
3 * Copyright (c) 2014, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 */
14
15#include <linux/module.h>
16#include <linux/dma-mapping.h>
17#include <linux/slab.h>
18#include <sound/core.h>
19#include <sound/pcm.h>
20#include <sound/pcm_params.h>
21#include <sound/soc.h>
22#include "sst-baytrail-ipc.h"
23#include "sst-dsp-priv.h"
24#include "sst-dsp.h"
25
26#define BYT_PCM_COUNT 2
27
28static const struct snd_pcm_hardware sst_byt_pcm_hardware = {
29 .info = SNDRV_PCM_INFO_MMAP |
30 SNDRV_PCM_INFO_MMAP_VALID |
31 SNDRV_PCM_INFO_INTERLEAVED |
32 SNDRV_PCM_INFO_PAUSE |
33 SNDRV_PCM_INFO_RESUME,
34 .formats = SNDRV_PCM_FMTBIT_S16_LE |
35 SNDRV_PCM_FORMAT_S24_LE,
36 .period_bytes_min = 384,
37 .period_bytes_max = 48000,
38 .periods_min = 2,
39 .periods_max = 250,
40 .buffer_bytes_max = 96000,
41};
42
43/* private data for each PCM DSP stream */
44struct sst_byt_pcm_data {
45 struct sst_byt_stream *stream;
46 struct snd_pcm_substream *substream;
47 struct mutex mutex;
48};
49
50/* private data for the driver */
51struct sst_byt_priv_data {
52 /* runtime DSP */
53 struct sst_byt *byt;
54
55 /* DAI data */
56 struct sst_byt_pcm_data pcm[BYT_PCM_COUNT];
57};
58
59/* this may get called several times by oss emulation */
60static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
61 struct snd_pcm_hw_params *params)
62{
63 struct snd_soc_pcm_runtime *rtd = substream->private_data;
64 struct sst_byt_priv_data *pdata =
65 snd_soc_platform_get_drvdata(rtd->platform);
66 struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
67 struct sst_byt *byt = pdata->byt;
68 u32 rate, bits;
69 u8 channels;
70 int ret, playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
71
72 dev_dbg(rtd->dev, "PCM: hw_params, pcm_data %p\n", pcm_data);
73
74 ret = sst_byt_stream_type(byt, pcm_data->stream,
75 1, 1, !playback);
76 if (ret < 0) {
77 dev_err(rtd->dev, "failed to set stream format %d\n", ret);
78 return ret;
79 }
80
81 rate = params_rate(params);
82 ret = sst_byt_stream_set_rate(byt, pcm_data->stream, rate);
83 if (ret < 0) {
84 dev_err(rtd->dev, "could not set rate %d\n", rate);
85 return ret;
86 }
87
88 bits = snd_pcm_format_width(params_format(params));
89 ret = sst_byt_stream_set_bits(byt, pcm_data->stream, bits);
90 if (ret < 0) {
91 dev_err(rtd->dev, "could not set formats %d\n",
92 params_rate(params));
93 return ret;
94 }
95
96 channels = (u8)(params_channels(params) & 0xF);
97 ret = sst_byt_stream_set_channels(byt, pcm_data->stream, channels);
98 if (ret < 0) {
99 dev_err(rtd->dev, "could not set channels %d\n",
100 params_rate(params));
101 return ret;
102 }
103
104 snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
105
106 ret = sst_byt_stream_buffer(byt, pcm_data->stream,
107 substream->dma_buffer.addr,
108 params_buffer_bytes(params));
109 if (ret < 0) {
110 dev_err(rtd->dev, "PCM: failed to set DMA buffer %d\n", ret);
111 return ret;
112 }
113
114 ret = sst_byt_stream_commit(byt, pcm_data->stream);
115 if (ret < 0) {
116 dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
117 return ret;
118 }
119
120 return 0;
121}
122
123static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
124{
125 struct snd_soc_pcm_runtime *rtd = substream->private_data;
126
127 dev_dbg(rtd->dev, "PCM: hw_free\n");
128 snd_pcm_lib_free_pages(substream);
129
130 return 0;
131}
132
133static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
134{
135 struct snd_soc_pcm_runtime *rtd = substream->private_data;
136 struct sst_byt_priv_data *pdata =
137 snd_soc_platform_get_drvdata(rtd->platform);
138 struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
139 struct sst_byt *byt = pdata->byt;
140
141 dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd);
142
143 switch (cmd) {
144 case SNDRV_PCM_TRIGGER_START:
145 sst_byt_stream_start(byt, pcm_data->stream);
146 break;
147 case SNDRV_PCM_TRIGGER_RESUME:
148 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
149 sst_byt_stream_resume(byt, pcm_data->stream);
150 break;
151 case SNDRV_PCM_TRIGGER_STOP:
152 sst_byt_stream_stop(byt, pcm_data->stream);
153 break;
154 case SNDRV_PCM_TRIGGER_SUSPEND:
155 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
156 sst_byt_stream_pause(byt, pcm_data->stream);
157 break;
158 default:
159 break;
160 }
161
162 return 0;
163}
164
165static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data)
166{
167 struct sst_byt_pcm_data *pcm_data = data;
168 struct snd_pcm_substream *substream = pcm_data->substream;
169 struct snd_pcm_runtime *runtime = substream->runtime;
170 struct snd_soc_pcm_runtime *rtd = substream->private_data;
171 u32 pos;
172
173 pos = frames_to_bytes(runtime,
174 (runtime->control->appl_ptr %
175 runtime->buffer_size));
176
177 dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
178
179 snd_pcm_period_elapsed(substream);
180 return pos;
181}
182
183static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream)
184{
185 struct snd_soc_pcm_runtime *rtd = substream->private_data;
186 struct snd_pcm_runtime *runtime = substream->runtime;
187 struct sst_byt_priv_data *pdata =
188 snd_soc_platform_get_drvdata(rtd->platform);
189 struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
190 struct sst_byt *byt = pdata->byt;
191 snd_pcm_uframes_t offset;
192 int pos;
193
194 pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
195 snd_pcm_lib_buffer_bytes(substream));
196 offset = bytes_to_frames(runtime, pos);
197
198 dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n",
199 frames_to_bytes(runtime, (u32)offset));
200 return offset;
201}
202
203static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
204{
205 struct snd_soc_pcm_runtime *rtd = substream->private_data;
206 struct sst_byt_priv_data *pdata =
207 snd_soc_platform_get_drvdata(rtd->platform);
208 struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
209 struct sst_byt *byt = pdata->byt;
210
211 dev_dbg(rtd->dev, "PCM: open\n");
212
213 pcm_data = &pdata->pcm[rtd->cpu_dai->id];
214 mutex_lock(&pcm_data->mutex);
215
216 snd_soc_pcm_set_drvdata(rtd, pcm_data);
217 pcm_data->substream = substream;
218
219 snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware);
220
221 pcm_data->stream = sst_byt_stream_new(byt, rtd->cpu_dai->id + 1,
222 byt_notify_pointer, pcm_data);
223 if (pcm_data->stream == NULL) {
224 dev_err(rtd->dev, "failed to create stream\n");
225 mutex_unlock(&pcm_data->mutex);
226 return -EINVAL;
227 }
228
229 mutex_unlock(&pcm_data->mutex);
230 return 0;
231}
232
233static int sst_byt_pcm_close(struct snd_pcm_substream *substream)
234{
235 struct snd_soc_pcm_runtime *rtd = substream->private_data;
236 struct sst_byt_priv_data *pdata =
237 snd_soc_platform_get_drvdata(rtd->platform);
238 struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
239 struct sst_byt *byt = pdata->byt;
240 int ret;
241
242 dev_dbg(rtd->dev, "PCM: close\n");
243
244 mutex_lock(&pcm_data->mutex);
245 ret = sst_byt_stream_free(byt, pcm_data->stream);
246 if (ret < 0) {
247 dev_dbg(rtd->dev, "Free stream fail\n");
248 goto out;
249 }
250 pcm_data->stream = NULL;
251
252out:
253 mutex_unlock(&pcm_data->mutex);
254 return ret;
255}
256
257static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream,
258 struct vm_area_struct *vma)
259{
260 struct snd_soc_pcm_runtime *rtd = substream->private_data;
261
262 dev_dbg(rtd->dev, "PCM: mmap\n");
263 return snd_pcm_lib_default_mmap(substream, vma);
264}
265
266static struct snd_pcm_ops sst_byt_pcm_ops = {
267 .open = sst_byt_pcm_open,
268 .close = sst_byt_pcm_close,
269 .ioctl = snd_pcm_lib_ioctl,
270 .hw_params = sst_byt_pcm_hw_params,
271 .hw_free = sst_byt_pcm_hw_free,
272 .trigger = sst_byt_pcm_trigger,
273 .pointer = sst_byt_pcm_pointer,
274 .mmap = sst_byt_pcm_mmap,
275};
276
277static void sst_byt_pcm_free(struct snd_pcm *pcm)
278{
279 snd_pcm_lib_preallocate_free_for_all(pcm);
280}
281
282static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
283{
284 struct snd_pcm *pcm = rtd->pcm;
285 size_t size;
286 int ret = 0;
287
288 ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32));
289 if (ret)
290 return ret;
291
292 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
293 pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
294 size = sst_byt_pcm_hardware.buffer_bytes_max;
295 ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
296 SNDRV_DMA_TYPE_DEV,
297 rtd->card->dev,
298 size, size);
299 if (ret) {
300 dev_err(rtd->dev, "dma buffer allocation failed %d\n",
301 ret);
302 return ret;
303 }
304 }
305
306 return ret;
307}
308
309static struct snd_soc_dai_driver byt_dais[] = {
310 {
311 .name = "Front-cpu-dai",
312 .playback = {
313 .stream_name = "System Playback",
314 .channels_min = 2,
315 .channels_max = 2,
316 .rates = SNDRV_PCM_RATE_48000,
317 .formats = SNDRV_PCM_FMTBIT_S24_3LE |
318 SNDRV_PCM_FMTBIT_S16_LE,
319 },
320 },
321 {
322 .name = "Mic1-cpu-dai",
323 .capture = {
324 .stream_name = "Analog Capture",
325 .channels_min = 2,
326 .channels_max = 2,
327 .rates = SNDRV_PCM_RATE_48000,
328 .formats = SNDRV_PCM_FMTBIT_S16_LE,
329 },
330 },
331};
332
333static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
334{
335 struct sst_pdata *plat_data = dev_get_platdata(platform->dev);
336 struct sst_byt_priv_data *priv_data;
337 int i;
338
339 if (!plat_data)
340 return -ENODEV;
341
342 priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data),
343 GFP_KERNEL);
344 priv_data->byt = plat_data->dsp;
345 snd_soc_platform_set_drvdata(platform, priv_data);
346
347 for (i = 0; i < ARRAY_SIZE(byt_dais); i++)
348 mutex_init(&priv_data->pcm[i].mutex);
349
350 return 0;
351}
352
353static int sst_byt_pcm_remove(struct snd_soc_platform *platform)
354{
355 return 0;
356}
357
358static struct snd_soc_platform_driver byt_soc_platform = {
359 .probe = sst_byt_pcm_probe,
360 .remove = sst_byt_pcm_remove,
361 .ops = &sst_byt_pcm_ops,
362 .pcm_new = sst_byt_pcm_new,
363 .pcm_free = sst_byt_pcm_free,
364};
365
366static const struct snd_soc_component_driver byt_dai_component = {
367 .name = "byt-dai",
368};
369
370static int sst_byt_pcm_dev_probe(struct platform_device *pdev)
371{
372 struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
373 int ret;
374
375 ret = sst_byt_dsp_init(&pdev->dev, sst_pdata);
376 if (ret < 0)
377 return -ENODEV;
378
379 ret = snd_soc_register_platform(&pdev->dev, &byt_soc_platform);
380 if (ret < 0)
381 goto err_plat;
382
383 ret = snd_soc_register_component(&pdev->dev, &byt_dai_component,
384 byt_dais, ARRAY_SIZE(byt_dais));
385 if (ret < 0)
386 goto err_comp;
387
388 return 0;
389
390err_comp:
391 snd_soc_unregister_platform(&pdev->dev);
392err_plat:
393 sst_byt_dsp_free(&pdev->dev, sst_pdata);
394 return ret;
395}
396
397static int sst_byt_pcm_dev_remove(struct platform_device *pdev)
398{
399 struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
400
401 snd_soc_unregister_platform(&pdev->dev);
402 snd_soc_unregister_component(&pdev->dev);
403 sst_byt_dsp_free(&pdev->dev, sst_pdata);
404
405 return 0;
406}
407
408static struct platform_driver sst_byt_pcm_driver = {
409 .driver = {
410 .name = "baytrail-pcm-audio",
411 .owner = THIS_MODULE,
412 },
413
414 .probe = sst_byt_pcm_dev_probe,
415 .remove = sst_byt_pcm_dev_remove,
416};
417module_platform_driver(sst_byt_pcm_driver);
418
419MODULE_AUTHOR("Jarkko Nikula");
420MODULE_DESCRIPTION("Baytrail PCM");
421MODULE_LICENSE("GPL v2");
422MODULE_ALIAS("platform:baytrail-pcm-audio");