~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/intel/baytrail/sst-baytrail-pcm.c

Version: ~ [ linux-5.10-rc1 ] ~ [ linux-5.9.1 ] ~ [ linux-5.8.16 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.72 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.152 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.202 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.240 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.240 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 "../common/sst-dsp-priv.h"
 24 #include "../common/sst-dsp.h"
 25 
 26 #define BYT_PCM_COUNT           2
 27 
 28 static 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_FMTBIT_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 */
 44 struct sst_byt_pcm_data {
 45         struct sst_byt_stream *stream;
 46         struct snd_pcm_substream *substream;
 47         struct mutex mutex;
 48 
 49         /* latest DSP DMA hw pointer */
 50         u32 hw_ptr;
 51 
 52         struct work_struct work;
 53 };
 54 
 55 /* private data for the driver */
 56 struct sst_byt_priv_data {
 57         /* runtime DSP */
 58         struct sst_byt *byt;
 59 
 60         /* DAI data */
 61         struct sst_byt_pcm_data pcm[BYT_PCM_COUNT];
 62 
 63         /* flag indicating is stream context restore needed after suspend */
 64         bool restore_stream;
 65 };
 66 
 67 /* this may get called several times by oss emulation */
 68 static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
 69                                  struct snd_pcm_hw_params *params)
 70 {
 71         struct snd_soc_pcm_runtime *rtd = substream->private_data;
 72         struct sst_byt_priv_data *pdata =
 73                 snd_soc_platform_get_drvdata(rtd->platform);
 74         struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
 75         struct sst_byt *byt = pdata->byt;
 76         u32 rate, bits;
 77         u8 channels;
 78         int ret, playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
 79 
 80         dev_dbg(rtd->dev, "PCM: hw_params, pcm_data %p\n", pcm_data);
 81 
 82         ret = sst_byt_stream_type(byt, pcm_data->stream,
 83                                   1, 1, !playback);
 84         if (ret < 0) {
 85                 dev_err(rtd->dev, "failed to set stream format %d\n", ret);
 86                 return ret;
 87         }
 88 
 89         rate = params_rate(params);
 90         ret = sst_byt_stream_set_rate(byt, pcm_data->stream, rate);
 91         if (ret < 0) {
 92                 dev_err(rtd->dev, "could not set rate %d\n", rate);
 93                 return ret;
 94         }
 95 
 96         bits = snd_pcm_format_width(params_format(params));
 97         ret = sst_byt_stream_set_bits(byt, pcm_data->stream, bits);
 98         if (ret < 0) {
 99                 dev_err(rtd->dev, "could not set formats %d\n",
100                         params_rate(params));
101                 return ret;
102         }
103 
104         channels = (u8)(params_channels(params) & 0xF);
105         ret = sst_byt_stream_set_channels(byt, pcm_data->stream, channels);
106         if (ret < 0) {
107                 dev_err(rtd->dev, "could not set channels %d\n",
108                         params_rate(params));
109                 return ret;
110         }
111 
112         snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
113 
114         ret = sst_byt_stream_buffer(byt, pcm_data->stream,
115                                     substream->dma_buffer.addr,
116                                     params_buffer_bytes(params));
117         if (ret < 0) {
118                 dev_err(rtd->dev, "PCM: failed to set DMA buffer %d\n", ret);
119                 return ret;
120         }
121 
122         ret = sst_byt_stream_commit(byt, pcm_data->stream);
123         if (ret < 0) {
124                 dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
125                 return ret;
126         }
127 
128         return 0;
129 }
130 
131 static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
132 {
133         struct snd_soc_pcm_runtime *rtd = substream->private_data;
134 
135         dev_dbg(rtd->dev, "PCM: hw_free\n");
136         snd_pcm_lib_free_pages(substream);
137 
138         return 0;
139 }
140 
141 static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream)
142 {
143         struct snd_soc_pcm_runtime *rtd = substream->private_data;
144         struct sst_byt_priv_data *pdata =
145                 snd_soc_platform_get_drvdata(rtd->platform);
146         struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
147         struct sst_byt *byt = pdata->byt;
148         int ret;
149 
150         /* commit stream using existing stream params */
151         ret = sst_byt_stream_commit(byt, pcm_data->stream);
152         if (ret < 0) {
153                 dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
154                 return ret;
155         }
156 
157         sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr);
158 
159         dev_dbg(rtd->dev, "stream context restored at offset %d\n",
160                 pcm_data->hw_ptr);
161 
162         return 0;
163 }
164 
165 static void sst_byt_pcm_work(struct work_struct *work)
166 {
167         struct sst_byt_pcm_data *pcm_data =
168                 container_of(work, struct sst_byt_pcm_data, work);
169 
170         if (snd_pcm_running(pcm_data->substream))
171                 sst_byt_pcm_restore_stream_context(pcm_data->substream);
172 }
173 
174 static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
175 {
176         struct snd_soc_pcm_runtime *rtd = substream->private_data;
177         struct sst_byt_priv_data *pdata =
178                 snd_soc_platform_get_drvdata(rtd->platform);
179         struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
180         struct sst_byt *byt = pdata->byt;
181 
182         dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd);
183 
184         switch (cmd) {
185         case SNDRV_PCM_TRIGGER_START:
186                 pcm_data->hw_ptr = 0;
187                 sst_byt_stream_start(byt, pcm_data->stream, 0);
188                 break;
189         case SNDRV_PCM_TRIGGER_RESUME:
190                 if (pdata->restore_stream == true)
191                         schedule_work(&pcm_data->work);
192                 else
193                         sst_byt_stream_resume(byt, pcm_data->stream);
194                 break;
195         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
196                 sst_byt_stream_resume(byt, pcm_data->stream);
197                 break;
198         case SNDRV_PCM_TRIGGER_STOP:
199                 sst_byt_stream_stop(byt, pcm_data->stream);
200                 break;
201         case SNDRV_PCM_TRIGGER_SUSPEND:
202                 pdata->restore_stream = false;
203         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
204                 sst_byt_stream_pause(byt, pcm_data->stream);
205                 break;
206         default:
207                 break;
208         }
209 
210         return 0;
211 }
212 
213 static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data)
214 {
215         struct sst_byt_pcm_data *pcm_data = data;
216         struct snd_pcm_substream *substream = pcm_data->substream;
217         struct snd_pcm_runtime *runtime = substream->runtime;
218         struct snd_soc_pcm_runtime *rtd = substream->private_data;
219         struct sst_byt_priv_data *pdata =
220                 snd_soc_platform_get_drvdata(rtd->platform);
221         struct sst_byt *byt = pdata->byt;
222         u32 pos, hw_pos;
223 
224         hw_pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
225                                           snd_pcm_lib_buffer_bytes(substream));
226         pcm_data->hw_ptr = hw_pos;
227         pos = frames_to_bytes(runtime,
228                               (runtime->control->appl_ptr %
229                                runtime->buffer_size));
230 
231         dev_dbg(rtd->dev, "PCM: App/DMA pointer %u/%u bytes\n", pos, hw_pos);
232 
233         snd_pcm_period_elapsed(substream);
234         return pos;
235 }
236 
237 static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream)
238 {
239         struct snd_soc_pcm_runtime *rtd = substream->private_data;
240         struct snd_pcm_runtime *runtime = substream->runtime;
241         struct sst_byt_priv_data *pdata =
242                 snd_soc_platform_get_drvdata(rtd->platform);
243         struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
244 
245         dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr);
246 
247         return bytes_to_frames(runtime, pcm_data->hw_ptr);
248 }
249 
250 static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
251 {
252         struct snd_soc_pcm_runtime *rtd = substream->private_data;
253         struct sst_byt_priv_data *pdata =
254                 snd_soc_platform_get_drvdata(rtd->platform);
255         struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
256         struct sst_byt *byt = pdata->byt;
257 
258         dev_dbg(rtd->dev, "PCM: open\n");
259 
260         mutex_lock(&pcm_data->mutex);
261 
262         pcm_data->substream = substream;
263 
264         snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware);
265 
266         pcm_data->stream = sst_byt_stream_new(byt, substream->stream + 1,
267                                               byt_notify_pointer, pcm_data);
268         if (pcm_data->stream == NULL) {
269                 dev_err(rtd->dev, "failed to create stream\n");
270                 mutex_unlock(&pcm_data->mutex);
271                 return -EINVAL;
272         }
273 
274         mutex_unlock(&pcm_data->mutex);
275         return 0;
276 }
277 
278 static int sst_byt_pcm_close(struct snd_pcm_substream *substream)
279 {
280         struct snd_soc_pcm_runtime *rtd = substream->private_data;
281         struct sst_byt_priv_data *pdata =
282                 snd_soc_platform_get_drvdata(rtd->platform);
283         struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
284         struct sst_byt *byt = pdata->byt;
285         int ret;
286 
287         dev_dbg(rtd->dev, "PCM: close\n");
288 
289         cancel_work_sync(&pcm_data->work);
290         mutex_lock(&pcm_data->mutex);
291         ret = sst_byt_stream_free(byt, pcm_data->stream);
292         if (ret < 0) {
293                 dev_dbg(rtd->dev, "Free stream fail\n");
294                 goto out;
295         }
296         pcm_data->stream = NULL;
297 
298 out:
299         mutex_unlock(&pcm_data->mutex);
300         return ret;
301 }
302 
303 static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream,
304                             struct vm_area_struct *vma)
305 {
306         struct snd_soc_pcm_runtime *rtd = substream->private_data;
307 
308         dev_dbg(rtd->dev, "PCM: mmap\n");
309         return snd_pcm_lib_default_mmap(substream, vma);
310 }
311 
312 static const struct snd_pcm_ops sst_byt_pcm_ops = {
313         .open           = sst_byt_pcm_open,
314         .close          = sst_byt_pcm_close,
315         .ioctl          = snd_pcm_lib_ioctl,
316         .hw_params      = sst_byt_pcm_hw_params,
317         .hw_free        = sst_byt_pcm_hw_free,
318         .trigger        = sst_byt_pcm_trigger,
319         .pointer        = sst_byt_pcm_pointer,
320         .mmap           = sst_byt_pcm_mmap,
321 };
322 
323 static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
324 {
325         struct snd_pcm *pcm = rtd->pcm;
326         size_t size;
327         struct snd_soc_platform *platform = rtd->platform;
328         struct sst_pdata *pdata = dev_get_platdata(platform->dev);
329         int ret = 0;
330 
331         if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
332             pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
333                 size = sst_byt_pcm_hardware.buffer_bytes_max;
334                 ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
335                                                             SNDRV_DMA_TYPE_DEV,
336                                                             pdata->dma_dev,
337                                                             size, size);
338                 if (ret) {
339                         dev_err(rtd->dev, "dma buffer allocation failed %d\n",
340                                 ret);
341                         return ret;
342                 }
343         }
344 
345         return ret;
346 }
347 
348 static struct snd_soc_dai_driver byt_dais[] = {
349         {
350                 .name  = "Baytrail PCM",
351                 .playback = {
352                         .stream_name = "System Playback",
353                         .channels_min = 2,
354                         .channels_max = 2,
355                         .rates = SNDRV_PCM_RATE_48000,
356                         .formats = SNDRV_PCM_FMTBIT_S24_3LE |
357                                    SNDRV_PCM_FMTBIT_S16_LE,
358                 },
359                 .capture = {
360                         .stream_name = "Analog Capture",
361                         .channels_min = 2,
362                         .channels_max = 2,
363                         .rates = SNDRV_PCM_RATE_48000,
364                         .formats = SNDRV_PCM_FMTBIT_S16_LE,
365                 },
366         },
367 };
368 
369 static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
370 {
371         struct sst_pdata *plat_data = dev_get_platdata(platform->dev);
372         struct sst_byt_priv_data *priv_data;
373         int i;
374 
375         if (!plat_data)
376                 return -ENODEV;
377 
378         priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data),
379                                  GFP_KERNEL);
380         if (!priv_data)
381                 return -ENOMEM;
382         priv_data->byt = plat_data->dsp;
383         snd_soc_platform_set_drvdata(platform, priv_data);
384 
385         for (i = 0; i < BYT_PCM_COUNT; i++) {
386                 mutex_init(&priv_data->pcm[i].mutex);
387                 INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work);
388         }
389 
390         return 0;
391 }
392 
393 static int sst_byt_pcm_remove(struct snd_soc_platform *platform)
394 {
395         return 0;
396 }
397 
398 static const struct snd_soc_platform_driver byt_soc_platform = {
399         .probe          = sst_byt_pcm_probe,
400         .remove         = sst_byt_pcm_remove,
401         .ops            = &sst_byt_pcm_ops,
402         .pcm_new        = sst_byt_pcm_new,
403 };
404 
405 static const struct snd_soc_component_driver byt_dai_component = {
406         .name           = "byt-dai",
407 };
408 
409 #ifdef CONFIG_PM
410 static int sst_byt_pcm_dev_suspend_late(struct device *dev)
411 {
412         struct sst_pdata *sst_pdata = dev_get_platdata(dev);
413         struct sst_byt_priv_data *priv_data = dev_get_drvdata(dev);
414         int ret;
415 
416         dev_dbg(dev, "suspending late\n");
417 
418         ret = sst_byt_dsp_suspend_late(dev, sst_pdata);
419         if (ret < 0) {
420                 dev_err(dev, "failed to suspend %d\n", ret);
421                 return ret;
422         }
423 
424         priv_data->restore_stream = true;
425 
426         return ret;
427 }
428 
429 static int sst_byt_pcm_dev_resume_early(struct device *dev)
430 {
431         struct sst_pdata *sst_pdata = dev_get_platdata(dev);
432         int ret;
433 
434         dev_dbg(dev, "resume early\n");
435 
436         /* load fw and boot DSP */
437         ret = sst_byt_dsp_boot(dev, sst_pdata);
438         if (ret)
439                 return ret;
440 
441         /* wait for FW to finish booting */
442         return sst_byt_dsp_wait_for_ready(dev, sst_pdata);
443 }
444 
445 static const struct dev_pm_ops sst_byt_pm_ops = {
446         .suspend_late = sst_byt_pcm_dev_suspend_late,
447         .resume_early = sst_byt_pcm_dev_resume_early,
448 };
449 
450 #define SST_BYT_PM_OPS  (&sst_byt_pm_ops)
451 #else
452 #define SST_BYT_PM_OPS  NULL
453 #endif
454 
455 static int sst_byt_pcm_dev_probe(struct platform_device *pdev)
456 {
457         struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
458         int ret;
459 
460         ret = sst_byt_dsp_init(&pdev->dev, sst_pdata);
461         if (ret < 0)
462                 return -ENODEV;
463 
464         ret = snd_soc_register_platform(&pdev->dev, &byt_soc_platform);
465         if (ret < 0)
466                 goto err_plat;
467 
468         ret = snd_soc_register_component(&pdev->dev, &byt_dai_component,
469                                          byt_dais, ARRAY_SIZE(byt_dais));
470         if (ret < 0)
471                 goto err_comp;
472 
473         return 0;
474 
475 err_comp:
476         snd_soc_unregister_platform(&pdev->dev);
477 err_plat:
478         sst_byt_dsp_free(&pdev->dev, sst_pdata);
479         return ret;
480 }
481 
482 static int sst_byt_pcm_dev_remove(struct platform_device *pdev)
483 {
484         struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
485 
486         snd_soc_unregister_platform(&pdev->dev);
487         snd_soc_unregister_component(&pdev->dev);
488         sst_byt_dsp_free(&pdev->dev, sst_pdata);
489 
490         return 0;
491 }
492 
493 static struct platform_driver sst_byt_pcm_driver = {
494         .driver = {
495                 .name = "baytrail-pcm-audio",
496                 .pm = SST_BYT_PM_OPS,
497         },
498 
499         .probe = sst_byt_pcm_dev_probe,
500         .remove = sst_byt_pcm_dev_remove,
501 };
502 module_platform_driver(sst_byt_pcm_driver);
503 
504 MODULE_AUTHOR("Jarkko Nikula");
505 MODULE_DESCRIPTION("Baytrail PCM");
506 MODULE_LICENSE("GPL v2");
507 MODULE_ALIAS("platform:baytrail-pcm-audio");
508 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp