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