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