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