diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index c49b59630101fa..167574c6ef7b83 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -418,6 +418,8 @@ int snd_soc_component_compr_pointer(struct snd_compr_stream *cstream, struct snd_compr_tstamp64 *tstamp); int snd_soc_component_compr_copy(struct snd_compr_stream *cstream, char __user *buf, size_t count); +int snd_soc_component_compr_mmap(struct snd_compr_stream *cstream, + struct vm_area_struct *vma); int snd_soc_component_compr_set_metadata(struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata); int snd_soc_component_compr_get_metadata(struct snd_compr_stream *cstream, diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index b610683fd8dbe4..157f57f685a0e4 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -242,6 +242,7 @@ struct snd_compr_task_status { #define SNDRV_COMPRESS_AVAIL _IOR('C', 0x21, struct snd_compr_avail) #define SNDRV_COMPRESS_TSTAMP64 _IOR('C', 0x22, struct snd_compr_tstamp64) #define SNDRV_COMPRESS_AVAIL64 _IOR('C', 0x23, struct snd_compr_avail64) +#define SNDRV_COMPRESS_ACK _IOW('C', 0x24, __u64) #define SNDRV_COMPRESS_PAUSE _IO('C', 0x30) #define SNDRV_COMPRESS_RESUME _IO('C', 0x31) #define SNDRV_COMPRESS_START _IO('C', 0x32) diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index ad68132b36f637..5703c602fd36c3 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -33,6 +33,7 @@ #include #include #include +#include /* struct snd_compr_codec_caps overflows the ioctl bit size for some * architectures, so we need to disable the relevant ioctls. @@ -310,6 +311,48 @@ static int snd_compr_ioctl_avail(struct snd_compr_stream *stream, return 0; } +static int snd_compr_ioctl_ack(struct snd_compr_stream *stream, unsigned long arg) +{ + __u64 bytes; + size_t avail; + int ret = 0; + + if (copy_from_user(&bytes, (void __user *)arg, sizeof(bytes))) + return -EFAULT; + + if (bytes == 0) + return 0; + + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_RUNNING: + break; + default: + return -EBADFD; + } + + avail = snd_compr_get_avail(stream); + if (bytes > avail) + return -EINVAL; + + if (stream->ops->ack) { + ret = stream->ops->ack(stream, bytes); + if (ret < 0) + return ret; + } + + stream->runtime->total_bytes_available += bytes; + + if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) { + stream->runtime->state = SNDRV_PCM_STATE_PREPARED; + pr_debug("stream prepared, Houston we are good to go\n"); + } + + wake_up(&stream->runtime->sleep); + return 0; +} + static int snd_compr_write_data(struct snd_compr_stream *stream, const char __user *buf, size_t count) { @@ -439,6 +482,33 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf, static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma) { + struct snd_compr_file *data = f->private_data; + struct snd_compr_stream *stream; + struct snd_compr_runtime *runtime; + + if (snd_BUG_ON(!data)) + return -EINVAL; + + stream = &data->stream; + runtime = stream->runtime; + + if (!runtime) + return -ENODATA; + + if (stream->direction == SND_COMPRESS_PLAYBACK) { + if (!(vma->vm_flags & (VM_WRITE | VM_READ))) + return -EINVAL; + } else { + if (!(vma->vm_flags & VM_READ)) + return -EINVAL; + } + + if (stream->ops->mmap) + return stream->ops->mmap(stream, vma); + + if (runtime->dma_buffer_p) + return snd_dma_buffer_mmap(runtime->dma_buffer_p, vma); + return -ENXIO; } @@ -1383,6 +1453,8 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) return snd_compr_ioctl_avail(stream, arg, true); case SNDRV_COMPRESS_AVAIL64: return snd_compr_ioctl_avail(stream, arg, false); + case SNDRV_COMPRESS_ACK: + return snd_compr_ioctl_ack(stream, arg); case SNDRV_COMPRESS_PAUSE: return snd_compr_pause(stream); case SNDRV_COMPRESS_RESUME: diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 463ea227cac819..99efac1477cd82 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -644,6 +644,30 @@ int snd_soc_component_compr_copy(struct snd_compr_stream *cstream, } EXPORT_SYMBOL_GPL(snd_soc_component_compr_copy); +int snd_soc_component_compr_mmap(struct snd_compr_stream *cstream, + struct vm_area_struct *vma) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component; + int i, ret = -ENXIO; + + snd_soc_dpcm_mutex_lock(rtd); + + for_each_rtd_components(rtd, i, component) { + if (component->driver->compress_ops && + component->driver->compress_ops->mmap) { + ret = component->driver->compress_ops->mmap( + component, cstream, vma); + break; + } + } + + snd_soc_dpcm_mutex_unlock(rtd); + + return soc_component_ret(component, ret); +} +EXPORT_SYMBOL_GPL(snd_soc_component_compr_mmap); + int snd_soc_component_compr_set_metadata(struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata) { diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 9fac3cf645df27..c6282ab6e2c336 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -657,6 +657,12 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, return snd_soc_component_compr_get_metadata(cstream, metadata); } +static int soc_compr_mmap(struct snd_compr_stream *cstream, + struct vm_area_struct *vma) +{ + return snd_soc_component_compr_mmap(cstream, vma); +} + /* ASoC Compress operations */ static struct snd_compr_ops soc_compr_ops = { .open = soc_compr_open, @@ -668,6 +674,7 @@ static struct snd_compr_ops soc_compr_ops = { .trigger = soc_compr_trigger, .pointer = soc_compr_pointer, .ack = soc_compr_ack, + .mmap = soc_compr_mmap, .get_caps = snd_soc_component_compr_get_caps, .get_codec_caps = snd_soc_component_compr_get_codec_caps, }; @@ -683,6 +690,7 @@ static struct snd_compr_ops soc_compr_dyn_ops = { .trigger = soc_compr_trigger_fe, .pointer = soc_compr_pointer, .ack = soc_compr_ack, + .mmap = soc_compr_mmap, .get_caps = snd_soc_component_compr_get_caps, .get_codec_caps = snd_soc_component_compr_get_codec_caps, }; diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index ffa987143af4aa..948c77d7d4594f 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "sof-audio.h" #include "sof-priv.h" #include "sof-utils.h" @@ -14,8 +15,8 @@ #include "ipc4-topology.h" #include "ipc4-fw-reg.h" -/* Maximum processing size of the decoder/encoder is 2048 bytes */ -#define SOF_IPC4_COMPR_MAX_PROCESSING_SIZE (SZ_2K) +/* Minimum fragment size aligned to Intel HDA/ACE DMA burst hardware (128 bytes) */ +#define SOF_IPC4_COMPR_MIN_FRAGMENT_SIZE (128) #define SOF_IPC4_COMPR_MIN_FRAGMENTS 3 #define SOF_IPC4_COMPR_MAX_FRAGMENT_SIZE (SZ_128K) @@ -58,10 +59,13 @@ static u32 sof_ipc4_compr_calc_min_fragment_size(struct snd_sof_pcm_stream *sps) host_buffer_estimate = snd_pcm_format_size(SNDRV_PCM_FORMAT_S32_LE, 2 * 48); host_buffer_estimate *= sps->dsp_max_burst_size_in_ms; /* - * The minimum fragment size must not be smaller than the processing size - * or in case of deep buffer on host side, the host DMA buffer size. + * The minimum fragment size must align with HDA DMA burst boundary (128 bytes) + * or cover the deep buffer host DMA buffer size if configured. */ - return max(SOF_IPC4_COMPR_MAX_PROCESSING_SIZE, host_buffer_estimate); + if (host_buffer_estimate) + return max_t(u32, SOF_IPC4_COMPR_MIN_FRAGMENT_SIZE, host_buffer_estimate); + + return SOF_IPC4_COMPR_MIN_FRAGMENT_SIZE; } static int sof_ipc4_compr_open(struct snd_soc_component *component, @@ -108,16 +112,18 @@ static int sof_ipc4_compr_stream_free(struct snd_sof_dev *sdev, int err = 0; if (spcm->prepared[dir]) { - if (spcm->pending_stop[dir]) + if (spcm->pending_stop[dir] && pcm_ops && pcm_ops->trigger) pcm_ops->trigger(sdev->component, NULL, spcm, SNDRV_PCM_TRIGGER_STOP, dir); snd_sof_compr_platform_trigger(sdev, cstream, SNDRV_PCM_TRIGGER_STOP); - err = pcm_ops->hw_free(sdev->component, NULL, spcm, dir); - if (err < 0) - spcm_err(spcm, dir, "pcm_ops->hw_free failed %d\n", err); + if (pcm_ops && pcm_ops->hw_free) { + err = pcm_ops->hw_free(sdev->component, NULL, spcm, dir); + if (err < 0) + spcm_err(spcm, dir, "pcm_ops->hw_free failed %d\n", err); + } } spcm->prepared[dir] = false; @@ -570,10 +576,12 @@ static int sof_ipc4_compr_trigger(struct snd_soc_component *component, spcm_dbg(spcm, dir, "Entry: trigger (cmd: %d)\n", cmd); - ret = pcm_ops->trigger(component, NULL, spcm, cmd, dir); - if (ret < 0) { - spcm_err(spcm, dir, "pcm_ops->trigger failed for cmd %d\n", cmd); - return ret; + if (pcm_ops && pcm_ops->trigger) { + ret = pcm_ops->trigger(component, NULL, spcm, cmd, dir); + if (ret < 0) { + spcm_err(spcm, dir, "pcm_ops->trigger failed for cmd %d\n", cmd); + return ret; + } } if (!ret && trigger_platform) { @@ -764,6 +772,28 @@ void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message) snd_compr_drain_notify(spcm->stream[dir].cstream); } +static int sof_ipc4_compr_mmap(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct vm_area_struct *vma) +{ + struct snd_compr_runtime *runtime = stream->runtime; + + if (!runtime || !runtime->dma_area) + return -ENXIO; + + if (runtime->dma_buffer_p) + return snd_dma_buffer_mmap(runtime->dma_buffer_p, vma); + + return snd_dma_buffer_mmap(&stream->dma_buffer, vma); +} + +static int sof_ipc4_compr_ack(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + size_t bytes) +{ + return 0; +} + const struct snd_compress_ops sof_ipc4_compressed_ops = { .open = sof_ipc4_compr_open, .free = sof_ipc4_compr_free, @@ -772,5 +802,7 @@ const struct snd_compress_ops sof_ipc4_compressed_ops = { .get_params = sof_ipc4_compr_get_params, .trigger = sof_ipc4_compr_trigger, .pointer = sof_ipc4_compr_pointer, + .mmap = sof_ipc4_compr_mmap, + .ack = sof_ipc4_compr_ack, .copy = sof_ipc4_compr_copy, };