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 | ||
9308d145 VK |
149 | if (state == true) { |
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 | ||
241 | if (stream->status == STREAM_RESET) { | |
242 | dev_dbg(dev, "stream in reset state...\n"); | |
243 | stream->status = STREAM_UN_INIT; | |
244 | ||
245 | retval = 0; | |
246 | goto put; | |
247 | } | |
248 | ||
249 | retval = sst_free_stream(ctx, str_id); | |
250 | put: | |
251 | stream->compr_cb_param = NULL; | |
252 | stream->compr_cb = NULL; | |
253 | ||
254 | if (retval) | |
255 | dev_err(dev, "free stream returned err %d\n", retval); | |
256 | ||
257 | dev_dbg(dev, "End\n"); | |
258 | return retval; | |
259 | ||
260 | } | |
261 | ||
262 | static int sst_cdev_ack(struct device *dev, unsigned int str_id, | |
263 | unsigned long bytes) | |
264 | { | |
265 | struct stream_info *stream; | |
266 | struct snd_sst_tstamp fw_tstamp = {0,}; | |
267 | int offset; | |
268 | void __iomem *addr; | |
269 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
270 | ||
271 | stream = get_stream_info(ctx, str_id); | |
272 | if (!stream) | |
273 | return -EINVAL; | |
274 | ||
275 | /* update bytes sent */ | |
276 | stream->cumm_bytes += bytes; | |
277 | dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes); | |
278 | ||
279 | memcpy_fromio(&fw_tstamp, | |
280 | ((void *)(ctx->mailbox + ctx->tstamp) | |
281 | +(str_id * sizeof(fw_tstamp))), | |
282 | sizeof(fw_tstamp)); | |
283 | ||
284 | fw_tstamp.bytes_copied = stream->cumm_bytes; | |
285 | dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n", | |
286 | fw_tstamp.bytes_copied, bytes); | |
287 | ||
288 | addr = ((void *)(ctx->mailbox + ctx->tstamp)) + | |
289 | (str_id * sizeof(fw_tstamp)); | |
290 | offset = offsetof(struct snd_sst_tstamp, bytes_copied); | |
291 | sst_shim_write(addr, offset, fw_tstamp.bytes_copied); | |
292 | return 0; | |
293 | } | |
294 | ||
295 | static int sst_cdev_set_metadata(struct device *dev, | |
296 | unsigned int str_id, struct snd_compr_metadata *metadata) | |
297 | { | |
298 | int retval = 0; | |
299 | struct stream_info *str_info; | |
300 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
301 | ||
302 | dev_dbg(dev, "set metadata for stream %d\n", str_id); | |
303 | ||
304 | str_info = get_stream_info(ctx, str_id); | |
305 | if (!str_info) | |
306 | return -EINVAL; | |
307 | ||
308 | dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id); | |
309 | retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD, | |
310 | IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id, | |
311 | sizeof(*metadata), metadata, NULL, | |
312 | true, true, true, false); | |
313 | ||
314 | return retval; | |
315 | } | |
316 | ||
317 | static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id) | |
318 | { | |
319 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
320 | ||
321 | return sst_pause_stream(ctx, str_id); | |
322 | } | |
323 | ||
324 | static int sst_cdev_stream_pause_release(struct device *dev, | |
325 | unsigned int str_id) | |
326 | { | |
327 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
328 | ||
329 | return sst_resume_stream(ctx, str_id); | |
330 | } | |
331 | ||
332 | static int sst_cdev_stream_start(struct device *dev, unsigned int str_id) | |
333 | { | |
334 | struct stream_info *str_info; | |
335 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
336 | ||
337 | str_info = get_stream_info(ctx, str_id); | |
338 | if (!str_info) | |
339 | return -EINVAL; | |
340 | str_info->prev = str_info->status; | |
341 | str_info->status = STREAM_RUNNING; | |
342 | return sst_start_stream(ctx, str_id); | |
343 | } | |
344 | ||
345 | static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id) | |
346 | { | |
347 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
348 | ||
349 | return sst_drop_stream(ctx, str_id); | |
350 | } | |
351 | ||
352 | static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id) | |
353 | { | |
354 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
355 | ||
356 | return sst_drain_stream(ctx, str_id, false); | |
357 | } | |
358 | ||
359 | static int sst_cdev_stream_partial_drain(struct device *dev, | |
360 | unsigned int str_id) | |
361 | { | |
362 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
363 | ||
364 | return sst_drain_stream(ctx, str_id, true); | |
365 | } | |
366 | ||
367 | static int sst_cdev_tstamp(struct device *dev, unsigned int str_id, | |
368 | struct snd_compr_tstamp *tstamp) | |
369 | { | |
370 | struct snd_sst_tstamp fw_tstamp = {0,}; | |
371 | struct stream_info *stream; | |
372 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
373 | ||
374 | memcpy_fromio(&fw_tstamp, | |
375 | ((void *)(ctx->mailbox + ctx->tstamp) | |
376 | +(str_id * sizeof(fw_tstamp))), | |
377 | sizeof(fw_tstamp)); | |
378 | ||
379 | stream = get_stream_info(ctx, str_id); | |
380 | if (!stream) | |
381 | return -EINVAL; | |
382 | dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter); | |
383 | ||
384 | tstamp->copied_total = fw_tstamp.ring_buffer_counter; | |
385 | tstamp->pcm_frames = fw_tstamp.frames_decoded; | |
386 | tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter, | |
75afbd05 | 387 | (u64)stream->num_ch * SST_GET_BYTES_PER_SAMPLE(24)); |
7adab122 VK |
388 | tstamp->sampling_rate = fw_tstamp.sampling_frequency; |
389 | ||
390 | dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames); | |
391 | dev_dbg(dev, "Ptr Query on strid = %d copied_total %d, decodec %d\n", | |
392 | str_id, tstamp->copied_total, tstamp->pcm_frames); | |
393 | dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames); | |
394 | ||
395 | return 0; | |
396 | } | |
397 | ||
398 | static int sst_cdev_caps(struct snd_compr_caps *caps) | |
399 | { | |
400 | caps->num_codecs = NUM_CODEC; | |
401 | caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */ | |
402 | caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */ | |
403 | caps->min_fragments = MIN_FRAGMENT; | |
404 | caps->max_fragments = MAX_FRAGMENT; | |
405 | caps->codecs[0] = SND_AUDIOCODEC_MP3; | |
406 | caps->codecs[1] = SND_AUDIOCODEC_AAC; | |
407 | return 0; | |
408 | } | |
409 | ||
410 | static struct snd_compr_codec_caps caps_mp3 = { | |
411 | .num_descriptors = 1, | |
412 | .descriptor[0].max_ch = 2, | |
413 | .descriptor[0].sample_rates[0] = 48000, | |
414 | .descriptor[0].sample_rates[1] = 44100, | |
415 | .descriptor[0].sample_rates[2] = 32000, | |
416 | .descriptor[0].sample_rates[3] = 16000, | |
417 | .descriptor[0].sample_rates[4] = 8000, | |
418 | .descriptor[0].num_sample_rates = 5, | |
419 | .descriptor[0].bit_rate[0] = 320, | |
420 | .descriptor[0].bit_rate[1] = 192, | |
421 | .descriptor[0].num_bitrates = 2, | |
422 | .descriptor[0].profiles = 0, | |
423 | .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, | |
424 | .descriptor[0].formats = 0, | |
425 | }; | |
426 | ||
427 | static struct snd_compr_codec_caps caps_aac = { | |
428 | .num_descriptors = 2, | |
429 | .descriptor[1].max_ch = 2, | |
430 | .descriptor[0].sample_rates[0] = 48000, | |
431 | .descriptor[0].sample_rates[1] = 44100, | |
432 | .descriptor[0].sample_rates[2] = 32000, | |
433 | .descriptor[0].sample_rates[3] = 16000, | |
434 | .descriptor[0].sample_rates[4] = 8000, | |
435 | .descriptor[0].num_sample_rates = 5, | |
436 | .descriptor[1].bit_rate[0] = 320, | |
437 | .descriptor[1].bit_rate[1] = 192, | |
438 | .descriptor[1].num_bitrates = 2, | |
439 | .descriptor[1].profiles = 0, | |
440 | .descriptor[1].modes = 0, | |
441 | .descriptor[1].formats = | |
442 | (SND_AUDIOSTREAMFORMAT_MP4ADTS | | |
443 | SND_AUDIOSTREAMFORMAT_RAW), | |
444 | }; | |
445 | ||
446 | static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec) | |
447 | { | |
448 | if (codec->codec == SND_AUDIOCODEC_MP3) | |
449 | *codec = caps_mp3; | |
450 | else if (codec->codec == SND_AUDIOCODEC_AAC) | |
451 | *codec = caps_aac; | |
452 | else | |
453 | return -EINVAL; | |
454 | ||
455 | return 0; | |
456 | } | |
457 | ||
458 | void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id) | |
459 | { | |
460 | struct stream_info *stream; | |
461 | ||
462 | dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n", | |
463 | str_id); | |
464 | stream = &ctx->streams[str_id]; | |
465 | if (stream->compr_cb) | |
466 | stream->compr_cb(stream->compr_cb_param); | |
467 | } | |
468 | ||
cc547054 VK |
469 | /* |
470 | * sst_close_pcm_stream - Close PCM interface | |
471 | * | |
472 | * @str_id: stream id to be closed | |
473 | * | |
474 | * This function is called by MID sound card driver to close | |
475 | * an existing pcm interface | |
476 | */ | |
477 | static int sst_close_pcm_stream(struct device *dev, unsigned int str_id) | |
478 | { | |
479 | struct stream_info *stream; | |
480 | int retval = 0; | |
481 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
482 | ||
483 | stream = get_stream_info(ctx, str_id); | |
484 | if (!stream) { | |
485 | dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id); | |
486 | return -EINVAL; | |
487 | } | |
488 | ||
489 | if (stream->status == STREAM_RESET) { | |
490 | /* silently fail here as we have cleaned the stream earlier */ | |
491 | dev_dbg(ctx->dev, "stream in reset state...\n"); | |
492 | ||
493 | retval = 0; | |
494 | goto put; | |
495 | } | |
496 | ||
497 | retval = free_stream_context(ctx, str_id); | |
498 | put: | |
499 | stream->pcm_substream = NULL; | |
500 | stream->status = STREAM_UN_INIT; | |
501 | stream->period_elapsed = NULL; | |
502 | ctx->stream_cnt--; | |
503 | ||
1a6db0bd SP |
504 | if (retval) |
505 | dev_err(ctx->dev, "free stream returned err %d\n", retval); | |
cc547054 VK |
506 | |
507 | dev_dbg(ctx->dev, "Exit\n"); | |
508 | return 0; | |
509 | } | |
510 | ||
511 | static inline int sst_calc_tstamp(struct intel_sst_drv *ctx, | |
512 | struct pcm_stream_info *info, | |
513 | struct snd_pcm_substream *substream, | |
514 | struct snd_sst_tstamp *fw_tstamp) | |
515 | { | |
516 | size_t delay_bytes, delay_frames; | |
517 | size_t buffer_sz; | |
518 | u32 pointer_bytes, pointer_samples; | |
519 | ||
520 | dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n", | |
521 | fw_tstamp->ring_buffer_counter); | |
522 | dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n", | |
523 | fw_tstamp->hardware_counter); | |
524 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
525 | delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter - | |
526 | fw_tstamp->hardware_counter); | |
527 | else | |
528 | delay_bytes = (size_t) (fw_tstamp->hardware_counter - | |
529 | fw_tstamp->ring_buffer_counter); | |
530 | delay_frames = bytes_to_frames(substream->runtime, delay_bytes); | |
531 | buffer_sz = snd_pcm_lib_buffer_bytes(substream); | |
532 | div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes); | |
533 | pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes); | |
534 | ||
535 | dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes); | |
536 | ||
537 | info->buffer_ptr = pointer_samples / substream->runtime->channels; | |
538 | ||
ffb3722b | 539 | info->pcm_delay = delay_frames; |
cc547054 VK |
540 | dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n", |
541 | info->buffer_ptr, info->pcm_delay); | |
542 | return 0; | |
543 | } | |
544 | ||
545 | static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info) | |
546 | { | |
547 | struct stream_info *stream; | |
548 | struct snd_pcm_substream *substream; | |
549 | struct snd_sst_tstamp fw_tstamp; | |
550 | unsigned int str_id; | |
551 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
552 | ||
553 | str_id = info->str_id; | |
554 | stream = get_stream_info(ctx, str_id); | |
555 | if (!stream) | |
556 | return -EINVAL; | |
557 | ||
558 | if (!stream->pcm_substream) | |
559 | return -EINVAL; | |
560 | substream = stream->pcm_substream; | |
561 | ||
562 | memcpy_fromio(&fw_tstamp, | |
563 | ((void *)(ctx->mailbox + ctx->tstamp) | |
564 | + (str_id * sizeof(fw_tstamp))), | |
565 | sizeof(fw_tstamp)); | |
566 | return sst_calc_tstamp(ctx, info, substream, &fw_tstamp); | |
567 | } | |
568 | ||
569 | static int sst_stream_start(struct device *dev, int str_id) | |
570 | { | |
571 | struct stream_info *str_info; | |
572 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
573 | ||
574 | if (ctx->sst_state != SST_FW_RUNNING) | |
575 | return 0; | |
576 | str_info = get_stream_info(ctx, str_id); | |
577 | if (!str_info) | |
578 | return -EINVAL; | |
579 | str_info->prev = str_info->status; | |
580 | str_info->status = STREAM_RUNNING; | |
581 | sst_start_stream(ctx, str_id); | |
582 | ||
583 | return 0; | |
584 | } | |
585 | ||
586 | static int sst_stream_drop(struct device *dev, int str_id) | |
587 | { | |
588 | struct stream_info *str_info; | |
589 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
590 | ||
591 | if (ctx->sst_state != SST_FW_RUNNING) | |
592 | return 0; | |
593 | ||
594 | str_info = get_stream_info(ctx, str_id); | |
595 | if (!str_info) | |
596 | return -EINVAL; | |
597 | str_info->prev = STREAM_UN_INIT; | |
598 | str_info->status = STREAM_INIT; | |
599 | return sst_drop_stream(ctx, str_id); | |
600 | } | |
601 | ||
e0b87d47 VK |
602 | static int sst_stream_pause(struct device *dev, int str_id) |
603 | { | |
604 | struct stream_info *str_info; | |
605 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
606 | ||
607 | if (ctx->sst_state != SST_FW_RUNNING) | |
608 | return 0; | |
609 | ||
610 | str_info = get_stream_info(ctx, str_id); | |
611 | if (!str_info) | |
612 | return -EINVAL; | |
613 | ||
614 | return sst_pause_stream(ctx, str_id); | |
615 | } | |
616 | ||
617 | static int sst_stream_resume(struct device *dev, int str_id) | |
618 | { | |
619 | struct stream_info *str_info; | |
620 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
621 | ||
622 | if (ctx->sst_state != SST_FW_RUNNING) | |
623 | return 0; | |
624 | ||
625 | str_info = get_stream_info(ctx, str_id); | |
626 | if (!str_info) | |
627 | return -EINVAL; | |
628 | return sst_resume_stream(ctx, str_id); | |
629 | } | |
630 | ||
cc547054 VK |
631 | static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info) |
632 | { | |
633 | int str_id = 0; | |
634 | struct stream_info *stream; | |
635 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
636 | ||
637 | str_id = str_info->str_id; | |
638 | ||
639 | if (ctx->sst_state != SST_FW_RUNNING) | |
640 | return 0; | |
641 | ||
642 | stream = get_stream_info(ctx, str_id); | |
643 | if (!stream) | |
644 | return -EINVAL; | |
645 | ||
646 | dev_dbg(ctx->dev, "setting the period ptrs\n"); | |
647 | stream->pcm_substream = str_info->arg; | |
648 | stream->period_elapsed = str_info->period_elapsed; | |
649 | stream->sfreq = str_info->sfreq; | |
650 | stream->prev = stream->status; | |
651 | stream->status = STREAM_INIT; | |
652 | dev_dbg(ctx->dev, | |
653 | "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n", | |
654 | stream->pcm_substream, stream->period_elapsed, | |
655 | stream->sfreq, stream->status); | |
656 | ||
657 | return 0; | |
658 | } | |
659 | ||
660 | /* | |
661 | * sst_set_byte_stream - Set generic params | |
662 | * | |
663 | * @cmd: control cmd to be set | |
664 | * @arg: command argument | |
665 | * | |
666 | * This function is called by MID sound card driver to configure | |
667 | * SST runtime params. | |
668 | */ | |
669 | static int sst_send_byte_stream(struct device *dev, | |
670 | struct snd_sst_bytes_v2 *bytes) | |
671 | { | |
672 | int ret_val = 0; | |
673 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | |
674 | ||
675 | if (NULL == bytes) | |
676 | return -EINVAL; | |
677 | ret_val = pm_runtime_get_sync(ctx->dev); | |
e1d46d30 JM |
678 | if (ret_val < 0) { |
679 | pm_runtime_put_sync(ctx->dev); | |
cc547054 | 680 | return ret_val; |
e1d46d30 | 681 | } |
cc547054 VK |
682 | |
683 | ret_val = sst_send_byte_stream_mrfld(ctx, bytes); | |
684 | sst_pm_runtime_put(ctx); | |
685 | ||
686 | return ret_val; | |
687 | } | |
688 | ||
689 | static struct sst_ops pcm_ops = { | |
690 | .open = sst_open_pcm_stream, | |
691 | .stream_init = sst_stream_init, | |
692 | .stream_start = sst_stream_start, | |
693 | .stream_drop = sst_stream_drop, | |
e0b87d47 VK |
694 | .stream_pause = sst_stream_pause, |
695 | .stream_pause_release = sst_stream_resume, | |
cc547054 VK |
696 | .stream_read_tstamp = sst_read_timestamp, |
697 | .send_byte_stream = sst_send_byte_stream, | |
698 | .close = sst_close_pcm_stream, | |
699 | .power = sst_power_control, | |
700 | }; | |
701 | ||
7adab122 VK |
702 | static struct compress_sst_ops compr_ops = { |
703 | .open = sst_cdev_open, | |
704 | .close = sst_cdev_close, | |
705 | .stream_pause = sst_cdev_stream_pause, | |
706 | .stream_pause_release = sst_cdev_stream_pause_release, | |
707 | .stream_start = sst_cdev_stream_start, | |
708 | .stream_drop = sst_cdev_stream_drop, | |
709 | .stream_drain = sst_cdev_stream_drain, | |
710 | .stream_partial_drain = sst_cdev_stream_partial_drain, | |
711 | .tstamp = sst_cdev_tstamp, | |
712 | .ack = sst_cdev_ack, | |
713 | .get_caps = sst_cdev_caps, | |
714 | .get_codec_caps = sst_cdev_codec_caps, | |
715 | .set_metadata = sst_cdev_set_metadata, | |
716 | .power = sst_power_control, | |
717 | }; | |
718 | ||
cc547054 VK |
719 | static struct sst_device sst_dsp_device = { |
720 | .name = "Intel(R) SST LPE", | |
721 | .dev = NULL, | |
722 | .ops = &pcm_ops, | |
7adab122 | 723 | .compr_ops = &compr_ops, |
cc547054 VK |
724 | }; |
725 | ||
726 | /* | |
727 | * sst_register - function to register DSP | |
728 | * | |
729 | * This functions registers DSP with the platform driver | |
730 | */ | |
731 | int sst_register(struct device *dev) | |
732 | { | |
733 | int ret_val; | |
734 | ||
735 | sst_dsp_device.dev = dev; | |
736 | ret_val = sst_register_dsp(&sst_dsp_device); | |
737 | if (ret_val) | |
738 | dev_err(dev, "Unable to register DSP with platform driver\n"); | |
739 | ||
740 | return ret_val; | |
741 | } | |
742 | ||
743 | int sst_unregister(struct device *dev) | |
744 | { | |
745 | return sst_unregister_dsp(&sst_dsp_device); | |
746 | } |