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