162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license.  When using or
462306a36Sopenharmony_ci// redistributing this file, you may do so under either license.
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright(c) 2019 Intel Corporation. All rights reserved.
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci// Authors: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/* Generic SOF IPC code */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/export.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <sound/pcm.h>
1862306a36Sopenharmony_ci#include <sound/sof/stream.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "ops.h"
2162306a36Sopenharmony_ci#include "sof-priv.h"
2262306a36Sopenharmony_ci#include "sof-audio.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct sof_stream {
2562306a36Sopenharmony_ci	size_t posn_offset;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* Mailbox-based Generic IPC implementation */
2962306a36Sopenharmony_ciint sof_ipc_msg_data(struct snd_sof_dev *sdev,
3062306a36Sopenharmony_ci		     struct snd_sof_pcm_stream *sps,
3162306a36Sopenharmony_ci		     void *p, size_t sz)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	if (!sps || !sdev->stream_box.size) {
3462306a36Sopenharmony_ci		snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
3562306a36Sopenharmony_ci	} else {
3662306a36Sopenharmony_ci		size_t posn_offset;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci		if (sps->substream) {
3962306a36Sopenharmony_ci			struct sof_stream *stream = sps->substream->runtime->private_data;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci			/* The stream might already be closed */
4262306a36Sopenharmony_ci			if (!stream)
4362306a36Sopenharmony_ci				return -ESTRPIPE;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci			posn_offset = stream->posn_offset;
4662306a36Sopenharmony_ci		} else {
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci			struct sof_compr_stream *sstream = sps->cstream->runtime->private_data;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci			if (!sstream)
5162306a36Sopenharmony_ci				return -ESTRPIPE;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci			posn_offset = sstream->posn_offset;
5462306a36Sopenharmony_ci		}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		snd_sof_dsp_mailbox_read(sdev, posn_offset, p, sz);
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return 0;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ciEXPORT_SYMBOL(sof_ipc_msg_data);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciint sof_set_stream_data_offset(struct snd_sof_dev *sdev,
6462306a36Sopenharmony_ci			       struct snd_sof_pcm_stream *sps,
6562306a36Sopenharmony_ci			       size_t posn_offset)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	/* check if offset is overflow or it is not aligned */
6862306a36Sopenharmony_ci	if (posn_offset > sdev->stream_box.size ||
6962306a36Sopenharmony_ci	    posn_offset % sizeof(struct sof_ipc_stream_posn) != 0)
7062306a36Sopenharmony_ci		return -EINVAL;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	posn_offset += sdev->stream_box.offset;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (sps->substream) {
7562306a36Sopenharmony_ci		struct sof_stream *stream = sps->substream->runtime->private_data;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		stream->posn_offset = posn_offset;
7862306a36Sopenharmony_ci		dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
7962306a36Sopenharmony_ci			sps->substream->stream, posn_offset);
8062306a36Sopenharmony_ci	} else if (sps->cstream) {
8162306a36Sopenharmony_ci		struct sof_compr_stream *sstream = sps->cstream->runtime->private_data;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		sstream->posn_offset = posn_offset;
8462306a36Sopenharmony_ci		dev_dbg(sdev->dev, "compr: stream dir %d, posn mailbox offset is %zu",
8562306a36Sopenharmony_ci			sps->cstream->direction, posn_offset);
8662306a36Sopenharmony_ci	} else {
8762306a36Sopenharmony_ci		dev_err(sdev->dev, "No stream opened");
8862306a36Sopenharmony_ci		return -EINVAL;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ciEXPORT_SYMBOL(sof_set_stream_data_offset);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ciint sof_stream_pcm_open(struct snd_sof_dev *sdev,
9662306a36Sopenharmony_ci			struct snd_pcm_substream *substream)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct sof_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (!stream)
10162306a36Sopenharmony_ci		return -ENOMEM;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* binding pcm substream to hda stream */
10462306a36Sopenharmony_ci	substream->runtime->private_data = stream;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* align to DMA minimum transfer size */
10762306a36Sopenharmony_ci	snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* avoid circular buffer wrap in middle of period */
11062306a36Sopenharmony_ci	snd_pcm_hw_constraint_integer(substream->runtime,
11162306a36Sopenharmony_ci				      SNDRV_PCM_HW_PARAM_PERIODS);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ciEXPORT_SYMBOL(sof_stream_pcm_open);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ciint sof_stream_pcm_close(struct snd_sof_dev *sdev,
11862306a36Sopenharmony_ci			 struct snd_pcm_substream *substream)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct sof_stream *stream = substream->runtime->private_data;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	substream->runtime->private_data = NULL;
12362306a36Sopenharmony_ci	kfree(stream);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return 0;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ciEXPORT_SYMBOL(sof_stream_pcm_close);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
130