1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 // 3 // This file is provided under a dual BSD/GPLv2 license. When using or 4 // redistributing this file, you may do so under either license. 5 // 6 // Copyright(c) 2019 Intel Corporation. All rights reserved. 7 // 8 // Authors: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com> 9 10 /* Generic SOF IPC code */ 11 12 #include <linux/device.h> 13 #include <linux/export.h> 14 #include <linux/module.h> 15 #include <linux/types.h> 16 17 #include <sound/pcm.h> 18 #include <sound/sof/stream.h> 19 20 #include "ops.h" 21 #include "sof-priv.h" 22 23 struct sof_stream { 24 size_t posn_offset; 25 }; 26 27 /* Mailbox-based Generic IPC implementation */ 28 int sof_ipc_msg_data(struct snd_sof_dev *sdev, 29 struct snd_pcm_substream *substream, 30 void *p, size_t sz) 31 { 32 if (!substream || !sdev->stream_box.size) { 33 snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); 34 } else { 35 struct sof_stream *stream = substream->runtime->private_data; 36 37 /* The stream might already be closed */ 38 if (!stream) 39 return -ESTRPIPE; 40 41 snd_sof_dsp_mailbox_read(sdev, stream->posn_offset, p, sz); 42 } 43 44 return 0; 45 } 46 EXPORT_SYMBOL(sof_ipc_msg_data); 47 48 int sof_ipc_pcm_params(struct snd_sof_dev *sdev, 49 struct snd_pcm_substream *substream, 50 const struct sof_ipc_pcm_params_reply *reply) 51 { 52 struct sof_stream *stream = substream->runtime->private_data; 53 size_t posn_offset = reply->posn_offset; 54 55 /* check if offset is overflow or it is not aligned */ 56 if (posn_offset > sdev->stream_box.size || 57 posn_offset % sizeof(struct sof_ipc_stream_posn) != 0) 58 return -EINVAL; 59 60 stream->posn_offset = sdev->stream_box.offset + posn_offset; 61 62 dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu", 63 substream->stream, stream->posn_offset); 64 65 return 0; 66 } 67 EXPORT_SYMBOL(sof_ipc_pcm_params); 68 69 int sof_stream_pcm_open(struct snd_sof_dev *sdev, 70 struct snd_pcm_substream *substream) 71 { 72 struct sof_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL); 73 74 if (!stream) 75 return -ENOMEM; 76 77 /* binding pcm substream to hda stream */ 78 substream->runtime->private_data = stream; 79 80 /* align to DMA minimum transfer size */ 81 snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); 82 83 /* avoid circular buffer wrap in middle of period */ 84 snd_pcm_hw_constraint_integer(substream->runtime, 85 SNDRV_PCM_HW_PARAM_PERIODS); 86 87 return 0; 88 } 89 EXPORT_SYMBOL(sof_stream_pcm_open); 90 91 int sof_stream_pcm_close(struct snd_sof_dev *sdev, 92 struct snd_pcm_substream *substream) 93 { 94 struct sof_stream *stream = substream->runtime->private_data; 95 96 substream->runtime->private_data = NULL; 97 kfree(stream); 98 99 return 0; 100 } 101 EXPORT_SYMBOL(sof_stream_pcm_close); 102 103 MODULE_LICENSE("Dual BSD/GPL"); 104