18e8e69d6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b97169daSJie Yang /*
3b97169daSJie Yang  *  sst_drv_interface.c - Intel SST Driver for audio engine
4b97169daSJie Yang  *
5b97169daSJie Yang  *  Copyright (C) 2008-14 Intel Corp
6b97169daSJie Yang  *  Authors:	Vinod Koul <vinod.koul@intel.com>
7b97169daSJie Yang  *		Harsha Priya <priya.harsha@intel.com>
8b97169daSJie Yang  *		Dharageswari R <dharageswari.r@intel.com)
9b97169daSJie Yang  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10b97169daSJie Yang  *
11b97169daSJie Yang  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12b97169daSJie Yang  */
13b97169daSJie Yang #include <linux/delay.h>
14b97169daSJie Yang #include <linux/pci.h>
15b97169daSJie Yang #include <linux/fs.h>
16b97169daSJie Yang #include <linux/firmware.h>
17b97169daSJie Yang #include <linux/pm_runtime.h>
18b97169daSJie Yang #include <linux/pm_qos.h>
19b97169daSJie Yang #include <linux/math64.h>
20b97169daSJie Yang #include <sound/core.h>
21b97169daSJie Yang #include <sound/pcm.h>
22b97169daSJie Yang #include <sound/soc.h>
23b97169daSJie Yang #include <sound/compress_driver.h>
24b97169daSJie Yang #include <asm/platform_sst_audio.h>
25b97169daSJie Yang #include "../sst-mfld-platform.h"
26b97169daSJie Yang #include "sst.h"
27b97169daSJie Yang 
28b97169daSJie Yang #define NUM_CODEC 2
29b97169daSJie Yang #define MIN_FRAGMENT 2
30b97169daSJie Yang #define MAX_FRAGMENT 4
31b97169daSJie Yang #define MIN_FRAGMENT_SIZE (50 * 1024)
32b97169daSJie Yang #define MAX_FRAGMENT_SIZE (1024 * 1024)
33b97169daSJie Yang #define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz)  (((pcm_wd_sz + 15) >> 4) << 1)
34412efa73SShilpa Sreeramalu #ifdef CONFIG_PM
35412efa73SShilpa Sreeramalu #define GET_USAGE_COUNT(dev) (atomic_read(&dev->power.usage_count))
36412efa73SShilpa Sreeramalu #else
37412efa73SShilpa Sreeramalu #define GET_USAGE_COUNT(dev) 1
38412efa73SShilpa Sreeramalu #endif
39b97169daSJie Yang 
free_stream_context(struct intel_sst_drv * ctx,unsigned int str_id)40b97169daSJie Yang int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
41b97169daSJie Yang {
42b97169daSJie Yang 	struct stream_info *stream;
43b97169daSJie Yang 	int ret = 0;
44b97169daSJie Yang 
45b97169daSJie Yang 	stream = get_stream_info(ctx, str_id);
46b97169daSJie Yang 	if (stream) {
47b97169daSJie Yang 		/* str_id is valid, so stream is alloacted */
48b97169daSJie Yang 		ret = sst_free_stream(ctx, str_id);
49b97169daSJie Yang 		if (ret)
50b97169daSJie Yang 			sst_clean_stream(&ctx->streams[str_id]);
51b97169daSJie Yang 		return ret;
52b97169daSJie Yang 	} else {
53b97169daSJie Yang 		dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id);
54b97169daSJie Yang 	}
55b97169daSJie Yang 	return ret;
56b97169daSJie Yang }
57b97169daSJie Yang 
sst_get_stream_allocated(struct intel_sst_drv * ctx,struct snd_sst_params * str_param,struct snd_sst_lib_download ** lib_dnld)58b97169daSJie Yang int sst_get_stream_allocated(struct intel_sst_drv *ctx,
59b97169daSJie Yang 	struct snd_sst_params *str_param,
60b97169daSJie Yang 	struct snd_sst_lib_download **lib_dnld)
61b97169daSJie Yang {
62b97169daSJie Yang 	int retval;
63b97169daSJie Yang 
64b97169daSJie Yang 	retval = ctx->ops->alloc_stream(ctx, str_param);
65b97169daSJie Yang 	if (retval > 0)
66b97169daSJie Yang 		dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
67b97169daSJie Yang 	return retval;
68b97169daSJie Yang 
69b97169daSJie Yang }
70b97169daSJie Yang 
71b97169daSJie Yang /*
72b97169daSJie Yang  * sst_get_sfreq - this function returns the frequency of the stream
73b97169daSJie Yang  *
74b97169daSJie Yang  * @str_param : stream params
75b97169daSJie Yang  */
sst_get_sfreq(struct snd_sst_params * str_param)76b97169daSJie Yang int sst_get_sfreq(struct snd_sst_params *str_param)
77b97169daSJie Yang {
78b97169daSJie Yang 	switch (str_param->codec) {
79b97169daSJie Yang 	case SST_CODEC_TYPE_PCM:
80b97169daSJie Yang 		return str_param->sparams.uc.pcm_params.sfreq;
81b97169daSJie Yang 	case SST_CODEC_TYPE_AAC:
82b97169daSJie Yang 		return str_param->sparams.uc.aac_params.externalsr;
83b97169daSJie Yang 	case SST_CODEC_TYPE_MP3:
84b97169daSJie Yang 		return 0;
85b97169daSJie Yang 	default:
86b97169daSJie Yang 		return -EINVAL;
87b97169daSJie Yang 	}
88b97169daSJie Yang }
89b97169daSJie Yang 
90b97169daSJie Yang /*
91b97169daSJie Yang  * sst_get_num_channel - get number of channels for the stream
92b97169daSJie Yang  *
93b97169daSJie Yang  * @str_param : stream params
94b97169daSJie Yang  */
sst_get_num_channel(struct snd_sst_params * str_param)95b97169daSJie Yang int sst_get_num_channel(struct snd_sst_params *str_param)
96b97169daSJie Yang {
97b97169daSJie Yang 	switch (str_param->codec) {
98b97169daSJie Yang 	case SST_CODEC_TYPE_PCM:
99b97169daSJie Yang 		return str_param->sparams.uc.pcm_params.num_chan;
100b97169daSJie Yang 	case SST_CODEC_TYPE_MP3:
101b97169daSJie Yang 		return str_param->sparams.uc.mp3_params.num_chan;
102b97169daSJie Yang 	case SST_CODEC_TYPE_AAC:
103b97169daSJie Yang 		return str_param->sparams.uc.aac_params.num_chan;
104b97169daSJie Yang 	default:
105b97169daSJie Yang 		return -EINVAL;
106b97169daSJie Yang 	}
107b97169daSJie Yang }
108b97169daSJie Yang 
109b97169daSJie Yang /*
110b97169daSJie Yang  * sst_get_stream - this function prepares for stream allocation
111b97169daSJie Yang  *
112b97169daSJie Yang  * @str_param : stream param
113b97169daSJie Yang  */
sst_get_stream(struct intel_sst_drv * ctx,struct snd_sst_params * str_param)114b97169daSJie Yang int sst_get_stream(struct intel_sst_drv *ctx,
115b97169daSJie Yang 			struct snd_sst_params *str_param)
116b97169daSJie Yang {
117b97169daSJie Yang 	int retval;
118b97169daSJie Yang 	struct stream_info *str_info;
119b97169daSJie Yang 
120b97169daSJie Yang 	/* stream is not allocated, we are allocating */
121b97169daSJie Yang 	retval = ctx->ops->alloc_stream(ctx, str_param);
122b97169daSJie Yang 	if (retval <= 0) {
123b97169daSJie Yang 		return -EIO;
124b97169daSJie Yang 	}
125b97169daSJie Yang 	/* store sampling freq */
126b97169daSJie Yang 	str_info = &ctx->streams[retval];
127b97169daSJie Yang 	str_info->sfreq = sst_get_sfreq(str_param);
128b97169daSJie Yang 
129b97169daSJie Yang 	return retval;
130b97169daSJie Yang }
131b97169daSJie Yang 
sst_power_control(struct device * dev,bool state)132b97169daSJie Yang static int sst_power_control(struct device *dev, bool state)
133b97169daSJie Yang {
134b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
135b97169daSJie Yang 	int ret = 0;
136b97169daSJie Yang 	int usage_count = 0;
137b97169daSJie Yang 
13810583cdaSPierre-Louis Bossart 	if (state) {
139*d879e944SPierre-Louis Bossart 		ret = pm_runtime_resume_and_get(dev);
140412efa73SShilpa Sreeramalu 		usage_count = GET_USAGE_COUNT(dev);
141b97169daSJie Yang 		dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
142b97169daSJie Yang 		if (ret < 0) {
143b97169daSJie Yang 			dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret);
144b97169daSJie Yang 			return ret;
145b97169daSJie Yang 		}
146b97169daSJie Yang 		if ((ctx->sst_state == SST_RESET) && (usage_count == 1)) {
147b97169daSJie Yang 			ret = sst_load_fw(ctx);
148b97169daSJie Yang 			if (ret) {
149b97169daSJie Yang 				dev_err(dev, "FW download fail %d\n", ret);
150b97169daSJie Yang 				sst_set_fw_state_locked(ctx, SST_RESET);
151b97169daSJie Yang 				ret = sst_pm_runtime_put(ctx);
152b97169daSJie Yang 			}
153b97169daSJie Yang 		}
154b97169daSJie Yang 	} else {
155412efa73SShilpa Sreeramalu 		usage_count = GET_USAGE_COUNT(dev);
156b97169daSJie Yang 		dev_dbg(ctx->dev, "Disable: pm usage count: %d\n", usage_count);
157b97169daSJie Yang 		return sst_pm_runtime_put(ctx);
158b97169daSJie Yang 	}
159b97169daSJie Yang 	return ret;
160b97169daSJie Yang }
161b97169daSJie Yang 
162b97169daSJie Yang /*
163b97169daSJie Yang  * sst_open_pcm_stream - Open PCM interface
164b97169daSJie Yang  *
165b97169daSJie Yang  * @str_param: parameters of pcm stream
166b97169daSJie Yang  *
167b97169daSJie Yang  * This function is called by MID sound card driver to open
168b97169daSJie Yang  * a new pcm interface
169b97169daSJie Yang  */
sst_open_pcm_stream(struct device * dev,struct snd_sst_params * str_param)170b97169daSJie Yang static int sst_open_pcm_stream(struct device *dev,
171b97169daSJie Yang 		struct snd_sst_params *str_param)
172b97169daSJie Yang {
173b97169daSJie Yang 	int retval;
174b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
175b97169daSJie Yang 
176b97169daSJie Yang 	if (!str_param)
177b97169daSJie Yang 		return -EINVAL;
178b97169daSJie Yang 
179b97169daSJie Yang 	retval = sst_get_stream(ctx, str_param);
180b97169daSJie Yang 	if (retval > 0)
181b97169daSJie Yang 		ctx->stream_cnt++;
182b97169daSJie Yang 	else
183b97169daSJie Yang 		dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval);
184b97169daSJie Yang 
185b97169daSJie Yang 	return retval;
186b97169daSJie Yang }
187b97169daSJie Yang 
sst_cdev_open(struct device * dev,struct snd_sst_params * str_params,struct sst_compress_cb * cb)188b97169daSJie Yang static int sst_cdev_open(struct device *dev,
189b97169daSJie Yang 		struct snd_sst_params *str_params, struct sst_compress_cb *cb)
190b97169daSJie Yang {
191b97169daSJie Yang 	int str_id, retval;
192b97169daSJie Yang 	struct stream_info *stream;
193b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
194b97169daSJie Yang 
195*d879e944SPierre-Louis Bossart 	retval = pm_runtime_resume_and_get(ctx->dev);
196*d879e944SPierre-Louis Bossart 	if (retval < 0)
197b97169daSJie Yang 		return retval;
198b97169daSJie Yang 
199b97169daSJie Yang 	str_id = sst_get_stream(ctx, str_params);
200b97169daSJie Yang 	if (str_id > 0) {
201b97169daSJie Yang 		dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id);
202b97169daSJie Yang 		stream = &ctx->streams[str_id];
203b97169daSJie Yang 		stream->compr_cb = cb->compr_cb;
204b97169daSJie Yang 		stream->compr_cb_param = cb->param;
205b97169daSJie Yang 		stream->drain_notify = cb->drain_notify;
206b97169daSJie Yang 		stream->drain_cb_param = cb->drain_cb_param;
207b97169daSJie Yang 	} else {
208b97169daSJie Yang 		dev_err(dev, "stream encountered error during alloc %d\n", str_id);
209b97169daSJie Yang 		str_id = -EINVAL;
210b97169daSJie Yang 		sst_pm_runtime_put(ctx);
211b97169daSJie Yang 	}
212b97169daSJie Yang 	return str_id;
213b97169daSJie Yang }
214b97169daSJie Yang 
sst_cdev_close(struct device * dev,unsigned int str_id)215b97169daSJie Yang static int sst_cdev_close(struct device *dev, unsigned int str_id)
216b97169daSJie Yang {
217b97169daSJie Yang 	int retval;
218b97169daSJie Yang 	struct stream_info *stream;
219b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
220b97169daSJie Yang 
221b97169daSJie Yang 	stream = get_stream_info(ctx, str_id);
222b97169daSJie Yang 	if (!stream) {
223b97169daSJie Yang 		dev_err(dev, "stream info is NULL for str %d!!!\n", str_id);
224b97169daSJie Yang 		return -EINVAL;
225b97169daSJie Yang 	}
226b97169daSJie Yang 
227b97169daSJie Yang 	retval = sst_free_stream(ctx, str_id);
228b97169daSJie Yang 	stream->compr_cb_param = NULL;
229b97169daSJie Yang 	stream->compr_cb = NULL;
230b97169daSJie Yang 
231b97169daSJie Yang 	if (retval)
232b97169daSJie Yang 		dev_err(dev, "free stream returned err %d\n", retval);
233b97169daSJie Yang 
234b97169daSJie Yang 	dev_dbg(dev, "End\n");
235b97169daSJie Yang 	return retval;
236b97169daSJie Yang }
237b97169daSJie Yang 
sst_cdev_ack(struct device * dev,unsigned int str_id,unsigned long bytes)238b97169daSJie Yang static int sst_cdev_ack(struct device *dev, unsigned int str_id,
239b97169daSJie Yang 		unsigned long bytes)
240b97169daSJie Yang {
241b97169daSJie Yang 	struct stream_info *stream;
242b97169daSJie Yang 	struct snd_sst_tstamp fw_tstamp = {0,};
243b97169daSJie Yang 	int offset;
244b97169daSJie Yang 	void __iomem *addr;
245b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
246b97169daSJie Yang 
247b97169daSJie Yang 	stream = get_stream_info(ctx, str_id);
248b97169daSJie Yang 	if (!stream)
249b97169daSJie Yang 		return -EINVAL;
250b97169daSJie Yang 
251b97169daSJie Yang 	/* update bytes sent */
252b97169daSJie Yang 	stream->cumm_bytes += bytes;
253b97169daSJie Yang 	dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);
254b97169daSJie Yang 
255ce1cfe29SPierre-Louis Bossart 	addr =  ((void __iomem *)(ctx->mailbox + ctx->tstamp)) +
256ce1cfe29SPierre-Louis Bossart 		(str_id * sizeof(fw_tstamp));
257ce1cfe29SPierre-Louis Bossart 
258ce1cfe29SPierre-Louis Bossart 	memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp));
259b97169daSJie Yang 
260b97169daSJie Yang 	fw_tstamp.bytes_copied = stream->cumm_bytes;
261b97169daSJie Yang 	dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
262b97169daSJie Yang 			fw_tstamp.bytes_copied, bytes);
263b97169daSJie Yang 
264b97169daSJie Yang 	offset =  offsetof(struct snd_sst_tstamp, bytes_copied);
265b97169daSJie Yang 	sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
266b97169daSJie Yang 	return 0;
267b97169daSJie Yang }
268b97169daSJie Yang 
sst_cdev_set_metadata(struct device * dev,unsigned int str_id,struct snd_compr_metadata * metadata)269b97169daSJie Yang static int sst_cdev_set_metadata(struct device *dev,
270b97169daSJie Yang 		unsigned int str_id, struct snd_compr_metadata *metadata)
271b97169daSJie Yang {
272b97169daSJie Yang 	int retval = 0;
273b97169daSJie Yang 	struct stream_info *str_info;
274b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
275b97169daSJie Yang 
276b97169daSJie Yang 	dev_dbg(dev, "set metadata for stream %d\n", str_id);
277b97169daSJie Yang 
278b97169daSJie Yang 	str_info = get_stream_info(ctx, str_id);
279b97169daSJie Yang 	if (!str_info)
280b97169daSJie Yang 		return -EINVAL;
281b97169daSJie Yang 
282b97169daSJie Yang 	dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id);
283b97169daSJie Yang 	retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD,
284b97169daSJie Yang 			IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id,
285b97169daSJie Yang 			sizeof(*metadata), metadata, NULL,
286b97169daSJie Yang 			true, true, true, false);
287b97169daSJie Yang 
288b97169daSJie Yang 	return retval;
289b97169daSJie Yang }
290b97169daSJie Yang 
sst_cdev_stream_pause(struct device * dev,unsigned int str_id)291b97169daSJie Yang static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id)
292b97169daSJie Yang {
293b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
294b97169daSJie Yang 
295b97169daSJie Yang 	return sst_pause_stream(ctx, str_id);
296b97169daSJie Yang }
297b97169daSJie Yang 
sst_cdev_stream_pause_release(struct device * dev,unsigned int str_id)298b97169daSJie Yang static int sst_cdev_stream_pause_release(struct device *dev,
299b97169daSJie Yang 		unsigned int str_id)
300b97169daSJie Yang {
301b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
302b97169daSJie Yang 
303b97169daSJie Yang 	return sst_resume_stream(ctx, str_id);
304b97169daSJie Yang }
305b97169daSJie Yang 
sst_cdev_stream_start(struct device * dev,unsigned int str_id)306b97169daSJie Yang static int sst_cdev_stream_start(struct device *dev, unsigned int str_id)
307b97169daSJie Yang {
308b97169daSJie Yang 	struct stream_info *str_info;
309b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
310b97169daSJie Yang 
311b97169daSJie Yang 	str_info = get_stream_info(ctx, str_id);
312b97169daSJie Yang 	if (!str_info)
313b97169daSJie Yang 		return -EINVAL;
314b97169daSJie Yang 	str_info->prev = str_info->status;
315b97169daSJie Yang 	str_info->status = STREAM_RUNNING;
316b97169daSJie Yang 	return sst_start_stream(ctx, str_id);
317b97169daSJie Yang }
318b97169daSJie Yang 
sst_cdev_stream_drop(struct device * dev,unsigned int str_id)319b97169daSJie Yang static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id)
320b97169daSJie Yang {
321b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
322b97169daSJie Yang 
323b97169daSJie Yang 	return sst_drop_stream(ctx, str_id);
324b97169daSJie Yang }
325b97169daSJie Yang 
sst_cdev_stream_drain(struct device * dev,unsigned int str_id)326b97169daSJie Yang static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id)
327b97169daSJie Yang {
328b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
329b97169daSJie Yang 
330b97169daSJie Yang 	return sst_drain_stream(ctx, str_id, false);
331b97169daSJie Yang }
332b97169daSJie Yang 
sst_cdev_stream_partial_drain(struct device * dev,unsigned int str_id)333b97169daSJie Yang static int sst_cdev_stream_partial_drain(struct device *dev,
334b97169daSJie Yang 		unsigned int str_id)
335b97169daSJie Yang {
336b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
337b97169daSJie Yang 
338b97169daSJie Yang 	return sst_drain_stream(ctx, str_id, true);
339b97169daSJie Yang }
340b97169daSJie Yang 
sst_cdev_tstamp(struct device * dev,unsigned int str_id,struct snd_compr_tstamp * tstamp)341b97169daSJie Yang static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
342b97169daSJie Yang 		struct snd_compr_tstamp *tstamp)
343b97169daSJie Yang {
344b97169daSJie Yang 	struct snd_sst_tstamp fw_tstamp = {0,};
345b97169daSJie Yang 	struct stream_info *stream;
346b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
347ce1cfe29SPierre-Louis Bossart 	void __iomem *addr;
348b97169daSJie Yang 
349ce1cfe29SPierre-Louis Bossart 	addr = (void __iomem *)(ctx->mailbox + ctx->tstamp) +
350ce1cfe29SPierre-Louis Bossart 		(str_id * sizeof(fw_tstamp));
351ce1cfe29SPierre-Louis Bossart 
352ce1cfe29SPierre-Louis Bossart 	memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp));
353b97169daSJie Yang 
354b97169daSJie Yang 	stream = get_stream_info(ctx, str_id);
355b97169daSJie Yang 	if (!stream)
356b97169daSJie Yang 		return -EINVAL;
357b97169daSJie Yang 	dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter);
358b97169daSJie Yang 
359b97169daSJie Yang 	tstamp->copied_total = fw_tstamp.ring_buffer_counter;
360b97169daSJie Yang 	tstamp->pcm_frames = fw_tstamp.frames_decoded;
361b97169daSJie Yang 	tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter,
36275afbd05SDan Carpenter 			(u64)stream->num_ch * SST_GET_BYTES_PER_SAMPLE(24));
363b97169daSJie Yang 	tstamp->sampling_rate = fw_tstamp.sampling_frequency;
364b97169daSJie Yang 
365b97169daSJie Yang 	dev_dbg(dev, "PCM  = %u\n", tstamp->pcm_io_frames);
366b97169daSJie Yang 	dev_dbg(dev, "Ptr Query on strid = %d  copied_total %d, decodec %d\n",
367b97169daSJie Yang 		str_id, tstamp->copied_total, tstamp->pcm_frames);
368b97169daSJie Yang 	dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames);
369b97169daSJie Yang 
370b97169daSJie Yang 	return 0;
371b97169daSJie Yang }
372b97169daSJie Yang 
sst_cdev_caps(struct snd_compr_caps * caps)373b97169daSJie Yang static int sst_cdev_caps(struct snd_compr_caps *caps)
374b97169daSJie Yang {
375b97169daSJie Yang 	caps->num_codecs = NUM_CODEC;
376b97169daSJie Yang 	caps->min_fragment_size = MIN_FRAGMENT_SIZE;  /* 50KB */
377b97169daSJie Yang 	caps->max_fragment_size = MAX_FRAGMENT_SIZE;  /* 1024KB */
378b97169daSJie Yang 	caps->min_fragments = MIN_FRAGMENT;
379b97169daSJie Yang 	caps->max_fragments = MAX_FRAGMENT;
380b97169daSJie Yang 	caps->codecs[0] = SND_AUDIOCODEC_MP3;
381b97169daSJie Yang 	caps->codecs[1] = SND_AUDIOCODEC_AAC;
382b97169daSJie Yang 	return 0;
383b97169daSJie Yang }
384b97169daSJie Yang 
38570bad123SJulia Lawall static const struct snd_compr_codec_caps caps_mp3 = {
386b97169daSJie Yang 	.num_descriptors = 1,
387b97169daSJie Yang 	.descriptor[0].max_ch = 2,
388b97169daSJie Yang 	.descriptor[0].sample_rates[0] = 48000,
389b97169daSJie Yang 	.descriptor[0].sample_rates[1] = 44100,
390b97169daSJie Yang 	.descriptor[0].sample_rates[2] = 32000,
391b97169daSJie Yang 	.descriptor[0].sample_rates[3] = 16000,
392b97169daSJie Yang 	.descriptor[0].sample_rates[4] = 8000,
393b97169daSJie Yang 	.descriptor[0].num_sample_rates = 5,
394b97169daSJie Yang 	.descriptor[0].bit_rate[0] = 320,
395b97169daSJie Yang 	.descriptor[0].bit_rate[1] = 192,
396b97169daSJie Yang 	.descriptor[0].num_bitrates = 2,
397b97169daSJie Yang 	.descriptor[0].profiles = 0,
398b97169daSJie Yang 	.descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
399b97169daSJie Yang 	.descriptor[0].formats = 0,
400b97169daSJie Yang };
401b97169daSJie Yang 
40270bad123SJulia Lawall static const struct snd_compr_codec_caps caps_aac = {
403b97169daSJie Yang 	.num_descriptors = 2,
404b97169daSJie Yang 	.descriptor[1].max_ch = 2,
405b97169daSJie Yang 	.descriptor[0].sample_rates[0] = 48000,
406b97169daSJie Yang 	.descriptor[0].sample_rates[1] = 44100,
407b97169daSJie Yang 	.descriptor[0].sample_rates[2] = 32000,
408b97169daSJie Yang 	.descriptor[0].sample_rates[3] = 16000,
409b97169daSJie Yang 	.descriptor[0].sample_rates[4] = 8000,
410b97169daSJie Yang 	.descriptor[0].num_sample_rates = 5,
411b97169daSJie Yang 	.descriptor[1].bit_rate[0] = 320,
412b97169daSJie Yang 	.descriptor[1].bit_rate[1] = 192,
413b97169daSJie Yang 	.descriptor[1].num_bitrates = 2,
414b97169daSJie Yang 	.descriptor[1].profiles = 0,
415b97169daSJie Yang 	.descriptor[1].modes = 0,
416b97169daSJie Yang 	.descriptor[1].formats =
417b97169daSJie Yang 			(SND_AUDIOSTREAMFORMAT_MP4ADTS |
418b97169daSJie Yang 				SND_AUDIOSTREAMFORMAT_RAW),
419b97169daSJie Yang };
420b97169daSJie Yang 
sst_cdev_codec_caps(struct snd_compr_codec_caps * codec)421b97169daSJie Yang static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
422b97169daSJie Yang {
423b97169daSJie Yang 	if (codec->codec == SND_AUDIOCODEC_MP3)
424b97169daSJie Yang 		*codec = caps_mp3;
425b97169daSJie Yang 	else if (codec->codec == SND_AUDIOCODEC_AAC)
426b97169daSJie Yang 		*codec = caps_aac;
427b97169daSJie Yang 	else
428b97169daSJie Yang 		return -EINVAL;
429b97169daSJie Yang 
430b97169daSJie Yang 	return 0;
431b97169daSJie Yang }
432b97169daSJie Yang 
sst_cdev_fragment_elapsed(struct intel_sst_drv * ctx,int str_id)433b97169daSJie Yang void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
434b97169daSJie Yang {
435b97169daSJie Yang 	struct stream_info *stream;
436b97169daSJie Yang 
437b97169daSJie Yang 	dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
438b97169daSJie Yang 			str_id);
439b97169daSJie Yang 	stream = &ctx->streams[str_id];
440b97169daSJie Yang 	if (stream->compr_cb)
441b97169daSJie Yang 		stream->compr_cb(stream->compr_cb_param);
442b97169daSJie Yang }
443b97169daSJie Yang 
444b97169daSJie Yang /*
445b97169daSJie Yang  * sst_close_pcm_stream - Close PCM interface
446b97169daSJie Yang  *
447b97169daSJie Yang  * @str_id: stream id to be closed
448b97169daSJie Yang  *
449b97169daSJie Yang  * This function is called by MID sound card driver to close
450b97169daSJie Yang  * an existing pcm interface
451b97169daSJie Yang  */
sst_close_pcm_stream(struct device * dev,unsigned int str_id)452b97169daSJie Yang static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
453b97169daSJie Yang {
454b97169daSJie Yang 	struct stream_info *stream;
455b97169daSJie Yang 	int retval = 0;
456b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
457b97169daSJie Yang 
458b97169daSJie Yang 	stream = get_stream_info(ctx, str_id);
459b97169daSJie Yang 	if (!stream) {
460b97169daSJie Yang 		dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id);
461b97169daSJie Yang 		return -EINVAL;
462b97169daSJie Yang 	}
463b97169daSJie Yang 
464b97169daSJie Yang 	retval = free_stream_context(ctx, str_id);
465b97169daSJie Yang 	stream->pcm_substream = NULL;
466b97169daSJie Yang 	stream->status = STREAM_UN_INIT;
467b97169daSJie Yang 	stream->period_elapsed = NULL;
468b97169daSJie Yang 	ctx->stream_cnt--;
469b97169daSJie Yang 
470b97169daSJie Yang 	if (retval)
471b97169daSJie Yang 		dev_err(ctx->dev, "free stream returned err %d\n", retval);
472b97169daSJie Yang 
473b97169daSJie Yang 	dev_dbg(ctx->dev, "Exit\n");
474b97169daSJie Yang 	return 0;
475b97169daSJie Yang }
476b97169daSJie Yang 
sst_calc_tstamp(struct intel_sst_drv * ctx,struct pcm_stream_info * info,struct snd_pcm_substream * substream,struct snd_sst_tstamp * fw_tstamp)477b97169daSJie Yang static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
478b97169daSJie Yang 		struct pcm_stream_info *info,
479b97169daSJie Yang 		struct snd_pcm_substream *substream,
480b97169daSJie Yang 		struct snd_sst_tstamp *fw_tstamp)
481b97169daSJie Yang {
482b97169daSJie Yang 	size_t delay_bytes, delay_frames;
483b97169daSJie Yang 	size_t buffer_sz;
484b97169daSJie Yang 	u32 pointer_bytes, pointer_samples;
485b97169daSJie Yang 
486b97169daSJie Yang 	dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n",
487b97169daSJie Yang 			fw_tstamp->ring_buffer_counter);
488b97169daSJie Yang 	dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n",
489b97169daSJie Yang 			 fw_tstamp->hardware_counter);
490b97169daSJie Yang 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
491b97169daSJie Yang 		delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter -
492b97169daSJie Yang 					fw_tstamp->hardware_counter);
493b97169daSJie Yang 	else
494b97169daSJie Yang 		delay_bytes = (size_t) (fw_tstamp->hardware_counter -
495b97169daSJie Yang 					fw_tstamp->ring_buffer_counter);
496b97169daSJie Yang 	delay_frames = bytes_to_frames(substream->runtime, delay_bytes);
497b97169daSJie Yang 	buffer_sz = snd_pcm_lib_buffer_bytes(substream);
498b97169daSJie Yang 	div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes);
499b97169daSJie Yang 	pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes);
500b97169daSJie Yang 
501b97169daSJie Yang 	dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes);
502b97169daSJie Yang 
503b97169daSJie Yang 	info->buffer_ptr = pointer_samples / substream->runtime->channels;
504b97169daSJie Yang 
505ffb3722bSFang, Yang A 	info->pcm_delay = delay_frames;
506b97169daSJie Yang 	dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
507b97169daSJie Yang 			info->buffer_ptr, info->pcm_delay);
508b97169daSJie Yang 	return 0;
509b97169daSJie Yang }
510b97169daSJie Yang 
sst_read_timestamp(struct device * dev,struct pcm_stream_info * info)511b97169daSJie Yang static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
512b97169daSJie Yang {
513b97169daSJie Yang 	struct stream_info *stream;
514b97169daSJie Yang 	struct snd_pcm_substream *substream;
515b97169daSJie Yang 	struct snd_sst_tstamp fw_tstamp;
516b97169daSJie Yang 	unsigned int str_id;
517b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
518ce1cfe29SPierre-Louis Bossart 	void __iomem *addr;
519b97169daSJie Yang 
520b97169daSJie Yang 	str_id = info->str_id;
521b97169daSJie Yang 	stream = get_stream_info(ctx, str_id);
522b97169daSJie Yang 	if (!stream)
523b97169daSJie Yang 		return -EINVAL;
524b97169daSJie Yang 
525b97169daSJie Yang 	if (!stream->pcm_substream)
526b97169daSJie Yang 		return -EINVAL;
527b97169daSJie Yang 	substream = stream->pcm_substream;
528b97169daSJie Yang 
529ce1cfe29SPierre-Louis Bossart 	addr = (void __iomem *)(ctx->mailbox + ctx->tstamp) +
530ce1cfe29SPierre-Louis Bossart 		(str_id * sizeof(fw_tstamp));
531ce1cfe29SPierre-Louis Bossart 
532ce1cfe29SPierre-Louis Bossart 	memcpy_fromio(&fw_tstamp, addr, sizeof(fw_tstamp));
533ce1cfe29SPierre-Louis Bossart 
534b97169daSJie Yang 	return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
535b97169daSJie Yang }
536b97169daSJie Yang 
sst_stream_start(struct device * dev,int str_id)537b97169daSJie Yang static int sst_stream_start(struct device *dev, int str_id)
538b97169daSJie Yang {
539b97169daSJie Yang 	struct stream_info *str_info;
540b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
541b97169daSJie Yang 
542b97169daSJie Yang 	if (ctx->sst_state != SST_FW_RUNNING)
543b97169daSJie Yang 		return 0;
544b97169daSJie Yang 	str_info = get_stream_info(ctx, str_id);
545b97169daSJie Yang 	if (!str_info)
546b97169daSJie Yang 		return -EINVAL;
547b97169daSJie Yang 	str_info->prev = str_info->status;
548b97169daSJie Yang 	str_info->status = STREAM_RUNNING;
549b97169daSJie Yang 	sst_start_stream(ctx, str_id);
550b97169daSJie Yang 
551b97169daSJie Yang 	return 0;
552b97169daSJie Yang }
553b97169daSJie Yang 
sst_stream_drop(struct device * dev,int str_id)554b97169daSJie Yang static int sst_stream_drop(struct device *dev, int str_id)
555b97169daSJie Yang {
556b97169daSJie Yang 	struct stream_info *str_info;
557b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
558b97169daSJie Yang 
559b97169daSJie Yang 	if (ctx->sst_state != SST_FW_RUNNING)
560b97169daSJie Yang 		return 0;
561b97169daSJie Yang 
562b97169daSJie Yang 	str_info = get_stream_info(ctx, str_id);
563b97169daSJie Yang 	if (!str_info)
564b97169daSJie Yang 		return -EINVAL;
565b97169daSJie Yang 	str_info->prev = STREAM_UN_INIT;
566b97169daSJie Yang 	str_info->status = STREAM_INIT;
567b97169daSJie Yang 	return sst_drop_stream(ctx, str_id);
568b97169daSJie Yang }
569b97169daSJie Yang 
sst_stream_pause(struct device * dev,int str_id)570b97169daSJie Yang static int sst_stream_pause(struct device *dev, int str_id)
571b97169daSJie Yang {
572b97169daSJie Yang 	struct stream_info *str_info;
573b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
574b97169daSJie Yang 
575b97169daSJie Yang 	if (ctx->sst_state != SST_FW_RUNNING)
576b97169daSJie Yang 		return 0;
577b97169daSJie Yang 
578b97169daSJie Yang 	str_info = get_stream_info(ctx, str_id);
579b97169daSJie Yang 	if (!str_info)
580b97169daSJie Yang 		return -EINVAL;
581b97169daSJie Yang 
582b97169daSJie Yang 	return sst_pause_stream(ctx, str_id);
583b97169daSJie Yang }
584b97169daSJie Yang 
sst_stream_resume(struct device * dev,int str_id)585b97169daSJie Yang static int sst_stream_resume(struct device *dev, int str_id)
586b97169daSJie Yang {
587b97169daSJie Yang 	struct stream_info *str_info;
588b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
589b97169daSJie Yang 
590b97169daSJie Yang 	if (ctx->sst_state != SST_FW_RUNNING)
591b97169daSJie Yang 		return 0;
592b97169daSJie Yang 
593b97169daSJie Yang 	str_info = get_stream_info(ctx, str_id);
594b97169daSJie Yang 	if (!str_info)
595b97169daSJie Yang 		return -EINVAL;
596b97169daSJie Yang 	return sst_resume_stream(ctx, str_id);
597b97169daSJie Yang }
598b97169daSJie Yang 
sst_stream_init(struct device * dev,struct pcm_stream_info * str_info)599b97169daSJie Yang static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
600b97169daSJie Yang {
601b97169daSJie Yang 	int str_id = 0;
602b97169daSJie Yang 	struct stream_info *stream;
603b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
604b97169daSJie Yang 
605b97169daSJie Yang 	str_id = str_info->str_id;
606b97169daSJie Yang 
607b97169daSJie Yang 	if (ctx->sst_state != SST_FW_RUNNING)
608b97169daSJie Yang 		return 0;
609b97169daSJie Yang 
610b97169daSJie Yang 	stream = get_stream_info(ctx, str_id);
611b97169daSJie Yang 	if (!stream)
612b97169daSJie Yang 		return -EINVAL;
613b97169daSJie Yang 
614b97169daSJie Yang 	dev_dbg(ctx->dev, "setting the period ptrs\n");
615b97169daSJie Yang 	stream->pcm_substream = str_info->arg;
616b97169daSJie Yang 	stream->period_elapsed = str_info->period_elapsed;
617b97169daSJie Yang 	stream->sfreq = str_info->sfreq;
618b97169daSJie Yang 	stream->prev = stream->status;
619b97169daSJie Yang 	stream->status = STREAM_INIT;
620b97169daSJie Yang 	dev_dbg(ctx->dev,
621b97169daSJie Yang 		"pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n",
622b97169daSJie Yang 		stream->pcm_substream, stream->period_elapsed,
623b97169daSJie Yang 		stream->sfreq, stream->status);
624b97169daSJie Yang 
625b97169daSJie Yang 	return 0;
626b97169daSJie Yang }
627b97169daSJie Yang 
628b97169daSJie Yang /*
629b97169daSJie Yang  * sst_set_byte_stream - Set generic params
630b97169daSJie Yang  *
631b97169daSJie Yang  * @cmd: control cmd to be set
632b97169daSJie Yang  * @arg: command argument
633b97169daSJie Yang  *
634b97169daSJie Yang  * This function is called by MID sound card driver to configure
635b97169daSJie Yang  * SST runtime params.
636b97169daSJie Yang  */
sst_send_byte_stream(struct device * dev,struct snd_sst_bytes_v2 * bytes)637b97169daSJie Yang static int sst_send_byte_stream(struct device *dev,
638b97169daSJie Yang 		struct snd_sst_bytes_v2 *bytes)
639b97169daSJie Yang {
640b97169daSJie Yang 	int ret_val = 0;
641b97169daSJie Yang 	struct intel_sst_drv *ctx = dev_get_drvdata(dev);
642b97169daSJie Yang 
643b97169daSJie Yang 	if (NULL == bytes)
644b97169daSJie Yang 		return -EINVAL;
645*d879e944SPierre-Louis Bossart 	ret_val = pm_runtime_resume_and_get(ctx->dev);
646*d879e944SPierre-Louis Bossart 	if (ret_val < 0)
647b97169daSJie Yang 		return ret_val;
648b97169daSJie Yang 
649b97169daSJie Yang 	ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
650b97169daSJie Yang 	sst_pm_runtime_put(ctx);
651b97169daSJie Yang 
652b97169daSJie Yang 	return ret_val;
653b97169daSJie Yang }
654b97169daSJie Yang 
655b97169daSJie Yang static struct sst_ops pcm_ops = {
656b97169daSJie Yang 	.open = sst_open_pcm_stream,
657b97169daSJie Yang 	.stream_init = sst_stream_init,
658b97169daSJie Yang 	.stream_start = sst_stream_start,
659b97169daSJie Yang 	.stream_drop = sst_stream_drop,
660b97169daSJie Yang 	.stream_pause = sst_stream_pause,
661b97169daSJie Yang 	.stream_pause_release = sst_stream_resume,
662b97169daSJie Yang 	.stream_read_tstamp = sst_read_timestamp,
663b97169daSJie Yang 	.send_byte_stream = sst_send_byte_stream,
664b97169daSJie Yang 	.close = sst_close_pcm_stream,
665b97169daSJie Yang 	.power = sst_power_control,
666b97169daSJie Yang };
667b97169daSJie Yang 
668b97169daSJie Yang static struct compress_sst_ops compr_ops = {
669b97169daSJie Yang 	.open = sst_cdev_open,
670b97169daSJie Yang 	.close = sst_cdev_close,
671b97169daSJie Yang 	.stream_pause = sst_cdev_stream_pause,
672b97169daSJie Yang 	.stream_pause_release = sst_cdev_stream_pause_release,
673b97169daSJie Yang 	.stream_start = sst_cdev_stream_start,
674b97169daSJie Yang 	.stream_drop = sst_cdev_stream_drop,
675b97169daSJie Yang 	.stream_drain = sst_cdev_stream_drain,
676b97169daSJie Yang 	.stream_partial_drain = sst_cdev_stream_partial_drain,
677b97169daSJie Yang 	.tstamp = sst_cdev_tstamp,
678b97169daSJie Yang 	.ack = sst_cdev_ack,
679b97169daSJie Yang 	.get_caps = sst_cdev_caps,
680b97169daSJie Yang 	.get_codec_caps = sst_cdev_codec_caps,
681b97169daSJie Yang 	.set_metadata = sst_cdev_set_metadata,
682b97169daSJie Yang 	.power = sst_power_control,
683b97169daSJie Yang };
684b97169daSJie Yang 
685b97169daSJie Yang static struct sst_device sst_dsp_device = {
686b97169daSJie Yang 	.name = "Intel(R) SST LPE",
687b97169daSJie Yang 	.dev = NULL,
688b97169daSJie Yang 	.ops = &pcm_ops,
689b97169daSJie Yang 	.compr_ops = &compr_ops,
690b97169daSJie Yang };
691b97169daSJie Yang 
692b97169daSJie Yang /*
693b97169daSJie Yang  * sst_register - function to register DSP
694b97169daSJie Yang  *
695b97169daSJie Yang  * This functions registers DSP with the platform driver
696b97169daSJie Yang  */
sst_register(struct device * dev)697b97169daSJie Yang int sst_register(struct device *dev)
698b97169daSJie Yang {
699b97169daSJie Yang 	int ret_val;
700b97169daSJie Yang 
701b97169daSJie Yang 	sst_dsp_device.dev = dev;
702b97169daSJie Yang 	ret_val = sst_register_dsp(&sst_dsp_device);
703b97169daSJie Yang 	if (ret_val)
704b97169daSJie Yang 		dev_err(dev, "Unable to register DSP with platform driver\n");
705b97169daSJie Yang 
706b97169daSJie Yang 	return ret_val;
707b97169daSJie Yang }
708b97169daSJie Yang 
sst_unregister(struct device * dev)709b97169daSJie Yang int sst_unregister(struct device *dev)
710b97169daSJie Yang {
711b97169daSJie Yang 	return sst_unregister_dsp(&sst_dsp_device);
712b97169daSJie Yang }
713